From 36588f9275b87ba8d5b83380d2ba8df91b814b74 Mon Sep 17 00:00:00 2001 From: Andi Nitshce Date: Sat, 16 Apr 2016 09:03:16 +0200 Subject: [PATCH 001/695] 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 d4b97724e220e8cc1e796ae8b13d8aaa179aa99b Mon Sep 17 00:00:00 2001 From: Tomas Mudrunka Date: Fri, 29 Jul 2016 16:10:19 +0200 Subject: [PATCH 002/695] added graph for netfilter ipset --- plugins/network/ipset | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 plugins/network/ipset diff --git a/plugins/network/ipset b/plugins/network/ipset new file mode 100644 index 00000000..832020bb --- /dev/null +++ b/plugins/network/ipset @@ -0,0 +1,25 @@ +#!/bin/bash +#Graph number of members of netfilter ipsets +#(c) Tomas Mudrunka 2016 +# +#Add this line to sudoers: +#ALL ALL = (root) NOPASSWD: /sbin/ipset list [!-]*, /sbin/ipset list -n + + +[ "$1" = "config" ] && { + echo graph_title Netfilter IPSets + echo graph_category network + echo graph_vlabel Members + echo graph_args --base 1000 --logarithmic --units=si +} + +sudo ipset list -n | while read list; do + [ "$1" = "config" ] && { + echo "$list.label $list" + echo "$list.min 0" + } || { + echo "$list.value $(( $(sudo ipset list $list | wc -l) - 7 ))" + } +done; + +exit 0 From 717f083de7624927c15aaeb4190471e51543c2db Mon Sep 17 00:00:00 2001 From: Tomas Mudrunka Date: Mon, 1 Aug 2016 16:58:20 +0200 Subject: [PATCH 003/695] autodetect ipset --- plugins/network/ipset | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/network/ipset b/plugins/network/ipset index 832020bb..d931deed 100644 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -4,7 +4,14 @@ # #Add this line to sudoers: #ALL ALL = (root) NOPASSWD: /sbin/ipset list [!-]*, /sbin/ipset list -n +# +#%# family=auto +#%# capabilities=autoconf +[ "$1" = "autoconf" ] && { + [ -e /sbin/ipset -o -n "$(which ipset)" ] && echo 'yes' || echo 'no (ipset binary not present)' + exit 0 +} [ "$1" = "config" ] && { echo graph_title Netfilter IPSets From 04089d03954ea2dd968b04f9421215dc0f52ef5a Mon Sep 17 00:00:00 2001 From: webstoney Date: Thu, 27 Oct 2016 15:57:50 +0200 Subject: [PATCH 004/695] w1_ plugin Plugin to graph the output of 1-Wire DS1820 temperature sensors on the Raspberry PI --- plugins/raspberry-pi/w1_ | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 plugins/raspberry-pi/w1_ diff --git a/plugins/raspberry-pi/w1_ b/plugins/raspberry-pi/w1_ new file mode 100644 index 00000000..60637cba --- /dev/null +++ b/plugins/raspberry-pi/w1_ @@ -0,0 +1,84 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +w1_ - Plugin to monitor 1-wire temperature sensors (DS1820) + +=head1 CONFIGURATION + +The following environment variables are used by this plugin + + sensorid - Sensor to check. By default taken from command name. + t_warn - Warning limit for nagios notification + t_crit - Critical limit for nagios notification + + +=head1 AUTHOR + +Copyright (C) 2016 Roland Steinbach + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 dated June, 1991. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest +=cut + +. $MUNIN_LIBDIR/plugins/plugin.sh + +case $0 in + *w1_*) + sensor_id=${0##*/w1_} + ;; +esac + +if [ "$1" = "suggest" ]; then + if [ -r /sys/bus/w1/devices ]; then + ls /sys/bus/w1/devices|head -n -1 + fi + exit 0 +fi + +if [ "$1" = "autoconf" ]; then + if [ -r /sys/bus/w1/devices ]; then + echo yes + exit 0 + else + echo "no (/sys/bus/w1/devices not found)" + exit 0 + fi +fi + +if [ "$1" = "config" ]; then + echo graph_title Temperature Sensor $sensor_id + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel temperature (°C)' + echo 'graph_category temperature' + echo 'graph_info This graph shows 1-wire sensor temperature.' + echo "w1.label $sensor_id" + echo "w1.info Temperature at $sensor_id." + print_warning w1 + print_critical w1 + exit 0 +fi + + +sed -n '/t=/ s/.*t=//p' /sys/bus/w1/devices/$sensor_id/w1_slave | perl -e '$l=;print "w1.value ", $l/1000 , "\n";' From 26ba6a3df5847f3f82cabc4a144b28dc6226eabd Mon Sep 17 00:00:00 2001 From: Evgeniy K Date: Sun, 11 Dec 2016 22:50:46 +0300 Subject: [PATCH 005/695] mysql_connections_per_user: replace non-alphanumeric in field names plus print real username in label --- plugins/mysql/mysql_connections_per_user | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/mysql/mysql_connections_per_user b/plugins/mysql/mysql_connections_per_user index 28552d4b..9b4a96e7 100755 --- a/plugins/mysql/mysql_connections_per_user +++ b/plugins/mysql/mysql_connections_per_user @@ -100,6 +100,9 @@ sub print_graph_data() { if($print_user eq "root") { $print_user = "root_"; } + if ( $print_user =~ /[^A-Za-z0-9]/ ) { + $print_user =~ s/[^A-Za-z0-9]/_/; + } print "$print_user.value $counts{$user}\n"; } my $other = $numthreads - $total; @@ -137,9 +140,13 @@ EOM if($print_user eq "root") { $print_user = "root_"; } + if ( $print_user =~ /[^A-Za-z0-9]/ ) { + $print_user =~ s/[^A-Za-z0-9]/_/; + } + print < Date: Sun, 18 Dec 2016 21:26:37 +0100 Subject: [PATCH 006/695] p/jmx: extract the source from the jar --- plugins/java/jmx/plugin/.classpath | 6 + plugins/java/jmx/plugin/.gitignore | 3 + plugins/java/jmx/plugin/.project | 17 ++ plugins/java/jmx/plugin/META-INF/MANIFEST.MF | 3 + plugins/java/jmx/plugin/jmxquery.jar | Bin 8977 -> 0 bytes .../plugin/src/org/munin/Configuration.java | 148 ++++++++++++ .../jmx/plugin/src/org/munin/JMXQuery.java | 228 ++++++++++++++++++ 7 files changed, 405 insertions(+) create mode 100644 plugins/java/jmx/plugin/.classpath create mode 100644 plugins/java/jmx/plugin/.gitignore create mode 100644 plugins/java/jmx/plugin/.project create mode 100644 plugins/java/jmx/plugin/META-INF/MANIFEST.MF delete mode 100644 plugins/java/jmx/plugin/jmxquery.jar create mode 100644 plugins/java/jmx/plugin/src/org/munin/Configuration.java create mode 100644 plugins/java/jmx/plugin/src/org/munin/JMXQuery.java diff --git a/plugins/java/jmx/plugin/.classpath b/plugins/java/jmx/plugin/.classpath new file mode 100644 index 00000000..fb501163 --- /dev/null +++ b/plugins/java/jmx/plugin/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/java/jmx/plugin/.gitignore b/plugins/java/jmx/plugin/.gitignore new file mode 100644 index 00000000..f00d6296 --- /dev/null +++ b/plugins/java/jmx/plugin/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/.settings/ +*.class diff --git a/plugins/java/jmx/plugin/.project b/plugins/java/jmx/plugin/.project new file mode 100644 index 00000000..8fe5521c --- /dev/null +++ b/plugins/java/jmx/plugin/.project @@ -0,0 +1,17 @@ + + + plugin + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/plugins/java/jmx/plugin/META-INF/MANIFEST.MF b/plugins/java/jmx/plugin/META-INF/MANIFEST.MF new file mode 100644 index 00000000..bd17a1b5 --- /dev/null +++ b/plugins/java/jmx/plugin/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.1 +Created-By: 1.6.0_22-b04-307-10M3261 (Apple Inc.) diff --git a/plugins/java/jmx/plugin/jmxquery.jar b/plugins/java/jmx/plugin/jmxquery.jar deleted file mode 100644 index 5c37d4bdec25e453d0891a5d12d899f30cecd157..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8977 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 fieldMap = new HashMap(); + private List fields = new ArrayList(); + + public class FieldProperties extends Properties { + private static final long serialVersionUID = 1L; + private ObjectName jmxObjectName; + private String jmxAttributeName; + private String jmxAttributeKey; + private String fieldname; + private static final String JMXOBJECT = "jmxObjectName"; + private static final String JMXATTRIBUTE = "jmxAttributeName"; + private static final String JMXATTRIBUTEKEY = "jmxAttributeKey"; + + public FieldProperties(Configuration paramConfiguration, + String fieldname) { + this.fieldname = fieldname; + } + + public String getJmxAttributeKey() { + return jmxAttributeKey; + } + + public String getJmxAttributeName() { + return jmxAttributeName; + } + + public ObjectName getJmxObjectName() { + return jmxObjectName; + } + + public String toString() { + return fieldname; + } + + public void set(String key, String value) + throws MalformedObjectNameException, NullPointerException { + if ("jmxObjectName".equals(key)) { + if (jmxObjectName != null) + throw new IllegalStateException( + "jmxObjectName already set for " + this); + jmxObjectName = new ObjectName(value); + } else if ("jmxAttributeName".equals(key)) { + if (jmxAttributeName != null) + throw new IllegalStateException( + "jmxAttributeName already set for " + this); + jmxAttributeName = value; + } else if ("jmxAttributeKey".equals(key)) { + if (jmxAttributeKey != null) + throw new IllegalStateException( + "jmxAttributeKey already set for " + this); + jmxAttributeKey = value; + } else { + put(key, value); + } + } + + public void report(PrintStream out) { + for (Iterator it = entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + out.println(fieldname + '.' + entry.getKey() + " " + + entry.getValue()); + } + } + + public String getFieldname() { + return fieldname; + } + } + + public static Configuration parse(String config_file) throws IOException, + MalformedObjectNameException, NullPointerException { + BufferedReader reader = new BufferedReader(new FileReader(config_file)); + Configuration configuration = new Configuration(); + try { + for (;;) { + String s = reader.readLine(); + if (s == null) + break; + if ((!s.startsWith("%")) && (s.length() > 5) + && (!s.startsWith(" "))) { + configuration.parseString(s); + } + } + } finally { + reader.close(); + } + + return configuration; + } + + private void parseString(String s) throws MalformedObjectNameException, + NullPointerException { + String[] nameval = s.split(" ", 2); + if (nameval[0].indexOf('.') > 0) { + String name = nameval[0]; + String fieldname = name.substring(0, name.lastIndexOf('.')); + if (!fieldMap.containsKey(fieldname)) { + Configuration.FieldProperties field = new Configuration.FieldProperties( + this, fieldname); + fieldMap.put(fieldname, field); + fields.add(field); + } + Configuration.FieldProperties field = (Configuration.FieldProperties) fieldMap + .get(fieldname); + String key = name.substring(name.lastIndexOf('.') + 1); + field.set(key, nameval[1]); + } else { + graph_properties.put(nameval[0], nameval[1]); + } + } + + public Properties getGraphProperties() { + return graph_properties; + } + + public void report(PrintStream out) { + for (Iterator it = graph_properties.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + out.println(entry.getKey() + " " + entry.getValue()); + } + + for (Configuration.FieldProperties field : fields) { + field.report(out); + } + } + + public List getFields() { + return fields; + } +} diff --git a/plugins/java/jmx/plugin/src/org/munin/JMXQuery.java b/plugins/java/jmx/plugin/src/org/munin/JMXQuery.java new file mode 100644 index 00000000..0ac91905 --- /dev/null +++ b/plugins/java/jmx/plugin/src/org/munin/JMXQuery.java @@ -0,0 +1,228 @@ +package org.munin; + +import java.io.IOException; +import java.text.NumberFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +public class JMXQuery { + private static final String USERNAME_KEY = "username"; + private static final String PASSWORD_KEY = "password"; + public static final String USAGE = "Usage of program is:\njava -cp jmxquery.jar org.munin.JMXQuery --url= [--user= --pass=] [--conf= [config]]\n, where is a JMX URL, for example: service:jmx:rmi:///jndi/rmi://HOST:PORT/jmxrmi\nWhen invoked with the config file (see examples folder) - operates as Munin plugin with the provided configuration\nWithout options just fetches all JMX attributes using provided URL"; + private String url; + private String username; + private String password; + private JMXConnector connector; + private MBeanServerConnection connection; + private Configuration config; + + public JMXQuery(String url) { + this(url, null, null); + } + + public JMXQuery(String url, String username, String password) { + this.url = url; + this.username = username; + this.password = password; + } + + private void connect() throws IOException { + Map environment = null; + if ((username != null) && (password != null)) { + environment = new HashMap(); + + environment.put("jmx.remote.credentials", new String[] { username, + password }); + environment.put("username", username); + environment.put("password", password); + } + + JMXServiceURL jmxUrl = new JMXServiceURL(url); + connector = JMXConnectorFactory.connect(jmxUrl, environment); + connection = connector.getMBeanServerConnection(); + } + + private void list() throws IOException, InstanceNotFoundException, + IntrospectionException, ReflectionException { + if (config == null) { + listAll(); + } else { + listConfig(); + } + } + + private void listConfig() { + for (Configuration.FieldProperties field : config.getFields()) { + try { + Object value = connection.getAttribute( + field.getJmxObjectName(), field.getJmxAttributeName()); + output(field.getFieldname(), value, field.getJmxAttributeKey()); + } catch (Exception e) { + System.err.println("Fail to output " + field); + e.printStackTrace(); + } + } + } + + private void output(String name, Object attr, String key) { + if ((attr instanceof CompositeDataSupport)) { + CompositeDataSupport cds = (CompositeDataSupport) attr; + if (key == null) { + throw new IllegalArgumentException( + "Key is null for composed data " + name); + } + System.out.println(name + ".value " + format(cds.get(key))); + } else { + System.out.println(name + ".value " + format(attr)); + } + } + + private void output(String name, Object attr) { + CompositeDataSupport cds; + Iterator it; + if ((attr instanceof CompositeDataSupport)) { + cds = (CompositeDataSupport) attr; + for (it = cds.getCompositeType().keySet().iterator(); it.hasNext();) { + String key = it.next().toString(); + System.out.println(name + "." + key + ".value " + + format(cds.get(key))); + } + } else { + System.out.println(name + ".value " + format(attr)); + } + } + + private void listAll() throws IOException, InstanceNotFoundException, + IntrospectionException, ReflectionException { + Set mbeans = connection.queryNames(null, null); + for (ObjectName name : mbeans) { + MBeanInfo info = connection.getMBeanInfo(name); + MBeanAttributeInfo[] attrs = info.getAttributes(); + String[] attrNames = new String[attrs.length]; + for (int i = 0; i < attrs.length; i++) { + attrNames[i] = attrs[i].getName(); + } + try { + AttributeList attributes = connection.getAttributes(name, + attrNames); + for (Attribute attribute : attributes.asList()) { + output(name.getCanonicalName() + "%" + attribute.getName(), + attribute.getValue()); + } + } catch (Exception e) { + System.err.println("error getting " + name + ":" + + e.getMessage()); + } + } + } + + private String format(Object value) { + if (value == null) + return null; + if ((value instanceof String)) + return (String) value; + if ((value instanceof Number)) { + NumberFormat f = NumberFormat.getInstance(); + f.setMaximumFractionDigits(2); + f.setGroupingUsed(false); + return f.format(value); + } + if ((value instanceof Object[])) { + return Integer.toString(Arrays.asList((Object[]) value).size()); + } + return value.toString(); + } + + private void disconnect() throws IOException { + connector.close(); + } + + public static void main(String[] args) { + int arglen = args.length; + if (arglen < 1) { + System.err + .println("Usage of program is:\njava -cp jmxquery.jar org.munin.JMXQuery --url= [--user= --pass=] [--conf= [config]]\n, where is a JMX URL, for example: service:jmx:rmi:///jndi/rmi://HOST:PORT/jmxrmi\nWhen invoked with the config file (see examples folder) - operates as Munin plugin with the provided configuration\nWithout options just fetches all JMX attributes using provided URL"); + System.exit(1); + } + + String url = null; + String user = null; + String pass = null; + String config_file = null; + boolean toconfig = false; + for (int i = 0; i < arglen; i++) { + if (args[i].startsWith("--url=")) { + url = args[i].substring(6); + } else if (args[i].startsWith("--user=")) { + user = args[i].substring(7); + } else if (args[i].startsWith("--pass=")) { + pass = args[i].substring(7); + } else if (args[i].startsWith("--conf=")) { + config_file = args[i].substring(7); + } else if (args[i].equals("config")) { + toconfig = true; + } + } + + if ((url == null) || ((user != null) && (pass == null)) + || ((user == null) && (pass != null)) + || ((config_file == null) && (toconfig))) { + System.err + .println("Usage of program is:\njava -cp jmxquery.jar org.munin.JMXQuery --url= [--user= --pass=] [--conf= [config]]\n, where is a JMX URL, for example: service:jmx:rmi:///jndi/rmi://HOST:PORT/jmxrmi\nWhen invoked with the config file (see examples folder) - operates as Munin plugin with the provided configuration\nWithout options just fetches all JMX attributes using provided URL"); + System.exit(1); + } + + if (toconfig) { + try { + Configuration.parse(config_file).report(System.out); + } catch (Exception e) { + System.err.println(e.getMessage() + " reading " + config_file); + System.exit(1); + } + } else { + JMXQuery query = new JMXQuery(url, user, pass); + try { + query.connect(); + if (config_file != null) { + query.setConfig(Configuration.parse(config_file)); + } + query.list(); + } catch (Exception ex) { + System.err.println(ex.getMessage() + " querying " + url); + ex.printStackTrace(); + System.exit(1); + } finally { + try { + query.disconnect(); + } catch (IOException e) { + System.err.println(e.getMessage() + " closing " + url); + } + } + } + } + + private void setConfig(Configuration configuration) { + config = configuration; + } + + public Configuration getConfig() { + return config; + } +} From 5dc3787f00676bba8f521dbf3fd228f38c6f7445 Mon Sep 17 00:00:00 2001 From: Ken-ichi Mito Date: Wed, 8 May 2013 23:51:44 +0900 Subject: [PATCH 007/695] add new option 'env.stack_usage' to lxc_ram * draw memory usages of containers as stacked area charts * the type of other graphs are not changed (default -> LINE2) --- plugins/lxc/lxc_ram | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index db770778..004f829a 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -12,10 +12,18 @@ lxc_ram - Plugin to monitor LXC memory usage. [lxc_*] user root + [lxc_ram] + env.stack_usage true + =head1 INTERPRETATION This plugin needs root privilege. +If env.stack_usage is set to true, all memory usages of containers will be + drawn as stacked area charts. +This option changes graph order, all of 'Mem usage' comes first and then others. +(default: empty) + =head1 AUTHOR vajtsz vajtsz@gmail.com @@ -62,13 +70,24 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel byte' echo 'graph_category memory' + if [ "$stack_usage" = "true" ]; then + for guest_name in $guest_names; do + guest="$(clean_fieldname $guest_name)" + + echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' + echo 'mem_usage_'$guest'.type GAUGE' + echo 'mem_usage_'$guest'.draw AREASTACK' + done + fi for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" - echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' - echo 'mem_usage_'$guest'.type GAUGE' + if [ "$stack_usage" != "true" ]; then + echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' + echo 'mem_usage_'$guest'.type GAUGE' + fi echo 'mem_cache_'$guest'.label '$guest_name': Cache' echo 'mem_cache_'$guest'.type GAUGE' echo 'mem_active_'$guest'.label '$guest_name': Active' From 1c917e64b084eff773a808220239e4972035fa0b Mon Sep 17 00:00:00 2001 From: Adrian Moisey Date: Tue, 8 Jul 2014 11:41:35 +0200 Subject: [PATCH 008/695] Add support for Ubuntu 14.04 --- plugins/lxc/lxc_proc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lxc/lxc_proc b/plugins/lxc/lxc_proc index c2e3b02d..d1aa7a9a 100755 --- a/plugins/lxc/lxc_proc +++ b/plugins/lxc/lxc_proc @@ -41,6 +41,7 @@ Unknown license # Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks # Ubuntu 12.04 with cgroup-lite: /sys/fs/cgroup/cpuacct/lxc//tasks # Ubuntu 12.04 with cgroup-bin: /sys/fs/cgroup/cpuacct/sysdefault/lxc//tasks +# Ubuntu 14.04: /sys/fs/cgroup/systemd/lxc//tasks count_processes () { [ -z "$1" ] && return 0 @@ -54,6 +55,7 @@ count_processes () { for SYSFS in \ /sys/fs/cgroup/$1/tasks \ /sys/fs/cgroup/lxc/$1/tasks \ + /sys/fs/cgroup/systemd/lxc/$1/tasks \ /sys/fs/cgroup/cpuacct/lxc/$1/tasks \ /sys/fs/cgroup/cpuacct/sysdefault/lxc/$1/tasks \ ; do From 58e5b2f32ca05f5e9410dd5a64998ef72ba0bdeb Mon Sep 17 00:00:00 2001 From: Ken-ichi Mito Date: Sun, 31 Jul 2016 21:42:32 +0900 Subject: [PATCH 009/695] rename option to 'env.areastack' --- plugins/lxc/lxc_ram | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 004f829a..c0437341 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -13,16 +13,16 @@ lxc_ram - Plugin to monitor LXC memory usage. user root [lxc_ram] - env.stack_usage true + env.areastack true =head1 INTERPRETATION This plugin needs root privilege. -If env.stack_usage is set to true, all memory usages of containers will be +If env.areastack is set to true, all memory usages of containers will be drawn as stacked area charts. This option changes graph order, all of 'Mem usage' comes first and then others. -(default: empty) +(default: false) =head1 AUTHOR @@ -70,7 +70,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel byte' echo 'graph_category memory' - if [ "$stack_usage" = "true" ]; then + if [ "$areastack" = "true" ]; then for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" @@ -84,7 +84,7 @@ if [ "$1" = "config" ]; then do guest="$(clean_fieldname $guest_name)" - if [ "$stack_usage" != "true" ]; then + if [ "$areastack" != "true" ]; then echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' echo 'mem_usage_'$guest'.type GAUGE' fi From 1c412d1907b4cbaa6645aa08f5af073dbffb59b3 Mon Sep 17 00:00:00 2001 From: Ken-ichi Mito Date: Sun, 5 Mar 2017 00:19:58 +0900 Subject: [PATCH 010/695] refactor: code of areastack option is hard to understand - reviewed on https://github.com/munin-monitoring/contrib/pull/738#pullrequestreview-5187474 - split two conditional branches to simple if-else code --- plugins/lxc/lxc_ram | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index c0437341..4208f875 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -78,16 +78,10 @@ if [ "$1" = "config" ]; then echo 'mem_usage_'$guest'.type GAUGE' echo 'mem_usage_'$guest'.draw AREASTACK' done - fi + + for guest_name in $guest_names; do + guest="$(clean_fieldname $guest_name)" - for guest_name in $guest_names; - do - guest="$(clean_fieldname $guest_name)" - - if [ "$areastack" != "true" ]; then - echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' - echo 'mem_usage_'$guest'.type GAUGE' - fi echo 'mem_cache_'$guest'.label '$guest_name': Cache' echo 'mem_cache_'$guest'.type GAUGE' echo 'mem_active_'$guest'.label '$guest_name': Active' @@ -96,6 +90,22 @@ if [ "$1" = "config" ]; then echo 'mem_inactive_'$guest'.type GAUGE' done + else + for guest_name in $guest_names; + do + guest="$(clean_fieldname $guest_name)" + + echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' + echo 'mem_usage_'$guest'.type GAUGE' + echo 'mem_cache_'$guest'.label '$guest_name': Cache' + echo 'mem_cache_'$guest'.type GAUGE' + echo 'mem_active_'$guest'.label '$guest_name': Active' + echo 'mem_active_'$guest'.type GAUGE' + echo 'mem_inactive_'$guest'.label '$guest_name': Inactive' + echo 'mem_inactive_'$guest'.type GAUGE' + + done + fi exit 0 fi From 8893cfa8a172ede6ad93d792bc62bf1790810f3c Mon Sep 17 00:00:00 2001 From: kaliko Date: Mon, 29 May 2017 13:50:38 +0200 Subject: [PATCH 011/695] Add missing apiid in URL --- plugins/weather/openweather_ | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index 45b9610d..cce41ab5 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -16,9 +16,10 @@ ## From Oct 9 2015 OpenWeather needs you to register and get an APIKEY # include this key by setting 'env.apikey' in munin plugin config, i.e.: # [openweather_*] -# env.apikey XYZ +# env.owapikey XYZ query_string=$(printf '%s' "${0#*_}" | tr '_' '=') +query_string="$query_string&appid=$owapikey" plugin_name=$( basename $0 ) OWAPI=$( curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}") From 7e0a33e40f79eef844903c848550bf12dda11911 Mon Sep 17 00:00:00 2001 From: kaliko Date: Tue, 30 May 2017 12:38:45 +0200 Subject: [PATCH 012/695] Switch to metric system, get rid of kelvin offset Improved readability Revert env. var. name change --- plugins/weather/openweather_ | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index cce41ab5..1aa3b7e4 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -16,16 +16,13 @@ ## From Oct 9 2015 OpenWeather needs you to register and get an APIKEY # include this key by setting 'env.apikey' in munin plugin config, i.e.: # [openweather_*] -# env.owapikey XYZ +# env.apikey XYZ -query_string=$(printf '%s' "${0#*_}" | tr '_' '=') -query_string="$query_string&appid=$owapikey" +location=$(printf '%s' "${0#*_}" | tr '_' '=') +query_string="${location}&appid=${apikey}&units=metric" plugin_name=$( basename $0 ) OWAPI=$( curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}") -# API returns temp in K, we have to convert it in C -# &units=metric would change that ;-) -KELVIN_BIAS=273 CITY=$( expr "$OWAPI" : '.*\ Date: Tue, 30 May 2017 21:16:43 +0200 Subject: [PATCH 013/695] Set upper/lower limit for humidity and wind direction --- plugins/weather/openweather_ | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index 1aa3b7e4..1eef9bd8 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -49,6 +49,7 @@ then graph_title Humidity in ${CITY} graph_vlabel % graph_category sensors + graph_args --upper-limit 100 --lower-limit 0 graph_info This graph show the humidity in ${CITY} humidity.label humidity @@ -70,8 +71,10 @@ then graph_title Wind direction in ${CITY} graph_vlabel direction graph_category sensors + graph_args --upper-limit 360 --lower-limit 0 graph_info This graph show the wind direction in ${CITY} direction.label wind direction + EOF # Continue if dirty config is enabled From 782c8138870f316c2af20e92a70b0b53ce8dab52 Mon Sep 17 00:00:00 2001 From: kaliko Date: Tue, 30 May 2017 21:18:32 +0200 Subject: [PATCH 014/695] Avoid same multigraph and field name (prevent hitting munin-monitoring/munin#828) --- plugins/weather/openweather_ | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index 1eef9bd8..1c84d826 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -51,14 +51,14 @@ then graph_category sensors graph_args --upper-limit 100 --lower-limit 0 graph_info This graph show the humidity in ${CITY} - humidity.label humidity + humi.label humidity multigraph $plugin_name.pressure graph_title Pressure in ${CITY} graph_vlabel hPa graph_category sensors graph_info This graph show the pressure in ${CITY} - pressure.label pressure + press.label pressure multigraph $plugin_name.wind_speed graph_title Wind Speed in ${CITY} @@ -101,10 +101,10 @@ cat <<- EOF temp_max.value $TEMP_MAX multigraph $plugin_name.humidity - humidity.value $HUMIDITY + humi.value $HUMIDITY multigraph $plugin_name.pressure - pressure.value $PRESSURE + press.value $PRESSURE multigraph $plugin_name.wind_speed speed.value $WD_SPEED From 32d620ccf3a3f87ec8c6a15c0a59bac2dcb9ec7c Mon Sep 17 00:00:00 2001 From: kaliko Date: Wed, 31 May 2017 11:25:16 +0200 Subject: [PATCH 015/695] Revert switch to metric system 7e0a33e4 --- plugins/weather/openweather_ | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index 1c84d826..ea232028 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -19,10 +19,11 @@ # env.apikey XYZ location=$(printf '%s' "${0#*_}" | tr '_' '=') -query_string="${location}&appid=${apikey}&units=metric" +query_string="${location}&appid=${apikey}" plugin_name=$( basename $0 ) OWAPI=$( curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}") +KELVIN_BIAS=273 CITY=$( expr "$OWAPI" : '.*\ Date: Thu, 20 Jul 2017 18:13:11 +0200 Subject: [PATCH 016/695] Allow to specify database password --- plugins/postgresql/postgresql_tablespace_size | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/postgresql/postgresql_tablespace_size b/plugins/postgresql/postgresql_tablespace_size index cae81458..044969ce 100755 --- a/plugins/postgresql/postgresql_tablespace_size +++ b/plugins/postgresql/postgresql_tablespace_size @@ -23,6 +23,7 @@ dbserver='localhost' dbuser='postgres' +dbpass='' if [ "$1" = "config" ]; then echo 'graph_args --base 1024 --lower-limit 0' @@ -31,7 +32,7 @@ if [ "$1" = "config" ]; then echo 'graph_title PostgreSQL Tablespace Sizes' echo 'graph_vlabel Size (bytes)' - psql -h ${dbserver} -U ${dbuser} -tc "SELECT spcname FROM pg_tablespace ORDER BY 1;" | while read name + PGPASSWORD="${dbpass}" psql -h ${dbserver} -U ${dbuser} -tc "SELECT spcname FROM pg_tablespace ORDER BY 1;" | while read name do test -z "${name}" && continue echo ${name}'.label '${name} @@ -47,7 +48,7 @@ if [ "$1" = "config" ]; then exit 0 fi -psql -h ${dbserver} -U ${dbuser} -tc "SELECT spcname, PG_TABLESPACE_SIZE(oid) FROM pg_tablespace ORDER BY 1;" | while read name sep num +PGPASSWORD="${dbpass}" psql -h ${dbserver} -U ${dbuser} -tc "SELECT spcname, PG_TABLESPACE_SIZE(oid) FROM pg_tablespace ORDER BY 1;" | while read name sep num do test -z "${name}" && continue echo ${name}'.value '${num} From a8d117a3f094c1766824299f547d71975bc4c4b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 11 Sep 2017 00:51:49 +0200 Subject: [PATCH 017/695] olsrd: adjustments for the output format used by olsrd v0.9.5 Changes include: * use short names for "urls" (e.g. "lin" instead of "links") * whitespace instead of semicolon used as separators in "mid" * ignore variable number of header lines --- plugins/network/olsrd | 54 +++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/plugins/network/olsrd b/plugins/network/olsrd index 5fcfedba..97daadf2 100755 --- a/plugins/network/olsrd +++ b/plugins/network/olsrd @@ -140,16 +140,21 @@ def query_olsrd_txtservice(section=""): 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 + in_header = True + in_body_count = 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 + if in_header: + if not line.strip(): + # the empty line marks the end of the header + in_header = False + # ignore header lines (nothing to be done) + else: + # skip the first two body lines - they are table headers + if in_body_count >= 2: + line = line.strip() + if line: + yield line + in_body_count += 1 fconn.close() conn.close() @@ -159,15 +164,17 @@ def get_address_device_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(";"): + # since olsr v0.9.5: + # 192.168.2.171 192.168.22.171 192.168.12.171 + device_id, mids = line.split(None, 1) + for mid in mids.replace(";", " ").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"): + for line in query_olsrd_txtservice("rou"): # example line content: # 192.168.1.79/32 192.168.12.38 4 4.008 wlan0 tokens = line.split() @@ -192,8 +199,12 @@ def get_olsr_links(): 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"): + for line in query_olsrd_txtservice("lin"): tokens = line.split() + # the "cost" may be infinite + if tokens[-1] == "INFINITE": + # "inf" is the python keyword for "maximum float number" + tokens[-1] = "inf" link = {} link["local"] = tokens.pop(0) remote = tokens.pop(0) @@ -217,7 +228,7 @@ def _read_file(filename): 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}'\ + command = 'for host in {hosts}; do echo -n "$host "; ping -c 1 -w 1 "$host" | grep /avg/ || echo; 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) @@ -331,17 +342,16 @@ if __name__ == "__main__": 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"]))) + value = "{:.4f}".format(ping_time) if ping_time is not None else "U" + print("neighbour_{remote}.value {value}" + .format(value=value, 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)) + value = "{:.4f}".format(ping_time) if ping_time is not None else "U" + remote = get_clean_fieldname(link["remote"]) + print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) + print("neighbour_{remote}.value {value}".format(remote=remote, value=value)) # final marker for shell / python hybrid script (see "Interpreter Selection") EOF = True From 608a2a5ad7068f9bc2e392dac87ea3c9eb0ce090 Mon Sep 17 00:00:00 2001 From: Wouter Verhelst Date: Mon, 18 Sep 2017 13:56:29 +0200 Subject: [PATCH 018/695] Allow for _ in original dataset name The current code escapes / (which is legal as part of a dataset name for ZFS, but illegal as part of a munin graph name) into _ (which is legal for both). The result is that any _ characters in the original filename get changed into / in the legend, which is confusing. Fix by escaping any existing _ characters into __ first, and then later replacing // into _ again. // is a sequence that will not occur in a ZFS dataset name, so any double-/ sequence must therefore have been a _ originally. --- plugins/zfs/zfs_usage_ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_ index 07187ee3..fdd5b3a9 100755 --- a/plugins/zfs/zfs_usage_ +++ b/plugins/zfs/zfs_usage_ @@ -160,6 +160,7 @@ sub do_collect { foreach my $line (split(/\n/, `$fsget` )) { my ($name, $key, $value, undef ) = (split(/\t/,$line)); + ($name =~ s/_/__/g); ($name =~ s/\//_/g); $filesystems->{$name}->{$key}=$value; } @@ -170,6 +171,7 @@ sub do_config_fs { my ($fs) = @_; my $fs_slash = ($fs); ($fs_slash =~ s/_/\//g); + ($fs_slash =~ s/\/\//_/g); if ( $fs ne $zpool ) { printf( "multigraph zfs_usage_%s.%s\n", From 87d30e559764675862de95546986ed483c19e420 Mon Sep 17 00:00:00 2001 From: Bas Couwenberg Date: Thu, 28 Sep 2017 09:54:22 +0200 Subject: [PATCH 019/695] Use pdns_control utility instead of pdns init script. The init script is not usable on systems using systemd (e.g. Debian stretch). --- plugins/pdns/pdns_errors | 11 ++++++++--- plugins/pdns/pdns_latency | 13 +++++++++---- plugins/pdns/pdns_qsize | 13 +++++++++---- plugins/pdns/pdns_queries | 11 ++++++++--- plugins/pdns/pdns_rel | 31 +++++++++++++++++++------------ 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/plugins/pdns/pdns_errors b/plugins/pdns/pdns_errors index 82b0ffcb..589d0c3a 100755 --- a/plugins/pdns/pdns_errors +++ b/plugins/pdns/pdns_errors @@ -9,11 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns dump" +command="/usr/bin/pdns_control list" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e /usr/bin/pdns_control ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi if [ "$1" = "config" ]; then diff --git a/plugins/pdns/pdns_latency b/plugins/pdns/pdns_latency index 5178dfd6..907785ca 100755 --- a/plugins/pdns/pdns_latency +++ b/plugins/pdns/pdns_latency @@ -9,11 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns show" +command="/usr/bin/pdns_control show" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e /usr/bin/pdns_control ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi if [ "$1" = "config" ]; then @@ -31,4 +36,4 @@ fi -echo "latency.value $($command latency | awk -F= '{print $2}')" +echo "latency.value $($command latency)" diff --git a/plugins/pdns/pdns_qsize b/plugins/pdns/pdns_qsize index 5256e8e6..19e1941b 100755 --- a/plugins/pdns/pdns_qsize +++ b/plugins/pdns/pdns_qsize @@ -9,11 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns show" +command="/usr/bin/pdns_control show" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e /usr/bin/pdns_control ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi if [ "$1" = "config" ]; then @@ -31,5 +36,5 @@ fi -echo "qsize.value $($command qsize_q | awk -F= '{print $2}')" +echo "qsize.value $($command qsize-q)" diff --git a/plugins/pdns/pdns_queries b/plugins/pdns/pdns_queries index 82f85477..87cf44e2 100755 --- a/plugins/pdns/pdns_queries +++ b/plugins/pdns/pdns_queries @@ -9,11 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns dump" +command="/usr/bin/pdns_control list" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e /usr/bin/pdns_control ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi if [ "$1" = "config" ]; then diff --git a/plugins/pdns/pdns_rel b/plugins/pdns/pdns_rel index eb660507..3eee007d 100755 --- a/plugins/pdns/pdns_rel +++ b/plugins/pdns/pdns_rel @@ -9,12 +9,17 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns show" +command="/usr/bin/pdns_control show" state_file=$MUNIN_PLUGSTATE/pdns_rel.state if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e /usr/bin/pdns_control ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi if [ "$1" = "config" ]; then @@ -31,17 +36,19 @@ if [ "$1" = "config" ]; then exit 0 fi -hits=$($command packetcache-hit | awk -F= '{print $2}') -queries=$($command udp-queries | awk -F= '{print $2}') -old_hits=$(cat $state_file | head -n1) -old_queries=$(cat $state_file | tail -n1) +hits=$($command packetcache-hit) +queries=$($command udp-queries) +if [ -f $state_file ]; then + old_hits=$(cat $state_file | head -n1) + old_queries=$(cat $state_file | tail -n1) +fi if [ -f $state_file ] && [ $(ls -l --time-style=+%s $state_file | awk '{print $6}') -gt $(date --date="7 minutes ago" +%s) ] ; then - d_hits=$(($hits - $old_hits)) - d_queries=$(($queries - $old_queries)) - if [ $d_queries -gt 0 ] ; then - echo packetcache_hitrate.value $(( $d_hits * 100 / $d_queries )) - fi + d_hits=$(($hits - $old_hits)) + d_queries=$(($queries - $old_queries)) + if [ $d_queries -gt 0 ] ; then + echo packetcache_hitrate.value $(( $d_hits * 100 / $d_queries )) + fi fi echo $hits > $state_file From 9aa6d7e977aa807273b5077a1ea06a19428ec9de Mon Sep 17 00:00:00 2001 From: Bas Couwenberg Date: Fri, 29 Sep 2017 09:27:34 +0200 Subject: [PATCH 020/695] shellcheck fixes for pdns-server plugins. --- plugins/pdns/pdns_errors | 2 +- plugins/pdns/pdns_queries | 2 +- plugins/pdns/pdns_rel | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/pdns/pdns_errors b/plugins/pdns/pdns_errors index 589d0c3a..c84373c0 100755 --- a/plugins/pdns/pdns_errors +++ b/plugins/pdns/pdns_errors @@ -43,4 +43,4 @@ if [ "$1" = "config" ]; then fi -$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | grep corrupt'\|'servfail'\|'timedout | sed 's/-/_/g' +$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | egrep "corrupt|servfail|timedout" | sed 's/-/_/g' diff --git a/plugins/pdns/pdns_queries b/plugins/pdns/pdns_queries index 87cf44e2..617d9cc7 100755 --- a/plugins/pdns/pdns_queries +++ b/plugins/pdns/pdns_queries @@ -55,4 +55,4 @@ if [ "$1" = "config" ]; then fi -$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | grep udp-'\|'recursing'\|'tcp | sed 's/-/_/g' +$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | egrep "udp-|recursing|tcp" | sed 's/-/_/g' diff --git a/plugins/pdns/pdns_rel b/plugins/pdns/pdns_rel index 3eee007d..586a0e51 100755 --- a/plugins/pdns/pdns_rel +++ b/plugins/pdns/pdns_rel @@ -38,18 +38,18 @@ fi hits=$($command packetcache-hit) queries=$($command udp-queries) -if [ -f $state_file ]; then - old_hits=$(cat $state_file | head -n1) - old_queries=$(cat $state_file | tail -n1) +if [ -f "$state_file" ]; then + old_hits=$(head -n1 "$state_file") + old_queries=$(tail -n1 "$state_file") fi -if [ -f $state_file ] && [ $(ls -l --time-style=+%s $state_file | awk '{print $6}') -gt $(date --date="7 minutes ago" +%s) ] ; then - d_hits=$(($hits - $old_hits)) - d_queries=$(($queries - $old_queries)) +if [ -f "$state_file" ] && [ "$(stat --format=%Y "$state_file")" -gt "$(date --date="7 minutes ago" +%s)" ] ; then + d_hits=$((hits - old_hits)) + d_queries=$((queries - old_queries)) if [ $d_queries -gt 0 ] ; then - echo packetcache_hitrate.value $(( $d_hits * 100 / $d_queries )) + echo packetcache_hitrate.value $(( d_hits * 100 / d_queries )) fi fi -echo $hits > $state_file -echo $queries >> $state_file +echo "$hits" > "$state_file" +echo "$queries" >> "$state_file" From f9426daca50d705d05b625399f41ae82272b8dbe Mon Sep 17 00:00:00 2001 From: Bas Couwenberg Date: Fri, 29 Sep 2017 09:34:25 +0200 Subject: [PATCH 021/695] shellcheck fixes for pdns-recursor plugins. --- plugins/pdns/pdns_rec_answers | 12 ++++++------ plugins/pdns/pdns_rec_cache | 8 ++++---- plugins/pdns/pdns_rec_cache_size | 4 ++-- plugins/pdns/pdns_rec_concurrent | 2 +- plugins/pdns/pdns_rec_issues | 10 +++++----- plugins/pdns/pdns_rec_outqueries | 4 ++-- plugins/pdns/pdns_rec_querylatency | 2 +- plugins/pdns/pdns_rec_questions | 4 ++-- plugins/pdns/pdns_rec_throttle | 2 +- plugins/pdns/pdns_rec_unauth | 6 +++--- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/plugins/pdns/pdns_rec_answers b/plugins/pdns/pdns_rec_answers index a78de5de..d3316234 100755 --- a/plugins/pdns/pdns_rec_answers +++ b/plugins/pdns/pdns_rec_answers @@ -65,11 +65,11 @@ if [ "$1" = "config" ]; then exit 0 fi -echo a.value `rec_control get answers0-1` -echo b.value `rec_control get answers1-10` -echo c.value `rec_control get answers10-100` -echo d.value `rec_control get answers100-1000` -echo e.value `rec_control get answers-slow` -echo f.value `rec_control get outgoing-timeouts` +echo a.value "$(rec_control get answers0-1)" +echo b.value "$(rec_control get answers1-10)" +echo c.value "$(rec_control get answers10-100)" +echo d.value "$(rec_control get answers100-1000)" +echo e.value "$(rec_control get answers-slow)" +echo f.value "$(rec_control get outgoing-timeouts)" exit 0 diff --git a/plugins/pdns/pdns_rec_cache b/plugins/pdns/pdns_rec_cache index e72d1ed0..93938300 100755 --- a/plugins/pdns/pdns_rec_cache +++ b/plugins/pdns/pdns_rec_cache @@ -19,7 +19,7 @@ if [ "$1" = "autoconf" ]; then fi fi -RESENDS=`rec_control get cache-resends` +RESENDS="$(rec_control get cache-resends)" ISRESENDS="" [ "$RESENDS" != "UNKNOWN" ] && ISRESENDS="resends" @@ -53,8 +53,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo hits.value `rec_control get cache-hits` -echo misses.value `rec_control get cache-misses` -[ "$RESENDS" != "UNKNOWN" ] && echo resends.value `rec_control get cache-resends` +echo hits.value "$(rec_control get cache-hits)" +echo misses.value "$(rec_control get cache-misses)" +[ "$RESENDS" != "UNKNOWN" ] && echo resends.value "$(rec_control get cache-resends)" exit 0 diff --git a/plugins/pdns/pdns_rec_cache_size b/plugins/pdns/pdns_rec_cache_size index 6e855253..e1b4413d 100755 --- a/plugins/pdns/pdns_rec_cache_size +++ b/plugins/pdns/pdns_rec_cache_size @@ -39,7 +39,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo entries.value `rec_control get cache-entries` -echo negative.value `rec_control get negcache-entries` +echo entries.value "$(rec_control get cache-entries)" +echo negative.value "$(rec_control get negcache-entries)" exit 0 diff --git a/plugins/pdns/pdns_rec_concurrent b/plugins/pdns/pdns_rec_concurrent index bb3ac11e..8c4c7063 100755 --- a/plugins/pdns/pdns_rec_concurrent +++ b/plugins/pdns/pdns_rec_concurrent @@ -34,6 +34,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo concurrent.value `rec_control get concurrent-queries` +echo concurrent.value "$(rec_control get concurrent-queries)" exit 0 diff --git a/plugins/pdns/pdns_rec_issues b/plugins/pdns/pdns_rec_issues index d605113f..87b09a9c 100755 --- a/plugins/pdns/pdns_rec_issues +++ b/plugins/pdns/pdns_rec_issues @@ -59,10 +59,10 @@ if [ "$1" = "config" ]; then exit 0 fi -echo spoofs.value `rec_control get spoof-prevents` -echo resource.value `rec_control get resource-limits` -echo client.value `rec_control get client-parse-errors` -echo server.value `rec_control get server-parse-errors` -echo overflow.value `rec_control get tcp-client-overflow` +echo spoofs.value "$(rec_control get spoof-prevents)" +echo resource.value "$(rec_control get resource-limits)" +echo client.value "$(rec_control get client-parse-errors)" +echo server.value "$(rec_control get server-parse-errors)" +echo overflow.value "$(rec_control get tcp-client-overflow)" exit 0 diff --git a/plugins/pdns/pdns_rec_outqueries b/plugins/pdns/pdns_rec_outqueries index 524d0056..a96f5c39 100755 --- a/plugins/pdns/pdns_rec_outqueries +++ b/plugins/pdns/pdns_rec_outqueries @@ -41,7 +41,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value `rec_control get all-outqueries` -echo tcp.value `rec_control get tcp-outqueries` +echo all.value "$(rec_control get all-outqueries)" +echo tcp.value "$(rec_control get tcp-outqueries)" exit 0 diff --git a/plugins/pdns/pdns_rec_querylatency b/plugins/pdns/pdns_rec_querylatency index 20b63973..d019c84a 100755 --- a/plugins/pdns/pdns_rec_querylatency +++ b/plugins/pdns/pdns_rec_querylatency @@ -34,6 +34,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo latency.value `rec_control get qa-latency` +echo latency.value "$(rec_control get qa-latency)" exit 0 diff --git a/plugins/pdns/pdns_rec_questions b/plugins/pdns/pdns_rec_questions index 2a84ef63..4bbf24be 100755 --- a/plugins/pdns/pdns_rec_questions +++ b/plugins/pdns/pdns_rec_questions @@ -41,7 +41,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value `rec_control get questions` -echo tcp.value `rec_control get tcp-questions` +echo all.value "$(rec_control get questions)" +echo tcp.value "$(rec_control get tcp-questions)" exit 0 diff --git a/plugins/pdns/pdns_rec_throttle b/plugins/pdns/pdns_rec_throttle index d82ffc1f..9980eafd 100755 --- a/plugins/pdns/pdns_rec_throttle +++ b/plugins/pdns/pdns_rec_throttle @@ -34,6 +34,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo throttled.value `rec_control get throttled-out` +echo throttled.value "$(rec_control get throttled-out)" exit 0 diff --git a/plugins/pdns/pdns_rec_unauth b/plugins/pdns/pdns_rec_unauth index 2f3df151..6cfc59c5 100755 --- a/plugins/pdns/pdns_rec_unauth +++ b/plugins/pdns/pdns_rec_unauth @@ -47,8 +47,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo tcp.value `rec_control get unauthorized-tcp` -echo udp.value `rec_control get unauthorized-udp` -echo unexpected.value `rec_control get unexpected-packets` +echo tcp.value "$(rec_control get unauthorized-tcp)" +echo udp.value "$(rec_control get unauthorized-udp)" +echo unexpected.value "$(rec_control get unexpected-packets)" exit 0 From a86de7871ac2d31ac1f6a917b3149deb92556f51 Mon Sep 17 00:00:00 2001 From: Bas Couwenberg Date: Fri, 29 Sep 2017 10:20:40 +0200 Subject: [PATCH 022/695] Use exit status 0 for autoconf, provide reason for 'no' answer. --- plugins/pdns/pdns_errors | 9 +++++---- plugins/pdns/pdns_latency | 9 +++++---- plugins/pdns/pdns_qsize | 9 +++++---- plugins/pdns/pdns_queries | 7 ++++--- plugins/pdns/pdns_rec_answers | 20 +++++++++++--------- plugins/pdns/pdns_rec_cache | 16 +++++++++------- plugins/pdns/pdns_rec_cache_size | 12 +++++++----- plugins/pdns/pdns_rec_concurrent | 10 ++++++---- plugins/pdns/pdns_rec_issues | 18 ++++++++++-------- plugins/pdns/pdns_rec_outqueries | 12 +++++++----- plugins/pdns/pdns_rec_qtypes | 10 ++++++---- plugins/pdns/pdns_rec_querylatency | 10 ++++++---- plugins/pdns/pdns_rec_questions | 12 +++++++----- plugins/pdns/pdns_rec_throttle | 10 ++++++---- plugins/pdns/pdns_rec_unauth | 14 ++++++++------ plugins/pdns/pdns_rel | 9 +++++---- 16 files changed, 107 insertions(+), 80 deletions(-) diff --git a/plugins/pdns/pdns_errors b/plugins/pdns/pdns_errors index c84373c0..b971860e 100755 --- a/plugins/pdns/pdns_errors +++ b/plugins/pdns/pdns_errors @@ -9,15 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/usr/bin/pdns_control list" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control list" if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/pdns_control ]; then + if [ -e "$pdns_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $pdns_control)" + exit 0 fi fi diff --git a/plugins/pdns/pdns_latency b/plugins/pdns/pdns_latency index 907785ca..47753079 100755 --- a/plugins/pdns/pdns_latency +++ b/plugins/pdns/pdns_latency @@ -9,15 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/usr/bin/pdns_control show" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/pdns_control ]; then + if [ -e "$pdns_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $pdns_control)" + exit 0 fi fi diff --git a/plugins/pdns/pdns_qsize b/plugins/pdns/pdns_qsize index 19e1941b..4d0a86c7 100755 --- a/plugins/pdns/pdns_qsize +++ b/plugins/pdns/pdns_qsize @@ -9,15 +9,16 @@ #%# family=auto #%# capabilities=autoconf -command="/usr/bin/pdns_control show" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/pdns_control ]; then + if [ -e "$pdns_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $pdns_control)" + exit 0 fi fi diff --git a/plugins/pdns/pdns_queries b/plugins/pdns/pdns_queries index 617d9cc7..edad5810 100755 --- a/plugins/pdns/pdns_queries +++ b/plugins/pdns/pdns_queries @@ -9,15 +9,16 @@ #%# family=auto #%# capabilities=autoconf +pdns_control="/usr/bin/pdns_control" command="/usr/bin/pdns_control list" if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/pdns_control ]; then + if [ -e "$pdns_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $pdns_control)" + exit 0 fi fi diff --git a/plugins/pdns/pdns_rec_answers b/plugins/pdns/pdns_rec_answers index d3316234..a7db89f6 100755 --- a/plugins/pdns/pdns_rec_answers +++ b/plugins/pdns/pdns_rec_answers @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -65,11 +67,11 @@ if [ "$1" = "config" ]; then exit 0 fi -echo a.value "$(rec_control get answers0-1)" -echo b.value "$(rec_control get answers1-10)" -echo c.value "$(rec_control get answers10-100)" -echo d.value "$(rec_control get answers100-1000)" -echo e.value "$(rec_control get answers-slow)" -echo f.value "$(rec_control get outgoing-timeouts)" +echo a.value "$($rec_control get answers0-1)" +echo b.value "$($rec_control get answers1-10)" +echo c.value "$($rec_control get answers10-100)" +echo d.value "$($rec_control get answers100-1000)" +echo e.value "$($rec_control get answers-slow)" +echo f.value "$($rec_control get outgoing-timeouts)" exit 0 diff --git a/plugins/pdns/pdns_rec_cache b/plugins/pdns/pdns_rec_cache index 93938300..007ad4cd 100755 --- a/plugins/pdns/pdns_rec_cache +++ b/plugins/pdns/pdns_rec_cache @@ -9,17 +9,19 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi -RESENDS="$(rec_control get cache-resends)" +RESENDS="$($rec_control get cache-resends)" ISRESENDS="" [ "$RESENDS" != "UNKNOWN" ] && ISRESENDS="resends" @@ -53,8 +55,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo hits.value "$(rec_control get cache-hits)" -echo misses.value "$(rec_control get cache-misses)" -[ "$RESENDS" != "UNKNOWN" ] && echo resends.value "$(rec_control get cache-resends)" +echo hits.value "$($rec_control get cache-hits)" +echo misses.value "$($rec_control get cache-misses)" +[ "$RESENDS" != "UNKNOWN" ] && echo resends.value "$($rec_control get cache-resends)" exit 0 diff --git a/plugins/pdns/pdns_rec_cache_size b/plugins/pdns/pdns_rec_cache_size index e1b4413d..cec24d94 100755 --- a/plugins/pdns/pdns_rec_cache_size +++ b/plugins/pdns/pdns_rec_cache_size @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -39,7 +41,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo entries.value "$(rec_control get cache-entries)" -echo negative.value "$(rec_control get negcache-entries)" +echo entries.value "$($rec_control get cache-entries)" +echo negative.value "$($rec_control get negcache-entries)" exit 0 diff --git a/plugins/pdns/pdns_rec_concurrent b/plugins/pdns/pdns_rec_concurrent index 8c4c7063..dcba6ad5 100755 --- a/plugins/pdns/pdns_rec_concurrent +++ b/plugins/pdns/pdns_rec_concurrent @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo concurrent.value "$(rec_control get concurrent-queries)" +echo concurrent.value "$($rec_control get concurrent-queries)" exit 0 diff --git a/plugins/pdns/pdns_rec_issues b/plugins/pdns/pdns_rec_issues index 87b09a9c..1191bb17 100755 --- a/plugins/pdns/pdns_rec_issues +++ b/plugins/pdns/pdns_rec_issues @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -59,10 +61,10 @@ if [ "$1" = "config" ]; then exit 0 fi -echo spoofs.value "$(rec_control get spoof-prevents)" -echo resource.value "$(rec_control get resource-limits)" -echo client.value "$(rec_control get client-parse-errors)" -echo server.value "$(rec_control get server-parse-errors)" -echo overflow.value "$(rec_control get tcp-client-overflow)" +echo spoofs.value "$($rec_control get spoof-prevents)" +echo resource.value "$($rec_control get resource-limits)" +echo client.value "$($rec_control get client-parse-errors)" +echo server.value "$($rec_control get server-parse-errors)" +echo overflow.value "$($rec_control get tcp-client-overflow)" exit 0 diff --git a/plugins/pdns/pdns_rec_outqueries b/plugins/pdns/pdns_rec_outqueries index a96f5c39..000c436c 100755 --- a/plugins/pdns/pdns_rec_outqueries +++ b/plugins/pdns/pdns_rec_outqueries @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -41,7 +43,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value "$(rec_control get all-outqueries)" -echo tcp.value "$(rec_control get tcp-outqueries)" +echo all.value "$($rec_control get all-outqueries)" +echo tcp.value "$($rec_control get tcp-outqueries)" exit 0 diff --git a/plugins/pdns/pdns_rec_qtypes b/plugins/pdns/pdns_rec_qtypes index caf35378..7c966c3e 100755 --- a/plugins/pdns/pdns_rec_qtypes +++ b/plugins/pdns/pdns_rec_qtypes @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -245,6 +247,6 @@ END { } ' -rec_control get-qtypelist | awk "$awkscript" +"$rec_control" get-qtypelist | awk "$awkscript" exit 0 diff --git a/plugins/pdns/pdns_rec_querylatency b/plugins/pdns/pdns_rec_querylatency index d019c84a..84de6803 100755 --- a/plugins/pdns/pdns_rec_querylatency +++ b/plugins/pdns/pdns_rec_querylatency @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo latency.value "$(rec_control get qa-latency)" +echo latency.value "$($rec_control get qa-latency)" exit 0 diff --git a/plugins/pdns/pdns_rec_questions b/plugins/pdns/pdns_rec_questions index 4bbf24be..f0cfe59c 100755 --- a/plugins/pdns/pdns_rec_questions +++ b/plugins/pdns/pdns_rec_questions @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -41,7 +43,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value "$(rec_control get questions)" -echo tcp.value "$(rec_control get tcp-questions)" +echo all.value "$($rec_control get questions)" +echo tcp.value "$($rec_control get tcp-questions)" exit 0 diff --git a/plugins/pdns/pdns_rec_throttle b/plugins/pdns/pdns_rec_throttle index 9980eafd..df6f0758 100755 --- a/plugins/pdns/pdns_rec_throttle +++ b/plugins/pdns/pdns_rec_throttle @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo throttled.value "$(rec_control get throttled-out)" +echo throttled.value "$($rec_control get throttled-out)" exit 0 diff --git a/plugins/pdns/pdns_rec_unauth b/plugins/pdns/pdns_rec_unauth index 6cfc59c5..92df5af1 100755 --- a/plugins/pdns/pdns_rec_unauth +++ b/plugins/pdns/pdns_rec_unauth @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -47,8 +49,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo tcp.value "$(rec_control get unauthorized-tcp)" -echo udp.value "$(rec_control get unauthorized-udp)" -echo unexpected.value "$(rec_control get unexpected-packets)" +echo tcp.value "$($rec_control get unauthorized-tcp)" +echo udp.value "$($rec_control get unauthorized-udp)" +echo unexpected.value "$($rec_control get unexpected-packets)" exit 0 diff --git a/plugins/pdns/pdns_rel b/plugins/pdns/pdns_rel index 586a0e51..b42439d8 100755 --- a/plugins/pdns/pdns_rel +++ b/plugins/pdns/pdns_rel @@ -9,16 +9,17 @@ #%# family=auto #%# capabilities=autoconf -command="/usr/bin/pdns_control show" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" state_file=$MUNIN_PLUGSTATE/pdns_rel.state if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/pdns_control ]; then + if [ -e "$pdns_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $pdns_control)" + exit 0 fi fi From 621744a6f0f3015fbeb0cbe4367d0da5facad528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Egil=20Vestb=C3=B8?= Date: Sat, 30 Sep 2017 17:42:37 +0200 Subject: [PATCH 023/695] Includes more kvm-processes --- plugins/libvirt/kvm_cpu | 2 +- plugins/libvirt/kvm_io | 2 +- plugins/libvirt/kvm_mem | 2 +- plugins/libvirt/kvm_net | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index d8ec7934..81e6cc75 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -75,7 +75,7 @@ def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() def fetch(vms): diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 065f1a19..ac072025 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -92,7 +92,7 @@ def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() if __name__ == "__main__": diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index 66e7f399..14de0275 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -89,7 +89,7 @@ def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() if __name__ == "__main__": diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index dfe2cd79..386c4bda 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -100,7 +100,7 @@ def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() def find_vms_tap(): From 67d6e559ec54351157623029ecb01d976440c310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Egil=20Vestb=C3=B8?= Date: Sat, 30 Sep 2017 17:44:16 +0200 Subject: [PATCH 024/695] Read correct values from /proc/net/dev --- plugins/libvirt/kvm_net | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 386c4bda..bca7b7f3 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -63,8 +63,8 @@ def fetch(vms): f = open("/proc/net/dev", "r") for line in f.readlines(): if tap in line: - print "%s_in.value %s" % (vms[pid], re.sub(r"%s:"%tap, "", line.split()[0])) - print "%s_out.value %s" % (vms[pid], line.split()[8]) + print "%s_in.value %s" % (vms[pid], line.split()[1]) + print "%s_out.value %s" % (vms[pid], line.split()[9]) break except Exception as inst: print inst From e9150b18929bd720dd8243f482ac810fbcac3a04 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 3 Oct 2017 11:08:25 +0200 Subject: [PATCH 025/695] Add specific updates to extinfo field. --- plugins/other/pacman_pending_updates | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index ed3b8e26..c6fda9bb 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -31,7 +31,7 @@ This plugin will draw one line: the number of updates pending. =head1 VERSION - 1.0.0 + 1.1.0 =head1 AUTHOR @@ -63,7 +63,13 @@ EOM ;; *) - echo updates.value $(checkupdates | wc -l) + tmpfile=$(mktemp) + updates=$(checkupdates | tee "$tmpfile" | wc -l) + echo updates.value $updates + if [ $updates -gt 0 ]; then + echo updates.extinfo $(paste -s -d, "$tmpfile") + fi + rm "$tmpfile" ;; esac From b5e31b66127fd506be09ba987362de7d8eafb11a Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 5 Oct 2017 11:28:15 +0200 Subject: [PATCH 026/695] Clean up pacman_pending_updates. No more environment variables since those can easily be configured with regular munin settings. Properly quote all strings. No longer use temp files for holding update information; instead hold it all in a variable. --- plugins/other/pacman_pending_updates | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index c6fda9bb..c5e66b93 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -14,12 +14,6 @@ All systems with pacman as their package manager. The plugin needs no additional configuration and works out of the box. -It is possible to add warnings for certain numbers of updates pending. The -following will send a warning when there are more than 10 updates pending. - - [pacman_pending_updates] - env.PENDING_UPDATES_WARNING :10 - =head1 INTERPRETATION This plugin will draw one line: the number of updates pending. @@ -53,9 +47,6 @@ graph_category security updates.label updates updates.info Current number of pending updates EOM - if [[ -n $PENDING_UPDATES_WARNING ]]; then - echo updates.warning $PENDING_UPDATES_WARNING - fi ;; autoconf) @@ -63,13 +54,13 @@ EOM ;; *) - tmpfile=$(mktemp) - updates=$(checkupdates | tee "$tmpfile" | wc -l) - echo updates.value $updates - if [ $updates -gt 0 ]; then - echo updates.extinfo $(paste -s -d, "$tmpfile") + updates="$(checkupdates)" + if [ -n "$updates" ]; then + echo "updates.value $(echo "$updates" | wc -l)" + echo "updates.extinfo $(echo "$updates" | paste -s -d,)" + else + echo updates.value 0 fi - rm "$tmpfile" ;; esac From 50e8e2772256d8faa8fcecc711924e77b22cb522 Mon Sep 17 00:00:00 2001 From: Henti Smith Date: Thu, 9 Nov 2017 09:54:57 +0000 Subject: [PATCH 027/695] Added recursion to jenkins_ The previous behaviour ignored the folder structure in Jenkins 2. --- plugins/jenkins/jenkins_ | 99 +++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index 9680030a..d3d90c70 100644 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -54,6 +54,7 @@ use warnings; use strict; use JSON; use File::Basename; +use URI; # VARS my $url = ($ENV{'url'} || 'localhost'); @@ -79,7 +80,7 @@ my %states = ( 'aborted'=>'failing', 'aborted_anime'=>'failing' ); -my %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); +my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { if( $type eq "results" ) { @@ -103,7 +104,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "build_stable.draw STACK\n"; print "build_stable.label stable\n"; print "build_stable.type GAUGE\n"; - print "build_stable.colour 294D99\n"; + print "build_stable.colour 294D99\n"; exit; } if( $type eq "queue" ) { @@ -125,46 +126,80 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "build_running.label running Builds\n"; print "build_running.type GAUGE\n"; exit; - } + } } else { - # CODE - my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); - my $cmd = "$wgetBin $auth -qO- $url:$port$context"; - + my $cmd = "$wgetBin $auth -qO- $url:$port$context"; + if( $type eq "results" ) { - my $result = `$cmd/api/json`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if (defined $states{$cur->{'color'}}) { - $counts{$states{$cur->{'color'}}} += 1; - } else { - warn "Ignoring unknown color " . $cur->{'color'} . "\n" - } - } - - foreach my $status (keys %counts) { - print "build_$status.value $counts{$status}\n"; + my $counts = get_results(''); + + foreach my $status (keys %{$counts}) { + print "build_$status.value $counts->{$status}\n"; } exit; } - + if( $type eq "running" ) { - my $count = 0; - my $result = `$cmd/api/json`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if( $cur->{'color'} =~ /anime$/ ) { - $count += 1; - } - } - print "build_running.value ", $count, "\n"; - exit; + my $running_count = get_running(''); + print "build_running.value ", $running_count, "\n"; + exit; } - + if( $type eq "queue" ) { my $result = `$cmd/queue/api/json`; - my $parsed = decode_json($result); + my $parsed = decode_json($result); print "build_count.value ", scalar( @{$parsed->{'items'}} ), "\n"; exit; } } + +{ + my %counts; + + sub get_results{ + my $query_context = shift; + die "get_results requires an argument" unless defined $query_context; + unless (%counts) { + # initialise + %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); + } + my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; + my $result = `$cmd`; + my $parsed = decode_json($result); + foreach my $cur(@{$parsed->{'jobs'}}) { + if (exists $cur->{'color'} && defined $states{$cur->{'color'}}) { + $counts{$states{$cur->{'color'}}} += 1; + } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ + my $uri = URI->new($cur->{'url'}); + my $folder = $uri->path; + get_results($folder); + } else { + warn "Ignoring unknown color " . $cur->{'color'} . "\n" + } + } + return \%counts; + } +} + +{ + my $running_count; + sub get_running{ + my $query_context = shift; + die "get_results requires an argument" unless defined $query_context; + $running_count //= 0; + my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; + my $result = `$cmd`; + my $parsed = decode_json($result); + foreach my $cur(@{$parsed->{'jobs'}}) { + if( exists $cur->{'color'} && $cur->{'color'} =~ /anime$/ ) { + $running_count += 1; + } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ + my $uri = URI->new($cur->{'url'}); + my $folder = $uri->path; + get_running($folder); + } + } + return $running_count; + } +} + From 6d50ab4900e3633571178316c2ffe61b207b930a Mon Sep 17 00:00:00 2001 From: Henti Smith Date: Thu, 9 Nov 2017 09:56:20 +0000 Subject: [PATCH 028/695] Check node offline status using boolean The previous behaviour checked offline status using a regular expression. Jenkins 2 uses a boolean value --- plugins/jenkins/jenkins_nodes_ | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) mode change 100644 => 100755 plugins/jenkins/jenkins_nodes_ diff --git a/plugins/jenkins/jenkins_nodes_ b/plugins/jenkins/jenkins_nodes_ old mode 100644 new mode 100755 index 8293caed..595e7f96 --- a/plugins/jenkins/jenkins_nodes_ +++ b/plugins/jenkins/jenkins_nodes_ @@ -51,7 +51,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -77,7 +77,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -103,7 +103,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -130,7 +130,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my %archs = (); my $cat; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $cat = $cur->{'monitorData'}{'hudson.node_monitors.ArchitectureMonitor'}; if (exists $archs{$cat} ) {} else { $archs{$cat} = 0; @@ -178,7 +178,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $total_mem = 0; my $used_mem = 0; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.SwapSpaceMonitor'}; $avail_mem += $monitor->{'availablePhysicalMemory'}; $total_mem += $monitor->{'totalPhysicalMemory'}; @@ -193,7 +193,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.SwapSpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -206,7 +206,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.TemporarySpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -219,7 +219,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.DiskSpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -233,7 +233,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my %archs = (); my $cat; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $cat = $cur->{'monitorData'}{'hudson.node_monitors.ArchitectureMonitor'}; if (exists $archs{$cat} ) { $archs{$cat} += 1; From 6ee4fc8d302e454c7306749973f82cfb4c33a12c Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Wed, 15 Nov 2017 13:54:43 +0100 Subject: [PATCH 029/695] Add syncthing plugin --- .../syncthing/example-graphs/syncthing_-1.png | Bin 0 -> 23834 bytes .../syncthing/example-graphs/syncthing_-2.png | Bin 0 -> 23223 bytes .../syncthing/example-graphs/syncthing_-3.png | Bin 0 -> 41568 bytes .../syncthing/example-graphs/syncthing_-4.png | Bin 0 -> 16551 bytes .../syncthing/example-graphs/syncthing_-5.png | Bin 0 -> 15762 bytes plugins/syncthing/syncthing_ | 151 ++++++++++++++++++ 6 files changed, 151 insertions(+) create mode 100644 plugins/syncthing/example-graphs/syncthing_-1.png create mode 100644 plugins/syncthing/example-graphs/syncthing_-2.png create mode 100644 plugins/syncthing/example-graphs/syncthing_-3.png create mode 100644 plugins/syncthing/example-graphs/syncthing_-4.png create mode 100644 plugins/syncthing/example-graphs/syncthing_-5.png create mode 100755 plugins/syncthing/syncthing_ diff --git a/plugins/syncthing/example-graphs/syncthing_-1.png b/plugins/syncthing/example-graphs/syncthing_-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a71e34d2c5c8c67f57d7804b590a5db04823dae0 GIT binary patch literal 23834 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 cS79 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

W%pmbBQZADf&r=Z_MSCZ wf5_KI&6@squ>6n6`F~CfZXHG-974srwGh3dr)P#707$*d`ivchYL2_|5ApZ+@Bjb+ literal 0 HcmV?d00001 diff --git a/plugins/syncthing/example-graphs/syncthing_-3.png b/plugins/syncthing/example-graphs/syncthing_-3.png new file mode 100644 index 0000000000000000000000000000000000000000..a1704be9555650d477546863f4bc86c293c78b04 GIT binary patch literal 41568 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 literal 0 HcmV?d00001 diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ new file mode 100755 index 00000000..b1572cd8 --- /dev/null +++ b/plugins/syncthing/syncthing_ @@ -0,0 +1,151 @@ +#!/bin/sh +# -*- sh -*- +: <<=cut +=head1 NAME +syncthing_ - Plugin to monitor Syncthing server + +=head1 DESCRIPTION +This plugin gathers metrics from a Syncthing server. + +This plugin requires the jq utility : https://stedolan.github.io/jq/ + +Available plugins : +syncthing_cpu # +syncthing_mem # +syncthing_goroutine # +syncthing_transfer # +syncthing_uptime # + +=head1 CONFIGURATION +To make the plugin connect to the Syncthing server one has to use this type of +configuration +[syncthing_*] + +env.ST_APIKEY myapikey0123456789 +env.ST_HOST 127.0.0.1 +env.ST_PORT 8384 +env.ST_PROTO http + +=head1 AUTHOR +Pierre-Alain TORET + +=head1 LICENSE +MIT +=cut + +function cpu { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server cpu usage +graph_args -u 100 +graph_vlabel % +graph_category network +syncthing_cpu.label cpu +EOM + exit 0;; + *) + printf "syncthing_cpu.value " + curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.cpuPercent' + esac +} + +function mem { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server memory +graph_category network +graph_order syncthing_mem_all syncthing_mem_sys +graph_args --base 1000 +graph_vlabel bits +syncthing_mem_all.label allocated +syncthing_mem_all.cdef syncthing_mem_all,8,* +syncthing_mem_sys.label obtained +syncthing_mem_sys.cdef syncthing_mem_sys,8,* +EOM + exit 0;; + *) + ALL=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.alloc') + SYS=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.sys') + printf "syncthing_mem_all.value $ALL\nsyncthing_mem_sys.value $SYS\n" + esac +} + +function uptime { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server uptime +graph_vlabel uptime in seconds +graph_category network +syncthing_uptime.label uptime +EOM + exit 0;; + *) + printf "syncthing_uptime.value " + curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.uptime' + esac +} + +function goroutine { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server go routines +graph_vlabel number of go routines +graph_category network +syncthing_goroutine.label routines +EOM + exit 0;; + *) + printf "syncthing_goroutine.value " + curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.goroutines' + esac +} + +function transfer { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server total transfer +graph_category network +graph_order syncthing_transfer_down syncthing_transfer_up +graph_args --base 1000 +graph_vlabel bits in (-) / out (+) per ${graph_period} +syncthing_transfer_down.label received +syncthing_transfer_down.type COUNTER +syncthing_transfer_down.graph no +syncthing_transfer_down.cdef syncthing_transfer_down,8,* +syncthing_transfer_up.label bps +syncthing_transfer_up.type COUNTER +syncthing_transfer_up.negative syncthing_transfer_down +syncthing_transfer_up.cdef syncthing_transfer_up,8,* +EOM + exit 0;; + *) + IBT=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | jq '.total | .inBytesTotal') + OBT=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | jq '.total | .outBytesTotal') + printf "syncthing_transfer_up.value $IBT\nsyncthing_transfer_down.value $OBT\n" + esac +} + +cd $(dirname $0) + +case $(basename $0) in + syncthing_cpu) + cpu $1 + exit 0;; + syncthing_mem) + mem $1 + exit 0;; + syncthing_uptime) + uptime $1 + exit 0;; + syncthing_goroutine) + goroutine $1 + exit 0;; + syncthing_transfer) + transfer $1 + exit 0;; +esac From 99123efbb81805c78f0c20048cd62ff565aea803 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Wed, 13 Dec 2017 19:46:47 +0100 Subject: [PATCH 030/695] Make syncthing_ more portable Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/syncthing_ | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index b1572cd8..8b41cad1 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # -*- sh -*- : <<=cut =head1 NAME @@ -8,6 +8,7 @@ syncthing_ - Plugin to monitor Syncthing server This plugin gathers metrics from a Syncthing server. This plugin requires the jq utility : https://stedolan.github.io/jq/ +This plugin requires the cURL utility : https://curl.haxx.se/ Available plugins : syncthing_cpu # @@ -46,7 +47,7 @@ EOM exit 0;; *) printf "syncthing_cpu.value " - curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.cpuPercent' + $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.cpuPercent' esac } @@ -66,8 +67,8 @@ syncthing_mem_sys.cdef syncthing_mem_sys,8,* EOM exit 0;; *) - ALL=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.alloc') - SYS=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.sys') + ALL=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.alloc') + SYS=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.sys') printf "syncthing_mem_all.value $ALL\nsyncthing_mem_sys.value $SYS\n" esac } @@ -84,7 +85,7 @@ EOM exit 0;; *) printf "syncthing_uptime.value " - curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.uptime' + $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.uptime' esac } @@ -100,7 +101,7 @@ EOM exit 0;; *) printf "syncthing_goroutine.value " - curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | jq '.goroutines' + $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.goroutines' esac } @@ -124,14 +125,17 @@ syncthing_transfer_up.cdef syncthing_transfer_up,8,* EOM exit 0;; *) - IBT=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | jq '.total | .inBytesTotal') - OBT=$(curl -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | jq '.total | .outBytesTotal') + IBT=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | $JQ '.total | .inBytesTotal') + OBT=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | $JQ '.total | .outBytesTotal') printf "syncthing_transfer_up.value $IBT\nsyncthing_transfer_down.value $OBT\n" esac } cd $(dirname $0) +CURL=$(which curl) +JQ=$(which jq) + case $(basename $0) in syncthing_cpu) cpu $1 From 452003a398d5e3c8736d695a9e46a035d1f1ba27 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Fri, 15 Dec 2017 14:47:29 +0100 Subject: [PATCH 031/695] Update tor_ plugin author Signed-off-by: Pierre-Alain TORET --- plugins/tor/tor_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 18929d3d..aa022538 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -49,7 +49,7 @@ env.torsocket '/var/run/tor/control' MIT License =head1 AUTHOR -daftaupe +Pierre-Alain TORET ''' from __future__ import print_function From abe4cea99d4d266adbb9c6b34519958cb6685f76 Mon Sep 17 00:00:00 2001 From: adrianp Date: Thu, 18 Jan 2018 10:44:07 +0200 Subject: [PATCH 032/695] Add support for monitoring packets and link status for TPLink SG108E switches --- plugins/tplink/tl_sg108e | 210 +++++++++++++++++++++++++++++++++++ plugins/tplink/tl_sg108e.png | Bin 0 -> 240473 bytes 2 files changed, 210 insertions(+) create mode 100755 plugins/tplink/tl_sg108e create mode 100644 plugins/tplink/tl_sg108e.png diff --git a/plugins/tplink/tl_sg108e b/plugins/tplink/tl_sg108e new file mode 100755 index 00000000..d76d4212 --- /dev/null +++ b/plugins/tplink/tl_sg108e @@ -0,0 +1,210 @@ +#!/usr/bin/perl +# -*- perl -*- +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# $Id$ +# +# Magic markers (used by munin-node-configure and some installation scripts): +#%# family=auto +#%# capabilities=autoconf + +use strict; +use warnings; +use WWW::Mechanize; + +=pod + +=encoding UTF-8 + +=head1 NAME + +tl_sg108e - Plugin to monitor packets per second and link speed for TP-Link SG108E switches + +=head1 APPLICABLE SYSTEMS + +TP-Link SG108E switches with web management (http). Tested with software version 1.0.2 Build 20160526 Rel.34615 + +=head1 CONFIGURATION + +Add this to the relevant munin-node config file. You can specify switch address, username, password and description for each port +(the switch management doesn't allow port descriptions) + + [tl_sg108e] + env.switch 192.168.1.12 + env.username admin + env.password mySecretPassword + env.p1 'Link to PC1' + env.p2 'Link to server1' + env.p3 'Not used' + env.p4 'Link to AP' + env.p5 'Link to PC2' + env.p6 'Link to PC3' + env.p7 'Not used' + env.p8 'Uplink' + +Requires WWW:Mechanize module: + sudo apt-get install libwww-mechanize-perl + +=head1 BUGS/GOTCHAS + +The link speed is represented as a number: + 0 - down + 5 - 100Mbps full + 6 - 1Gbps + +=head1 AUTHOR + +Adrian Popa (https://github.com/mad-ady) + +=head1 COPYRIGHT + +Copyright (c) 2018, Adrian Popa + +All rights reserved. This program is free software; you can +redistribute it and/or modify it under the terms of the GNU General +Public License as published by the Free Software Foundation; version 2 +dated June, 1991. + +=head1 VERSION + + $Id$ + +=cut + +# read parameters from munin +my $switch = ( $ENV{switch} || '192.168.1.1' ); +my $username = ( $ENV{username} || 'admin' ); +my $password = ( $ENV{password} || 'admin' ); +my $numberOfPorts = 8; + +#populate the ports and descriptions based on ENV +my %ports = (); +for ( 1 .. $numberOfPorts ) { + my $i = $_; + if ( defined $ENV{"p$i"} ) { + $ports{$i} = $ENV{"p$i"}; + } + else { + #no description + $ports{$i} = ""; + } +} + +if ( $ARGV[0] and $ARGV[0] eq 'autoconf' ) { + print "no (manual configuration needed)\n"; + exit 0; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + foreach my $graphType (qw/Packets Speed/) { + foreach my $port ( sort keys %ports ) { + print "multigraph ${graphType}_if_$port\n", + "graph_title $graphType for $switch port $port $ports{$port}\n", + "graph_info $graphType graph for port $port $ports{$port}\n", + "graph_args --base 1000 -l 0\n", + "graph_scale yes\n", + "graph_category switch\n"; + if ( $graphType eq 'Speed' ) { + print "graph_vlabel speed\n"; + print "p${port}.label Port $port Link speed\n"; + print "p${port}.type GAUGE\n"; + } + else { + print "graph_vlabel packets\n"; + foreach my $gb (qw/good bad/) { + foreach my $direction (qw/tx rx/) { + print "p${port}${direction}${gb}.label Port $port $direction ($gb)\n"; + print "p${port}${direction}${gb}.type COUNTER\n"; + } + } + } + } + } + exit 0; +} + +# extract data from the switch - uses web scraping (tested with 1.0.2 Build 20160526 Rel.34615) + +# print STDERR "Connecting to $switch with user $username"; +my $mech = WWW::Mechanize->new( + autocheck => 0, + requests_redirectable => [ 'GET', 'HEAD', 'POST' ], + user_agent => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0' +); +my $result = $mech->post( + "http://$switch/logon.cgi", + [ username => $username, password => $password, logon => 'Login' ], + "Referer" => "http://$switch/" +); +my $response = $mech->response(); + +# navigate to the page with the network stats +$result = $mech->get("http://$switch/PortStatisticsRpm.htm"); +$response = $mech->response(); + +# print STDERR $response->code()."\n"; + +# get the data +my $data = $mech->content( raw => 1 ); + +# print STDERR "$data\n"; + +# The page stores the data in a table, but internally it is stored in 3 javascript arrays: +# state:[1,1,1,1,1,1,1,1,0,0], +# link_status:[0,5,0,0,5,5,5,6,0,0], +# pkts:[0,0,0,0,14141090,0,10461386,0,14226,0,12252,0,0,0,0,0,2872063,0,1402200,0,59764503,0,34619246,0,4913873,0,4393574,0,44170456,0,68499653,0,0,0] + +# state: 1 - Enabled, 0 - Disabled (administratively) +# link_status: 0 - down, 5 - 100Mbps full, 6 - 1Gbps +# pkts: every group of 4 values represent txGoodPkt, txBadPkt, rxGoodPkt, rxBadPkt + +# parse good/bad packets +if ( $data =~ /pkts:\[([0-9,]+)\]/ ) { + + my $packetString = $1; + my @packets = split( /,/, $packetString ); + + for ( 1 .. $numberOfPorts ) { + my $currentPort = $_; + my $txGoodPkt = $packets[ ( $currentPort - 1 ) * 4 ]; + my $txBadPkt = $packets[ ( $currentPort - 1 ) * 4 + 1 ]; + my $rxGoodPkt = $packets[ ( $currentPort - 1 ) * 4 + 2 ]; + my $rxBadPkt = $packets[ ( $currentPort - 1 ) * 4 + 3 ]; + print "multigraph Packets_if_$currentPort\n"; + + print "p${currentPort}txgood.value $txGoodPkt\n"; + print "p${currentPort}rxgood.value $rxGoodPkt\n"; + print "p${currentPort}txbad.value $txBadPkt\n"; + print "p${currentPort}rxbad.value $rxBadPkt\n"; + } +} + +# parse link speed +if ( $data =~ /link_status:\[([0-9,]+)\]/ ) { + + my $linkString = $1; + my @links = split( /,/, $linkString ); + for ( 1 .. $numberOfPorts ) { + my $currentPort = $_; + my $link = $links[ $currentPort - 1 ]; + print "multigraph Speed_if_$currentPort\n"; + + print "p${currentPort}.value $link\n"; + } + +} + +# vim: ft=perl : ts=4 : expandtab diff --git a/plugins/tplink/tl_sg108e.png b/plugins/tplink/tl_sg108e.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8599eec9d8453cef77df92f80031c7fbd19fc3 GIT binary patch literal 240473 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%0ArcC#8N$LcKPn$7HO7BL$#6^$83$ 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 zℜ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%0DniTE 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 literal 0 HcmV?d00001 From f4c192469fbdd038e7f7aca890cfa8e467eba189 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 00:54:20 +0100 Subject: [PATCH 033/695] nginx_error: introduce real version number --- plugins/nginx/nginx_error | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 34e47b82..e3385158 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -48,7 +48,7 @@ None known. =head1 VERSION -$Id:$ +1.0 - 2017/02/21 =head1 AUTHOR From 808a21d4fb6194ac0c60731a636f82aeb5538361 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:00:12 +0100 Subject: [PATCH 034/695] nginx_error: improve readability of symlink configuration code --- plugins/nginx/nginx_error | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index e3385158..7b42a143 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -48,8 +48,12 @@ None known. =head1 VERSION +1.1 - 2018/01/20 + * improve readability of symlink configuration code + 1.0 - 2017/02/21 + =head1 AUTHOR vovansystems@gmail.com, 2013 @@ -60,24 +64,25 @@ GPLv3 =cut -if [ -z $logpath ]; then - logpath='/var/log/nginx' -fi -name=`basename $0` +# default environment variable values +logpath=${logpath:-/var/log/nginx} -domain=${name/nginx_error/} -if [[ $domain != '_' && ${#domain} -ne 0 ]]; then - domain=${domain:1} - if [ -z $logpattern ]; then - logpattern='a.*.log' - fi - logpattern=${logpattern/\*/$domain} + +# derive the name of the log file from a potential symlink-configured virtual host +script_name=$(basename "$0") +plugin_suffix=${script_name#nginx_error} +if [ -n "${plugin_suffix#_}" ]; then + # a domain was given via symlink configuration: adjust the logpattern + domain=${plugin_suffix#_} + # default logpattern for symlink configuration mode + logpattern=${logpattern:-a.*.log} + log_filename=${logpattern/\*/$domain} else - logpattern='access.log' + log_filename='access.log' fi -log="$logpath/$logpattern" +log="$logpath/$log_filename" # declaring an array with http status codes, we are interested in declare -A http_codes @@ -111,7 +116,7 @@ do_ () { # Fetch } do_config () { - echo "graph_title $logpattern - Nginx errors per minute" + echo "graph_title $(basename "$log") - Nginx errors per minute" echo 'graph_vlabel pages with http error codes / ${graph_period}' echo "graph_category webserver" echo "graph_period minute" From 8a68200a6787dc85c8639654ad4855b178061384 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:02:19 +0100 Subject: [PATCH 035/695] nginx_error: fix style issues reported by shellcheck --- plugins/nginx/nginx_error | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 7b42a143..1d6244cd 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -49,6 +49,7 @@ None known. =head1 VERSION 1.1 - 2018/01/20 + * fix shell style issues reported by shellcheck * improve readability of symlink configuration code 1.0 - 2017/02/21 @@ -99,29 +100,34 @@ http_codes[500]='Internal Server Error' http_codes[502]='Bad Gateway' http_codes[503]='Service Unavailable' -do_ () { # Fetch + +# parse error counts from log file +do_ () { + local k values declare -A line_counts - values=`awk '{print $9}' $log | sort | uniq -c` + values=$(awk '{print $9}' "$log" | sort | uniq -c) if [ -n "$values" ]; then while read -r line; do - read -a tmp <<< "$line"; - line_counts[${tmp[1]}]=${tmp[0]}; + read -r -a tmp <<< "$line" + line_counts[${tmp[1]}]=${tmp[0]} done <<< "$values" fi - for k in ${!http_codes[@]}; do + for k in "${!http_codes[@]}"; do echo "error$k.value ${line_counts[$k]:-0}" done exit 0 } + do_config () { + local k echo "graph_title $(basename "$log") - Nginx errors per minute" - echo 'graph_vlabel pages with http error codes / ${graph_period}' + echo "graph_vlabel pages with http error codes / \${graph_period}" echo "graph_category webserver" echo "graph_period minute" echo "graph_info This graph shows nginx error amount per minute" - for k in ${!http_codes[@]}; do + for k in "${!http_codes[@]}"; do echo "error$k.type DERIVE" echo "error$k.min 0" echo "error$k.label $k ${http_codes[$k]}" @@ -129,14 +135,16 @@ do_config () { exit 0 } + do_autoconf () { echo yes exit 0 } + case $1 in config|autoconf|'') - eval do_$1 + eval "do_$1" esac exit $? From 0e864944d76b19278d82a602aca850c0d91ac51a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:25:14 +0100 Subject: [PATCH 036/695] nginx_error: improve readability of array accesses --- plugins/nginx/nginx_error | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 1d6244cd..667aef2c 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -103,34 +103,30 @@ http_codes[503]='Service Unavailable' # parse error counts from log file do_ () { - local k values + local count status_code declare -A line_counts - values=$(awk '{print $9}' "$log" | sort | uniq -c) - if [ -n "$values" ]; then - while read -r line; do - read -r -a tmp <<< "$line" - line_counts[${tmp[1]}]=${tmp[0]} - done <<< "$values" - fi + while read -r count status_code; do + line_counts[$status_code]=$count + done <<< "$(awk '{print $9}' "$log" | sort | uniq -c)" - for k in "${!http_codes[@]}"; do - echo "error$k.value ${line_counts[$k]:-0}" + for status_code in "${!http_codes[@]}"; do + echo "error${status_code}.value ${line_counts[$status_code]:-0}" done exit 0 } do_config () { - local k + local status_code echo "graph_title $(basename "$log") - Nginx errors per minute" echo "graph_vlabel pages with http error codes / \${graph_period}" echo "graph_category webserver" echo "graph_period minute" echo "graph_info This graph shows nginx error amount per minute" - for k in "${!http_codes[@]}"; do - echo "error$k.type DERIVE" - echo "error$k.min 0" - echo "error$k.label $k ${http_codes[$k]}" + for status_code in "${!http_codes[@]}"; do + echo "error${status_code}.type DERIVE" + echo "error${status_code}.min 0" + echo "error${status_code}.label $status_code ${http_codes[$status_code]}" done exit 0 } From 7ed71b441d4debfa427c2860d32e921b78df4727 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:36:03 +0100 Subject: [PATCH 037/695] nginx_error: add support for 'dirty config' capability --- plugins/nginx/nginx_error | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 667aef2c..4b22081e 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -49,6 +49,7 @@ None known. =head1 VERSION 1.1 - 2018/01/20 + * add 'dirty config' capability support * fix shell style issues reported by shellcheck * improve readability of symlink configuration code @@ -102,7 +103,7 @@ http_codes[503]='Service Unavailable' # parse error counts from log file -do_ () { +do_fetch () { local count status_code declare -A line_counts while read -r count status_code; do @@ -112,7 +113,6 @@ do_ () { for status_code in "${!http_codes[@]}"; do echo "error${status_code}.value ${line_counts[$status_code]:-0}" done - exit 0 } @@ -128,19 +128,26 @@ do_config () { echo "error${status_code}.min 0" echo "error${status_code}.label $status_code ${http_codes[$status_code]}" done - exit 0 } do_autoconf () { echo yes - exit 0 } case $1 in - config|autoconf|'') - eval "do_$1" + config) + do_config + # support "dirty config" capability + if [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ]; then do_fetch; fi + ;; + autoconf) + do_autoconf + ;; + '') + do_fetch + ;; esac exit $? From 24ea5d29e9ed8127e44c3f226c82d17ef2dc9e04 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:45:26 +0100 Subject: [PATCH 038/695] nginx_error: improve documentation --- plugins/nginx/nginx_error | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 4b22081e..73f77b07 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -7,21 +7,27 @@ nginx error - Munin plugin to monitor nginx error rates (http status codes per minute). + =head1 APPLICABLE SYSTEMS -Any Linux host, running nginx, with bash version > 4.0 +Any host running nginx, with bash version > 4.0 + =head1 CONFIGURATION This shows the default configuration of this plugin. You can override the log file path and the logpattern. +Additionally you may want to adjust 'group' (or 'user') based on the +permissions required for reading the log file. [nginx_error] + group adm env.logpath /var/log/nginx env.logpattern a.*.log Nginx must also be configured to log accesses in "combined" log format (default) + =head1 USAGE Link this plugin to /etc/munin/plugins/ and restart the munin-node. @@ -33,19 +39,25 @@ will parse the log file /var/log/nginx/a.mydomaincom.log You can change 'env.logpattern' using asterisk ('*') to match your logs filenames. +'env.logpattern' is ignored for a non-symlink configuration. + + =head1 INTERPRETATION The plugin shows nginx http "error" status rates by parsing access log. + =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf + =head1 BUGS None known. + =head1 VERSION 1.1 - 2018/01/20 @@ -60,6 +72,7 @@ None known. vovansystems@gmail.com, 2013 + =head1 LICENSE GPLv3 @@ -122,7 +135,7 @@ do_config () { echo "graph_vlabel pages with http error codes / \${graph_period}" echo "graph_category webserver" echo "graph_period minute" - echo "graph_info This graph shows nginx error amount per minute" + echo "graph_info This graph shows nginx error rate per minute" for status_code in "${!http_codes[@]}"; do echo "error${status_code}.type DERIVE" echo "error${status_code}.min 0" From a4882b1912652777202a0679c2640f4f6b327c28 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 20 Jan 2018 01:49:15 +0100 Subject: [PATCH 039/695] nginx_error: enable 'set -eu' flags for shell --- plugins/nginx/nginx_error | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 73f77b07..9cb3d7df 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -80,6 +80,9 @@ GPLv3 =cut +set -eu + + # default environment variable values logpath=${logpath:-/var/log/nginx} @@ -149,7 +152,7 @@ do_autoconf () { } -case $1 in +case ${1:-} in config) do_config # support "dirty config" capability From bbec1ffb436aed34a4af935d77beb81694545260 Mon Sep 17 00:00:00 2001 From: Dobrica Pavlinusic Date: Sun, 28 Jan 2018 15:28:00 +0100 Subject: [PATCH 040/695] count failed disks and report critical warning --- plugins/disk/raid | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/disk/raid b/plugins/disk/raid index 88f99dfe..c835757a 100755 --- a/plugins/disk/raid +++ b/plugins/disk/raid @@ -42,7 +42,7 @@ my($devinfo_re, $devstat_re, $action_re) = ( # Interestingly, swap is presented as "active (auto-read-only)" # and mdadm has '--readonly' option to make the array 'active (read-only)' -my($dev, $ro, $type, $members, $nmem, $nact, $status, $action, $proc, $minute); +my($dev, $ro, $type, $members, $failed, $nmem, $nact, $status, $action, $proc, $minute); while (@text) { my $line = shift @text; if ($line =~ /$devinfo_re/) { @@ -51,6 +51,9 @@ while (@text) { $ro = $2 || ''; $type = $3; $members = $4; + $failed = $members; + $failed =~ s/[^F]+//g; + $failed = length($failed); $line = shift @text; if ($line =~ /$devstat_re/) { @@ -104,6 +107,9 @@ while (@text) { print $dev, "_rebuild.critical 98:\n"; print $dev, "_check.label $dev check/resync \n"; print $dev, "_check.info $action $minute\n"; + print $dev, "_failed.label $dev failed disks \n"; + print $dev, "_failed.info $action $minute\n"; + print $dev, "_failed.critical 0:0\n"; } else { my $pct = 100 * $nact / $nmem; my $rpct = 100; @@ -127,6 +133,7 @@ while (@text) { print "$dev.value $pct\n"; print $dev, "_rebuild.value $rpct\n"; print $dev, "_check.value $cpct\n"; + print $dev, "_failed.value $failed\n"; } } From c2513bd2e795e895fce56c9d72ca699d30ddc3d3 Mon Sep 17 00:00:00 2001 From: poiuty Date: Sun, 4 Feb 2018 04:37:23 +0300 Subject: [PATCH 041/695] fix symlink --- plugins/nginx/nginx-cache-multi_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nginx/nginx-cache-multi_ b/plugins/nginx/nginx-cache-multi_ index 05245ffc..97e9f25f 100755 --- a/plugins/nginx/nginx-cache-multi_ +++ b/plugins/nginx/nginx-cache-multi_ @@ -11,7 +11,7 @@ # This plugin generates two graphs - with number of requests and with percents. # Create these symlinks: # ln -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_number -# ls -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_percent +# ln -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_percent # # You can override the log file location. # From 1f99e53cccbacd69dceb703c98fc330c9e865c96 Mon Sep 17 00:00:00 2001 From: batmac Date: Sat, 17 Feb 2018 19:39:54 +0100 Subject: [PATCH 042/695] update wifi_signal to correct a typo trivial change, needed to have the right marker to enable autoconf --- plugins/wifi/wifi_signal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wifi/wifi_signal b/plugins/wifi/wifi_signal index 7892cbe5..4456532b 100755 --- a/plugins/wifi/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -6,7 +6,7 @@ # License: GPL v. 2 # #%# family=auto -#%# capabilitoes=autoconf +#%# capabilities=autoconf PNWL=/proc/net/wireless From 8ea8a4c81e0b16f1eab55e2f9085bf27f01c5f62 Mon Sep 17 00:00:00 2001 From: Anton Anikin Date: Wed, 21 Feb 2018 23:06:04 +0800 Subject: [PATCH 043/695] Skip holded packages --- plugins/ubuntu/apt_ubuntu | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ubuntu/apt_ubuntu b/plugins/ubuntu/apt_ubuntu index a54e1008..d13654a3 100755 --- a/plugins/ubuntu/apt_ubuntu +++ b/plugins/ubuntu/apt_ubuntu @@ -116,6 +116,7 @@ if len(sys.argv) > 1: try: import apt + import apt_pkg except ImportError: print "The module 'apt' is currently not installed. You can install it by typing:\nsudo apt-get install python-apt\nImportError: No module named apt" sys.exit(1) @@ -123,7 +124,7 @@ except ImportError: pkgs = {} total = 0 for pkg in apt.Cache(): - if pkg.is_upgradable: + if (pkg.is_upgradable) and (pkg._pkg.selected_state != apt_pkg.SELSTATE_HOLD): a = check_origin(pkg) pkgs[a] = pkgs.get(a, 0) + 1 total += 1 From 00c0da18d58545a64f1ae16dadb74a8ef63993c7 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 12:26:16 +0100 Subject: [PATCH 044/695] env bash --- plugins/gpu/nvidia_gpu_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index a61f492b..6b0871d5 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # -*- sh -*- : << =cut From 2a84478fea44d27cfaa71c08a520423671b29501 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 12:30:35 +0100 Subject: [PATCH 045/695] Use $(STATEMENT) instead of legacy `STATEMENT` https://github.com/koalaman/shellcheck/wiki/SC2006 --- plugins/gpu/nvidia_gpu_ | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 6b0871d5..faeac130 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -64,7 +64,7 @@ faken@fakenmc.com =cut # Determine name of parameter to monitor -name=`basename $0 | sed 's/^nvidia_gpu_//g'` +name=$(basename "$0" | sed 's/^nvidia_gpu_//g') # Get location of nvidia-smi executable or use default nvSmiExec=${smiexec:-'/usr/bin/nvidia-smi'} @@ -91,8 +91,8 @@ if [ "$1" = "suggest" ]; then fi # Get number of GPUs -nGpusOutput=`$nvSmiExec -L` -nGpus=`echo "$nGpusOutput" | wc -l` +nGpusOutput=$($nvSmiExec -L) +nGpus=$(echo "$nGpusOutput" | wc -l) if [ $nGpus -eq 0 ]; then # Exit if no GPUs found echo "No NVIDIA GPUs detected. Exiting." @@ -100,13 +100,13 @@ if [ $nGpus -eq 0 ]; then fi # Get full output from nvidia-smi -smiOutput=`$nvSmiExec -q` +smiOutput=$($nvSmiExec -q) # Check if config was requested if [ "$1" = "config" ]; then # Get driver version - driverVersion=`nvidia-smi -q | grep "Driver Version" | cut -d : -f 2 | tr -d ' '` + driverVersion=$(nvidia-smi -q | grep "Driver Version" | cut -d : -f 2 | tr -d ' ') # Configure graph depending on what which quantity will be plotted case $name in @@ -119,7 +119,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) echo "temp${nGpusCounter}.warning ${warning:-75}" echo "temp${nGpusCounter}.critical ${critical:-95}" echo "temp${nGpusCounter}.info Temperature information for $gpuName" @@ -128,14 +128,14 @@ if [ "$1" = "config" ]; then ;; mem) # First determine total memory of each GPU... - gpusTotalMemOutput=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' '` + gpusTotalMemOutput=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' ') gpusTotalMem='' nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) echo "mem${nGpusCounter}.info Memory information for $gpuName" - gpuMem=`echo "$gpusTotalMemOutput"| sed -n $(( $nGpusCounter + 1 ))p` + gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $(( $nGpusCounter + 1 ))p) gpusTotalMem="${gpusTotalMem}${gpuMem} for GPU ${nGpusCounter}" : $(( nGpusCounter = $nGpusCounter + 1 )) if [ $nGpusCounter -lt $nGpus ]; then @@ -158,7 +158,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) echo "fan${nGpusCounter}.info Fan information for $gpuName" : $(( nGpusCounter = $nGpusCounter + 1 )) done @@ -171,7 +171,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) echo "power${nGpusCounter}.info power consumption of $gpuName" : $(( nGpusCounter = $nGpusCounter + 1 )) done @@ -187,7 +187,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.label $gpuName" : $(( nGpusCounter = $nGpusCounter + 1 )) #print_warning $name @@ -200,27 +200,27 @@ fi # Get requested value case $name in temp) - valueGpus=`echo "$smiOutput" | grep -A 1 "Temperature" | grep -i "Gpu" | cut -d : -f 2 | cut -d ' ' -f 2` + valueGpus=$(echo "$smiOutput" | grep -A 1 "Temperature" | grep -i "Gpu" | cut -d : -f 2 | cut -d ' ' -f 2) ;; mem) - totalMemGpus=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | cut -d ' ' -f 2` - usedMemGpus=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2` + totalMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | cut -d ' ' -f 2) + usedMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2) valueGpus='' nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - totalMemGpu=`echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` - usedMemGpu=`echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` + totalMemGpu=$(echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p) + usedMemGpu=$(echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p) percentMemUsed=$(( $usedMemGpu * 100 / $totalMemGpu )) valueGpus="${valueGpus}${percentMemUsed}"$'\n' : $(( nGpusCounter = $nGpusCounter + 1 )) done ;; fan) - valueGpus=`echo "$smiOutput" | grep "Fan Speed" | cut -d ':' -f 2 | cut -d ' ' -f 2` + valueGpus=$(echo "$smiOutput" | grep "Fan Speed" | cut -d ':' -f 2 | cut -d ' ' -f 2) ;; power) - valueGpus=`echo "$smiOutput" | grep "Power Draw" | cut -d ':' -f 2 | cut -d ' ' -f 2` + valueGpus=$(echo "$smiOutput" | grep "Power Draw" | cut -d ':' -f 2 | cut -d ' ' -f 2) ;; *) echo "Can't run without a proper symlink. Exiting." @@ -234,7 +234,7 @@ case $name in nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - value=`echo "$valueGpus" | sed -n $(( $nGpusCounter + 1 ))p` + value=$(echo "$valueGpus" | sed -n $(( $nGpusCounter + 1 ))p) echo "${name}${nGpusCounter}.value $value" : $(( nGpusCounter = $nGpusCounter + 1 )) done From 15125852e89025715f754a31082e355c60e18087 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 12:36:25 +0100 Subject: [PATCH 046/695] remove $ on regular variables in arithmetic contexts https://github.com/koalaman/shellcheck/wiki/SC2004 --- plugins/gpu/nvidia_gpu_ | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index faeac130..7238e43f 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -119,11 +119,11 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "temp${nGpusCounter}.warning ${warning:-75}" echo "temp${nGpusCounter}.critical ${critical:-95}" echo "temp${nGpusCounter}.info Temperature information for $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done ;; mem) @@ -133,11 +133,11 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "mem${nGpusCounter}.info Memory information for $gpuName" - gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $(( $nGpusCounter + 1 ))p) + gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $((nGpusCounter+1))p) gpusTotalMem="${gpusTotalMem}${gpuMem} for GPU ${nGpusCounter}" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) if [ $nGpusCounter -lt $nGpus ]; then gpusTotalMem="${gpusTotalMem}, " fi @@ -158,9 +158,9 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "fan${nGpusCounter}.info Fan information for $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done ;; power) @@ -171,9 +171,9 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "power${nGpusCounter}.info power consumption of $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done ;; *) @@ -187,9 +187,9 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=$(echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.label $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) #print_warning $name #print_critical $name done @@ -209,11 +209,11 @@ case $name in nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - totalMemGpu=$(echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p) - usedMemGpu=$(echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p) - percentMemUsed=$(( $usedMemGpu * 100 / $totalMemGpu )) + totalMemGpu=$(echo "$totalMemGpus" | sed -n $((nGpusCounter+1))p) + usedMemGpu=$(echo "$usedMemGpus" | sed -n $((nGpusCounter+1))p) + percentMemUsed=$((usedMemGpu*100/totalMemGpu)) valueGpus="${valueGpus}${percentMemUsed}"$'\n' - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done ;; fan) @@ -234,9 +234,9 @@ case $name in nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - value=$(echo "$valueGpus" | sed -n $(( $nGpusCounter + 1 ))p) + value=$(echo "$valueGpus" | sed -n $((nGpusCounter+1))p) echo "${name}${nGpusCounter}.value $value" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done From 5c08e0744c90332f8eb26a322c39c0e96bf2751a Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 12:38:25 +0100 Subject: [PATCH 047/695] Double quote to prevent globbing and word splitting https://github.com/koalaman/shellcheck/wiki/SC2086 --- plugins/gpu/nvidia_gpu_ | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 7238e43f..ccad1b83 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -72,7 +72,7 @@ nvSmiExec=${smiexec:-'/usr/bin/nvidia-smi'} # Check if autoconf was requested if [ "$1" = "autoconf" ]; then # Autoconf only returns yes if nvidia-smi exists and is executable - if [ -x $nvSmiExec ]; then + if [ -x "$nvSmiExec" ]; then echo yes exit 0 else @@ -93,7 +93,7 @@ fi # Get number of GPUs nGpusOutput=$($nvSmiExec -L) nGpus=$(echo "$nGpusOutput" | wc -l) -if [ $nGpus -eq 0 ]; then +if [ "$nGpus" -eq 0 ]; then # Exit if no GPUs found echo "No NVIDIA GPUs detected. Exiting." exit 1 @@ -117,7 +117,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info Temperature information for NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "temp${nGpusCounter}.warning ${warning:-75}" @@ -131,14 +131,14 @@ if [ "$1" = "config" ]; then gpusTotalMemOutput=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' ') gpusTotalMem='' nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "mem${nGpusCounter}.info Memory information for $gpuName" gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $((nGpusCounter+1))p) gpusTotalMem="${gpusTotalMem}${gpuMem} for GPU ${nGpusCounter}" : $((nGpusCounter=nGpusCounter+1)) - if [ $nGpusCounter -lt $nGpus ]; then + if [ "$nGpusCounter" -lt "$nGpus" ]; then gpusTotalMem="${gpusTotalMem}, " fi done @@ -156,7 +156,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info Fan speed of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "fan${nGpusCounter}.info Fan information for $gpuName" @@ -169,7 +169,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info power consumption of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "power${nGpusCounter}.info power consumption of $gpuName" @@ -185,7 +185,7 @@ if [ "$1" = "config" ]; then # Common stuff for all quantities nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.label $gpuName" @@ -207,7 +207,7 @@ case $name in usedMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2) valueGpus='' nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ "$nGpusCounter" -lt "$nGpus" ] do totalMemGpu=$(echo "$totalMemGpus" | sed -n $((nGpusCounter+1))p) usedMemGpu=$(echo "$usedMemGpus" | sed -n $((nGpusCounter+1))p) @@ -232,7 +232,7 @@ case $name in # Print requested value nGpusCounter=0 -while [ $nGpusCounter -lt $nGpus ] +while [ "$nGpusCounter" -lt "$nGpus" ] do value=$(echo "$valueGpus" | sed -n $((nGpusCounter+1))p) echo "${name}${nGpusCounter}.value $value" From a5ccb70da91e81d64e44e5ab1287dc5f15a72ce7 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 12:58:21 +0100 Subject: [PATCH 048/695] New: GPU utilization --- plugins/gpu/nvidia_gpu_ | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index ccad1b83..ffe3034c 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -37,8 +37,7 @@ C =item * -Add support for specific professional GPU features such as number of compute -processes, clocks, power draw, utilization, and so on. +Add support for specific professional GPU features such as number of compute processes, clocks and so on. =item * @@ -87,6 +86,7 @@ if [ "$1" = "suggest" ]; then echo "mem" echo "fan" echo "power" + echo "utilization" exit 0 fi @@ -176,6 +176,20 @@ if [ "$1" = "config" ]; then : $((nGpusCounter=nGpusCounter+1)) done ;; + utilization) + echo 'graph_title GPU utilization' + echo 'graph_args -l 0 -u 100' + echo 'graph_vlabel Percentage' + echo 'graph_category sensors' + echo "graph_info GPU utilization of NVIDIA GPUs using driver version $driverVersion" + nGpusCounter=0 + while [ "$nGpusCounter" -lt "$nGpus" ] + do + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "utilization${nGpusCounter}.info GPU utilization information for $gpuName" + : $((nGpusCounter=nGpusCounter+1)) + done + ;; *) echo "Can't run without a proper symlink. Exiting." echo "Try running munin-node-configure --suggest." @@ -222,6 +236,9 @@ case $name in power) valueGpus=$(echo "$smiOutput" | grep "Power Draw" | cut -d ':' -f 2 | cut -d ' ' -f 2) ;; + utilization) + valueGpus=$(echo "$smiOutput" | grep "Gpu" | cut -d ':' -f 2 | cut -d ' ' -f 2) + ;; *) echo "Can't run without a proper symlink. Exiting." echo "Try running munin-node-configure --suggest." From cf52a44380e9e5d5de8600657f231f63696fd661 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 13:03:38 +0100 Subject: [PATCH 049/695] labels that match the core plugins --- plugins/gpu/nvidia_gpu_ | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index ffe3034c..679cabdd 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -113,7 +113,7 @@ if [ "$1" = "config" ]; then temp) echo 'graph_title GPU temperature' echo 'graph_args -l 0 -u 120' - echo 'graph_vlabel Degrees (C)' + echo 'graph_vlabel degrees Celsius' echo 'graph_category sensors' echo "graph_info Temperature information for NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 @@ -145,14 +145,14 @@ if [ "$1" = "config" ]; then # ...then output config data. echo 'graph_title GPU memory usage' echo 'graph_args -l 0 -u 100' - echo 'graph_vlabel Percentage' + echo 'graph_vlabel %' echo 'graph_category memory' echo "graph_info FB Memory usage for NVIDIA GPUs using driver version $driverVersion (total memory is $gpusTotalMem)" ;; fan) echo 'graph_title GPU fan speed' echo 'graph_args -l 0 -u 100' - echo 'graph_vlabel Percentage' + echo 'graph_vlabel %' echo 'graph_category sensors' echo "graph_info Fan speed of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 @@ -179,8 +179,8 @@ if [ "$1" = "config" ]; then utilization) echo 'graph_title GPU utilization' echo 'graph_args -l 0 -u 100' - echo 'graph_vlabel Percentage' - echo 'graph_category sensors' + echo 'graph_vlabel %' + echo 'graph_category system' echo "graph_info GPU utilization of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 while [ "$nGpusCounter" -lt "$nGpus" ] From 60a209360586675b3cafb5b3e93c7ef0850a26ce Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 22 Feb 2018 13:14:19 +0100 Subject: [PATCH 050/695] use variable names also in the configuration --- plugins/gpu/nvidia_gpu_ | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 679cabdd..b1308680 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -120,11 +120,11 @@ if [ "$1" = "config" ]; then while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) - echo "temp${nGpusCounter}.warning ${warning:-75}" - echo "temp${nGpusCounter}.critical ${critical:-95}" - echo "temp${nGpusCounter}.info Temperature information for $gpuName" + echo "${name}${nGpusCounter}.warning ${warning:-75}" + echo "${name}${nGpusCounter}.critical ${critical:-95}" + echo "${name}${nGpusCounter}.info Temperature information for $gpuName" : $((nGpusCounter=nGpusCounter+1)) - done + done ;; mem) # First determine total memory of each GPU... @@ -134,7 +134,7 @@ if [ "$1" = "config" ]; then while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) - echo "mem${nGpusCounter}.info Memory information for $gpuName" + echo "${name}${nGpusCounter}.info Memory information for $gpuName" gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $((nGpusCounter+1))p) gpusTotalMem="${gpusTotalMem}${gpuMem} for GPU ${nGpusCounter}" : $((nGpusCounter=nGpusCounter+1)) @@ -159,9 +159,9 @@ if [ "$1" = "config" ]; then while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) - echo "fan${nGpusCounter}.info Fan information for $gpuName" + echo "${name}${nGpusCounter}.info Fan information for $gpuName" : $((nGpusCounter=nGpusCounter+1)) - done + done ;; power) echo 'graph_title GPU power consumption' @@ -172,7 +172,7 @@ if [ "$1" = "config" ]; then while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) - echo "power${nGpusCounter}.info power consumption of $gpuName" + echo "${name}${nGpusCounter}.info power consumption of $gpuName" : $((nGpusCounter=nGpusCounter+1)) done ;; @@ -186,7 +186,7 @@ if [ "$1" = "config" ]; then while [ "$nGpusCounter" -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) - echo "utilization${nGpusCounter}.info GPU utilization information for $gpuName" + echo "${name}${nGpusCounter}.info GPU utilization information for $gpuName" : $((nGpusCounter=nGpusCounter+1)) done ;; @@ -255,6 +255,3 @@ do echo "${name}${nGpusCounter}.value $value" : $((nGpusCounter=nGpusCounter+1)) done - - - From 7ef2911ee1ba07c57ac4be34f4fbd4db172c60bd Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 23 Feb 2018 18:52:39 +0100 Subject: [PATCH 051/695] back to bash --- plugins/gpu/nvidia_gpu_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index b1308680..376b833a 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash # -*- sh -*- : << =cut From cc0efe410b82717f1a33a55c0b32845293fedc11 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 23 Feb 2018 18:55:59 +0100 Subject: [PATCH 052/695] quote $nvSmiExec and use for driverVersion --- plugins/gpu/nvidia_gpu_ | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 376b833a..1d20f03f 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -91,7 +91,7 @@ if [ "$1" = "suggest" ]; then fi # Get number of GPUs -nGpusOutput=$($nvSmiExec -L) +nGpusOutput=$("$nvSmiExec" -L) nGpus=$(echo "$nGpusOutput" | wc -l) if [ "$nGpus" -eq 0 ]; then # Exit if no GPUs found @@ -100,13 +100,13 @@ if [ "$nGpus" -eq 0 ]; then fi # Get full output from nvidia-smi -smiOutput=$($nvSmiExec -q) +smiOutput=$("$nvSmiExec" -q) # Check if config was requested if [ "$1" = "config" ]; then # Get driver version - driverVersion=$(nvidia-smi -q | grep "Driver Version" | cut -d : -f 2 | tr -d ' ') + driverVersion=$(echo "$smiOutput" | grep "Driver Version" | cut -d : -f 2 | tr -d ' ') # Configure graph depending on what which quantity will be plotted case $name in From 52917d2beaa3ec2c6ada73306914909a1da6e764 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 23 Feb 2018 19:27:22 +0100 Subject: [PATCH 053/695] remove quote from nGpusCounter --- plugins/gpu/nvidia_gpu_ | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 1d20f03f..f50d1d26 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -117,7 +117,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info Temperature information for NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.warning ${warning:-75}" @@ -131,7 +131,7 @@ if [ "$1" = "config" ]; then gpusTotalMemOutput=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' ') gpusTotalMem='' nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.info Memory information for $gpuName" @@ -156,7 +156,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info Fan speed of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.info Fan information for $gpuName" @@ -169,7 +169,7 @@ if [ "$1" = "config" ]; then echo 'graph_category sensors' echo "graph_info power consumption of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.info power consumption of $gpuName" @@ -183,7 +183,7 @@ if [ "$1" = "config" ]; then echo 'graph_category system' echo "graph_info GPU utilization of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.info GPU utilization information for $gpuName" @@ -199,7 +199,7 @@ if [ "$1" = "config" ]; then # Common stuff for all quantities nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.label $gpuName" @@ -221,7 +221,7 @@ case $name in usedMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2) valueGpus='' nGpusCounter=0 - while [ "$nGpusCounter" -lt "$nGpus" ] + while [ $nGpusCounter -lt "$nGpus" ] do totalMemGpu=$(echo "$totalMemGpus" | sed -n $((nGpusCounter+1))p) usedMemGpu=$(echo "$usedMemGpus" | sed -n $((nGpusCounter+1))p) @@ -249,7 +249,7 @@ case $name in # Print requested value nGpusCounter=0 -while [ "$nGpusCounter" -lt "$nGpus" ] +while [ $nGpusCounter -lt "$nGpus" ] do value=$(echo "$valueGpus" | sed -n $((nGpusCounter+1))p) echo "${name}${nGpusCounter}.value $value" From c9157be3fe0bdcc3fb54855056e1cebe94e4dcd5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 24 Feb 2018 22:58:04 +0100 Subject: [PATCH 054/695] iptables-related plugins: use "-w" for consistent results The parameter "-w" forces iptables to wait for a lock before printing data. The parameter requires iptables 1.6 (released 2015). See https://github.com/munin-monitoring/munin/issues/586 --- plugins/accounting/accounting_ | 22 +++++++++++----------- plugins/ip6/ip6_ | 8 ++++---- plugins/ip6/ip6t_accounting | 4 ++-- plugins/network/fwbuilder_ | 8 ++++---- plugins/network/ipt_accounting_ | 8 ++++---- plugins/network/ipt_basic_ | 8 ++++---- plugins/network/traffic_ipt | 4 ++-- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/plugins/accounting/accounting_ b/plugins/accounting/accounting_ index f6fd2116..7628d3a2 100644 --- a/plugins/accounting/accounting_ +++ b/plugins/accounting/accounting_ @@ -115,7 +115,7 @@ fi if [ "$1" == "autoconf" ]; then if [ -r /proc/net/dev ]; then - $IPTABLES -L INPUT -v -n -x >/dev/null 2>/dev/null + $IPTABLES -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -132,11 +132,11 @@ fi if [ "$1" = "suggest" ]; then if [ $PROTO = "ipv4" ]; then - $IPTABLES -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' - $IPTABLES -L OUTPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' + $IPTABLES -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' + $IPTABLES -L OUTPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' elif [ $PROTO == "ipv6" ]; then - $IPTABLES -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' - $IPTABLES -L OUTPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' + $IPTABLES -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' + $IPTABLES -L OUTPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' fi exit 0 @@ -192,12 +192,12 @@ if [ "$1" = "config" ]; then fi; echo 'multigraph '${0##*/}'_in' -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-in \*\/" | tr -s '*' '-' | awk "{ print \"tcpIN.value \" \$2 }" -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-in \*\/" | tr -s '*' '-' | awk "{ print \"udpIN.value \" \$2 }" -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-in \*\/" | tr -s '*' '-' | awk "{ print \"icmpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-in \*\/" | tr -s '*' '-' | awk "{ print \"tcpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-in \*\/" | tr -s '*' '-' | awk "{ print \"udpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-in \*\/" | tr -s '*' '-' | awk "{ print \"icmpIN.value \" \$2 }" echo echo 'multigraph '${0##*/}'_out' -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-out \*\/" | tr -s '*' '-' | awk "{ print \"tcpOUT.value \" \$2 }" -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-out \*\/" | tr -s '*' '-' | awk "{ print \"udpOUT.value \" \$2 }" -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-out \*\/" | tr -s '*' '-' | awk "{ print \"icmpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-out \*\/" | tr -s '*' '-' | awk "{ print \"tcpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-out \*\/" | tr -s '*' '-' | awk "{ print \"udpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-out \*\/" | tr -s '*' '-' | awk "{ print \"icmpOUT.value \" \$2 }" diff --git a/plugins/ip6/ip6_ b/plugins/ip6/ip6_ index 099de58c..20408378 100755 --- a/plugins/ip6/ip6_ +++ b/plugins/ip6/ip6_ @@ -28,7 +28,7 @@ IP=`basename $0 | sed 's/^ip6_//g' | tr '_' ':' ` if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - ip6tables -L INPUT -v -n -x >/dev/null 2>/dev/null + ip6tables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run ip6tables as user `whoami`)" exit 1 @@ -43,7 +43,7 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "suggest" ]; then - ip6tables -L INPUT -v -n -x 2>/dev/null | awk --posix '$8 ~ /^([0-9a-f]{1,4}(\:|\:\:)){1,7}([0-9a-f]{1,4})\/([0-9]{1,3})$/ { if (done[$8]!=1) {print $8; done[$8]=1;}}'|sed "s#/[0-9]\{1,3\}##" + ip6tables -L INPUT -v -n -x -w 2>/dev/null | awk --posix '$8 ~ /^([0-9a-f]{1,4}(\:|\:\:)){1,7}([0-9a-f]{1,4})\/([0-9]{1,3})$/ { if (done[$8]!=1) {print $8; done[$8]=1;}}'|sed "s#/[0-9]\{1,3\}##" exit 0 fi @@ -64,5 +64,5 @@ if [ "$1" = "config" ]; then exit 0 fi; -ip6tables -L INPUT -v -n -x | grep -m1 $IP | awk "{ print \"in.value \" \$2 }" -ip6tables -L OUTPUT -v -n -x | grep -m1 $IP | awk "{ print \"out.value \" \$2 }" +ip6tables -L INPUT -v -n -x -w | grep -m1 $IP | awk "{ print \"in.value \" \$2 }" +ip6tables -L OUTPUT -v -n -x -w | grep -m1 $IP | awk "{ print \"out.value \" \$2 }" diff --git a/plugins/ip6/ip6t_accounting b/plugins/ip6/ip6t_accounting index 2919a9c2..aaaebe50 100755 --- a/plugins/ip6/ip6t_accounting +++ b/plugins/ip6/ip6t_accounting @@ -10,7 +10,7 @@ graph_category network graph_args -l 0 graph_info ip6tables bites ipv6 EOF -ip6tables -vxL|grep -E 'ACC|REJ'|grep -v ^Chain|sed -e 's/dpt://g' -e 's/ .*://g'|awk '{print $NF"-"$4".label", $NF" "$4"\n",$NF"-"$4".min", 0}'|sed 's/^\s*//g' +ip6tables -vx -w -L | grep -E 'ACC|REJ'|grep -v ^Chain|sed -e 's/dpt://g' -e 's/ .*://g'|awk '{print $NF"-"$4".label", $NF" "$4"\n",$NF"-"$4".min", 0}'|sed 's/^\s*//g' exit 0 fi if [ "$1" = "autoconf" ]; then @@ -18,5 +18,5 @@ if [ "$1" = "autoconf" ]; then exit 0 fi -ip6tables -vxL|grep -E 'ACC|REJ'|grep -v ^Chain|awk '{print $NF"-"$4".value", $2}'|sed 's/^dpt://' +ip6tables -vx -w -L | grep -E 'ACC|REJ'|grep -v ^Chain|awk '{print $NF"-"$4".value", $2}'|sed 's/^dpt://' diff --git a/plugins/network/fwbuilder_ b/plugins/network/fwbuilder_ index c7742d75..23f8ba3c 100755 --- a/plugins/network/fwbuilder_ +++ b/plugins/network/fwbuilder_ @@ -61,7 +61,7 @@ IP=${IP/-/\/} if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - iptables -L INPUT -v -n -x >/dev/null 2>/dev/null + iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -77,7 +77,7 @@ fi if [ "$1" = "suggest" ]; then # find Chains for Accounting - iptables -L -n |grep ^ACCOUNTING |awk '{printf "%s\n%s\n",$4,$5}'| sort -u |sed 's#\/#-#' + iptables -L -n -w | grep ^ACCOUNTING |awk '{printf "%s\n%s\n",$4,$5}'| sort -u |sed 's#\/#-#' exit 0 fi @@ -99,6 +99,6 @@ if [ "$1" = "config" ]; then exit 0 fi; -echo "in.value $(( $(iptables -L -n -v -x |grep "ACCOUNTING" |awk '{printf "%s %s\n",$2,$9}' |grep $IP |awk '{printf "%s + ",$1}') 0 ))" -echo "out.value $(( $(iptables -L -n -v -x |grep "ACCOUNTING" |awk '{printf "%s %s\n",$2,$8}' |grep $IP |awk '{printf "%s + ",$1}') 0 ))" +echo "in.value $(( $(iptables -L -n -v -x -w | grep "ACCOUNTING" | awk '{printf "%s %s\n",$2,$9}' | grep $IP | awk '{printf "%s + ",$1}') 0 ))" +echo "out.value $(( $(iptables -L -n -v -x -w |grep "ACCOUNTING" | awk '{printf "%s %s\n",$2,$8}' | grep $IP | awk '{printf "%s + ",$1}') 0 ))" diff --git a/plugins/network/ipt_accounting_ b/plugins/network/ipt_accounting_ index bd5a2281..3050c368 100755 --- a/plugins/network/ipt_accounting_ +++ b/plugins/network/ipt_accounting_ @@ -42,7 +42,7 @@ ACC=`basename $0 | sed 's/^ipt_accounting_//g'` if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - iptables -L INPUT -v -n -x >/dev/null 2>/dev/null + iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -57,7 +57,7 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "suggest" ]; then - iptables -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACC\-\([a-zA-Z]*\) \*\/.*$/\1/p' + iptables -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACC\-\([a-zA-Z]*\) \*\/.*$/\1/p' exit 0 fi @@ -79,5 +79,5 @@ if [ "$1" = "config" ]; then exit 0 fi; -iptables -L INPUT -v -n -x | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"in.value \" \$2 }" -iptables -L OUTPUT -v -n -x | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"out.value \" \$2 }" +iptables -L INPUT -v -n -x -w | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"in.value \" \$2 }" +iptables -L OUTPUT -v -n -x -w | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"out.value \" \$2 }" diff --git a/plugins/network/ipt_basic_ b/plugins/network/ipt_basic_ index 6f05638c..ec17102f 100755 --- a/plugins/network/ipt_basic_ +++ b/plugins/network/ipt_basic_ @@ -45,7 +45,7 @@ iptables='/sbin/iptables' if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - RES=`$iptables -L $TNAME -nvx 2>&1 >/dev/null` + RES=`$iptables -L $TNAME -nvx -w 2>&1 >/dev/null` if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`; $RES)" exit 1 @@ -79,7 +79,7 @@ if [ "$1" = "initialise" ]; then exit 1 fi -IFACES=`$iptables -L munin_node -nvx | awk '$6 ~ /(eth|ppp)[0-9]/ { if (done[$6]!=1) {print $6; done[$6]=1;}}'` +IFACES=`$iptables -L munin_node -nvx -w | awk '$6 ~ /(eth|ppp)[0-9]/ { if (done[$6]!=1) {print $6; done[$6]=1;}}'` if [ "$1" = "config" ]; then @@ -108,7 +108,7 @@ if [ "$1" = "config" ]; then fi; if [ "$TYPE" = "pkts" ]; then - $iptables -L munin_node -nvx | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$1 }" + $iptables -L munin_node -nvx -w | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$1 }" else - $iptables -L munin_node -nvx | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$2 }" + $iptables -L munin_node -nvx -w | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$2 }" fi diff --git a/plugins/network/traffic_ipt b/plugins/network/traffic_ipt index a2a994e4..29f899f5 100755 --- a/plugins/network/traffic_ipt +++ b/plugins/network/traffic_ipt @@ -110,7 +110,7 @@ ipv6=0 diffv4=0 diffv6=0 -IPv4_bytes=$(iptables -L -n -v -x | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W*$' | while read pkts bytes rest; do echo $bytes; done) +IPv4_bytes=$(iptables -L -n -v -x -w | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W*$' | while read pkts bytes rest; do echo $bytes; done) if [ -z "$IPv4_bytes" ]; then echo "W: Unable to read rule from iptables, please add rules" >&2 @@ -118,7 +118,7 @@ else ipv4=$(echo $IPv4_bytes | sed -e 's/ / + /' | bc -l) fi -IPv6_bytes=$(ip6tables -L -n -v -x | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W*$' | while read pkts bytes rest; do echo $bytes; done) +IPv6_bytes=$(ip6tables -L -n -v -x -w | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W*$' | while read pkts bytes rest; do echo $bytes; done) if [ -z "$IPv6_bytes" ]; then echo "W: Unable to read rule from ip6tables, please add rules" >&2 From 0d775b2827f54d130b5f5c8c5821eeb44022e2a2 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 5 Mar 2018 13:48:38 +0100 Subject: [PATCH 055/695] Implement plugin for monitoring nullmailer. --- plugins/mail/nullmailer_queue | 90 +++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 plugins/mail/nullmailer_queue diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue new file mode 100755 index 00000000..d5a92867 --- /dev/null +++ b/plugins/mail/nullmailer_queue @@ -0,0 +1,90 @@ +#!/bin/bash + +: <<=cut + +=head1 NAME + +nullmailer_queue - monitor for nullmailer's send queue. + +=head1 APPLICABLE SYSTEMS + +This plugin is applicable to any system running the nullmailer MTA. + +=head1 CONFIGURATION + +This plugin needs to be able to read the nullmailer queue, so it +will need to run as that user or root. + +Additionally, the nullmailer queue directory may vary; in that case you +can specify it with the "queuedir" and "errordir" environment variables. + +Example: + +[nullmailer_queue] +user nullmail + +env.queuedir /var/spool/nullmailer/queue +env.errordir /var/spool/nullmailer/failed + +=head1 INTERPRETATION + +This plugin will draw a stack of 2: the number of emails in the queue, and +the number of emails that have permanently failed sending. The latter has +a warning attached, since those should never happen. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 1.0.0 + +=head1 AUTHOR + +Bert Peters + +=head1 LICENSE + +GPLv2 + +=cut + +queuedir=${queuedir:-/var/spool/nullmailer/queue} +errordir=${errordir:-/var/spool/nullmailer/failed} + +case $1 in + autoconf) + if hash nullmailer-queue &> /dev/null; then + [[ -r "$queuedir" ]] && echo yes || echo "no (queue dir not readable)" + else + echo "no (nullmailer not installed)" + fi + ;; + + config) + cat <<-EOF +graph_args -l 0 +graph_title Nullmailer queue +graph_vlabel emails +graph_total total +graph_category mail + +queue.label queued +queue.draw AREASTACK +queue.info Number of emails currently in the queue + +failed.label failed +failed.draw AREASTACK +failed.warning 0:0 +failed.info Number of emails that have permanently failed to send +EOF + ;; + + *) + echo "queue.value $(find "$queuedir" -type f | wc -l)" + # Failed does not exist until there has been a failure, so mute the "file not found" + echo "failed.value $(find "$errordir" -type f 2> /dev/null | wc -l)" + ;; +esac From 4ca4e8d937bc29cba417b704b09ba39e59489db4 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 6 Mar 2018 11:08:02 +0100 Subject: [PATCH 056/695] Implement suggested changes. --- plugins/mail/nullmailer_queue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue index d5a92867..47a0f1e2 100755 --- a/plugins/mail/nullmailer_queue +++ b/plugins/mail/nullmailer_queue @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh : <<=cut @@ -22,7 +22,6 @@ Example: [nullmailer_queue] user nullmail - env.queuedir /var/spool/nullmailer/queue env.errordir /var/spool/nullmailer/failed @@ -56,8 +55,8 @@ errordir=${errordir:-/var/spool/nullmailer/failed} case $1 in autoconf) - if hash nullmailer-queue &> /dev/null; then - [[ -r "$queuedir" ]] && echo yes || echo "no (queue dir not readable)" + if hash nullmailer-queue >/dev/null 2>/dev/null; then + [ -r "$queuedir" ] && echo yes || echo "no (queue dir not readable)" else echo "no (nullmailer not installed)" fi From 2078cab6905f551be192d529d243c93f65289904 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 6 Mar 2018 11:29:54 +0100 Subject: [PATCH 057/695] Bugfix: fix graph scaling in munstrap. Bootstrap sets box-sizing to border-box to make lay-outing easier, but this damages the scaling of the munin graphs, which get a border to indicate their warning state. This patch makes all images have box-sizing: content-box, so that the graphs are actually the intended size, rather than the intended size minus the border, causing a blurry image. --- templates/munstrap/static/css/style-munstrap.css | 3 ++- templates/munstrap/static/css/style-munstrap.min.css | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/munstrap/static/css/style-munstrap.css b/templates/munstrap/static/css/style-munstrap.css index ae76f730..89210370 100644 --- a/templates/munstrap/static/css/style-munstrap.css +++ b/templates/munstrap/static/css/style-munstrap.css @@ -96,6 +96,7 @@ ul.groupview, ul.groupview ul { } img { + box-sizing: content-box; border: 2px solid transparent; } @@ -117,4 +118,4 @@ img.unkn { .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 8c7da5e5..ae3ad298 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}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 +@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{box-sizing:content-box;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 c6ade37de46ac57ecc71296b2465e7822d89eb7b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 01:21:49 +0100 Subject: [PATCH 058/695] cleanup: remove patch rejection garbage --- plugins/dovecot/dovecot.rej | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 plugins/dovecot/dovecot.rej diff --git a/plugins/dovecot/dovecot.rej b/plugins/dovecot/dovecot.rej deleted file mode 100644 index 6df1087a..00000000 --- a/plugins/dovecot/dovecot.rej +++ /dev/null @@ -1,10 +0,0 @@ -diff a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot (rejected hunks) -@@ -23,7 +23,7 @@ $aborted = 0; - - $logfile = $ENV{'LOGFILE'} || '/var/log/mail.log'; - --# Use an overridden $PATH for all external programs if needed -+# Use an overrided $PATH for all external programs if needed - $DOVEADM = "doveadm"; - - if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { From 451af405b75752d74895aec0e27033bdf5f00aea Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 01:23:29 +0100 Subject: [PATCH 059/695] deb_packages: rename config file from "*.munin-conf" to "*.munin.conf" preparation for an additional file permission test (based on extensions) --- plugins/apt/deb_packages/README.md | 4 ++-- .../{deb_packages.munin-conf => deb_packages.munin.conf} | 0 plugins/apt/deb_packages/deb_packages.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename plugins/apt/deb_packages/{deb_packages.munin-conf => deb_packages.munin.conf} (100%) diff --git a/plugins/apt/deb_packages/README.md b/plugins/apt/deb_packages/README.md index 36fe2c32..7dbfc489 100644 --- a/plugins/apt/deb_packages/README.md +++ b/plugins/apt/deb_packages/README.md @@ -30,14 +30,14 @@ check out this git repository from git clone git://github.com/munin-monitoring/contrib.git cd contrib/plugins/apt/deb_packages sudo cp deb_packages.py /etc/munin/plugins/deb_packages - sudo cp deb_packages.munin-conf /etc/munin/plugin-conf.d/deb_packages + sudo cp deb_packages.munin.conf /etc/munin/plugin-conf.d/deb_packages Verify the installation by sudo munin-run deb_packages ### Configuration -If you copied deb_packages.munin-conf to plugin-conf.d you have a starting point. +If you copied deb_packages.munin.conf to plugin-conf.d you have a starting point. A typical configuration looks like this [deb_packages] diff --git a/plugins/apt/deb_packages/deb_packages.munin-conf b/plugins/apt/deb_packages/deb_packages.munin.conf similarity index 100% rename from plugins/apt/deb_packages/deb_packages.munin-conf rename to plugins/apt/deb_packages/deb_packages.munin.conf diff --git a/plugins/apt/deb_packages/deb_packages.py b/plugins/apt/deb_packages/deb_packages.py index 371e4d10..538a8efc 100755 --- a/plugins/apt/deb_packages/deb_packages.py +++ b/plugins/apt/deb_packages/deb_packages.py @@ -895,7 +895,7 @@ check out this git repository from git clone git://github.com/munin-monitoring/contrib.git cd contrib/plugins/apt/deb_packages sudo cp deb_packages.py /etc/munin/plugins/deb_packages - sudo cp deb_packages.munin-conf /etc/munin/plugin-conf.d/deb_packages + sudo cp deb_packages.munin.conf /etc/munin/plugin-conf.d/deb_packages =back @@ -910,7 +910,7 @@ Verify the installation by =head1 CONFIGURATION -If you copied deb_packages.munin-conf to plugin-conf.d you have a starting point. +If you copied deb_packages.munin.conf to plugin-conf.d you have a starting point. A typical configuration looks like this From 864863c20309df415367b5f4eb33837d7d2e6fb3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 01:56:59 +0100 Subject: [PATCH 060/695] snmp__netscaler_connections: fixed syntax error proposed by Cyclenerd (see #853) --- plugins/netscaler/snmp__netscaler_connections | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/netscaler/snmp__netscaler_connections b/plugins/netscaler/snmp__netscaler_connections index d8048643..9cc822f1 100755 --- a/plugins/netscaler/snmp__netscaler_connections +++ b/plugins/netscaler/snmp__netscaler_connections @@ -148,7 +148,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "ssl.draw LINE2\n"; print "ssl.info Currently active SSL sessions.\n"; - for my $field qw(client server ssl) { + for my $field (qw(client server ssl)) { print_thresholds($field); } exit 0; From c1acbea2db9a3d7a61db2bc0073ac07ff65f8288 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 02:46:47 +0100 Subject: [PATCH 061/695] reddit_karma_: fix shell quoting issues --- plugins/reddit_karma/reddit_karma_ | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/reddit_karma/reddit_karma_ b/plugins/reddit_karma/reddit_karma_ index b7389846..6d1d146e 100755 --- a/plugins/reddit_karma/reddit_karma_ +++ b/plugins/reddit_karma/reddit_karma_ @@ -61,8 +61,9 @@ fi # Main ## # Get current karma stats. -link_karma=$(curl -s http://www.reddit.com/user/${reddit_user}/about.json | grep -Eo 'link_karma": [0-9]+' | cut -d' ' -f2) -comment_karma=$(curl -s http://www.reddit.com/user/${reddit_user}/about.json | grep -Eo 'comment_karma": [0-9]+' | cut -d' ' -f2) +about_user_url="http://www.reddit.com/user/${reddit_user}/about.json" +link_karma=$(curl -s "$about_user_url" | grep -Eo 'link_karma": [0-9]+' | cut -d' ' -f2) +comment_karma=$(curl -s "$about_user_url" | grep -Eo 'comment_karma": [0-9]+' | cut -d' ' -f2) # Output karma stats. echo "link_karma.value $link_karma" From 277730393da1990004b1752fc59a45ee06fbd158 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 03:00:18 +0100 Subject: [PATCH 062/695] zpool_iostat: fix 'checkbashisms' issue It was not a shell syntax problem - but just a checkbashisms issue. --- plugins/zfs/zpool_iostat | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/zfs/zpool_iostat b/plugins/zfs/zpool_iostat index 63400422..0e655f58 100755 --- a/plugins/zfs/zpool_iostat +++ b/plugins/zfs/zpool_iostat @@ -71,8 +71,7 @@ get_device_iostat_column() { local stat_column="$2" # convert all numeric values into kB echo "$iostats" \ - | gawk '{ if ($1 == "'"$device_label"'") - print $'"$stat_column"'; }' \ + | gawk '{ if ($1 == "'"$device_label"'") print $'"$stat_column"'; }' \ | gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' From bd26b1b42dbf445b4d2e670a00b35f5a93684d64 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 03:09:25 +0100 Subject: [PATCH 063/695] raspi_temp: improve quoting --- plugins/raspberry-pi/raspi_temp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/raspberry-pi/raspi_temp b/plugins/raspberry-pi/raspi_temp index 733271b7..55f85e45 100755 --- a/plugins/raspberry-pi/raspi_temp +++ b/plugins/raspberry-pi/raspi_temp @@ -48,4 +48,4 @@ EOM exit 0;; esac -echo "temp.value `vcgencmd measure_temp | sed -n "s/temp=\(.*\)'C/\1/p"`" +echo "temp.value $(vcgencmd measure_temp | sed -n "s/temp=\(.*\)\'C/\1/p")" From 189c3953e761031276e3540989cb7bc9054156fa Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 1 Jul 2017 15:51:37 +0200 Subject: [PATCH 064/695] shell: use 'command -v' instead of 'hash' (for non-bash) see https://stackoverflow.com/a/677212 --- plugins/chat/tinychat_users_ | 2 +- plugins/mail/nullmailer_queue | 2 +- plugins/network/multibandwidth | 2 +- plugins/rabbitmq/rabbitmq_connections | 2 +- plugins/reddit_karma/reddit_karma_ | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/chat/tinychat_users_ b/plugins/chat/tinychat_users_ index 39a40679..d366b372 100755 --- a/plugins/chat/tinychat_users_ +++ b/plugins/chat/tinychat_users_ @@ -19,7 +19,7 @@ room=${0##*tinychat_users_} ## if [ "$1" = "autoconf" ]; then # Check that curl is installed - if hash curl >/dev/null 2>&1; then + if command -v curl >/dev/null 2>&1; then echo "yes" else echo "no (no curl installed)" diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue index 47a0f1e2..65806b88 100755 --- a/plugins/mail/nullmailer_queue +++ b/plugins/mail/nullmailer_queue @@ -55,7 +55,7 @@ errordir=${errordir:-/var/spool/nullmailer/failed} case $1 in autoconf) - if hash nullmailer-queue >/dev/null 2>/dev/null; then + if command -v nullmailer-queue >/dev/null 2>/dev/null; then [ -r "$queuedir" ] && echo yes || echo "no (queue dir not readable)" else echo "no (nullmailer not installed)" diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index 4f4f7298..a7edf4c8 100644 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -78,7 +78,7 @@ case $1 in done exit 0;; autoconf) - if hash bing 2>/dev/null; then + if command -v bing >/dev/null 2>&1; then echo 'yes' exit 0; else diff --git a/plugins/rabbitmq/rabbitmq_connections b/plugins/rabbitmq/rabbitmq_connections index e0fc2a89..f7adbc5a 100755 --- a/plugins/rabbitmq/rabbitmq_connections +++ b/plugins/rabbitmq/rabbitmq_connections @@ -89,7 +89,7 @@ fi # real work - i.e. display the data. Almost always this will be # "value" subfield for every data field. -if hash rabbitmqctl >/dev/null 2>&1; then +if command -v rabbitmqctl >/dev/null 2>&1; then connections=$(HOME=/tmp rabbitmqctl list_connections state | grep -c running) else echo "$0: Could not run rabbitmqctl" >&2 diff --git a/plugins/reddit_karma/reddit_karma_ b/plugins/reddit_karma/reddit_karma_ index 6d1d146e..689a283c 100755 --- a/plugins/reddit_karma/reddit_karma_ +++ b/plugins/reddit_karma/reddit_karma_ @@ -32,7 +32,7 @@ reddit_user=${0##*reddit_karma_} ## if [ "$1" = "autoconf" ]; then # Check that curl is installed - if hash curl >/dev/null 2>&1; then + if command -v curl >/dev/null 2>&1; then echo "yes" else echo "no (no curl installed)" From 7ec35e1753e54d53e3ac85ad37b35c62e3495440 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 04:17:18 +0100 Subject: [PATCH 065/695] tests: work around 'checkbashisms' complaining about "command -v" "command -v" should be the preferred way of determining the location (or existence) of an executable. Sadly "checkbashisms" interprets "command -v" as a possible bashism, since "command -v" was introduced in IEEE 1003.1 2013. But checkbashisms checks for compliance with IEEE 1003.1 2004 - here "command -v" is listed only as a portability extension. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=733511 for the discussion about checkbashism and "command -v". See https://unix.stackexchange.com/a/85250 for the discussion about "command -v" being the most portable check for a program's location. The workaround itself is bit ugly: 'checkbashisms' is monkey-patched and fed into the perl interpreter. This makes it harder to re-run the check command manually (due to complicated quoting). --- t/test.t | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/t/test.t b/t/test.t index 08debf70..ed255731 100644 --- a/t/test.t +++ b/t/test.t @@ -56,8 +56,22 @@ sub process_file { description => 'sh syntax check' } ); + my $checkbashisms_location = `command -v checkbashisms 2>/dev/null`; + chomp($checkbashisms_location); + my $command; + if ($checkbashisms_location ne "") { + # monkey-patch "checkbashisms" in order to allow "command -v" + # see https://unix.stackexchange.com/a/85250: "command -v" vs. which/hash/... + # see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=733511 + my $run_modified_checkbashisms = q/sed 's#command\\\s+-\[\^p\]#command\s+-[^pvV]#'/ + . " '$checkbashisms_location' | perl - '$file'"; + $command = [ 'sh', '-c', $run_modified_checkbashisms ]; + } else { + # make sure that the non-confusing "checkbashisms not found" message is displayed + $command = [ 'checkbashisms', $file ]; + } run_check( - { command => [ 'checkbashisms', $file ], + { command => $command, description => 'checkbashisms' } ); From ea06de7c4f5f8e282a8ecd8924e6382260d281be Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 04:42:46 +0100 Subject: [PATCH 066/695] vserver_jiffies: remove unused variable 'KCIN' --- plugins/vserver/vserver_jiffies | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/vserver/vserver_jiffies b/plugins/vserver/vserver_jiffies index caf196c7..dfb8dd41 100755 --- a/plugins/vserver/vserver_jiffies +++ b/plugins/vserver/vserver_jiffies @@ -27,7 +27,6 @@ VSERVERS=(${=vservers}) STRIPDOMAIN="$stripdomain" INFO=($(sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo '')) -KCIN="$[ 16#${INFO[2]} ]"; NAMELOC="nsproxy" From 22523e8f5b096a8b6c2a31a6982ac6a4ebad2777 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 04:45:00 +0100 Subject: [PATCH 067/695] snmp__netscaler_cpu: unified whitespace in 'config' --- plugins/netscaler/snmp__netscaler_cpu | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/netscaler/snmp__netscaler_cpu b/plugins/netscaler/snmp__netscaler_cpu index b9ff57f1..34dc2f2c 100755 --- a/plugins/netscaler/snmp__netscaler_cpu +++ b/plugins/netscaler/snmp__netscaler_cpu @@ -153,17 +153,17 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "avg.label avg\n"; print "avg.draw AREA\n"; print "avg.info Average load.\n"; - foreach my $v (@cpu_name){ - print $v.".label ".$v."\n"; - print $v.".draw LINE2\n"; - print $v.".info CPU usage of ".$v."\n"; - print_thresholds($v); + foreach my $v (@cpu_name){ + print $v.".label ".$v."\n"; + print $v.".draw LINE2\n"; + print $v.".info CPU usage of ".$v."\n"; + print_thresholds($v); } for my $field qw(avg) { print_thresholds($field); } - &close_session($session); + &close_session($session); exit 0; } From 74b58a592ad2cd6b830cee4a30a05d99546ce861 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 04:49:45 +0100 Subject: [PATCH 068/695] perl modules: fix braces for loop array --- plugins/apache/apache_vhosts/apache_pipelogger | 2 +- plugins/netscaler/snmp__netscaler_cpu | 2 +- plugins/squeezebox/squeezebox_multi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/apache/apache_vhosts/apache_pipelogger b/plugins/apache/apache_vhosts/apache_pipelogger index 09e39617..73c73eee 100644 --- a/plugins/apache/apache_vhosts/apache_pipelogger +++ b/plugins/apache/apache_vhosts/apache_pipelogger @@ -95,7 +95,7 @@ sub periodic_write { $old{$vpm}{'avg_bytes'}=sprintf("%d",($old{$vpm}{'avg_bytes'}+$temp{$vpm}{'avg_bytes'})/2); # reset local counters - foreach my $check qw(requests bytes time cml_time max_bytes avg_bytes max_time avg_time) { + foreach my $check (qw(requests bytes time cml_time max_bytes avg_bytes max_time avg_time)) { $temp{$vpm}{$check}=0; } diff --git a/plugins/netscaler/snmp__netscaler_cpu b/plugins/netscaler/snmp__netscaler_cpu index 34dc2f2c..e0ad7bb5 100755 --- a/plugins/netscaler/snmp__netscaler_cpu +++ b/plugins/netscaler/snmp__netscaler_cpu @@ -160,7 +160,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print_thresholds($v); } - for my $field qw(avg) { + for my $field (qw(avg)) { print_thresholds($field); } &close_session($session); diff --git a/plugins/squeezebox/squeezebox_multi b/plugins/squeezebox/squeezebox_multi index 62fe34e7..9c49ea8c 100755 --- a/plugins/squeezebox/squeezebox_multi +++ b/plugins/squeezebox/squeezebox_multi @@ -139,7 +139,7 @@ my $converter = Text::Iconv->new("UTF-8", "LATIN1"); # $converted = $converter->convert("Text to convert"); # First all the simple readings -foreach my $attr qw (albums artists genres songs) { +foreach my $attr (qw(albums artists genres songs)) { $conn->print ("info total ${attr} ?"); my $line = uri_unescape($conn->getline); if ($line =~ /^info total ${attr} (\d+)$/) { From 620526a2d8630811d14d252a5468e3646c1f03a3 Mon Sep 17 00:00:00 2001 From: Andi Nitsche Date: Thu, 8 Mar 2018 12:11:32 +0100 Subject: [PATCH 069/695] Update amd_gpu_ --- plugins/gpu/amd_gpu_ | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/gpu/amd_gpu_ b/plugins/gpu/amd_gpu_ index bdfe54d7..4accf67c 100755 --- a/plugins/gpu/amd_gpu_ +++ b/plugins/gpu/amd_gpu_ @@ -116,7 +116,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` + 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 "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` + 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 "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` + 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 "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` + 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 "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` + 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 a836f07d56e35d230cdd9586bd35aae6343e9771 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 23:08:25 +0100 Subject: [PATCH 070/695] nginx_byprojects: use more compatible perl expression "%ENV{foo}" does not work with older perl versions (e.g. v5.14). --- plugins/nginx/nginx_byprojects/byprojects_access | 2 +- plugins/nginx/nginx_byprojects/byprojects_bandwidth | 2 +- plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/nginx/nginx_byprojects/byprojects_access b/plugins/nginx/nginx_byprojects/byprojects_access index bc400504..22572bf3 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_access +++ b/plugins/nginx/nginx_byprojects/byprojects_access @@ -38,7 +38,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = %ENV{@loglist}; +my %envLogs = $ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } diff --git a/plugins/nginx/nginx_byprojects/byprojects_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_bandwidth index af245e96..42a036ec 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_bandwidth @@ -44,7 +44,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = %ENV{@loglist}; +my %envLogs = $ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } diff --git a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth index 21b36902..0feb15fd 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth @@ -44,7 +44,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = %ENV{@loglist}; +my %envLogs = $ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } From c01fb0a9041a1c3c66aa64cf2bd95d554da2d49e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Jun 2017 00:13:59 +0200 Subject: [PATCH 071/695] travis: libsys-virt-perl installed via apt --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd210c31..1f3ad4bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ install: - 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 + - sudo apt-get --no-install-recommends install libsys-virt-perl # Modules used by test script - cpanm --notest Capture::Tiny @@ -51,11 +52,9 @@ install: - 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 # - File::Tail::Multi # - Sun::Solaris::Kstat # - VMware::VIRuntime # - MythTV -script: "PERL5LIB=$PERL5LIB:/usr/share/perl5 prove" +script: "PERL5LIB=$PERL5LIB:/usr/share/perl5:/usr/lib/perl5 prove" From a3d0fc15d9ce015a48adfa7b94b665b1aeee6fa1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Jun 2017 00:14:19 +0200 Subject: [PATCH 072/695] travis: Switch installed via cpan --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1f3ad4bd..546428f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,8 +43,9 @@ install: - cpanm --notest POE::Quickie - cpanm --notest Proc::ProcessTable - cpanm --notest Redis - - cpanm --notest WWW::Mechanize::TreeBuilder + - cpanm --notest Switch - cpanm --notest Text::Iconv + - cpanm --notest WWW::Mechanize::TreeBuilder - cpanm --notest YAML - cpanm --notest XML::LibXML - cpanm --notest XML::Simple From 5eb8251fa6a7b0964086d4a4ce5542016b3570ea Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Jun 2017 05:31:05 +0200 Subject: [PATCH 073/695] travis: upgrade distribution to Ubuntu trusty --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 546428f1..04aa8947 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ --- +dist: trusty language: perl install: - sudo apt-get update @@ -58,4 +59,4 @@ install: # - Sun::Solaris::Kstat # - VMware::VIRuntime # - MythTV -script: "PERL5LIB=$PERL5LIB:/usr/share/perl5:/usr/lib/perl5 prove" +script: "PERL5LIB=$PERL5LIB:/usr/share/perl5 prove" From 42c8483c4f54b722fb9842775a9ade437f1fd260 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Jun 2017 00:56:46 +0200 Subject: [PATCH 074/695] travis: assemble module search path for mixture of cpan and apt modules --- .travis.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 04aa8947..4a4f4e3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - cpanm --notest Capture::Tiny - cpanm --notest File::Find - cpanm --notest Test::More - # + # Modules used by plugins - cpanm --notest Asterisk::AMI - cpanm --notest BerkeleyDB @@ -59,4 +59,20 @@ install: # - Sun::Solaris::Kstat # - VMware::VIRuntime # - MythTV -script: "PERL5LIB=$PERL5LIB:/usr/share/perl5 prove" + +# Mixing modules installed via cpan and apt is a bit problematic: +# * perl (as it is used by travis) seems to be built without threading support +# * perl modules installed via apt were built against a threaded perl (e.g. DBI/DBI.so) +# Thus we need to make sure, that the modules installed via CPAN and the binary modules prepared by +# travis are preferred over the binary perl modules installed via apt (e.g. below /usr/lib/perl). +# Since the PERL5LIB environment variable precedes all other search paths, we use the sequence +# below in order to achieve the following order of preference: +# 1) /usr/share/perl5 (unknown requirement) +# 2) perl search path defined by travis environment +# 3) binary system-wide installed perl modules (via apt) +script: + - dpkg -L libsys-virt-perl + - mkdir -p ~/system-perl/auto + - ln -s /usr/lib/perl5/Sys ~/system-perl/ + - ln -s /usr/lib/perl5/auto/Sys ~/system-perl/auto/ + - "PERL5LIB=$PERL5LIB:/usr/share/perl5:~/system-perl prove" From 34c2ca4dcd5291c19b45cf2e2c1c637cb9f26bca Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 1 Jul 2017 00:41:15 +0200 Subject: [PATCH 075/695] remove perl --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a4f4e3e..7a8c452d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ --- dist: trusty -language: perl install: - sudo apt-get update - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint From 81c1afa7badfd2da4cd8c8896442c6f1cbb99fbd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 1 Jul 2017 00:53:02 +0200 Subject: [PATCH 076/695] perl: switch to system-wide perl --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7a8c452d..c74bd2b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ install: - sudo apt-get --no-install-recommends install munin-node - sudo apt-get --no-install-recommends install libsys-virt-perl + - cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) + # Modules used by test script - cpanm --notest Capture::Tiny - cpanm --notest File::Find From a639d4b447acc7e46ab05e4f3b7b9b307979bd2d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 1 Jul 2017 01:15:12 +0200 Subject: [PATCH 077/695] travis: explain change of perl environment more details can be found in the discussion regarding this change: https://github.com/munin-monitoring/contrib/pull/853 --- .travis.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index c74bd2b4..97decfbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ install: - sudo apt-get --no-install-recommends install munin-node - sudo apt-get --no-install-recommends install libsys-virt-perl + # we use locally installed cpan modules instead of travis' builtin 'homebrew' perl support + # The homebrew variation does not work for us, since: + # * it is built without threading + # * the 'libvirt' plugin requires Sys::Virt, which is not available via cpan anymore - thus we need to install the Debian package 'libsys-virt-perl', which was built with threading - cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) # Modules used by test script @@ -61,19 +65,5 @@ install: # - VMware::VIRuntime # - MythTV -# Mixing modules installed via cpan and apt is a bit problematic: -# * perl (as it is used by travis) seems to be built without threading support -# * perl modules installed via apt were built against a threaded perl (e.g. DBI/DBI.so) -# Thus we need to make sure, that the modules installed via CPAN and the binary modules prepared by -# travis are preferred over the binary perl modules installed via apt (e.g. below /usr/lib/perl). -# Since the PERL5LIB environment variable precedes all other search paths, we use the sequence -# below in order to achieve the following order of preference: -# 1) /usr/share/perl5 (unknown requirement) -# 2) perl search path defined by travis environment -# 3) binary system-wide installed perl modules (via apt) script: - - dpkg -L libsys-virt-perl - - mkdir -p ~/system-perl/auto - - ln -s /usr/lib/perl5/Sys ~/system-perl/ - - ln -s /usr/lib/perl5/auto/Sys ~/system-perl/auto/ - - "PERL5LIB=$PERL5LIB:/usr/share/perl5:~/system-perl prove" + - prove From 29e6f53cad09ecff23f4510965eb8c25f215e39d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 23:21:55 +0100 Subject: [PATCH 078/695] test: removed duplicate branch for "zsh" added (by mistake) in c503e0dbcfd8ccffbe7e86c1437266baf53802c9 --- t/test.t | 8 -------- 1 file changed, 8 deletions(-) diff --git a/t/test.t b/t/test.t index ed255731..7f2b5fc9 100644 --- a/t/test.t +++ b/t/test.t @@ -85,14 +85,6 @@ 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 ebe5be1d0656b52727ffcdb2cd4c9d15b7a582e4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 8 Mar 2018 23:33:33 +0100 Subject: [PATCH 079/695] tests: verify executable flag for plugins with shebang --- t/test.t | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/t/test.t b/t/test.t index 7f2b5fc9..86b76b95 100644 --- a/t/test.t +++ b/t/test.t @@ -48,7 +48,14 @@ sub process_file { my ( $file, $filename, $interpreter, $arguments ) = @_; use v5.10.1; - if ( $interpreter =~ m{/bin/sh} ) { + if ( ! -x $file ) { + # missing executable flag + diag( + sprintf("\nFile '%s' lacks executable permission bits. Maybe try 'chmod +x $file'?\n", + $file) + ); + } + elsif ( $interpreter =~ m{/bin/sh} ) { subtest $filename => sub { plan tests => 2; run_check( From 7c62b9b6deb8191889ba817df10005366524fa8b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 00:18:05 +0100 Subject: [PATCH 080/695] added executable permission for multiple plugins --- plugins/accounting/accounting_ | 0 plugins/apache/apache_average_time_last_n_requests | 0 plugins/apache/apache_byprojects/byprojects_access | 0 plugins/apache/apache_byprojects/byprojects_bandwidth | 0 plugins/apache/apache_byprojects/byprojects_inout_bandwidth | 0 plugins/apache/apache_vhosts/apache_logparser | 0 plugins/apache/apache_vhosts/apache_pipelogger | 0 plugins/apache/apache_vhosts/apache_vhosts | 0 plugins/apache/page_load | 0 plugins/arp/arp_bsd_ | 0 plugins/dhcp/dhcpd-pools | 0 plugins/disk/btrfs_subvol_usage | 0 plugins/disk/e2 | 0 plugins/disk/hdsentinel | 0 plugins/disk/quota2percent_ | 0 plugins/djabberd/djabberd_ | 0 plugins/dovecot/dovecot_stats_ | 0 plugins/drupal/drupal_page_speed_test.php | 0 plugins/dvb/2wcomdsr_ | 0 plugins/freeradius/freeradius_queue | 0 plugins/games/game | 0 plugins/glassfish/glassfish_counters_ | 0 plugins/icecast/icecast2_ | 0 plugins/icecast/icecast2_all | 0 plugins/jchkmail/jchkmail_counters_ | 0 plugins/jenkins/jenkins_ | 0 plugins/mail/postfwd-rbl | 0 plugins/mediawiki/mediawiki_api | 0 plugins/memcached/memcached_servers_ | 0 plugins/minecraft/bukkit-jsonapi-players | 0 plugins/minecraft/bukkit-jsonapi-ramusage | 0 plugins/minecraft/bukkit-jsonapi-tps | 0 plugins/minecraft/bukkit-statistician-killshostile | 0 plugins/minecraft/bukkit-statistician-killsneutral | 0 plugins/minecraft/bukkit-statistician-killspassive | 0 plugins/minecraft/bukkit-statistician-players | 0 plugins/minecraft/bukkit-ultrabans-shame | 0 plugins/minecraft/minecraft-users-ram_ | 0 plugins/mongodb/mongo_collection_ | 0 plugins/moodle/moodle_files | 0 plugins/moodle/moodle_logs | 0 plugins/moodle/moodle_mod_chat | 0 plugins/moodle/moodle_mod_forum | 0 plugins/moodle/moodle_mod_quiz | 0 plugins/moodle/moodle_modules_total | 0 plugins/moodle/moodle_users_online | 0 plugins/moodle/moodle_users_total | 0 plugins/mysql/hs_read | 0 plugins/mysql/hs_write | 0 plugins/network/ddclient | 0 plugins/network/multibandwidth | 0 plugins/network/ubiquiti_airfiber_ | 0 plugins/network/ubiquiti_airos_ | 0 plugins/nfs-freebsd/nfs_client | 0 plugins/nfs-freebsd/nfs_client_cache | 0 plugins/percona/percona_ | 0 plugins/pf/pf_tables_ | 0 plugins/php/php_errors_ | 0 plugins/php/php_opcache.php | 0 plugins/php/php_time_execution | 0 plugins/postgresql/postgres_tuplesratio_ | 0 plugins/power/eatonups_ | 0 plugins/power/upsmonpro_ | 0 plugins/printer/snmp__hpclj | 0 plugins/prosody/prosody_ | 0 plugins/puma/puma_ | 0 plugins/rackspace/rackspace_cdn_count.php | 0 plugins/rackspace/rackspace_cdn_size.php | 0 plugins/rethinkdb/rethinkdb_node_io | 0 plugins/riak/riak_fsm_time_95 | 0 plugins/router/snmp__cisco_sbs_cpu | 0 plugins/sabnzbd/sabnzbd_dataleft | 0 plugins/sabnzbd/sabnzbd_speed | 0 plugins/security/fail2ban_ | 0 plugins/senderscore/senderscore | 0 plugins/sendmail/sendmail_mailq | 0 plugins/sensors/voltcraft_tcm220_ | 0 plugins/sge/sge_queue_xml_ | 0 plugins/smf/smf_errors | 0 plugins/smf/smf_mail_queue | 0 plugins/smf/smf_online | 0 plugins/smf/smf_stats | 0 plugins/snmp/snmp__airport | 0 plugins/snmp/snmp__cyberpower | 0 plugins/synology/snmp__synology_hddtemp | 0 plugins/synology/snmp__synology_temperature | 0 plugins/synology/snmp__synology_ups | 0 plugins/syslog/syslog_ng_stats | 0 plugins/unicorn/unicorn_ | 0 plugins/uwsgi/uwsgi_ | 0 plugins/varnish/varnish4_ | 0 plugins/vbulletin/vbulletin4_users | 0 plugins/virtualbox/virtualbox_cpu_kernel | 0 plugins/virtualbox/virtualbox_cpu_user | 0 plugins/virtualbox/virtualbox_ram_usage | 0 plugins/voldemort/voldemort | 0 plugins/wordpress/wordpress2 | 0 97 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 plugins/accounting/accounting_ mode change 100644 => 100755 plugins/apache/apache_average_time_last_n_requests mode change 100644 => 100755 plugins/apache/apache_byprojects/byprojects_access mode change 100644 => 100755 plugins/apache/apache_byprojects/byprojects_bandwidth mode change 100644 => 100755 plugins/apache/apache_byprojects/byprojects_inout_bandwidth mode change 100644 => 100755 plugins/apache/apache_vhosts/apache_logparser mode change 100644 => 100755 plugins/apache/apache_vhosts/apache_pipelogger mode change 100644 => 100755 plugins/apache/apache_vhosts/apache_vhosts mode change 100644 => 100755 plugins/apache/page_load mode change 100644 => 100755 plugins/arp/arp_bsd_ mode change 100644 => 100755 plugins/dhcp/dhcpd-pools mode change 100644 => 100755 plugins/disk/btrfs_subvol_usage mode change 100644 => 100755 plugins/disk/e2 mode change 100644 => 100755 plugins/disk/hdsentinel mode change 100644 => 100755 plugins/disk/quota2percent_ mode change 100644 => 100755 plugins/djabberd/djabberd_ mode change 100644 => 100755 plugins/dovecot/dovecot_stats_ mode change 100644 => 100755 plugins/drupal/drupal_page_speed_test.php mode change 100644 => 100755 plugins/dvb/2wcomdsr_ mode change 100644 => 100755 plugins/freeradius/freeradius_queue mode change 100644 => 100755 plugins/games/game mode change 100644 => 100755 plugins/glassfish/glassfish_counters_ mode change 100644 => 100755 plugins/icecast/icecast2_ mode change 100644 => 100755 plugins/icecast/icecast2_all mode change 100644 => 100755 plugins/jchkmail/jchkmail_counters_ mode change 100644 => 100755 plugins/jenkins/jenkins_ mode change 100644 => 100755 plugins/mail/postfwd-rbl mode change 100644 => 100755 plugins/mediawiki/mediawiki_api mode change 100644 => 100755 plugins/memcached/memcached_servers_ mode change 100644 => 100755 plugins/minecraft/bukkit-jsonapi-players mode change 100644 => 100755 plugins/minecraft/bukkit-jsonapi-ramusage mode change 100644 => 100755 plugins/minecraft/bukkit-jsonapi-tps mode change 100644 => 100755 plugins/minecraft/bukkit-statistician-killshostile mode change 100644 => 100755 plugins/minecraft/bukkit-statistician-killsneutral mode change 100644 => 100755 plugins/minecraft/bukkit-statistician-killspassive mode change 100644 => 100755 plugins/minecraft/bukkit-statistician-players mode change 100644 => 100755 plugins/minecraft/bukkit-ultrabans-shame mode change 100644 => 100755 plugins/minecraft/minecraft-users-ram_ mode change 100644 => 100755 plugins/mongodb/mongo_collection_ mode change 100644 => 100755 plugins/moodle/moodle_files mode change 100644 => 100755 plugins/moodle/moodle_logs mode change 100644 => 100755 plugins/moodle/moodle_mod_chat mode change 100644 => 100755 plugins/moodle/moodle_mod_forum mode change 100644 => 100755 plugins/moodle/moodle_mod_quiz mode change 100644 => 100755 plugins/moodle/moodle_modules_total mode change 100644 => 100755 plugins/moodle/moodle_users_online mode change 100644 => 100755 plugins/moodle/moodle_users_total mode change 100644 => 100755 plugins/mysql/hs_read mode change 100644 => 100755 plugins/mysql/hs_write mode change 100644 => 100755 plugins/network/ddclient mode change 100644 => 100755 plugins/network/multibandwidth mode change 100644 => 100755 plugins/network/ubiquiti_airfiber_ mode change 100644 => 100755 plugins/network/ubiquiti_airos_ mode change 100644 => 100755 plugins/nfs-freebsd/nfs_client mode change 100644 => 100755 plugins/nfs-freebsd/nfs_client_cache mode change 100644 => 100755 plugins/percona/percona_ mode change 100644 => 100755 plugins/pf/pf_tables_ mode change 100644 => 100755 plugins/php/php_errors_ mode change 100644 => 100755 plugins/php/php_opcache.php mode change 100644 => 100755 plugins/php/php_time_execution mode change 100644 => 100755 plugins/postgresql/postgres_tuplesratio_ mode change 100644 => 100755 plugins/power/eatonups_ mode change 100644 => 100755 plugins/power/upsmonpro_ mode change 100644 => 100755 plugins/printer/snmp__hpclj mode change 100644 => 100755 plugins/prosody/prosody_ mode change 100644 => 100755 plugins/puma/puma_ mode change 100644 => 100755 plugins/rackspace/rackspace_cdn_count.php mode change 100644 => 100755 plugins/rackspace/rackspace_cdn_size.php mode change 100644 => 100755 plugins/rethinkdb/rethinkdb_node_io mode change 100644 => 100755 plugins/riak/riak_fsm_time_95 mode change 100644 => 100755 plugins/router/snmp__cisco_sbs_cpu mode change 100644 => 100755 plugins/sabnzbd/sabnzbd_dataleft mode change 100644 => 100755 plugins/sabnzbd/sabnzbd_speed mode change 100644 => 100755 plugins/security/fail2ban_ mode change 100644 => 100755 plugins/senderscore/senderscore mode change 100644 => 100755 plugins/sendmail/sendmail_mailq mode change 100644 => 100755 plugins/sensors/voltcraft_tcm220_ mode change 100644 => 100755 plugins/sge/sge_queue_xml_ mode change 100644 => 100755 plugins/smf/smf_errors mode change 100644 => 100755 plugins/smf/smf_mail_queue mode change 100644 => 100755 plugins/smf/smf_online mode change 100644 => 100755 plugins/smf/smf_stats mode change 100644 => 100755 plugins/snmp/snmp__airport mode change 100644 => 100755 plugins/snmp/snmp__cyberpower mode change 100644 => 100755 plugins/synology/snmp__synology_hddtemp mode change 100644 => 100755 plugins/synology/snmp__synology_temperature mode change 100644 => 100755 plugins/synology/snmp__synology_ups mode change 100644 => 100755 plugins/syslog/syslog_ng_stats mode change 100644 => 100755 plugins/unicorn/unicorn_ mode change 100644 => 100755 plugins/uwsgi/uwsgi_ mode change 100644 => 100755 plugins/varnish/varnish4_ mode change 100644 => 100755 plugins/vbulletin/vbulletin4_users mode change 100644 => 100755 plugins/virtualbox/virtualbox_cpu_kernel mode change 100644 => 100755 plugins/virtualbox/virtualbox_cpu_user mode change 100644 => 100755 plugins/virtualbox/virtualbox_ram_usage mode change 100644 => 100755 plugins/voldemort/voldemort mode change 100644 => 100755 plugins/wordpress/wordpress2 diff --git a/plugins/accounting/accounting_ b/plugins/accounting/accounting_ old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_average_time_last_n_requests b/plugins/apache/apache_average_time_last_n_requests old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_byprojects/byprojects_access b/plugins/apache/apache_byprojects/byprojects_access old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_byprojects/byprojects_bandwidth b/plugins/apache/apache_byprojects/byprojects_bandwidth old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_byprojects/byprojects_inout_bandwidth b/plugins/apache/apache_byprojects/byprojects_inout_bandwidth old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_vhosts/apache_logparser b/plugins/apache/apache_vhosts/apache_logparser old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_vhosts/apache_pipelogger b/plugins/apache/apache_vhosts/apache_pipelogger old mode 100644 new mode 100755 diff --git a/plugins/apache/apache_vhosts/apache_vhosts b/plugins/apache/apache_vhosts/apache_vhosts old mode 100644 new mode 100755 diff --git a/plugins/apache/page_load b/plugins/apache/page_load old mode 100644 new mode 100755 diff --git a/plugins/arp/arp_bsd_ b/plugins/arp/arp_bsd_ old mode 100644 new mode 100755 diff --git a/plugins/dhcp/dhcpd-pools b/plugins/dhcp/dhcpd-pools old mode 100644 new mode 100755 diff --git a/plugins/disk/btrfs_subvol_usage b/plugins/disk/btrfs_subvol_usage old mode 100644 new mode 100755 diff --git a/plugins/disk/e2 b/plugins/disk/e2 old mode 100644 new mode 100755 diff --git a/plugins/disk/hdsentinel b/plugins/disk/hdsentinel old mode 100644 new mode 100755 diff --git a/plugins/disk/quota2percent_ b/plugins/disk/quota2percent_ old mode 100644 new mode 100755 diff --git a/plugins/djabberd/djabberd_ b/plugins/djabberd/djabberd_ old mode 100644 new mode 100755 diff --git a/plugins/dovecot/dovecot_stats_ b/plugins/dovecot/dovecot_stats_ old mode 100644 new mode 100755 diff --git a/plugins/drupal/drupal_page_speed_test.php b/plugins/drupal/drupal_page_speed_test.php old mode 100644 new mode 100755 diff --git a/plugins/dvb/2wcomdsr_ b/plugins/dvb/2wcomdsr_ old mode 100644 new mode 100755 diff --git a/plugins/freeradius/freeradius_queue b/plugins/freeradius/freeradius_queue old mode 100644 new mode 100755 diff --git a/plugins/games/game b/plugins/games/game old mode 100644 new mode 100755 diff --git a/plugins/glassfish/glassfish_counters_ b/plugins/glassfish/glassfish_counters_ old mode 100644 new mode 100755 diff --git a/plugins/icecast/icecast2_ b/plugins/icecast/icecast2_ old mode 100644 new mode 100755 diff --git a/plugins/icecast/icecast2_all b/plugins/icecast/icecast2_all old mode 100644 new mode 100755 diff --git a/plugins/jchkmail/jchkmail_counters_ b/plugins/jchkmail/jchkmail_counters_ old mode 100644 new mode 100755 diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ old mode 100644 new mode 100755 diff --git a/plugins/mail/postfwd-rbl b/plugins/mail/postfwd-rbl old mode 100644 new mode 100755 diff --git a/plugins/mediawiki/mediawiki_api b/plugins/mediawiki/mediawiki_api old mode 100644 new mode 100755 diff --git a/plugins/memcached/memcached_servers_ b/plugins/memcached/memcached_servers_ old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-jsonapi-players b/plugins/minecraft/bukkit-jsonapi-players old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-jsonapi-ramusage b/plugins/minecraft/bukkit-jsonapi-ramusage old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-jsonapi-tps b/plugins/minecraft/bukkit-jsonapi-tps old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-statistician-killshostile b/plugins/minecraft/bukkit-statistician-killshostile old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-statistician-killsneutral b/plugins/minecraft/bukkit-statistician-killsneutral old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-statistician-killspassive b/plugins/minecraft/bukkit-statistician-killspassive old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-statistician-players b/plugins/minecraft/bukkit-statistician-players old mode 100644 new mode 100755 diff --git a/plugins/minecraft/bukkit-ultrabans-shame b/plugins/minecraft/bukkit-ultrabans-shame old mode 100644 new mode 100755 diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ old mode 100644 new mode 100755 diff --git a/plugins/mongodb/mongo_collection_ b/plugins/mongodb/mongo_collection_ old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_files b/plugins/moodle/moodle_files old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_logs b/plugins/moodle/moodle_logs old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_mod_chat b/plugins/moodle/moodle_mod_chat old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_mod_forum b/plugins/moodle/moodle_mod_forum old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_mod_quiz b/plugins/moodle/moodle_mod_quiz old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_modules_total b/plugins/moodle/moodle_modules_total old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_users_online b/plugins/moodle/moodle_users_online old mode 100644 new mode 100755 diff --git a/plugins/moodle/moodle_users_total b/plugins/moodle/moodle_users_total old mode 100644 new mode 100755 diff --git a/plugins/mysql/hs_read b/plugins/mysql/hs_read old mode 100644 new mode 100755 diff --git a/plugins/mysql/hs_write b/plugins/mysql/hs_write old mode 100644 new mode 100755 diff --git a/plugins/network/ddclient b/plugins/network/ddclient old mode 100644 new mode 100755 diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth old mode 100644 new mode 100755 diff --git a/plugins/network/ubiquiti_airfiber_ b/plugins/network/ubiquiti_airfiber_ old mode 100644 new mode 100755 diff --git a/plugins/network/ubiquiti_airos_ b/plugins/network/ubiquiti_airos_ old mode 100644 new mode 100755 diff --git a/plugins/nfs-freebsd/nfs_client b/plugins/nfs-freebsd/nfs_client old mode 100644 new mode 100755 diff --git a/plugins/nfs-freebsd/nfs_client_cache b/plugins/nfs-freebsd/nfs_client_cache old mode 100644 new mode 100755 diff --git a/plugins/percona/percona_ b/plugins/percona/percona_ old mode 100644 new mode 100755 diff --git a/plugins/pf/pf_tables_ b/plugins/pf/pf_tables_ old mode 100644 new mode 100755 diff --git a/plugins/php/php_errors_ b/plugins/php/php_errors_ old mode 100644 new mode 100755 diff --git a/plugins/php/php_opcache.php b/plugins/php/php_opcache.php old mode 100644 new mode 100755 diff --git a/plugins/php/php_time_execution b/plugins/php/php_time_execution old mode 100644 new mode 100755 diff --git a/plugins/postgresql/postgres_tuplesratio_ b/plugins/postgresql/postgres_tuplesratio_ old mode 100644 new mode 100755 diff --git a/plugins/power/eatonups_ b/plugins/power/eatonups_ old mode 100644 new mode 100755 diff --git a/plugins/power/upsmonpro_ b/plugins/power/upsmonpro_ old mode 100644 new mode 100755 diff --git a/plugins/printer/snmp__hpclj b/plugins/printer/snmp__hpclj old mode 100644 new mode 100755 diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ old mode 100644 new mode 100755 diff --git a/plugins/puma/puma_ b/plugins/puma/puma_ old mode 100644 new mode 100755 diff --git a/plugins/rackspace/rackspace_cdn_count.php b/plugins/rackspace/rackspace_cdn_count.php old mode 100644 new mode 100755 diff --git a/plugins/rackspace/rackspace_cdn_size.php b/plugins/rackspace/rackspace_cdn_size.php old mode 100644 new mode 100755 diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io old mode 100644 new mode 100755 diff --git a/plugins/riak/riak_fsm_time_95 b/plugins/riak/riak_fsm_time_95 old mode 100644 new mode 100755 diff --git a/plugins/router/snmp__cisco_sbs_cpu b/plugins/router/snmp__cisco_sbs_cpu old mode 100644 new mode 100755 diff --git a/plugins/sabnzbd/sabnzbd_dataleft b/plugins/sabnzbd/sabnzbd_dataleft old mode 100644 new mode 100755 diff --git a/plugins/sabnzbd/sabnzbd_speed b/plugins/sabnzbd/sabnzbd_speed old mode 100644 new mode 100755 diff --git a/plugins/security/fail2ban_ b/plugins/security/fail2ban_ old mode 100644 new mode 100755 diff --git a/plugins/senderscore/senderscore b/plugins/senderscore/senderscore old mode 100644 new mode 100755 diff --git a/plugins/sendmail/sendmail_mailq b/plugins/sendmail/sendmail_mailq old mode 100644 new mode 100755 diff --git a/plugins/sensors/voltcraft_tcm220_ b/plugins/sensors/voltcraft_tcm220_ old mode 100644 new mode 100755 diff --git a/plugins/sge/sge_queue_xml_ b/plugins/sge/sge_queue_xml_ old mode 100644 new mode 100755 diff --git a/plugins/smf/smf_errors b/plugins/smf/smf_errors old mode 100644 new mode 100755 diff --git a/plugins/smf/smf_mail_queue b/plugins/smf/smf_mail_queue old mode 100644 new mode 100755 diff --git a/plugins/smf/smf_online b/plugins/smf/smf_online old mode 100644 new mode 100755 diff --git a/plugins/smf/smf_stats b/plugins/smf/smf_stats old mode 100644 new mode 100755 diff --git a/plugins/snmp/snmp__airport b/plugins/snmp/snmp__airport old mode 100644 new mode 100755 diff --git a/plugins/snmp/snmp__cyberpower b/plugins/snmp/snmp__cyberpower old mode 100644 new mode 100755 diff --git a/plugins/synology/snmp__synology_hddtemp b/plugins/synology/snmp__synology_hddtemp old mode 100644 new mode 100755 diff --git a/plugins/synology/snmp__synology_temperature b/plugins/synology/snmp__synology_temperature old mode 100644 new mode 100755 diff --git a/plugins/synology/snmp__synology_ups b/plugins/synology/snmp__synology_ups old mode 100644 new mode 100755 diff --git a/plugins/syslog/syslog_ng_stats b/plugins/syslog/syslog_ng_stats old mode 100644 new mode 100755 diff --git a/plugins/unicorn/unicorn_ b/plugins/unicorn/unicorn_ old mode 100644 new mode 100755 diff --git a/plugins/uwsgi/uwsgi_ b/plugins/uwsgi/uwsgi_ old mode 100644 new mode 100755 diff --git a/plugins/varnish/varnish4_ b/plugins/varnish/varnish4_ old mode 100644 new mode 100755 diff --git a/plugins/vbulletin/vbulletin4_users b/plugins/vbulletin/vbulletin4_users old mode 100644 new mode 100755 diff --git a/plugins/virtualbox/virtualbox_cpu_kernel b/plugins/virtualbox/virtualbox_cpu_kernel old mode 100644 new mode 100755 diff --git a/plugins/virtualbox/virtualbox_cpu_user b/plugins/virtualbox/virtualbox_cpu_user old mode 100644 new mode 100755 diff --git a/plugins/virtualbox/virtualbox_ram_usage b/plugins/virtualbox/virtualbox_ram_usage old mode 100644 new mode 100755 diff --git a/plugins/voldemort/voldemort b/plugins/voldemort/voldemort old mode 100644 new mode 100755 diff --git a/plugins/wordpress/wordpress2 b/plugins/wordpress/wordpress2 old mode 100644 new mode 100755 From 23d890cd595e607963a94b27fa582289e7e70cab Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 03:49:12 +0100 Subject: [PATCH 081/695] gitignore: add '__pycache__' directories --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 596cff88..20420e43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .*.swp *~ + +__pycache__/ From c4c73eae4cfd698be614b79cf0662290154df8cb Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 03:55:41 +0100 Subject: [PATCH 082/695] zfs_usage_: explain the slash/underscore substitutions see 608a2a5ad7068f9bc2e392dac87ea3c9eb0ce090 --- plugins/zfs/zfs_usage_ | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_ index fdd5b3a9..e44caf12 100755 --- a/plugins/zfs/zfs_usage_ +++ b/plugins/zfs/zfs_usage_ @@ -160,6 +160,9 @@ sub do_collect { foreach my $line (split(/\n/, `$fsget` )) { my ($name, $key, $value, undef ) = (split(/\t/,$line)); + # try to preserve underscores: + # * duplicate existing ones + # * afterwards replace slashs with single ones ($name =~ s/_/__/g); ($name =~ s/\//_/g); $filesystems->{$name}->{$key}=$value; @@ -170,6 +173,9 @@ sub do_collect { sub do_config_fs { my ($fs) = @_; my $fs_slash = ($fs); + # try to restore underscores (see "do_collect" for the reverse operation): + # * substitute all underscores with slashs + # * afterwards transform *double* slashs back into a single underscore ($fs_slash =~ s/_/\//g); ($fs_slash =~ s/\/\//_/g); From ba66ff74902fc24703a723e52e968ece619e5e8e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 04:46:18 +0100 Subject: [PATCH 083/695] README: rework "plugin" section * replace "exchange" with "gallery" * mention the most common issues: * multigraph * magic markers * gallery * style checks --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46578d99..c1b2f974 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,21 @@ This is the repository for all user contributed stuff **This is usually where you want to begin your journey.** -Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/. -That web site is for the time being disabled, new updates are done here. +Here you find a plethora of plugins for the most diverse topics. Please take a look and +refresh/improve existing or propose new plugins. + +See the [gallery](http://gallery.munin-monitoring.org/) for a browsable overview of these plugins. + +Hints for code submissions: + +* add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) +* pick a suitable [category](guide.munin-monitoring.org/en/latest/reference/graph-category.html) +* you should probably use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins +* use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) +* bonus: add [example graphs](http://munin-monitoring.org/wiki/PluginGallery#Rulesforplugincontributors) for the [gallery](http://gallery.munin-monitoring.org/) + +See the [plugin development documentation](http://guide.munin-monitoring.org/en/latest/develop/plugins/index.html) for more details. -If a dedicated website comes back alive, its plugin backend will be this git repo. # contrib/templates/ - 3rd-party templates From 8fa7e61b43dda4c04f53aed52973304423fb53f0 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 05:25:13 +0100 Subject: [PATCH 084/695] quake plugins: fix shellcheck issues --- plugins/quake/qstat | 26 +++++++++++++------------- plugins/quake/qstatet_ | 27 +++++++++++++-------------- plugins/quake/qstatqw_ | 29 +++++++++++++---------------- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/plugins/quake/qstat b/plugins/quake/qstat index 7438b2a8..d0657b0d 100755 --- a/plugins/quake/qstat +++ b/plugins/quake/qstat @@ -13,7 +13,7 @@ qstat_exe='/usr/local/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,20 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -raw ";" -nh -${gametype} ${ip}:${port}) + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + dummy=$("$qstat_exe" -raw ";" -nh "-$gametype" "${ip}:${port}") - playervalue=$(echo ${dummy} | cut -d\; -f6) - maxplayervalue=$(echo ${dummy} | cut -d\; -f5) + playervalue=$(echo "$dummy" | cut -d\; -f6) + maxplayervalue=$(echo "$dummy" | cut -d\; -f5) if [ -z "${playervalue}" ]; then playervalue=0 @@ -88,7 +88,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,7 +99,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac diff --git a/plugins/quake/qstatet_ b/plugins/quake/qstatet_ index 105d4b67..d792f235 100755 --- a/plugins/quake/qstatet_ +++ b/plugins/quake/qstatet_ @@ -13,7 +13,7 @@ qstat_exe='/usr/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,19 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep frags | grep -wv 0ms | wc -l) - dummy2=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep frags | grep -w 0ms | wc -l) - playervalue=$dummy - maxplayervalue=$dummy2 + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + stat_output=$("$qstat_exe" -P -pa -sort P "-$gametype" "${ip}:${port}" | grep frags) + playervalue=$(echo "$stat_output" | grep -cwv 0ms) + maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) if [ -z "${playervalue}" ]; then playervalue=0 @@ -88,7 +87,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,7 +98,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac diff --git a/plugins/quake/qstatqw_ b/plugins/quake/qstatqw_ index 1538b87b..df29156d 100755 --- a/plugins/quake/qstatqw_ +++ b/plugins/quake/qstatqw_ @@ -13,7 +13,7 @@ qstat_exe='/usr/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,19 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep team | grep -wv 0ms | wc -l) - dummy2=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep team | grep -w 0ms | wc -l) - playervalue=$dummy - maxplayervalue=$dummy2 + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + stat_output=$("$qstat_exe" -P -pa -sort P "-${gametype}" "${ip}:${port}" | grep team) + playervalue=$(echo "$stat_output" | grep -cwv 0ms) + maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) if [ -z "${playervalue}" ]; then playervalue=0 @@ -74,7 +73,6 @@ quake_stat() { maxplayervalue=0 fi - echo "maxplayer.value "${maxplayervalue}; echo "player.value "${playervalue}; else @@ -88,7 +86,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,8 +97,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac - From f08727035f1132d49a63a0edeb4b84a0760b752f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 06:09:54 +0100 Subject: [PATCH 085/695] solaris-memstat: fix shellcheck issue --- plugins/solaris/solaris-memstat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/solaris/solaris-memstat b/plugins/solaris/solaris-memstat index 13e30d1c..71957f53 100755 --- a/plugins/solaris/solaris-memstat +++ b/plugins/solaris/solaris-memstat @@ -89,7 +89,7 @@ fi echo "::memstat" | mdb -k | nawk ' BEGIN { - pagesize='$(getconf PAGESIZE)' + pagesize='"$(getconf PAGESIZE)"' kernel=0 zfs=0 anon=0 From 6c0e154e07e1dde9286e935f017a23bc01c42126 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 9 Mar 2018 05:33:37 +0100 Subject: [PATCH 086/695] tests: add "shellcheck" tests for "ksh" scripts --- .travis.yml | 2 +- t/test.t | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 97decfbd..f9b78219 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: trusty install: - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint shellcheck - 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 86b76b95..db30c126 100644 --- a/t/test.t +++ b/t/test.t @@ -85,12 +85,20 @@ sub process_file { }; } elsif ( $interpreter =~ m{/bin/ksh} ) { - run_check( - { command => [ 'ksh', '-n', $file ], - description => 'ksh syntax check', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'ksh', '-n', $file ], + description => 'ksh syntax check', + filename => $filename + } + ); + run_check( + { command => [ 'shellcheck', $file ], + description => 'shellcheck' + } + ); + } } elsif ( $interpreter =~ m{bash} ) { run_check( From e59e2d01510e0cf61dc50cfbad58316570a3774c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 10 Mar 2018 17:53:45 +0100 Subject: [PATCH 087/695] move plugin documentation to separate README --- README.md | 14 +++--------- plugins/README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 plugins/README.md diff --git a/README.md b/README.md index c1b2f974..fa10af06 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,12 @@ This is the repository for all user contributed stuff **This is usually where you want to begin your journey.** Here you find a plethora of plugins for the most diverse topics. Please take a look and -refresh/improve existing or propose new plugins. +improve existing or propose new plugins. + +Please read the [hints for plugin contributions](./plugins#contributed-munin-plugins). See the [gallery](http://gallery.munin-monitoring.org/) for a browsable overview of these plugins. -Hints for code submissions: - -* add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) -* pick a suitable [category](guide.munin-monitoring.org/en/latest/reference/graph-category.html) -* you should probably use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins -* use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) -* bonus: add [example graphs](http://munin-monitoring.org/wiki/PluginGallery#Rulesforplugincontributors) for the [gallery](http://gallery.munin-monitoring.org/) - -See the [plugin development documentation](http://guide.munin-monitoring.org/en/latest/develop/plugins/index.html) for more details. - # contrib/templates/ - 3rd-party templates diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 00000000..e041fa7a --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,54 @@ +# Contributed Munin Plugins + +This plethora of plugins covering various topics was contributed by many different users of [munin](http://munin-monitoring.org). + +See the [gallery](http://gallery.munin-monitoring.org/) for a browsable overview of these plugins. + + +## Purpose of this repository + +This repository of contributed plugin strives to achieve the following goals: + +* allow users to find interesting plugins +* allow contributors to publish their plugins +* simplify cooperative maintenance of plugins + +Contributed plugins are maintained primarily by their authors. +You may file bug reports for plugin issue here in this repository (`munin-contrib`), but please do not forget to notify the author the plugin, as well. + +Please note, that this repository is not supposed to be a dumping site for random plugins of low quality. The related infrastructure (e.g the [gallery](http://gallery.munin-monitoring.org/) or automated tests) require a certain level of quality. Please see below for details. + + +## Submit a new plugin + +1. check if a similar plugin exists and if it can be extended/changed instead of adding a new plugin + * please avoid code copies - they are a maintenance burden +2. add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) +3. pick a suitable [category](guide.munin-monitoring.org/en/latest/reference/graph-category.html) +5. use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) +6. pick a suitable [name and location](#Plugin_name_and_location) +7. bonus: + * use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins + * add [example graphs](http://munin-monitoring.org/wiki/PluginGallery#Rulesforplugincontributors) for the [gallery](http://gallery.munin-monitoring.org/) + * support [dirtyconfig](http://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html#plugin-protocol-dirtyconfig) if it is suitable +8. open a [pull request](https://github.com/munin-monitoring/contrib/pull/) with your new plugin or send it attached to an email to the [mailing list](https://lists.sourceforge.net/lists/listinfo/munin-users) + +See the [plugin development documentation](http://guide.munin-monitoring.org/en/latest/develop/plugins/index.html) for more details. + + +## Modify an existing plugin + +* *try* to keep the plugin backwards compatible (e.g. keep data fieldnames unchanged) + * improvements of code quality and features can justify incompatible changes of existing plugins +* bonus: + * improve the existing plugins according to the [wishlist for new plugins](#Submit_a_new_plugin) + * upgrades from simple plugins to a [multigraph plugin](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) are welcome + + +## Plugin name and location + +The following descriptions are *intentions* - they do not necessarily describe the current state for all plugins. Please open a [pull request](https://github.com/munin-monitoring/contrib/pull/) if you want to align the current structure along the goals outlined below: + +* the top level directory should describe a related *software* or *vendor* + * use *concepts* or *platforms* only if it is really necessary (e.g. *cpu*, *bsd*, *memory*) +* subdirectories are usually not required From bda54ebba2e6ceb5d0fae4fc3d9d3e08533ff909 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 15 Mar 2018 10:48:41 +0100 Subject: [PATCH 088/695] Fix broken link in plugin README Missing protocol caused it to link to a nonexistent place on github. --- plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/README.md b/plugins/README.md index e041fa7a..6257568a 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -24,7 +24,7 @@ Please note, that this repository is not supposed to be a dumping site for rando 1. check if a similar plugin exists and if it can be extended/changed instead of adding a new plugin * please avoid code copies - they are a maintenance burden 2. add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) -3. pick a suitable [category](guide.munin-monitoring.org/en/latest/reference/graph-category.html) +3. pick a suitable [category](http://guide.munin-monitoring.org/en/latest/reference/graph-category.html) 5. use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) 6. pick a suitable [name and location](#Plugin_name_and_location) 7. bonus: From 051ee8eae78f53ee3becdbfcc73b3378a50cdaa5 Mon Sep 17 00:00:00 2001 From: Pascal Seeland Date: Fri, 1 Jan 2016 00:00:00 +0100 Subject: [PATCH 089/695] Add Munin ILIAS plugin --- plugins/ilias/ilias_ | 119 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 plugins/ilias/ilias_ diff --git a/plugins/ilias/ilias_ b/plugins/ilias/ilias_ new file mode 100644 index 00000000..dc41d8d0 --- /dev/null +++ b/plugins/ilias/ilias_ @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# The Plugin needs the following configuration settings +#[ilias_*] +#env.ildbuser ilias +#env.ildbpassword youriliaspasword +#env.ildb ilias + +#%# family=auto +#%# capabilities=autoconf suggest + +import os +import sys +import MySQLdb +import _mysql +class ILIAS(): + title = "ILIAS session" + args = "--base 1000 -l 0" + vlabel = "ilias" + category = "ilias" + + def __init__(self): + self.con = None + self.user = os.environ.get('ildbuser', 'root') + self.pw = os.environ.get('ildbpassword', '') + self.ildb = os.environ.get('ildb','ilias') + + def connectdb(self): + self.con = MySQLdb.connect("localhost", self.user, self.pw,self.ildb) + self.pluginname = sys.argv[0].split('_')[1] + + + def config_sessions(self): + print("graph_title ILIAS Session") + print("graph_vlabel ilsessions") + print("graph_category ILIAS") + print("ilsessions.label ilSessions") + + def execute_sessions(self): + cursor = self.con.cursor() + cursor.execute("select count(user_id) from usr_session where `expires` > UNIX_TIMESTAMP( NOW( ) ) AND user_id != 0") + usrs = cursor.fetchone()[0] + print("ilsessions %s"%(usrs)) + + + def config_10minavg(self): + print("graph_title ILIAS 10 avg") + print("graph_vlabel il10minavg") + print("graph_category ILIAS") + print("il10minavg.label 10 min Count") + + def execute_10minavg(self): + cursor = self.con.cursor() + cursor.execute("select count(user_id) from usr_session where 10 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0") + usrs = cursor.fetchone()[0] + print("il10minavg %s"%(usrs)) + + def config_60minavg(self): + print("graph_title ILIAS 60 avg") + print("graph_vlabel il60minavg") + print("graph_category ILIAS") + print("il60minavg.label 60 min Count") + + def execute_60minavg(self): + cursor = self.con.cursor() + cursor.execute("select count(user_id) from usr_session where 60 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0") + usrs = cursor.fetchone()[0] + print("il60minavg %s"%(usrs)) + + def config_total1day(self): + print("graph_title Users in 24h") + print("graph_vlabel iltotal1day") + print("graph_category ILIAS") + print("iltotal1day.label User/24h") + + def execute_total1day(self): + cursor = self.con.cursor() + cursor.execute("select count(usr_id) FROM `usr_data` WHERE last_login >= DATE_SUB( NOW( ) , INTERVAL 1 DAY )") + usrs = cursor.fetchone()[0] + print("iltotal1day %s"%(usrs)) + + + def run(self): + cmd = ((len(sys.argv) > 1) and sys.argv[1] or None) or "execute" + function = None + if cmd == "execute": + function = "execute" + elif cmd == "config": + function = "config" + if function != None: + self.connectdb() + try: + func = getattr(self, "%s_%s"%(function,self.pluginname)) + except AttributeError: + print 'function not found "%s" (%s)' % ("config_%s"%self.pluginname, "self") + else: + func() + if self.con: + self.con.close() + if cmd == "suggest": + print ("sessions") + print ("10minavg") + print ("60minavg") + print ("total1day") + if cmd == "autoconf": + try: + con = MySQLdb.connect("localhost", self.user, self.pw,self.ildb) + cursor = con.cursor() + cursor.execute("SELECT count(component) FROM il_pluginslot") + except _mysql.Error, e: + print "no (Error %d: %s)" % (e.args[0], e.args[1]) + else: + print "yes" + sys.exit(0) + + +if __name__ == "__main__": + ILIAS().run() + \ No newline at end of file From 989f09e66c1a9875f22e0c245e0e9381ba8dfe0b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 24 Mar 2018 04:03:08 +0100 Subject: [PATCH 090/695] rename plugin example graphs for plugin gallery visualization Some example graphs did not follow the naming convention required by the plugin gallery. --- .../deb_packages.py-1.png} | Bin .../bsd/{ => example-graphs}/netstat_bsd_m_-day.png | Bin plugins/cpu/{ => example-graphs}/multicpu1sec | 0 plugins/cpu/{ => example-graphs}/multicpu1sec-c.c | 0 .../cpu/{ => example-graphs}/multicpu1sec-days.png | Bin .../cpu/{ => example-graphs}/multicpu1sec-hours.png | Bin .../{ => example-graphs}/multicpu1sec-minutes.png | Bin .../docker_cpu-day.png} | Bin .../docker_memory-day.png} | Bin .../mpdstats_-day.png} | Bin .../netstat_s_-day.png} | Bin .../netstat_s_-month.png} | Bin .../linux_if-1.png} | Bin .../linux_if-2.png} | Bin plugins/network/{netstat_s_ => }/netstat_s_ | 0 plugins/ping/{ => example-graphs}/ping-day.png | Bin .../{snmp__fn => example-graphs}/snmp__fn-cpu.png | Bin .../snmp__fn-memory.png | Bin .../snmp__fn-sessions.png | Bin .../snmp__fn-vpnsessions.png | Bin plugins/snmp/{snmp__fn => }/snmp__fn | 0 21 files changed, 0 insertions(+), 0 deletions(-) rename plugins/apt/deb_packages/{example/packages_label_archive_upgradable-week.png => example-graphs/deb_packages.py-1.png} (100%) rename plugins/bsd/{ => example-graphs}/netstat_bsd_m_-day.png (100%) rename plugins/cpu/{ => example-graphs}/multicpu1sec (100%) rename plugins/cpu/{ => example-graphs}/multicpu1sec-c.c (100%) rename plugins/cpu/{ => example-graphs}/multicpu1sec-days.png (100%) rename plugins/cpu/{ => example-graphs}/multicpu1sec-hours.png (100%) rename plugins/cpu/{ => example-graphs}/multicpu1sec-minutes.png (100%) rename plugins/docker/{example_graphs/docker_cpu_usage.png => example-graphs/docker_cpu-day.png} (100%) rename plugins/docker/{example_graphs/docker_memory_usage.png => example-graphs/docker_memory-day.png} (100%) rename plugins/mpd/{mpdstats_songs-day.png => example-graphs/mpdstats_-day.png} (100%) rename plugins/network/{netstat_s_/netstat_s_tcp_connections_connections-day-linux.png => example-graphs/netstat_s_-day.png} (100%) rename plugins/network/{netstat_s_/netstat_s_tcp_connections_connections-month-freebsd.png => example-graphs/netstat_s_-month.png} (100%) rename plugins/network/linux_if/{linux_if_bonding.png => example-graphs/linux_if-1.png} (100%) rename plugins/network/linux_if/{linux_if_vlans.png => example-graphs/linux_if-2.png} (100%) rename plugins/network/{netstat_s_ => }/netstat_s_ (100%) rename plugins/ping/{ => example-graphs}/ping-day.png (100%) rename plugins/snmp/{snmp__fn => example-graphs}/snmp__fn-cpu.png (100%) rename plugins/snmp/{snmp__fn => example-graphs}/snmp__fn-memory.png (100%) rename plugins/snmp/{snmp__fn => example-graphs}/snmp__fn-sessions.png (100%) rename plugins/snmp/{snmp__fn => example-graphs}/snmp__fn-vpnsessions.png (100%) rename plugins/snmp/{snmp__fn => }/snmp__fn (100%) diff --git a/plugins/apt/deb_packages/example/packages_label_archive_upgradable-week.png b/plugins/apt/deb_packages/example-graphs/deb_packages.py-1.png similarity index 100% rename from plugins/apt/deb_packages/example/packages_label_archive_upgradable-week.png rename to plugins/apt/deb_packages/example-graphs/deb_packages.py-1.png diff --git a/plugins/bsd/netstat_bsd_m_-day.png b/plugins/bsd/example-graphs/netstat_bsd_m_-day.png similarity index 100% rename from plugins/bsd/netstat_bsd_m_-day.png rename to plugins/bsd/example-graphs/netstat_bsd_m_-day.png diff --git a/plugins/cpu/multicpu1sec b/plugins/cpu/example-graphs/multicpu1sec similarity index 100% rename from plugins/cpu/multicpu1sec rename to plugins/cpu/example-graphs/multicpu1sec diff --git a/plugins/cpu/multicpu1sec-c.c b/plugins/cpu/example-graphs/multicpu1sec-c.c similarity index 100% rename from plugins/cpu/multicpu1sec-c.c rename to plugins/cpu/example-graphs/multicpu1sec-c.c diff --git a/plugins/cpu/multicpu1sec-days.png b/plugins/cpu/example-graphs/multicpu1sec-days.png similarity index 100% rename from plugins/cpu/multicpu1sec-days.png rename to plugins/cpu/example-graphs/multicpu1sec-days.png diff --git a/plugins/cpu/multicpu1sec-hours.png b/plugins/cpu/example-graphs/multicpu1sec-hours.png similarity index 100% rename from plugins/cpu/multicpu1sec-hours.png rename to plugins/cpu/example-graphs/multicpu1sec-hours.png diff --git a/plugins/cpu/multicpu1sec-minutes.png b/plugins/cpu/example-graphs/multicpu1sec-minutes.png similarity index 100% rename from plugins/cpu/multicpu1sec-minutes.png rename to plugins/cpu/example-graphs/multicpu1sec-minutes.png diff --git a/plugins/docker/example_graphs/docker_cpu_usage.png b/plugins/docker/example-graphs/docker_cpu-day.png similarity index 100% rename from plugins/docker/example_graphs/docker_cpu_usage.png rename to plugins/docker/example-graphs/docker_cpu-day.png diff --git a/plugins/docker/example_graphs/docker_memory_usage.png b/plugins/docker/example-graphs/docker_memory-day.png similarity index 100% rename from plugins/docker/example_graphs/docker_memory_usage.png rename to plugins/docker/example-graphs/docker_memory-day.png diff --git a/plugins/mpd/mpdstats_songs-day.png b/plugins/mpd/example-graphs/mpdstats_-day.png similarity index 100% rename from plugins/mpd/mpdstats_songs-day.png rename to plugins/mpd/example-graphs/mpdstats_-day.png diff --git a/plugins/network/netstat_s_/netstat_s_tcp_connections_connections-day-linux.png b/plugins/network/example-graphs/netstat_s_-day.png similarity index 100% rename from plugins/network/netstat_s_/netstat_s_tcp_connections_connections-day-linux.png rename to plugins/network/example-graphs/netstat_s_-day.png diff --git a/plugins/network/netstat_s_/netstat_s_tcp_connections_connections-month-freebsd.png b/plugins/network/example-graphs/netstat_s_-month.png similarity index 100% rename from plugins/network/netstat_s_/netstat_s_tcp_connections_connections-month-freebsd.png rename to plugins/network/example-graphs/netstat_s_-month.png diff --git a/plugins/network/linux_if/linux_if_bonding.png b/plugins/network/linux_if/example-graphs/linux_if-1.png similarity index 100% rename from plugins/network/linux_if/linux_if_bonding.png rename to plugins/network/linux_if/example-graphs/linux_if-1.png diff --git a/plugins/network/linux_if/linux_if_vlans.png b/plugins/network/linux_if/example-graphs/linux_if-2.png similarity index 100% rename from plugins/network/linux_if/linux_if_vlans.png rename to plugins/network/linux_if/example-graphs/linux_if-2.png diff --git a/plugins/network/netstat_s_/netstat_s_ b/plugins/network/netstat_s_ similarity index 100% rename from plugins/network/netstat_s_/netstat_s_ rename to plugins/network/netstat_s_ diff --git a/plugins/ping/ping-day.png b/plugins/ping/example-graphs/ping-day.png similarity index 100% rename from plugins/ping/ping-day.png rename to plugins/ping/example-graphs/ping-day.png diff --git a/plugins/snmp/snmp__fn/snmp__fn-cpu.png b/plugins/snmp/example-graphs/snmp__fn-cpu.png similarity index 100% rename from plugins/snmp/snmp__fn/snmp__fn-cpu.png rename to plugins/snmp/example-graphs/snmp__fn-cpu.png diff --git a/plugins/snmp/snmp__fn/snmp__fn-memory.png b/plugins/snmp/example-graphs/snmp__fn-memory.png similarity index 100% rename from plugins/snmp/snmp__fn/snmp__fn-memory.png rename to plugins/snmp/example-graphs/snmp__fn-memory.png diff --git a/plugins/snmp/snmp__fn/snmp__fn-sessions.png b/plugins/snmp/example-graphs/snmp__fn-sessions.png similarity index 100% rename from plugins/snmp/snmp__fn/snmp__fn-sessions.png rename to plugins/snmp/example-graphs/snmp__fn-sessions.png diff --git a/plugins/snmp/snmp__fn/snmp__fn-vpnsessions.png b/plugins/snmp/example-graphs/snmp__fn-vpnsessions.png similarity index 100% rename from plugins/snmp/snmp__fn/snmp__fn-vpnsessions.png rename to plugins/snmp/example-graphs/snmp__fn-vpnsessions.png diff --git a/plugins/snmp/snmp__fn/snmp__fn b/plugins/snmp/snmp__fn similarity index 100% rename from plugins/snmp/snmp__fn/snmp__fn rename to plugins/snmp/snmp__fn From 82f76ceafb5a32727d13aad08867ea010ff5047f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 24 Mar 2018 06:11:31 +0100 Subject: [PATCH 091/695] multicpu1sec: move back to proper location; fix example graph filenames --- ...ticpu1sec-days.png => multicpu1sec-c.c-days.png} | Bin ...cpu1sec-hours.png => multicpu1sec-c.c-hours.png} | Bin ...sec-minutes.png => multicpu1sec-c.c-minutes.png} | Bin plugins/cpu/{example-graphs => }/multicpu1sec | 0 plugins/cpu/{example-graphs => }/multicpu1sec-c.c | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename plugins/cpu/example-graphs/{multicpu1sec-days.png => multicpu1sec-c.c-days.png} (100%) rename plugins/cpu/example-graphs/{multicpu1sec-hours.png => multicpu1sec-c.c-hours.png} (100%) rename plugins/cpu/example-graphs/{multicpu1sec-minutes.png => multicpu1sec-c.c-minutes.png} (100%) rename plugins/cpu/{example-graphs => }/multicpu1sec (100%) rename plugins/cpu/{example-graphs => }/multicpu1sec-c.c (100%) diff --git a/plugins/cpu/example-graphs/multicpu1sec-days.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-days.png similarity index 100% rename from plugins/cpu/example-graphs/multicpu1sec-days.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-days.png diff --git a/plugins/cpu/example-graphs/multicpu1sec-hours.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-hours.png similarity index 100% rename from plugins/cpu/example-graphs/multicpu1sec-hours.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-hours.png diff --git a/plugins/cpu/example-graphs/multicpu1sec-minutes.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-minutes.png similarity index 100% rename from plugins/cpu/example-graphs/multicpu1sec-minutes.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-minutes.png diff --git a/plugins/cpu/example-graphs/multicpu1sec b/plugins/cpu/multicpu1sec similarity index 100% rename from plugins/cpu/example-graphs/multicpu1sec rename to plugins/cpu/multicpu1sec diff --git a/plugins/cpu/example-graphs/multicpu1sec-c.c b/plugins/cpu/multicpu1sec-c.c similarity index 100% rename from plugins/cpu/example-graphs/multicpu1sec-c.c rename to plugins/cpu/multicpu1sec-c.c From f360f189fdcd968c6895c38b3c359b9971ddc995 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 24 Mar 2018 16:28:06 +0100 Subject: [PATCH 092/695] rpi_temp: remove dependency on 'bc' --- plugins/raspberry-pi/rpi_temp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/raspberry-pi/rpi_temp b/plugins/raspberry-pi/rpi_temp index 8eff2283..ffdd803e 100755 --- a/plugins/raspberry-pi/rpi_temp +++ b/plugins/raspberry-pi/rpi_temp @@ -28,6 +28,6 @@ fi # values TEMP_MILLI_C=$(cat /sys/class/thermal/thermal_zone0/temp) -echo thermal_zone0.value $( echo "scale=3; $TEMP_MILLI_C / 1000" | bc ) +echo "thermal_zone0.value $(echo "$TEMP_MILLI_C" | awk '{printf("%0.3f", $1 / 1000)}')" exit 0 From 6c42d08476de80c25f57bf87517a03c1401902d5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 24 Mar 2018 16:29:44 +0100 Subject: [PATCH 093/695] rpi_temp: remove unused variables (causing shellcheck failure) --- plugins/raspberry-pi/rpi_temp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/raspberry-pi/rpi_temp b/plugins/raspberry-pi/rpi_temp index ffdd803e..3fe34b1f 100755 --- a/plugins/raspberry-pi/rpi_temp +++ b/plugins/raspberry-pi/rpi_temp @@ -1,11 +1,6 @@ #! /bin/sh # (c) 2013 - LGPL - Steve Schnepp -pluginfull="$0" # full name of plugin -plugin="${0##*/}" # name of plugin -pidfile="$MUNIN_PLUGSTATE/munin.$plugin.pid" -cache="$MUNIN_PLUGSTATE/munin.$plugin.value" - if [ ! -r "/sys/class/thermal/thermal_zone0/temp" ] then From 5d5651ca0940af39fcd422e502db915f5e4567ac Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 24 Mar 2018 18:43:32 +0100 Subject: [PATCH 094/695] Change variables names to more readable ones in syncthing_ Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/syncthing_ | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index 8b41cad1..b71bb48b 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -22,10 +22,10 @@ To make the plugin connect to the Syncthing server one has to use this type of configuration [syncthing_*] -env.ST_APIKEY myapikey0123456789 -env.ST_HOST 127.0.0.1 -env.ST_PORT 8384 -env.ST_PROTO http +env.syncthing_apikey myapikey0123456789 +env.syncthing_host 127.0.0.1 +env.syncthing_port 8384 +env.syncthing_proto http =head1 AUTHOR Pierre-Alain TORET @@ -47,7 +47,7 @@ EOM exit 0;; *) printf "syncthing_cpu.value " - $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.cpuPercent' + $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.cpuPercent' esac } @@ -67,8 +67,8 @@ syncthing_mem_sys.cdef syncthing_mem_sys,8,* EOM exit 0;; *) - ALL=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.alloc') - SYS=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.sys') + ALL=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.alloc') + SYS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.sys') printf "syncthing_mem_all.value $ALL\nsyncthing_mem_sys.value $SYS\n" esac } @@ -85,7 +85,7 @@ EOM exit 0;; *) printf "syncthing_uptime.value " - $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.uptime' + $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.uptime' esac } @@ -101,7 +101,7 @@ EOM exit 0;; *) printf "syncthing_goroutine.value " - $CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/status | $JQ '.goroutines' + $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.goroutines' esac } @@ -125,8 +125,8 @@ syncthing_transfer_up.cdef syncthing_transfer_up,8,* EOM exit 0;; *) - IBT=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | $JQ '.total | .inBytesTotal') - OBT=$($CURL -s -X GET -H "X-API-Key: $ST_APIKEY" $ST_PROTO://$ST_HOST:$ST_PORT/rest/system/connections | $JQ '.total | .outBytesTotal') + IBT=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections | $JQ '.total | .inBytesTotal') + OBT=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections | $JQ '.total | .outBytesTotal') printf "syncthing_transfer_up.value $IBT\nsyncthing_transfer_down.value $OBT\n" esac } From f1afd73f71f837e8fdaebbc2fbe98f727effb1e1 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 24 Mar 2018 19:16:03 +0100 Subject: [PATCH 095/695] Fix shellcheck warnings in syncthing_ Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/syncthing_ | 58 +++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index b71bb48b..a26236cc 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -*- sh -*- : <<=cut =head1 NAME @@ -34,7 +34,7 @@ Pierre-Alain TORET MIT =cut -function cpu { +cpu() { case $1 in config) cat <<'EOM' @@ -46,12 +46,12 @@ syncthing_cpu.label cpu EOM exit 0;; *) - printf "syncthing_cpu.value " - $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.cpuPercent' + CPU=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.cpuPercent') + printf "syncthing_cpu.value %s\\n" "$CPU" esac } -function mem { +mem() { case $1 in config) cat <<'EOM' @@ -66,14 +66,16 @@ syncthing_mem_sys.label obtained syncthing_mem_sys.cdef syncthing_mem_sys,8,* EOM exit 0;; - *) - ALL=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.alloc') - SYS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.sys') - printf "syncthing_mem_all.value $ALL\nsyncthing_mem_sys.value $SYS\n" + *) + STATUS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status) + ALL=$(echo "$STATUS" | $JQ '.alloc') + SYS=$(echo "$STATUS" | $JQ '.sys') + printf "syncthing_mem_all.value %s\\n" "$ALL" + printf "syncthing_mem_sys.value %s\\n" "$SYS" esac } -function uptime { +uptime() { case $1 in config) cat <<'EOM' @@ -84,12 +86,12 @@ syncthing_uptime.label uptime EOM exit 0;; *) - printf "syncthing_uptime.value " - $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.uptime' + UPTIME=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.uptime') + printf "syncthing_uptime.value %s\\n" "$UPTIME" esac } -function goroutine { +goroutine() { case $1 in config) cat <<'EOM' @@ -100,12 +102,12 @@ syncthing_goroutine.label routines EOM exit 0;; *) - printf "syncthing_goroutine.value " - $CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status | $JQ '.goroutines' + GOROUTINES=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.goroutines') + printf "syncthing_goroutine.value %s\\n" "$GOROUTINES" esac } -function transfer { +transfer() { case $1 in config) cat <<'EOM' @@ -124,32 +126,34 @@ syncthing_transfer_up.negative syncthing_transfer_down syncthing_transfer_up.cdef syncthing_transfer_up,8,* EOM exit 0;; - *) - IBT=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections | $JQ '.total | .inBytesTotal') - OBT=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" $syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections | $JQ '.total | .outBytesTotal') - printf "syncthing_transfer_up.value $IBT\nsyncthing_transfer_down.value $OBT\n" + *) + CONNECTIONS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/connections) + IBT=$(echo "$CONNECTIONS" | $JQ '.total | .inBytesTotal') + OBT=$(echo "$CONNECTIONS" | $JQ '.total | .outBytesTotal') + printf "syncthing_transfer_up.value %s\\n" "$IBT" + printf "syncthing_transfer_down.value %s\\n" "$OBT" esac } -cd $(dirname $0) +cd "$(dirname "$0")" || exit CURL=$(which curl) JQ=$(which jq) -case $(basename $0) in +case $(basename "$0") in syncthing_cpu) - cpu $1 + cpu "$1" exit 0;; syncthing_mem) - mem $1 + mem "$1" exit 0;; syncthing_uptime) - uptime $1 + uptime "$1" exit 0;; syncthing_goroutine) - goroutine $1 + goroutine "$1" exit 0;; syncthing_transfer) - transfer $1 + transfer "$1" exit 0;; esac From cc24efff2d78b2e231915e0247b34ccd50b492ee Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 24 Mar 2018 19:55:12 +0100 Subject: [PATCH 096/695] Add strelaysrv plugin Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/strelaysrv_ | 145 ++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100755 plugins/syncthing/strelaysrv_ diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ new file mode 100755 index 00000000..8ca9bcb7 --- /dev/null +++ b/plugins/syncthing/strelaysrv_ @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +: <<=cut +=head1 NAME +strelaysrv_ - Plugin to monitor Syncthing relay server + +=head1 DESCRIPTION +This plugin gathers metrics from a Syncthing relay server. + +This plugin requires the jq utility : https://stedolan.github.io/jq/ +This plugin requires the curl utility : https://curl.haxx.se/ + +Available plugins : +strelaysrv_goroutine # +strelaysrv_num # +strelaysrv_proxied # +strelaysrv_transfer # +strelaysrv_uptime # + +=head1 CONFIGURATION +To make the plugin connect to the Syncthing relay server one has to use this type of +configuration +[strelaysrv_*] + +env.STR_HOST 127.0.0.1 +env.STR_PORT 22070 + +=head1 AUTHOR +Pierre-Alain TORET + +=head1 LICENSE +MIT +=cut + +function num { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay numbers +graph_category network +graph_vlabel numbers +strelaysrv_num_sessions.label sessions +strelaysrv_num_connections.label connections +strelaysrv_num_pending.label pending session keys +strelaysrv_num_proxies.label proxies +EOM + exit 0;; + *) + NS=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numActiveSessions ') + NC=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numConnections ') + NK=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numPendingSessionKeys ') + NP=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numProxies ') + printf "strelaysrv_num_sessions.value $NS\nstrelaysrv_num_connections.value $NC\nstrelaysrv_num_pending.value $NK\nstrelaysrv_num_proxies.value $NP\n" + esac +} + +function uptime { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay uptime +graph_vlabel uptime in seconds +graph_category network +strelaysrv_uptime.label uptime +EOM + exit 0;; + *) + printf "strelaysrv_uptime.value " + $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.uptimeSeconds' + esac +} + +function goroutine { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay go routines +graph_vlabel number of go routines +graph_category network +strelaysrv_goroutine.label routines +EOM + exit 0;; + *) +printf "strelaysrv_goroutine.value " +$CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.goNumRoutine' + esac +} + +function proxied { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay total proxied bits +graph_category network +graph_vlabel bits +graph_args --base 1000 +strelaysrv_proxied.label bits +strelaysrv_proxied.cdef strelaysrv_proxied,8,* +EOM + exit 0;; + *) + printf "strelaysrv_proxied.value " + $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.bytesProxied ' + esac +} + +function transfer { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay transfer rate +graph_category network +graph_vlabel bps +graph_args --base 1000 +strelaysrv_transfer.label bps +strelaysrv_transfer.cdef strelaysrv_transfer,1000,* +EOM + exit 0;; + *) + printf "strelaysrv_transfer.value " + $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.kbps10s1m5m15m30m60m[2] ' + esac +} + +cd $(dirname $0) + +CURL=$(which curl) +JQ=$(which jq) + +case $(basename $0) in + strelaysrv_num) + num $1 + exit 0;; + strelaysrv_uptime) + uptime $1 + exit 0;; + strelaysrv_goroutine) + goroutine $1 + exit 0;; + strelaysrv_proxied) + proxied $1 + exit 0;; + strelaysrv_transfer) + transfer $1 + exit 0;; +esac From ccee75e1ebc4f780669acfc9aad32680bf3aeb72 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 24 Mar 2018 19:56:32 +0100 Subject: [PATCH 097/695] Change variables names to more readable ones in strelaysrv_ Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/strelaysrv_ | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index 8ca9bcb7..4ea18745 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -21,8 +21,8 @@ To make the plugin connect to the Syncthing relay server one has to use this typ configuration [strelaysrv_*] -env.STR_HOST 127.0.0.1 -env.STR_PORT 22070 +env.syncthing_relaysrv_host 127.0.0.1 +env.syncthing_relaysrv_port 22070 =head1 AUTHOR Pierre-Alain TORET @@ -45,10 +45,10 @@ strelaysrv_num_proxies.label proxies EOM exit 0;; *) - NS=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numActiveSessions ') - NC=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numConnections ') - NK=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numPendingSessionKeys ') - NP=$($CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.numProxies ') + NS=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numActiveSessions ') + NC=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numConnections ') + NK=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numPendingSessionKeys ') + NP=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numProxies ') printf "strelaysrv_num_sessions.value $NS\nstrelaysrv_num_connections.value $NC\nstrelaysrv_num_pending.value $NK\nstrelaysrv_num_proxies.value $NP\n" esac } @@ -65,7 +65,7 @@ EOM exit 0;; *) printf "strelaysrv_uptime.value " - $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.uptimeSeconds' + $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.uptimeSeconds' esac } @@ -81,7 +81,7 @@ EOM exit 0;; *) printf "strelaysrv_goroutine.value " -$CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.goNumRoutine' +$CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.goNumRoutine' esac } @@ -99,7 +99,7 @@ EOM exit 0;; *) printf "strelaysrv_proxied.value " - $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.bytesProxied ' + $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.bytesProxied ' esac } @@ -117,7 +117,7 @@ EOM exit 0;; *) printf "strelaysrv_transfer.value " - $CURL -s http://$STR_HOST:$STR_PORT/status | $JQ '.kbps10s1m5m15m30m60m[2] ' + $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.kbps10s1m5m15m30m60m[2] ' esac } From ba2640da343558959a6988c79a8fa26ac1d28c56 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 24 Mar 2018 20:23:40 +0100 Subject: [PATCH 098/695] Fix shellcheck warnings in strelaysrv_ Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/strelaysrv_ | 76 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index 4ea18745..525a0c9f 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh : <<=cut =head1 NAME strelaysrv_ - Plugin to monitor Syncthing relay server @@ -31,9 +31,9 @@ Pierre-Alain TORET MIT =cut -function num { +num() { case $1 in - config) + config) cat <<'EOM' graph_title Syncthing relay numbers graph_category network @@ -43,51 +43,55 @@ strelaysrv_num_connections.label connections strelaysrv_num_pending.label pending session keys strelaysrv_num_proxies.label proxies EOM - exit 0;; + exit 0;; *) - NS=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numActiveSessions ') - NC=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numConnections ') - NK=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numPendingSessionKeys ') - NP=$($CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.numProxies ') - printf "strelaysrv_num_sessions.value $NS\nstrelaysrv_num_connections.value $NC\nstrelaysrv_num_pending.value $NK\nstrelaysrv_num_proxies.value $NP\n" + STATUS=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status) + NS=$(echo "$STATUS" | $JQ '.numActiveSessions ') + NC=$(echo "$STATUS" | $JQ '.numConnections ') + NK=$(echo "$STATUS" | $JQ '.numPendingSessionKeys ') + NP=$(echo "$STATUS" | $JQ '.numProxies ') + printf "strelaysrv_num_sessions.value %s\\n" "$NS" + printf "strelaysrv_num_connections.value %s\\n" "$NC" + printf "strelaysrv_num_pending.value %s\\n" "$NK" + printf "strelaysrv_num_proxies.value %s\\n" "$NP" esac } -function uptime { +uptime() { case $1 in - config) + config) cat <<'EOM' graph_title Syncthing relay uptime graph_vlabel uptime in seconds graph_category network strelaysrv_uptime.label uptime EOM - exit 0;; + exit 0;; *) - printf "strelaysrv_uptime.value " - $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.uptimeSeconds' + UPTIME=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.uptimeSeconds') + printf "strelaysrv_uptime.value %s\\n" "$UPTIME" esac } -function goroutine { +goroutine() { case $1 in - config) + config) cat <<'EOM' graph_title Syncthing relay go routines graph_vlabel number of go routines graph_category network strelaysrv_goroutine.label routines EOM - exit 0;; + exit 0;; *) -printf "strelaysrv_goroutine.value " -$CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.goNumRoutine' + GOROUTINE=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.goNumRoutine') + printf "strelaysrv_goroutine.value %s\\n" "$GOROUTINE" esac } -function proxied { +proxied() { case $1 in - config) + config) cat <<'EOM' graph_title Syncthing relay total proxied bits graph_category network @@ -97,15 +101,15 @@ strelaysrv_proxied.label bits strelaysrv_proxied.cdef strelaysrv_proxied,8,* EOM exit 0;; - *) - printf "strelaysrv_proxied.value " - $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.bytesProxied ' + *) + BP=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.bytesProxied ') + printf "strelaysrv_proxied.value %s\\n" "$BP" esac } -function transfer { +transfer() { case $1 in - config) + config) cat <<'EOM' graph_title Syncthing relay transfer rate graph_category network @@ -115,31 +119,31 @@ strelaysrv_transfer.label bps strelaysrv_transfer.cdef strelaysrv_transfer,1000,* EOM exit 0;; - *) - printf "strelaysrv_transfer.value " - $CURL -s http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status | $JQ '.kbps10s1m5m15m30m60m[2] ' + *) + TRANSFER=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.kbps10s1m5m15m30m60m[2] ') + printf "strelaysrv_transfer.value %s\\n" "$TRANSFER" esac } -cd $(dirname $0) +cd "$(dirname "$0")" || exit CURL=$(which curl) JQ=$(which jq) -case $(basename $0) in +case $(basename "$0") in strelaysrv_num) - num $1 + num "$1" exit 0;; strelaysrv_uptime) - uptime $1 + uptime "$1" exit 0;; strelaysrv_goroutine) - goroutine $1 + goroutine "$1" exit 0;; strelaysrv_proxied) - proxied $1 + proxied "$1" exit 0;; strelaysrv_transfer) - transfer $1 + transfer "$1" exit 0;; esac From c3431051a5a27114e6e95bc494a82ce9cabbe045 Mon Sep 17 00:00:00 2001 From: "pierre.boesch" Date: Mon, 26 Mar 2018 19:52:31 +0200 Subject: [PATCH 099/695] Resolved issue 884 with munstrap tabid --- templates/munstrap/templates/munin-overview.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/munstrap/templates/munin-overview.tmpl b/templates/munstrap/templates/munin-overview.tmpl index 8449022d..670747d1 100644 --- a/templates/munstrap/templates/munin-overview.tmpl +++ b/templates/munstrap/templates/munin-overview.tmpl @@ -1,4 +1,7 @@ + From 3533998c5bbde684c783f6dd50cdba16e95f9de6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 00:48:07 +0200 Subject: [PATCH 100/695] remove executable permissions from example graphs --- .../example-graphs/emc_vnx_block_lun_perfdata-1.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-2.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-3.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-4.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-5.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-6.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-7.png | Bin .../example-graphs/emc_vnx_block_lun_perfdata-8.png | Bin plugins/emc/example-graphs/emc_vnx_file_-1.png | Bin plugins/emc/example-graphs/emc_vnx_file_-2.png | Bin plugins/emc/example-graphs/emc_vnx_file_-3.png | Bin plugins/emc/example-graphs/emc_vnx_file_-4.png | Bin plugins/emc/example-graphs/emc_vnx_file_-5.png | Bin plugins/emc/example-graphs/emc_vnx_file_-6.png | Bin plugins/emc/example-graphs/emc_vnx_file_-7.png | Bin plugins/emc/example-graphs/emc_vnx_file_-8.png | Bin plugins/emc/example-graphs/emc_vnx_file_-9.png | Bin plugins/emc/example-graphs/emc_vnx_file_-a.png | Bin plugins/emc/example-graphs/emc_vnx_file_-b.png | Bin plugins/emc/example-graphs/emc_vnx_file_-c.png | Bin plugins/emc/example-graphs/emc_vnx_file_-g.png | Bin plugins/emc/example-graphs/emc_vnx_file_-h.png | Bin plugins/emc/example-graphs/emc_vnx_file_-i.png | Bin plugins/emc/example-graphs/emc_vnx_file_-j.png | Bin 24 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-1.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-2.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-3.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-4.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-5.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-6.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-7.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-8.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-9.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-a.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-b.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-c.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-g.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-h.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-i.png mode change 100755 => 100644 plugins/emc/example-graphs/emc_vnx_file_-j.png diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-1.png b/plugins/emc/example-graphs/emc_vnx_file_-1.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-2.png b/plugins/emc/example-graphs/emc_vnx_file_-2.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-3.png b/plugins/emc/example-graphs/emc_vnx_file_-3.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-4.png b/plugins/emc/example-graphs/emc_vnx_file_-4.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-5.png b/plugins/emc/example-graphs/emc_vnx_file_-5.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-6.png b/plugins/emc/example-graphs/emc_vnx_file_-6.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-7.png b/plugins/emc/example-graphs/emc_vnx_file_-7.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-8.png b/plugins/emc/example-graphs/emc_vnx_file_-8.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-9.png b/plugins/emc/example-graphs/emc_vnx_file_-9.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-a.png b/plugins/emc/example-graphs/emc_vnx_file_-a.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-b.png b/plugins/emc/example-graphs/emc_vnx_file_-b.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-c.png b/plugins/emc/example-graphs/emc_vnx_file_-c.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-g.png b/plugins/emc/example-graphs/emc_vnx_file_-g.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-h.png b/plugins/emc/example-graphs/emc_vnx_file_-h.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-i.png b/plugins/emc/example-graphs/emc_vnx_file_-i.png old mode 100755 new mode 100644 diff --git a/plugins/emc/example-graphs/emc_vnx_file_-j.png b/plugins/emc/example-graphs/emc_vnx_file_-j.png old mode 100755 new mode 100644 From fa896dffaa75a545b8732c2a7d5c7521a7f2dd30 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:02:37 +0200 Subject: [PATCH 101/695] Plugin bacula_job: various improvements, flake8-clean * avoid hiding builtins with variable names (input, bytes, str) * return exitcode=0 for autoconf "no" * unify indentation * fix all issues reported by flake8 --- plugins/bacula/bacula_job | 198 +++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 97 deletions(-) diff --git a/plugins/bacula/bacula_job b/plugins/bacula/bacula_job index 0774712e..6b8d7122 100755 --- a/plugins/bacula/bacula_job +++ b/plugins/bacula/bacula_job @@ -22,145 +22,149 @@ # # Parameters: # -# config (required) -# autoconf (optional - only used by munin-config) +# config (required) +# autoconf (optional - only used by munin-config) # # Magic markers (optional - only used by munin-config and some # installation scripts): # -#%# family=contrib -#%# capabilities=autoconf +# #%# family=contrib +# #%# capabilities=autoconf import subprocess -import time import sys import re import os + def parse_running_jobs(): - """ Parse the bconsole output once to get the running jobs """ + """ Parse the bconsole output once to get the running jobs """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n1\nstatus\n3\n.") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n1\nstatus\n3\n.") - jobs = [] - clients = [] - clientlist = False + jobs = [] + clients = [] + clientlist = False - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Connecting to Director "): - hostname = line.split()[-1].split(":")[0] + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Connecting to Director "): + hostname = line.split()[-1].split(":")[0] - if line.endswith(" is running"): - jobs.append(line.split()[2].split(".")[0]) + if line.endswith(" is running"): + jobs.append(line.split()[2].split(".")[0]) - # Parse the clientlist, warning, order of statements is important - if line.startswith("Select Client (File daemon) resource"): - clientlist = False + # Parse the clientlist, warning, order of statements is important + if line.startswith("Select Client (File daemon) resource"): + clientlist = False - if clientlist is True: - client_id, client_name = line.split() - client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) - client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) - clients.append((client_name, client_clean, client_id[:-1])) + if clientlist is True: + client_id, client_name = line.split() + client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) + client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) + clients.append((client_name, client_clean, client_id[:-1])) - if line.startswith("The defined Client resources are:"): - clientlist = True + if line.startswith("The defined Client resources are:"): + clientlist = True - return hostname, jobs, clients + return hostname, jobs, clients def parse(clients): - """ Parse the bconsole output """ + """ Parse the bconsole output """ - query_str = "" - for client in clients: - query_str = query_str + "status\n3\n" + client[1] + "\n" - query_str = query_str + "quit" + query_str = "" + for client in clients: + query_str = query_str + "status\n3\n" + client[1] + "\n" + query_str = query_str + "quit" - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate(query_str) + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate(query_str) - input = stdout.split("\n") + input_lines = stdout.split("\n") - jobstats = [] + jobstats = [] - for line, pos in zip(input, range(0, len(input))): + for line, pos in zip(input_lines, range(0, len(input_lines))): - # Get the client name - if line.startswith("Connecting to Client "): - # client_name = input[pos].split()[3].split(".")[0] - client_name = line.split()[3] - client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) - client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) + # Get the client name + if line.startswith("Connecting to Client "): + # client_name = input_lines[pos].split()[3].split(".")[0] + client_name = line.split()[3] + client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) + client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) - # Get the current bytes - if line.endswith(" is running."): - bytes = long(input[pos+2].split()[1].split("=")[1].replace(",", "")) - jobstats.append([client_name, client_clean, bytes]) + # Get the current bytes + if line.endswith(" is running."): + bytes_count_text = input_lines[pos+2].split()[1].split("=")[1].replace(",", "") + try: + # python2 + bytes_count = long(bytes_count_text) + except NameError: + # python3 + bytes_count = int(bytes_count_text) + jobstats.append([client_name, client_clean, bytes_count]) - job_dict = {} - for job in jobstats: - job_dict[job[0].split("-")[0]] = job + job_dict = {} + for job in jobstats: + job_dict[job[0].split("-")[0]] = job - return job_dict + return job_dict def print_config(): - hostname, jobs, clients = parse_running_jobs() - print "graph_title Bacula Job throughput" - print "graph_vlabel bytes per ${graph_period}" - print "graph_args --base 1024 -l 0" - print "graph_scale yes" - print "graph_info Bacula Job measurement." - print "graph_category backup" - print "graph_order", - for fd in clients: - print fd[1], - print - if os.getenv("report_hostname") is not None and \ - os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"]: - print "host_name", hostname - for client in clients: - print "%s.label %s" % (client[1], client[0]) - print "%s.type DERIVE" % (client[1]) - print "%s.min 0" % (client[1]) -# print "%s.max %s" % (client[1], str(1024*1024*1024*16)) -# print "%s.cdef up,8,*" (client[1]) - sys.exit(0) + hostname, jobs, clients = parse_running_jobs() + print("graph_title Bacula Job throughput") + print("graph_vlabel bytes per ${graph_period}") + print("graph_args --base 1024 -l 0") + print("graph_scale yes") + print("graph_info Bacula Job measurement.") + print("graph_category backup") + print("graph_order", " ".join(fd[1] for fd in clients)) + print() + if ((os.getenv("report_hostname") is not None) and + (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): + print("host_name", hostname) + for client in clients: + print("%s.label %s" % (client[1], client[0])) + print("%s.type DERIVE" % (client[1])) + print("%s.min 0" % (client[1])) +# print("%s.max %s" % (client[1], str(1024*1024*1024*16))) +# print("%s.cdef up,8,*" (client[1])) + sys.exit(0) if "config" in sys.argv[1:]: - print_config() + print_config() elif "autoconf" in sys.argv[1:]: - for dir in os.getenv("PATH").split(":"): - for root, dirs, files in os.walk(dir): - if "bconsole" in files: - print "yes" - sys.exit(0) - print "no" - sys.exit(1) + for directory in os.getenv("PATH").split(":"): + for root, dirs, files in os.walk(directory): + if "bconsole" in files: + print("yes") + sys.exit(0) + print("no") + sys.exit(0) elif "suggest" in sys.argv[1:]: - sys.exit(1) + sys.exit(1) else: - hostname, jobs, clients = parse_running_jobs() - str = [] - for client in clients: - if client[0].split("-")[0] in jobs: - str.append((client[0], client[2])) + hostname, jobs, clients = parse_running_jobs() + client_pairs = [] + for client in clients: + if client[0].split("-")[0] in jobs: + client_pairs.append((client[0], client[2])) - client_values = parse(str) + client_values = parse(client_pairs) - for client in clients: - client_name_short = client[0].split("-")[0] - if client_name_short in client_values: - print "%s.value %s" % (client_values[client_name_short][1], client_values[client_name_short][2]) - else: - print "%s.value %s" % (client[1], "0") + for client in clients: + client_name_short = client[0].split("-")[0] + if client_name_short in client_values: + print("%s.value %s" % (client_values[client_name_short][1], + client_values[client_name_short][2])) + else: + print("%s.value %s" % (client[1], "0")) - sys.exit(0) + sys.exit(0) From a4a9e755b51eb8f66dd53b08012b11e645b10e37 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:14:26 +0200 Subject: [PATCH 102/695] Plugin sphindex_: fix style issues --- plugins/sphinx/sphindex_ | 53 +++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/plugins/sphinx/sphindex_ b/plugins/sphinx/sphindex_ index 5cb775dd..7c37f2c4 100755 --- a/plugins/sphinx/sphindex_ +++ b/plugins/sphinx/sphindex_ @@ -18,49 +18,56 @@ # This plugin requires pythons sphinxsearch module which can be installed via easy_install. # # ## Installation -# Copy file to directory /usr/share/munin/pligins/ and create symbolic links for each index you wish to monitor. +# Copy file to directory /usr/share/munin/pligins/ and create symbolic links for each index you +# wish to monitor. # For example, if you've got indexes called index1 and index2 create these symlinks: # # ln -s /usr/share/munin/plugins/sphindex_ /etc/munin/plugins/sphindex_index1 # ln -s /usr/share/munin/plugins/sphindex_ /etc/munin/plugins/sphindex_index2 # -# If you run munin-node at different box than Sphinxsearch you can specify hostname and port options in munin-node.conf: +# If you run munin-node at different box than Sphinxsearch you can specify hostname and port +# options in munin-node.conf: # # [sphindex_*] # env.server 10.216.0.141 # env.port 9312 # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib -import os, sys, sphinxsearch -progName = sys.argv[0] -indexName = progName[progName.find("_")+1:] +import os +import sys + +import sphinxsearch + + +prog_name = sys.argv[0] +index_name = prog_name[prog_name.find("_")+1:] if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": warning = "0:" critical = "0:" - if "warning" in os.environ and os.environ["warning"] != None: + if "warning" in os.environ and os.environ["warning"]: warning = os.environ["warning"] - if "critical" in os.environ and os.environ["critical"] != None: + if "critical" in os.environ and os.environ["critical"]: critical = os.environ["critical"] - print "graph_title Sphinx index %s stats" % indexName - print "graph_vlabel docs count" - print "graph_category search" - print "documents_count.warning %s" % warning - print "documents_count.critical %s" % critical - print "documents_count.label Documents count in index" - print "graph_args --base 1000 -l 0" + print("graph_title Sphinx index %s stats" % index_name) + print("graph_vlabel docs count") + print("graph_category search") + print("documents_count.warning %s" % warning) + print("documents_count.critical %s" % critical) + print("documents_count.label Documents count in index") + print("graph_args --base 1000 -l 0") else: - if "server" in os.environ and os.environ["server"] != None: + if "server" in os.environ and os.environ["server"]: server = os.environ["server"] else: - server = "localhost" + server = "localhost" - if "port" in os.environ and os.environ["port"] != None: + if "port" in os.environ and os.environ["port"]: try: port = int(os.environ["port"]) except ValueError: @@ -71,7 +78,7 @@ else: client = sphinxsearch.SphinxClient() client.SetServer(server, port) client.SetLimits(0, 1, 0, 0) - result = client.Query("", indexName) - docCount = result["total_found"] + result = client.Query("", index_name) + doc_count = result["total_found"] - print "documents_count.value %d" % docCount + print("documents_count.value %d" % doc_count) From 34840ad9dbda8c4dbf06170c82d2ab6cf31f9a1f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:18:45 +0200 Subject: [PATCH 103/695] Plugin weather_: fixes style issues --- plugins/weather/weather_ | 72 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/plugins/weather/weather_ b/plugins/weather/weather_ index d91580b7..c083df41 100755 --- a/plugins/weather/weather_ +++ b/plugins/weather/weather_ @@ -11,51 +11,51 @@ re_tmp = re.compile('realTemp: "(\d+)"') re_hum = re.compile('relativeHumidity: "(\d+)"') re_loc = re.compile('locName: "([\w ]+)"') -#code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] +# code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] code = os.environ.get('code', sys.argv[0][(sys.argv[0].rfind('_') + 1):]) -if code == None: sys.exit(1) - -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - u = urllib.urlopen(url % code) - txt = u.read() - u.close() + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - LOC_list = re_loc.findall(txt) - - if len(LOC_list): - LOC = LOC_list[0] - else: - LOC = "Unknown" + LOC_list = re_loc.findall(txt) - print 'graph_title Weather in %s' % LOC - print 'graph_vlabel Temperature and Humidity' - print 'graph_category sensors' + if len(LOC_list): + LOC = LOC_list[0] + else: + LOC = "Unknown" - print 'temperature.label Temperature' - print 'humidity.label Humidity' + print('graph_title Weather in %s' % LOC) + print('graph_vlabel Temperature and Humidity') + print('graph_category sensors') - print 'graph_args --base 1000 -l 0' + print('temperature.label Temperature') + print('humidity.label Humidity') + + print('graph_args --base 1000 -l 0') else: - u = urllib.urlopen(url % code) - txt = u.read() - u.close() + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - TMP_F_list = re_tmp.findall(txt) - HUM_list = re_hum.findall(txt) + TMP_F_list = re_tmp.findall(txt) + HUM_list = re_hum.findall(txt) - if len(HUM_list): - HUM = HUM_list[0] - else: - sys.exit(1) + if len(HUM_list): + HUM = HUM_list[0] + else: + sys.exit(1) - if len(TMP_F_list): - TMP_F = TMP_F_list[0] - TMP_C = (int(TMP_F) - 32) * 5/9 - else: - sys.exit(1) + if len(TMP_F_list): + TMP_F = TMP_F_list[0] + TMP_C = (int(TMP_F) - 32) * 5/9 + else: + sys.exit(1) - print 'temperature.value %s' % TMP_C - print 'humidity.value %s' % HUM + print('temperature.value %s' % TMP_C) + print('humidity.value %s' % HUM) From e216f1be0108b38609ca26c681dec8a1f8826e6c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:21:46 +0200 Subject: [PATCH 104/695] Plugin: weather_temp_: fix style issues --- plugins/weather/weather_temp_ | 41 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index eee22379..2addff40 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -22,32 +22,25 @@ re_C = re.compile('Temperature:.*\((-?\d+\.?\d?) C\)') re_DewC = re.compile('Dew.*\((-?\d+\.?\d?) C\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] -if code == None: sys.exit(1) -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - - print "yes" +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - - print 'graph_title Temperature and Dew Point at code %s' % code - print 'graph_vlabel Temperature and Dew Point in C' - print 'graph_category sensors' - - print 'temperature.label Temperature' - print 'dewpoint.label Dew Point' - - print 'graph_args --base 1000 -l 0' - + print('graph_title Temperature and Dew Point at code %s' % code) + print('graph_vlabel Temperature and Dew Point in C') + print('graph_category sensors') + print('temperature.label Temperature') + print('dewpoint.label Dew Point') + print('graph_args --base 1000 -l 0') else: + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - u = urllib.urlopen(url % code) - txt = u.read() - u.close() - - C = re_C.findall(txt)[0] - DewC = re_DewC.findall(txt)[0] - - print 'temperature.value %s' % C - print 'dewpoint.value %s' % DewC - + C = re_C.findall(txt)[0] + DewC = re_DewC.findall(txt)[0] + print('temperature.value %s' % C) + print('dewpoint.value %s' % DewC) From 47a97d56fac5d4f549329b41bdbbb92d00c6154e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:30:53 +0200 Subject: [PATCH 105/695] Plugin glance_size_: fixed style issues --- plugins/glance/glance_size_ | 75 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/plugins/glance/glance_size_ b/plugins/glance/glance_size_ index c45455d6..4b3ba99f 100755 --- a/plugins/glance/glance_size_ +++ b/plugins/glance/glance_size_ @@ -13,24 +13,22 @@ # To show tenant name plugin must run as root # # Magic markers -#%# capabilities=autoconf suggest -#%# family=auto +# #%# capabilities=autoconf suggest +# #%# family=auto import sys import os -try: +try: from sqlalchemy.orm import joinedload import sqlalchemy.exc - + from glance.common.cfg import CommonConfigOpts from glance.registry.db import models from glance.registry.db.api import get_session, configure_db from keystone.common import utils - from keystone import config - from keystone import exception - from keystone import identity + from keystone import config, exception, identity except ImportError: successful_import = False @@ -40,7 +38,7 @@ else: def get_name_from_tenant(tenant): try: - KEYSTONE_CONF = config.CONF(config_files=[utils.find_config('keystone.conf')]) + config.CONF(config_files=[utils.find_config('keystone.conf')]) except: # keystone configuration can not be loaded, use id as name" return tenant @@ -53,65 +51,67 @@ def get_name_from_tenant(tenant): # keystone database can not be connected, use id as name" return tenant - if not tenant_info: + if not tenant_info: return tenant else: return tenant_info["name"] - + def load_conf(): - CONF = CommonConfigOpts(project="glance", prog="glance-registry") - CONF() + loaded_config = CommonConfigOpts(project="glance", prog="glance-registry") + loaded_config() # Hide missing logger warning message sys.stderr = open(os.devnull, 'w') - configure_db(CONF) + configure_db(loaded_config) sys.stderr = sys.__stderr__ def print_config(tenant): if tenant == "Global": - print 'graph_title Glance used size for all tenants' - print 'graph_info This graph shows the used size in glance for all tenants' + print('graph_title Glance used size for all tenants') + print('graph_info This graph shows the used size in glance for all tenants') else: - print 'graph_title Glance used size for tenant %s' % get_name_from_tenant(tenant) - print 'graph_info This graph shows the used size in glance for tenant %s' % tenant - print 'graph_vlabel Bytes' - print 'graph_args --base 1024 --lower-limit 0' - print 'graph_category cloud' - print '%s.label %s' % (tenant, get_name_from_tenant(tenant)) - print '%s.draw LINE2' % tenant - print '%s.info %s MBytes' % (tenant, tenant) + print('graph_title Glance used size for tenant %s' % get_name_from_tenant(tenant)) + print('graph_info This graph shows the used size in glance for tenant %s' % tenant) + print('graph_vlabel Bytes') + print('graph_args --base 1024 --lower-limit 0') + print('graph_category cloud') + print('%s.label %s' % (tenant, get_name_from_tenant(tenant))) + print('%s.draw LINE2' % tenant) + print('%s.info %s MBytes' % (tenant, tenant)) + def request(**kwargs): session = get_session() try: - query = session.query(models.Image).\ - options(joinedload(models.Image.properties)).\ - options(joinedload(models.Image.members)) + query = session.query(models.Image).options(joinedload(models.Image.properties)).options( + joinedload(models.Image.members)) if kwargs: - query = query.filter_by(**kwargs) + query = query.filter_by(**kwargs) images = query.all() - except exc.NoResultFound: + except exception.NoResultFound: return [] return images + def print_suggest(): - print "Global" - print "\n".join(set( image["owner"] for image in request(deleted=False) )) + print("Global") + print("\n".join(set(image["owner"] for image in request(deleted=False)))) + def print_values(tenant): - if tenant != "Global" : + if tenant != "Global": images = request(deleted=False, owner=tenant) else: images = request(deleted=False) - total_size = sum([ image["size"] for image in images ]) - print '%s.value %s' % (tenant, total_size) + total_size = sum([image["size"] for image in images]) + print('%s.value %s' % (tenant, total_size)) if __name__ == '__main__': @@ -125,18 +125,17 @@ if __name__ == '__main__': load_conf() print_suggest() elif argv[1] == 'autoconf': - if not successful_import: - print 'no (failed import glance and/or sqlachemy module)' + if not successful_import: + print('no (failed import glance and/or sqlachemy module)') sys.exit(0) try: load_conf() get_session() except: - print 'no (failed to connect glance backend, check user)' + print('no (failed to connect glance backend, check user)') sys.exit(0) - print 'yes' + print('yes') elif successful_import: load_conf() print_values(tenant) - From 585f6f15394e7b82051c6956e9d106591c10b186 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:31:43 +0200 Subject: [PATCH 106/695] various plugins: fixed small style issues --- plugins/ntp/ntp_packets | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/ntp/ntp_packets b/plugins/ntp/ntp_packets index 987ed1fd..1fb19b05 100755 --- a/plugins/ntp/ntp_packets +++ b/plugins/ntp/ntp_packets @@ -34,7 +34,8 @@ import sys if len(sys.argv) == 2 and sys.argv[1] == 'config': print('graph_title NTP traffic') print('graph_vlabel Packets/${graph_period} received(-)/sent(+)') - print('graph_info This graph shows the packet rates of this ntpd. Bad means packets received with bad length or format. Authfailed means packets for which authentication failed.') + print('graph_info This graph shows the packet rates of this ntpd. Bad means packets received ' + 'with bad length or format. Authfailed means packets for which authentication failed.') print('graph_category time') print('received.label Received') print('received.type DERIVE') @@ -72,7 +73,8 @@ os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' + os.environ['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(['ntpq', '-c', 'version'], + universal_newlines=True).split()[1][0:5].replace('.', '') if int(version) >= 427: cmd = 'ntpq' @@ -81,9 +83,11 @@ else: stats = dict() -stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], universal_newlines=True).splitlines() +stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], + universal_newlines=True).splitlines() -for line in stats_output: stats[line.split(':')[0]] = int(line.split(':')[1]) +for line in stats_output: + stats[line.split(':')[0]] = int(line.split(':')[1]) print('received.value ' + str(stats['received packets'])) print('sent.value ' + str(stats['packets sent'])) From 98106443906a33cf7d25881460cab50eda4113b0 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:32:20 +0200 Subject: [PATCH 107/695] Plugin prosody_: fix style issues --- plugins/prosody/prosody_ | 137 ++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index f121e45b..4cab0d6a 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -25,6 +25,7 @@ import os import telnetlib import re + def main(): try: mode = sys.argv[1] @@ -35,56 +36,50 @@ def main(): port = int(os.environ.get('port', 5582)) if mode == "suggest": - print "c2s" - print "s2s" - print "presence" - print "uptime" - print "users" + print("c2s") + print("s2s") + print("presence") + print("uptime") + print("users") sys.exit(0) if wildcard == "c2s": if mode == "config": - print "graph_title Prosody C2S Connections" - print "graph_vlabel users" - print "graph_category chat" + print("graph_title Prosody C2S Connections") + print("graph_vlabel users") + print("graph_category chat") - print "all_client_connections.label client connections" - print "secure_client_connections.label secure client connections" - print "insecure_client_connections.label insecure client " \ - "connections" + print("all_client_connections.label client connections") + print("secure_client_connections.label secure client connections") + print("insecure_client_connections.label insecure client connections") sys.exit(0) else: connection_count_re = re.compile(r"Total:\s(\d+)\s") telnet = telnetlib.Telnet(host, port) telnet.write("c2s:show_secure()\n") - telnet_response = telnet.read_until("secure client connections", - 5) + telnet_response = telnet.read_until("secure client connections", 5) parsed_info = connection_count_re.findall(telnet_response) secure_client_connections = int(parsed_info[0]) - print "secure_client_connections.value %s" % \ - (secure_client_connections) + print("secure_client_connections.value %s" % secure_client_connections) telnet.write("c2s:show_insecure()\n") - telnet_response = telnet.read_until("insecure client connections", - 5) + telnet_response = telnet.read_until("insecure client connections", 5) parsed_info = connection_count_re.findall(telnet_response) insecure_client_connections = int(parsed_info[0]) - print "insecure_client_connections.value %s" % \ - (insecure_client_connections) - all_client_connections = secure_client_connections + \ - insecure_client_connections - print "all_client_connections.value %s" % (all_client_connections) + print("insecure_client_connections.value %s" % insecure_client_connections) + all_client_connections = secure_client_connections + insecure_client_connections + print("all_client_connections.value %s" % (all_client_connections)) telnet.write("quit\n") elif wildcard == "s2s": if mode == "config": - print "graph_title Prosody S2S Connections" - print "graph_vlabel servers" - print "graph_category chat" + print("graph_title Prosody S2S Connections") + print("graph_vlabel servers") + print("graph_category chat") - print "outgoing_connections.label outgoing connections" - print "incoming_connections.label incoming connections" + print("outgoing_connections.label outgoing connections") + print("incoming_connections.label incoming connections") sys.exit(0) else: @@ -93,21 +88,21 @@ def main(): telnet.write("s2s:show()\n") telnet_response = telnet.read_until("connections", 5) parsed_info = server_connections_re.findall(telnet_response) - print "outgoing_connections.value %s" % (parsed_info[0][0]) - print "incoming_connections.value %s" % (parsed_info[0][1]) + print("outgoing_connections.value %s" % (parsed_info[0][0])) + print("incoming_connections.value %s" % (parsed_info[0][1])) telnet.write("quit\n") elif wildcard == "presence": if mode == "config": - print "graph_title Prosody Client Presence" - print "graph_vlabel clients" - print "graph_category chat" + print("graph_title Prosody Client Presence") + print("graph_vlabel clients") + print("graph_category chat") - print "available.label Avaible Clients" - print "chat.label Ready for Chat Clients" - print "away.label Away Clients" - print "xa.label Extended Away Clients" - print "dnd.label Do Not Disturb Clients" + print("available.label Avaible Clients") + print("chat.label Ready for Chat Clients") + print("away.label Away Clients") + print("xa.label Extended Away Clients") + print("dnd.label Do Not Disturb Clients") sys.exit(0) else: @@ -116,26 +111,26 @@ def main(): telnet.write("c2s:show()\n") telnet_response = telnet.read_until("clients", 5) parsed_info = client_presence_re.findall(telnet_response) - print "available.value %s" % (parsed_info.count("available")) - print "chat.value %s" % (parsed_info.count("chat")) - print "away.value %s" % (parsed_info.count("away")) - print "xa.value %s" % (parsed_info.count("xa")) - print "dnd.value %s" % (parsed_info.count("dnd")) + print("available.value %s" % parsed_info.count("available")) + print("chat.value %s" % (parsed_info.count("chat"))) + print("away.value %s" % (parsed_info.count("away"))) + print("xa.value %s" % (parsed_info.count("xa"))) + print("dnd.value %s" % (parsed_info.count("dnd"))) telnet.write("quit\n") elif wildcard == "uptime": if mode == "config": - print "graph_title Prosody Uptime" - print "graph_args --base 1000 -l 0" - print "graph_scale no" - print "graph_vlabel uptime in days" - print "graph_category chat" - print "graph_order uptime" - print "uptime.draw AREA" - print "uptime.min U" - print "uptime.max U" - print "uptime.label uptime" - print "uptime.type GAUGE" + print("graph_title Prosody Uptime") + print("graph_args --base 1000 -l 0") + print("graph_scale no") + print("graph_vlabel uptime in days") + print("graph_category chat") + print("graph_order uptime") + print("uptime.draw AREA") + print("uptime.min U") + print("uptime.max U") + print("uptime.label uptime") + print("uptime.type GAUGE") sys.exit(0) else: @@ -144,16 +139,16 @@ def main(): telnet.write("server:uptime()\n") telnet_response = telnet.read_until("minutes (", 5) parsed_info = uptime_re.findall(telnet_response) - uptime_value = float(parsed_info[0]) + float(parsed_info[1])/24 +\ - float(parsed_info[2])/60/24 - print "uptime.value %s" % (uptime_value) + uptime_value = (float(parsed_info[0]) + float(parsed_info[1])/24 + + float(parsed_info[2])/60/24) + print("uptime.value %s" % (uptime_value)) telnet.write("quit\n") elif wildcard == "users": if mode == "config": - print "graph_title Prosody Registered Users" - print "graph_vlabel users" - print "graph_category chat" + print("graph_title Prosody Registered Users") + print("graph_vlabel users") + print("graph_category chat") base_dir = os.environ.get('internal_storage_path', "/var/lib/prosody") if os.path.isdir(base_dir): @@ -161,35 +156,40 @@ def main(): for vhost in vhosts: account_dir = os.path.join(base_dir, vhost, "accounts") if os.path.isdir(account_dir): - vhost = vhost.replace("%2e",".") - munin_var = vhost.replace(".","_") + vhost = vhost.replace("%2e", ".") + munin_var = vhost.replace(".", "_") if mode == "config": - print "%s.label %s" % (munin_var, vhost) + print("%s.label %s" % (munin_var, vhost)) else: accounts = len(list(listfiles(account_dir))) - print "%s.value %s" % (munin_var, accounts) + print("%s.value %s" % (munin_var, accounts)) + def listdirs(folder): for x in os.listdir(folder): if os.path.isdir(os.path.join(folder, x)): yield x + def listfiles(folder): for x in os.listdir(folder): if os.path.isfile(os.path.join(folder, x)): yield x + if __name__ == '__main__': main() -### Here starts the prosody_ plugin documentation, intended to be used with munindoc and in plugin gallery +# Here starts the prosody_ plugin documentation, intended to be used with munindoc and in +# plugin gallery. """ =head1 NAME prosody_ - Munin wildcard-plugin to monitor a L xmpp server. -This wildcard plugin provides at the moment only the suffixes C, C, C, C and C suffixes. +This wildcard plugin provides at the moment only the suffixes C, C, C, +C and C suffixes. =head1 INSTALLATION @@ -219,7 +219,7 @@ After the installation you need to restart your munin-node: =head1 CONFIGURATION -When you want to change the default host (localhost) and port (5582) do it in a file named prosody +When you want to change the default host (localhost) and port (5582) do it in a file named prosody placed in the directory /etc/munin/plugin-conf.d/ with a config like this: =over 2 @@ -230,7 +230,8 @@ placed in the directory /etc/munin/plugin-conf.d/ with a config like this: =back -If you want to get the number of registered users, add the following lines to /etc/munin/plugin-conf.d/prosody: +If you want to get the number of registered users, add the following lines to +/etc/munin/plugin-conf.d/prosody: =over 2 From 04bf94274869e0f8583f56f430879b445fe02534 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:32:26 +0200 Subject: [PATCH 108/695] add trivial .flake8 file (for flake8/PEP8) checks --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..61d90815 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 99 From e8dd273047b5abae242c3773833c7a9ee8a91c39 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:46:21 +0200 Subject: [PATCH 109/695] Plugin bacula_sd: fix type issues --- plugins/bacula/bacula_sd | 165 ++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/plugins/bacula/bacula_sd b/plugins/bacula/bacula_sd index 7cb0922d..b05ddae3 100755 --- a/plugins/bacula/bacula_sd +++ b/plugins/bacula/bacula_sd @@ -15,126 +15,129 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # - +# # # Munin Plugin to get storage device throughput for Bacula by parsing the bconsole # output. # # Parameters: # -# config (required) -# autoconf (optional - only used by munin-config) +# config (required) +# autoconf (optional - only used by munin-config) +# # - # Magic markers (optional - only used by munin-config and some # installation scripts): # -#%# family=contrib -#%# capabilities=autoconf +# #%# family=contrib +# #%# capabilities=autoconf import subprocess -import time import sys import re import os + def parse_devices(): - """ Parse the bconsole output once to get the device names """ + """ Parse the bconsole output once to get the device names """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n2") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n2") - devs = [] + devs = [] - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + dev_line = [] + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Connecting to Storage daemon "): - hostname = line.split()[-1].split(":")[0] - if line.startswith("Device \""): - dev_line.append(i) + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Connecting to Storage daemon "): + hostname = line.split()[-1].split(":")[0] + if line.startswith("Device \""): + dev_line.append(i) - for pos in dev_line: - # Get the device name - dev_name = input[pos].split()[1][1:-1] - dev_dev = input[pos].split()[2][1:-1] - dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) - dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) - devs.append([dev_name, dev_dev, dev_dev_clean]) + for pos in dev_line: + # Get the device name + dev_name = input_lines[pos].split()[1][1:-1] + dev_dev = input_lines[pos].split()[2][1:-1] + dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) + dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) + devs.append([dev_name, dev_dev, dev_dev_clean]) - return hostname, devs + return hostname, devs def parse(): - """ Parse the bconsole output """ + """ Parse the bconsole output """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n2") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n2") - devstats = [] + devstats = [] - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + dev_line = [] + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Device \""): - dev_line.append(i) + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Device \""): + dev_line.append(i) - for pos in dev_line: - # Get the device name - dev_dev = input[pos].split()[2][1:-1] - dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) - dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) + for pos in dev_line: + # Get the device name + dev_dev = input_lines[pos].split()[2][1:-1] + dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) + dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) - # Get the current bytes - if input[pos].endswith("is mounted with:"): - bytes = long(input[pos+5].split()[1].split("=")[1].replace(",", "")) - devstats.append([dev_dev, dev_dev_clean, bytes]) - else: - devstats.append([dev_dev, dev_dev_clean, 0]) + # Get the current bytes + if input_lines[pos].endswith("is mounted with:"): + bytes_count_text = input_lines[pos+5].split()[1].split("=")[1].replace(",", "") + try: + bytes_count = long(bytes_count_text) + except NameError: + bytes_count = int(bytes_count_text) + devstats.append([dev_dev, dev_dev_clean, bytes_count]) + else: + devstats.append([dev_dev, dev_dev_clean, 0]) - return devstats + return devstats def print_config(): - hostname, devstats = parse_devices() - print "graph_title Bacula Storage Daemon throughput" - print "graph_vlabel bytes per ${graph_period}" - print "graph_args --base 1024 -l 0" - print "graph_scale yes" - print "graph_info Bacula Storage Daemon througput measurement based on written bytes. This may be somewhat inacurate whenever a tape is changed." - print "graph_category backup" - print "graph_order", - for dev in devstats: - print dev[2], - print - if os.getenv("report_hostname") is not None and \ - os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"]: - print "host_name", hostname - for dev in devstats: - print "%s.label %s" % (dev[2], dev[1]) - print "%s.type DERIVE" % (dev[2]) - print "%s.min 0" % (dev[2]) -# print "%s.max %s" % (dev[2], str(1024*1024*1024*16)) -# print "%s.cdef up,8,*" (dev[2]) - sys.exit(0) + hostname, devstats = parse_devices() + print("graph_title Bacula Storage Daemon throughput") + print("graph_vlabel bytes per ${graph_period}") + print("graph_args --base 1024 -l 0") + print("graph_scale yes") + print("graph_info Bacula Storage Daemon througput measurement based on written bytes. " + "This may be somewhat inacurate whenever a tape is changed.") + print("graph_category backup") + print("graph_order", " ".join([dev[2] for dev in devstats])) + print() + if (os.getenv("report_hostname") is not None + and (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): + print("host_name", hostname) + for dev in devstats: + print("%s.label %s" % (dev[2], dev[1])) + print("%s.type DERIVE" % (dev[2])) + print("%s.min 0" % (dev[2])) +# print("%s.max %s" % (dev[2], str(1024*1024*1024*16))) +# print("%s.cdef up,8,*" (dev[2])) + sys.exit(0) if "config" in sys.argv[1:]: - print_config() + print_config() elif "autoconf" in sys.argv[1:]: - for dir in os.getenv("PATH").split(":"): - for root, dirs, files in os.walk(dir): - if "bconsole" in files: - print "yes" - sys.exit(0) - print "no" - sys.exit(1) + for directory in os.getenv("PATH").split(":"): + for root, dirs, files in os.walk(directory): + if "bconsole" in files: + print("yes") + sys.exit(0) + print("no") + sys.exit(0) elif "suggest" in sys.argv[1:]: - sys.exit(1) + sys.exit(1) else: - for dev in parse(): - print "%s.value %s" % (dev[1], dev[2]) + for dev in parse(): + print("%s.value %s" % (dev[1], dev[2])) From 417bebc316c4726da7670a177005d36ac2e6d4f7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:50:21 +0200 Subject: [PATCH 110/695] Plugin weather_press_: fix style issues --- plugins/weather/weather_press_ | 40 +++++++++++++++------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index b5dc8623..ce55e3a2 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -18,33 +18,27 @@ import re url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' -re_hPa = re.compile('Pressure.*\((\d+) hPa\)') +re_hpa = re.compile('Pressure.*\((\d+) hPa\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] -if code == None: sys.exit(1) - -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - - print "yes" - +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": + print('graph_title Atmospheric pressure at code %s' % code) + print('graph_vlabel Pressure in hPa') + print('graph_category sensors') - print 'graph_title Atmospheric pressure at code %s' % code - print 'graph_vlabel Pressure in hPa' - print 'graph_category sensors' - - print 'pressure.label Pressure' - print 'pressure.type GAUGE' - print 'graph_args --base 1000 -l 850 -u 1050 --rigid' - print 'graph_scale no' - + print('pressure.label Pressure') + print('pressure.type GAUGE') + print('graph_args --base 1000 -l 850 -u 1050 --rigid') + print('graph_scale no') else: + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - u = urllib.urlopen(url % code) - txt = u.read() - u.close() - - hPa = re_hPa.findall(txt)[0] - - print 'pressure.value %s' % hPa + hpa = re_hpa.findall(txt)[0] + print('pressure.value %s' % hpa) From 7f3ce966320a72499d81c007842a5d6c86a6dc8f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:55:14 +0200 Subject: [PATCH 111/695] Plugin monit_parser: fix style issues --- plugins/monit/monit_parser | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 8cdb36b1..f7fb73a0 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -61,8 +61,8 @@ MONIT_XML_URL = ("http://{host}:{port}/_status?format=xml" def sanitize(s): - OK_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789" - return "".join([char for char in s if char in OK_CHARS]) + ok_chars = "abcdefghijklmnopqrstuvwxyz0123456789" + return "".join([char for char in s if char in ok_chars]) def get_monit_status_xml(): From 86692d70214441b5e5d40c3c8f24c38d8ba188c3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:56:30 +0200 Subject: [PATCH 112/695] Plugin sockstat-via-procfs: fix style issues --- plugins/network/sockstat-via-procfs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/network/sockstat-via-procfs b/plugins/network/sockstat-via-procfs index 4c875aa2..88afe093 100755 --- a/plugins/network/sockstat-via-procfs +++ b/plugins/network/sockstat-via-procfs @@ -20,13 +20,13 @@ fields = ( if len(sys.argv) > 1: arg = sys.argv[1] if arg == 'autoconfig': - print 'yes' + print('yes') sys.exit(0) if arg == 'config': - print 'graph_title sockstat' - print 'graph_category network' + print('graph_title sockstat') + print('graph_category network') for name, label in fields: - print '%s.label %s' % (name, label) + print('%s.label %s' % (name, label)) sys.exit(0) re_num = re.compile('(\d+)') @@ -34,6 +34,6 @@ sockstat = open('/proc/net/sockstat').read() numbers = re_num.findall(sockstat) for i, (name, label) in enumerate(fields): - print '%s.value %s' % (name, numbers[i]) + print('%s.value %s' % (name, numbers[i])) sys.exit(0) From 23811e34905b9990ac88c82ed4a7e797987114b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 04:59:57 +0200 Subject: [PATCH 113/695] Plugin etherscan_balance_: fix style issues; change to python3 --- plugins/currency/ethereum/etherscan_balance_ | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/currency/ethereum/etherscan_balance_ b/plugins/currency/ethereum/etherscan_balance_ index 2445b7f6..497889f4 100755 --- a/plugins/currency/ethereum/etherscan_balance_ +++ b/plugins/currency/ethereum/etherscan_balance_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ =head1 NAME @@ -86,7 +86,8 @@ if command == 'config': print("wei_balance_{}.label ETH".format(eth_address)) sys.exit(0) -ethercan_balance_api_url = 'https://api.etherscan.io/api?module=account&action=balance&tag=latest&address=' + eth_address +ethercan_balance_api_url = ('https://api.etherscan.io/api' + '?module=account&action=balance&tag=latest&address=' + eth_address) etherscan_req = Request(ethercan_balance_api_url) # User-Agent to bypass Cloudflare @@ -109,8 +110,8 @@ except ValueError: try: eth = int(etherscan_balance['result']) -except: +except (KeyError, ValueError) as exc: print("JSON result error!", file=sys.stderr) sys.exit(9) -print("wei_balance_{}.value {}".format(eth_address, eth)); +print("wei_balance_{}.value {}".format(eth_address, eth)) From 1e717d8901eb2ede7034a8195fa5eb9b781c3363 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 05:02:23 +0200 Subject: [PATCH 114/695] Plugin swift-dispersion: fix style issues --- plugins/swift/swift-dispersion | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/plugins/swift/swift-dispersion b/plugins/swift/swift-dispersion index 99538b77..73f0d3c5 100755 --- a/plugins/swift/swift-dispersion +++ b/plugins/swift/swift-dispersion @@ -27,46 +27,46 @@ import json try: if sys.argv[1] == "config": - print 'graph_title Swift cluster dispersion' - print 'graph_category fs' + print('graph_title Swift cluster dispersion') + print('graph_category fs') - print 'object_missing_two.type GAUGE' - print 'object_missing_two.label Objects missing two copies' - print 'object_retries.type GAUGE' - print 'object_retries.label Objects retries' - print 'object_copies_expected.type GAUGE' - print 'object_copies_expected.label Objects copies expected' - print 'object_missing_one.type GAUGE' - print 'object_missing_one.label Objects missing one copy' - print 'object_copies_found.type GAUGE' - print 'object_copies_found.label Objects copies found' - print 'object_missing_all.type GAUGE' - print 'object_missing_all.label Objects missing all copies' - print 'object_overlapping.type GAUGE' - print 'object_overlapping.label Objects overlapping partitions' + print('object_missing_two.type GAUGE') + print('object_missing_two.label Objects missing two copies') + print('object_retries.type GAUGE') + print('object_retries.label Objects retries') + print('object_copies_expected.type GAUGE') + print('object_copies_expected.label Objects copies expected') + print('object_missing_one.type GAUGE') + print('object_missing_one.label Objects missing one copy') + print('object_copies_found.type GAUGE') + print('object_copies_found.label Objects copies found') + print('object_missing_all.type GAUGE') + print('object_missing_all.label Objects missing all copies') + print('object_overlapping.type GAUGE') + print('object_overlapping.label Objects overlapping partitions') - print 'container_missing_two.type GAUGE' - print 'container_missing_two.label Containers missing two copies' - print 'container_retries.type GAUGE' - print 'container_retries.label Containers retries' - print 'container_copies_expected.type GAUGE' - print 'container_copies_expected.label Containers copies expected' - print 'container_missing_one.type GAUGE' - print 'container_missing_one.label Containers missing one copy' - print 'container_copies_found.type GAUGE' - print 'container_copies_found.label Containers copies found' - print 'container_missing_all.type GAUGE' - print 'container_missing_all.label Containers missing all copies' - print 'container_overlapping.type GAUGE' - print 'container_overlapping.label Containers overlapping paritions' + print('container_missing_two.type GAUGE') + print('container_missing_two.label Containers missing two copies') + print('container_retries.type GAUGE') + print('container_retries.label Containers retries') + print('container_copies_expected.type GAUGE') + print('container_copies_expected.label Containers copies expected') + print('container_missing_one.type GAUGE') + print('container_missing_one.label Containers missing one copy') + print('container_copies_found.type GAUGE') + print('container_copies_found.label Containers copies found') + print('container_missing_all.type GAUGE') + print('container_missing_all.label Containers missing all copies') + print('container_overlapping.type GAUGE') + print('container_overlapping.label Containers overlapping paritions') sys.exit(0) except IndexError: pass -with os.popen("swift-dispersion-report -j %s" \ - % os.getenv("SWIFT_DISPERSION_CONFIG", "/etc/swift/dispersion.conf")) as report: +with os.popen("swift-dispersion-report -j %s" + % os.getenv("SWIFT_DISPERSION_CONFIG", "/etc/swift/dispersion.conf")) as report: stats = json.load(report) for type_, values in stats.iteritems(): for key, value in values.iteritems(): - print "%s_%s.value %d" % (type_, key, value) + print("%s_%s.value %d" % (type_, key, value)) From 6b42464ca75500096f7d973832e03c7908f7ff17 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 27 Mar 2018 05:04:12 +0200 Subject: [PATCH 115/695] Plugin deluge_: fix style issues --- plugins/torrent/deluge_ | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/torrent/deluge_ b/plugins/torrent/deluge_ index 3387f518..6812ae21 100755 --- a/plugins/torrent/deluge_ +++ b/plugins/torrent/deluge_ @@ -304,7 +304,8 @@ def print_config(mode): ".info The number of connections used by Deluge Torrent") elif mode == "bandwidth": print("graph_title Bandwidth usage") - print("graph_order payloadDownloadRate overheadDownloadRate payloadUploadRate overheadUploadRate") + print("graph_order payloadDownloadRate overheadDownloadRate payloadUploadRate " + "overheadUploadRate") print("graph_args --base 1024 -r") print("graph_vlabel bytes/s : down(-) and up(+)") print("graph_scale yes") @@ -322,7 +323,8 @@ def print_config(mode): print("overheadDownloadRate.draw STACK") print("overheadDownloadRate.min 0") print("overheadDownloadRate.graph no") - print("overheadDownloadRate.info Bandwidth 'lost' due to overhead while downloading and uploading torrents") + print("overheadDownloadRate.info Bandwidth 'lost' due to overhead while downloading and " + "uploading torrents") print("payloadUploadRate.label payload") print("payloadUploadRate.draw AREA") @@ -334,7 +336,8 @@ def print_config(mode): print("overheadUploadRate.draw STACK") print("overheadUploadRate.min 0") print("overheadUploadRate.negative overheadDownloadRate") - print("overheadUploadRate.info Bandwidth 'lost' due to overhead while downloading and uploading torrents") + print("overheadUploadRate.info Bandwidth 'lost' due to overhead while downloading and " + "uploading torrents") elif mode == "states": print("graph_title Torrents states") From 4d9a7e2cabc970d65ba12cf446236c6499ff77a0 Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 27 Mar 2018 11:36:14 +0200 Subject: [PATCH 116/695] Zcash Flypool Hashrate Munin plugin to monitor your zcash.flypool.org hashrate. --- .../currency/zcash/zcash_flypool_hashrate_ | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 plugins/currency/zcash/zcash_flypool_hashrate_ diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ new file mode 100755 index 00000000..d11fb7fb --- /dev/null +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +""" +=head1 NAME + +zcash_flypool_hashrate_ - Munin plugin to monitor your zcash.flypool.org hashrate (H/s) + +=head1 APPLICABLE SYSTEMS + +All systems with "python" and "munin" + +=head1 CONFIGURATION + +zcash_flypool_hashrate__ + +=head1 SYNOPSIS + + ln -s /usr/share/munin/plugins/zcash_flypool_hashrate_ \ + /etc/munin/plugins/zcash_flypool_hashrate_t1gMVWjGhdjvb71UU11JDrFmiZhgUf4x5TH_mine + +=head1 INTERPRETATION + +This plugin shows the zcash.flypool.org mining pool hashrate (H/s) of a given Zcasg address and rig name. +Hashrate is queried via Flypool API L. + +=head1 VERSION + +0.0.1 + +=head1 AUTHOR + +L + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut +""" + +from __future__ import print_function + +import sys +import json +import codecs + +try: + # python3 + from urllib.request import urlopen + from urllib.request import Request +except ImportError: + # python2 + from urllib2 import urlopen + from urllib2 import Request + +command = '' +if len(sys.argv) > 1: + command = sys.argv[1] + +try: + zcash_address, miner = sys.argv[0].split("_")[3:] +except ValueError: + print("The filename of this plugin (or its symlink) should follow this pattern: " + "'zcash_flypool_hashrate__'", file=sys.stderr) + sys.exit(9) + +if command == 'config': + print("graph_title Flypool {}".format(miner)) + print("graph_info zcash.flypool.org Mining Pool Hashrate for {}_{}".format(zcash_address, miner)) + print("graph_vlabel Flypool Hashrate") + print("graph_category other") + print("flypool_hs_{}_{}.warning 200:".format(zcash_address, miner)) + print("flypool_hs_{}_{}.critical 100:".format(zcash_address, miner)) + print("flypool_hs_{}_{}.label H/s:".format(zcash_address, miner)) + sys.exit(0) + + +flypool_api_url = 'https://api-zcash.flypool.org/miner/' + zcash_address + '/worker/' + miner + '/currentStats' + +mining_req = Request(flypool_api_url) +# User-Agent to bypass Cloudflare +mining_req.add_header('User-Agent', 'Flypool Munin Plugin/1.0') + +try: + mining_stats_raw = urlopen(mining_req, timeout=15) +except IOError as exc: + print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) + +reader = codecs.getreader("utf-8") + +try: + mining_stats = json.load(reader(mining_stats_raw)) +except ValueError: + print("Failed to parse JSON responce.", file=sys.stderr) + sys.exit(9) + +try: + worker = mining_stats['data'] +except: + print("JSON result error!", file=sys.stderr) + sys.exit(9) + +try: + hash_rate = worker['currentHashrate'] +except: + print("No current Hashrate!", file=sys.stderr) + sys.exit(9) + +print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) \ No newline at end of file From c594a164841a9e0abd6dcda93644871c3d3b75f0 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Tue, 27 Mar 2018 16:58:10 +0200 Subject: [PATCH 117/695] Add strelay_ example graphs Signed-off-by: Pierre-Alain TORET --- .../syncthing/example-graphs/strelaysrv_-1.png | Bin 0 -> 19072 bytes .../syncthing/example-graphs/strelaysrv_-2.png | Bin 0 -> 20439 bytes .../syncthing/example-graphs/strelaysrv_-3.png | Bin 0 -> 22775 bytes .../syncthing/example-graphs/strelaysrv_-4.png | Bin 0 -> 12203 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/syncthing/example-graphs/strelaysrv_-1.png create mode 100644 plugins/syncthing/example-graphs/strelaysrv_-2.png create mode 100644 plugins/syncthing/example-graphs/strelaysrv_-3.png create mode 100644 plugins/syncthing/example-graphs/strelaysrv_-4.png diff --git a/plugins/syncthing/example-graphs/strelaysrv_-1.png b/plugins/syncthing/example-graphs/strelaysrv_-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ea26f57d9907faf01080022b22fe9fe53661e8b2 GIT binary patch literal 19072 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 z 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 N{A7!@sl{A>@Gqx>Zma+R literal 0 HcmV?d00001 diff --git a/plugins/syncthing/example-graphs/strelaysrv_-4.png b/plugins/syncthing/example-graphs/strelaysrv_-4.png new file mode 100644 index 0000000000000000000000000000000000000000..244e3eda224ac7b146b1c55af99856364483a984 GIT binary patch literal 12203 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 ML+41Q`nenb2Ob;UjQ{`u literal 0 HcmV?d00001 From e527db57a53d6456a68e3512e7f5aa2f222e07f2 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Tue, 27 Mar 2018 18:04:29 +0200 Subject: [PATCH 118/695] Improve plugins call to status and few other fixes Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/strelaysrv_ | 36 +++++++++++++++++++++------------- plugins/syncthing/syncthing_ | 37 +++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index 525a0c9f..c5a9fe3d 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/sh : <<=cut =head1 NAME strelaysrv_ - Plugin to monitor Syncthing relay server @@ -31,6 +31,10 @@ Pierre-Alain TORET MIT =cut +getstatus() { + "$CURL" -s "http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status" +} + num() { case $1 in config) @@ -45,15 +49,15 @@ strelaysrv_num_proxies.label proxies EOM exit 0;; *) - STATUS=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status) + STATUS=$(getstatus) NS=$(echo "$STATUS" | $JQ '.numActiveSessions ') NC=$(echo "$STATUS" | $JQ '.numConnections ') NK=$(echo "$STATUS" | $JQ '.numPendingSessionKeys ') NP=$(echo "$STATUS" | $JQ '.numProxies ') - printf "strelaysrv_num_sessions.value %s\\n" "$NS" - printf "strelaysrv_num_connections.value %s\\n" "$NC" - printf "strelaysrv_num_pending.value %s\\n" "$NK" - printf "strelaysrv_num_proxies.value %s\\n" "$NP" + printf "strelaysrv_num_sessions.value %s\n" "$NS" + printf "strelaysrv_num_connections.value %s\n" "$NC" + printf "strelaysrv_num_pending.value %s\n" "$NK" + printf "strelaysrv_num_proxies.value %s\n" "$NP" esac } @@ -68,8 +72,9 @@ strelaysrv_uptime.label uptime EOM exit 0;; *) - UPTIME=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.uptimeSeconds') - printf "strelaysrv_uptime.value %s\\n" "$UPTIME" + STATUS=$(getstatus) + UPTIME=$(echo "$STATUS" | "$JQ" '.uptimeSeconds') + printf "strelaysrv_uptime.value %s\n" "$UPTIME" esac } @@ -84,8 +89,9 @@ strelaysrv_goroutine.label routines EOM exit 0;; *) - GOROUTINE=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.goNumRoutine') - printf "strelaysrv_goroutine.value %s\\n" "$GOROUTINE" + STATUS=$(getstatus) + GOROUTINE=$(echo "$STATUS" | "$JQ" '.goNumRoutine') + printf "strelaysrv_goroutine.value %s\n" "$GOROUTINE" esac } @@ -102,8 +108,9 @@ strelaysrv_proxied.cdef strelaysrv_proxied,8,* EOM exit 0;; *) - BP=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.bytesProxied ') - printf "strelaysrv_proxied.value %s\\n" "$BP" + STATUS=$(getstatus) + BP=$(echo "$STATUS" | "$JQ" '.bytesProxied ') + printf "strelaysrv_proxied.value %s\n" "$BP" esac } @@ -120,8 +127,9 @@ strelaysrv_transfer.cdef strelaysrv_transfer,1000,* EOM exit 0;; *) - TRANSFER=$($CURL -s http://"$syncthing_relaysrv_host":"$syncthing_relaysrv_port"/status | $JQ '.kbps10s1m5m15m30m60m[2] ') - printf "strelaysrv_transfer.value %s\\n" "$TRANSFER" + STATUS=$(getstatus) + TRANSFER=$(echo "$STATUS" | "$JQ" '.kbps10s1m5m15m30m60m[2] ') + printf "strelaysrv_transfer.value %s\n" "$TRANSFER" esac } diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index a26236cc..25ac50c0 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/sh # -*- sh -*- : <<=cut =head1 NAME @@ -34,6 +34,10 @@ Pierre-Alain TORET MIT =cut +getstatus() { + "$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status" +} + cpu() { case $1 in config) @@ -46,8 +50,9 @@ syncthing_cpu.label cpu EOM exit 0;; *) - CPU=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.cpuPercent') - printf "syncthing_cpu.value %s\\n" "$CPU" + STATUS=$(getstatus) + CPU=$(echo "$STATUS" | "$JQ" '.cpuPercent') + printf "syncthing_cpu.value %s\n" "$CPU" esac } @@ -67,11 +72,11 @@ syncthing_mem_sys.cdef syncthing_mem_sys,8,* EOM exit 0;; *) - STATUS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status) - ALL=$(echo "$STATUS" | $JQ '.alloc') - SYS=$(echo "$STATUS" | $JQ '.sys') - printf "syncthing_mem_all.value %s\\n" "$ALL" - printf "syncthing_mem_sys.value %s\\n" "$SYS" + STATUS=$(getstatus) + ALL=$(echo "$STATUS" | "$JQ" '.alloc') + SYS=$(echo "$STATUS" | "$JQ" '.sys') + printf "syncthing_mem_all.value %s\n" "$ALL" + printf "syncthing_mem_sys.value %s\n" "$SYS" esac } @@ -86,8 +91,9 @@ syncthing_uptime.label uptime EOM exit 0;; *) - UPTIME=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.uptime') - printf "syncthing_uptime.value %s\\n" "$UPTIME" + STATUS=$(getstatus) + UPTIME=$(echo "$STATUS" | "$JQ" '.uptime') + printf "syncthing_uptime.value %s\n" "$UPTIME" esac } @@ -102,8 +108,9 @@ syncthing_goroutine.label routines EOM exit 0;; *) - GOROUTINES=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/status | $JQ '.goroutines') - printf "syncthing_goroutine.value %s\\n" "$GOROUTINES" + STATUS=$(getstatus) + GOROUTINES=$(echo "$STATUS" | "$JQ" '.goroutines') + printf "syncthing_goroutine.value %s\n" "$GOROUTINES" esac } @@ -127,9 +134,9 @@ syncthing_transfer_up.cdef syncthing_transfer_up,8,* EOM exit 0;; *) - CONNECTIONS=$($CURL -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto"://"$syncthing_host":"$syncthing_port"/rest/system/connections) - IBT=$(echo "$CONNECTIONS" | $JQ '.total | .inBytesTotal') - OBT=$(echo "$CONNECTIONS" | $JQ '.total | .outBytesTotal') + CONNECTIONS=$("$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections") + IBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .inBytesTotal') + OBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .outBytesTotal') printf "syncthing_transfer_up.value %s\\n" "$IBT" printf "syncthing_transfer_down.value %s\\n" "$OBT" esac From e79604f7f72647f08f84a830a89dc872a664ce05 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 02:40:20 +0200 Subject: [PATCH 119/695] nagios_multi_: fix categories according to well-known category list --- plugins/nagios/nagios_multi_ | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/nagios/nagios_multi_ b/plugins/nagios/nagios_multi_ index f9031dd9..20dbedc7 100755 --- a/plugins/nagios/nagios_multi_ +++ b/plugins/nagios/nagios_multi_ @@ -127,7 +127,7 @@ $graphs{services} = { config => { args => '--lower-limit 0', vlabel => 'Service Problems', - category => 'nagios', + category => 'munin', title => 'Service Problems', info => 'Current Service Problems by Alert Status', }, @@ -144,7 +144,7 @@ $graphs{svcchkdetail} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Service Checks', - category => 'details', + category => 'munin', title => 'Detailed Service Info', info => 'Detailed Service Check Information', }, @@ -164,7 +164,7 @@ $graphs{svcchksc} = { config => { args => '--lower-limit 0 --upper-limit 100', vlabel => '%', - category => 'statechange', + category => 'munin', title => 'Service State Change', info => 'Total Percent of State Change between checks', }, @@ -180,7 +180,7 @@ $graphs{svcchklat} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'latency', + category => 'munin', title => 'Service Check Latency Times', info => 'Service Check Latency Times', }, @@ -196,7 +196,7 @@ $graphs{svcchkext} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'execution', + category => 'munin', title => 'Service Check Execution Times', info => 'Service Check Execution Times', }, @@ -212,7 +212,7 @@ $graphs{hosts} = { config => { args => '--lower-limit 0', vlabel => 'Host Problems', - category => 'nagios', + category => 'munin', title => 'Host Problems', info => 'Current Host Problems by Alert Status', }, @@ -228,7 +228,7 @@ $graphs{hostchkdetail} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Host Checks', - category => 'details', + category => 'munin', title => 'Detailed Host Info', info => 'Detailed Host Check Information', }, @@ -247,7 +247,7 @@ $graphs{hostchksc} = { config => { args => '--lower-limit 0 --upper-limit 100', vlabel => '%', - category => 'statechange', + category => 'munin', title => 'Host State Change', info => 'Total Percent of State Change between checks', }, @@ -263,7 +263,7 @@ $graphs{hostchklat} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'latency', + category => 'munin', title => 'Host Check Latency Times', info => 'Host Check Latency Times', }, @@ -279,7 +279,7 @@ $graphs{hostchkext} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'execution', + category => 'munin', title => 'Host Check Execution Times', info => 'Host Check Execution Times', }, @@ -295,7 +295,7 @@ $graphs{checks} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Checks', - category => 'nagios', + category => 'munin', title => 'Totals', info => 'Total Number of Service and Host Checks', }, @@ -310,7 +310,7 @@ $graphs{hostchkactcount} = { config => { args => '--lower-limit 0', vlabel => 'Number Host Checks', - category => 'active', + category => 'munin', title => 'Host Checks', info => 'Total Number of Active Host Checks', order => 'NUMHSTACTCHK60M NUMHSTACTCHK15M NUMHSTACTCHK5M NUMHSTACTCHK1M', @@ -328,7 +328,7 @@ $graphs{hostchkpsvcount} = { config => { args => '--lower-limit 0', vlabel => 'Number Host Checks', - category => 'passive', + category => 'munin', title => 'Host Checks', info => 'Total Number of Passive Host Checks', order => 'NUMHSTPSVCHK60M NUMHSTPSVCHK15M NUMHSTPSVCHK5M NUMHSTPSVCHK1M', @@ -346,7 +346,7 @@ $graphs{svcchkactcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Service Checks', - category => 'active', + category => 'munin', title => 'Service Checks', info => 'Total Number of Active Service Checks', order => 'NUMSVCACTCHK60M NUMSVCACTCHK15M NUMSVCACTCHK5M NUMSVCACTCHK1M', @@ -364,7 +364,7 @@ $graphs{svcchkpsvcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Service Checks', - category => 'passive', + category => 'munin', title => 'Service Checks', info => 'Total Number of Passive Service Checks', order => 'NUMSVCPSVCHK60M NUMSVCPSVCHK15M NUMSVCPSVCHK5M NUMSVCPSVCHK1M', @@ -382,7 +382,7 @@ $graphs{extcmdcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Ext Command Slots', - category => 'externalcmds', + category => 'munin', title => 'External Commands', info => 'External Command Buffer Slot Information', }, From 1ad3baeb098f78b5a5b3ee24fa8451b7de7a9eb7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:28:02 +0200 Subject: [PATCH 120/695] plugin categories: other -> voip --- plugins/asterisk/asterisk_14_fax_ffa/TEMPLATE | 5 +---- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_current_sessions | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_failed_completed | 2 +- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail | 2 +- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_licensed_channels | 2 +- .../asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_negotiations_failed | 2 +- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax | 2 +- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial | 2 +- .../asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error | 2 +- plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success | 2 +- .../asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 | 2 +- .../asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure | 2 +- .../asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts | 2 +- plugins/asterisk/asterisk_14_fax_ffa/fax_test | 5 +---- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_cancelled | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_channels | 2 +- .../asterisk_16_fax_ffa/asterisk_fax_current_sessions | 2 +- .../asterisk_16_fax_ffa/asterisk_fax_failed_completed | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iofail | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iopartial | 2 +- .../asterisk_16_fax_ffa/asterisk_fax_licensed_channels | 2 +- .../asterisk/asterisk_16_fax_ffa/asterisk_fax_max_concurrent | 2 +- .../asterisk_16_fax_ffa/asterisk_fax_negotiations_failed | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_nofax | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_partial | 2 +- .../asterisk/asterisk_16_fax_ffa/asterisk_fax_protocol_error | 2 +- plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_success | 2 +- .../asterisk/asterisk_16_fax_ffa/asterisk_fax_switched2t38 | 2 +- .../asterisk/asterisk_16_fax_ffa/asterisk_fax_train_failure | 2 +- .../asterisk/asterisk_16_fax_ffa/asterisk_fax_txrx_attempts | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_call_dropped | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_current_sessions | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_failed_completed | 2 +- .../asterisk/asterisk_18_fax_spandsp/asterisk_fax_file_error | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_memory_error | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_negotiations_failed | 2 +- plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_protocol_error | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error | 2 +- .../asterisk/asterisk_18_fax_spandsp/asterisk_fax_success | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_switched2t38 | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_train_failure | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts | 2 +- .../asterisk_18_fax_spandsp/asterisk_fax_unknown_error | 2 +- 48 files changed, 48 insertions(+), 54 deletions(-) diff --git a/plugins/asterisk/asterisk_14_fax_ffa/TEMPLATE b/plugins/asterisk/asterisk_14_fax_ffa/TEMPLATE index c5a1e329..2e523337 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/TEMPLATE +++ b/plugins/asterisk/asterisk_14_fax_ffa/TEMPLATE @@ -13,7 +13,7 @@ print "graph_title Asterisk Fax - Cancelled Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Cancelled Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_cancelled.draw AREA\n"; print "t38_cancelled.label Cancelled T.38 Faxes\n"; print "g711_cancelled.draw AREA\n"; @@ -60,6 +60,3 @@ print "g711_cancelled.value $faxstats{'Digium G.711'}{'Canceled'}\n"; exit( 0 ); - - - diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled index 068c8bc0..9351eb4a 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Cancelled Faxes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Cancelled Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "cancelled.draw AREA\n"; print "cancelled.label Cancelled Faxes\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions index edc27685..f3155b21 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Current Sessions\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Current Sessions\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "sessions.draw AREA\n"; print "sessions.label Current Sessions\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed index 5a6d55a5..1b115136 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Failed and Completed Faxes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Failed and Completed Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "failed.draw AREA\n"; print "failed.label Failed faxes\n"; print "completed.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail index 004c3787..ef31b90d 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - IO Fail\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of IO Failures\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "iofail.draw AREA\n"; print "iofail.label IO Failures\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial index d247f3d8..d94a0d85 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - IO Partial\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of IO Partials\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "iopartial.draw AREA\n"; print "iopartial.label IO Partial\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels index 3054013f..5b779350 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Licensed Channels\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Licensed Channels\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "channels.draw AREA\n"; print "channels.label Licensed Channels\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent index 1d6350ee..998bd139 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Max Concurrent Sessions\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Max Concurrent Sessions\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "maxsessions.draw AREA\n"; print "maxsessions.label Sessions\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed index b8334759..fd402f63 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Negotiations Failed\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Negotiations Failed\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "failed.draw AREA\n"; print "failed.label Negotiations Failed\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax index 86272cc7..b284676b 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - No Fax\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of No Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "nofax.draw AREA\n"; print "nofax.label Number of No Faxes\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial index 445f2ff6..81ede198 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial @@ -86,7 +86,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Partial Faxes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Partial Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "partial.draw AREA\n"; print "partial.label Partial Faxes\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error index 273a5286..5ee5c896 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error @@ -86,7 +86,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Protocol Error\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "proterror.draw AREA\n"; print "proterror.label Protocol Errors\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success index 3a2fb55a..15ce0d71 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Successful Tx\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Successful Tx's\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "success.draw AREA\n"; print "success.label successful tx\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 index 0ecc5d4c..7980aa00 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Switched to T.38\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Switches to T.38\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "switched.draw AREA\n"; print "switched.label Switches to T.38\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure index ac488825..9252ff75 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Train Failure\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Train Failures\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "failure.draw AREA\n"; print "failure.label Train Failures\n"; exit 0; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts index 23a89fea..cd495ea9 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Tx/Rx Attempts\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Tx and Rx Attempts\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "transmit.draw AREA\n"; print "transmit.label Tx Attempts\n"; print "receive.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/fax_test b/plugins/asterisk/asterisk_14_fax_ffa/fax_test index 220859ae..a2f444bb 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/fax_test +++ b/plugins/asterisk/asterisk_14_fax_ffa/fax_test @@ -14,7 +14,7 @@ print "graph_title Asterisk active fax channels\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel channels\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "channels.draw AREA\n"; print "channels.label channels\n"; exit 0; @@ -37,6 +37,3 @@ print "channels.value $channels\n"; exit( 0 ); - - - diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_cancelled b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_cancelled index d614b6dd..0d79c0f4 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_cancelled +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_cancelled @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Cancelled Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Cancelled Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_cancelled.draw AREA\n"; print "t38_cancelled.label Cancelled T.38 Faxes\n"; print "g711_cancelled.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_channels b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_channels index 51f1f57b..ba3de427 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_channels +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_channels @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Licensed Channels\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel channels\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "channels.draw AREA\n"; print "channels.label channels\n"; exit 0; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_current_sessions b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_current_sessions index a3a46d71..2de04533 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_current_sessions +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_current_sessions @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Current Sessions\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Current Sessions\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "sessions.draw AREA\n"; print "sessions.label Current Sessions\n"; exit 0; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_failed_completed b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_failed_completed index 802ac102..746a5df5 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_failed_completed +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_failed_completed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Failed and Completed Faxes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Failed and Completed Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "failed.draw AREA\n"; print "failed.label Failed faxes\n"; print "completed.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iofail b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iofail index eae77859..99442989 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iofail +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iofail @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - IO Failures (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of IO Failures\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_iofail.draw AREA\n"; print "t38_iofail.label T.38 IO Failures\n"; print "g711_iofail.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iopartial b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iopartial index 0a0f63d4..6fa081c5 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iopartial +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_iopartial @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - IO Partial (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Number of IO Partials\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_iopartial.draw AREA\n"; print "t38_iopartial.label Partial T.38 Faxes\n"; print "g711_iopartial.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_licensed_channels b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_licensed_channels index 8040e4c8..d049671e 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_licensed_channels +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_licensed_channels @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Licensed Channels (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Licensed Channels\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_channels.draw AREA\n"; print "t38_channels.label T.38 Licensed Channels\n"; print "g711_channels.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_max_concurrent b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_max_concurrent index 1a4a4bde..d09b575f 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_max_concurrent +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_max_concurrent @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Max Concurrent Sessions (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Max Concurrent Sessions\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_maxsessions.draw AREA\n"; print "t38_maxsessions.label Max Concurrent T.38 Sessions\n"; print "g711_maxsessions.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_negotiations_failed b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_negotiations_failed index d4c851ff..6c15cdfc 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_negotiations_failed +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_negotiations_failed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Negotiations Failed (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Negotiations Failed\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_failed.draw AREA\n"; print "t38_failed.label T.38 Negotiations Failed\n"; print "g711_failed.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_nofax b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_nofax index 73571c93..f2a56e85 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_nofax +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_nofax @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - No Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of No Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_nofax.draw AREA\n"; print "t38_nofax.label No T.38 Faxes\n"; print "g711_nofax.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_partial b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_partial index 2b9f425b..c17dc2ee 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_partial +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_partial @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Partial Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Partial Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_partial.draw AREA\n"; print "t38_partial.label Partial T.38 Faxes\n"; print "g711_partial.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_protocol_error b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_protocol_error index 483493c1..17cd9269 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_protocol_error +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_protocol_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Protocol Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_proterror.draw AREA\n"; print "t38_proterror.label T.38 Protocol Errors\n"; print "g711_proterror.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_success b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_success index 2ec335fb..ea5f9ef6 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_success +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_success @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Successful Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Successful Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_success.draw AREA\n"; print "t38_success.label Successful T.38 Faxes\n"; print "g711_success.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_switched2t38 b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_switched2t38 index 9de7a3b4..a4bd7339 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_switched2t38 +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_switched2t38 @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Switched to T.38\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Switches to T.38\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "switched.draw AREA\n"; print "switched.label Switched to T.38\n"; exit 0; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_train_failure b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_train_failure index b0d98edb..900c087c 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_train_failure +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_train_failure @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Train Failures (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Train Failures\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_failure.draw AREA\n"; print "t38_failure.label T.38 Train Failures\n"; print "g711_failure.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_txrx_attempts b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_txrx_attempts index f53ef89a..f9cb5dda 100755 --- a/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_txrx_attempts +++ b/plugins/asterisk/asterisk_16_fax_ffa/asterisk_fax_txrx_attempts @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Tx/Rx Attempts\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Tx and Rx Attempts\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "transmit.draw AREA\n"; print "transmit.label Tx Attempts\n"; print "receive.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_call_dropped b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_call_dropped index 13b3953d..3a43cdb9 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_call_dropped +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_call_dropped @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Dropped Calls (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Dropped Calls\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_dropped.draw AREA\n"; print "t38_dropped.label T.38 Dropped Calls\n"; print "g711_dropped.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_current_sessions b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_current_sessions index 4614b386..b17330ab 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_current_sessions +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_current_sessions @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Current Sessions\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Current Sessions\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "sessions.draw AREA\n"; print "sessions.label Current Sessions\n"; exit 0; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_failed_completed b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_failed_completed index 39e2c477..a7ec3281 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_failed_completed +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_failed_completed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Failed and Completed Faxes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Failed and Completed Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "failed.draw AREA\n"; print "failed.label Failed faxes\n"; print "completed.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_file_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_file_error index fa64d47f..1b14129f 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_file_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_file_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - File Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of File Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_file_error.draw AREA\n"; print "t38_file_error.label T.38 File Errors\n"; print "g711_file_error.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_memory_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_memory_error index 4ae6b5a3..7bb98b1c 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_memory_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_memory_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Memory Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Memory Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_errors.draw AREA\n"; print "t38_errors.label T.38 Memory Errors\n"; print "g711_errors.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_negotiations_failed b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_negotiations_failed index 3334085d..06e52290 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_negotiations_failed +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_negotiations_failed @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Negotiations Failed (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Negotiations Failed\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_failed.draw AREA\n"; print "t38_failed.label T.38 Negotiations Failed\n"; print "g711_failed.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax index 9d7f7722..89db8c71 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - No Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of No Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_nofax.draw AREA\n"; print "t38_nofax.label No T.38 Faxes\n"; print "g711_nofax.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error index a02fbb2b..316ca022 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Protocol Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_proterror.draw AREA\n"; print "t38_proterror.label T.38 Protocol Errors\n"; print "g711_proterror.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded index 015c9ec4..b0f674f5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Retries Exceeded (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Retries Exceeded\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_retries.draw AREA\n"; print "t38_retries.label T.38 Retries Exceeded\n"; print "g711_retries.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error index 231ef3e1..adf09b54 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Rx and Tx Protocol Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_rx_proterror.draw AREA\n"; print "t38_rx_proterror.label T.38 Rx Protocol Errors\n"; print "t38_tx_proterror.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success index b9df45ee..18809fa2 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Successful Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Successful Faxes\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_success.draw AREA\n"; print "t38_success.label Successful T.38 Faxes\n"; print "g711_success.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 index 48b95697..c236ecc5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Switched to T.38\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Switches to T.38\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "switched.draw AREA\n"; print "switched.label Switched to T.38\n"; exit 0; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure index b9d19a00..0dfd6c98 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Train Failures (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Train Failures\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_failure.draw AREA\n"; print "t38_failure.label T.38 Train Failures\n"; print "g711_failure.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts index 22e09e59..47e5f1a8 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Tx/Rx Attempts\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Tx and Rx Attempts\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "transmit.draw AREA\n"; print "transmit.label Tx Attempts\n"; print "receive.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error index a537b5e7..724404d5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Unknown Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Unknown Errors\n"; - print "graph_category other\n"; + print "graph_category voip\n"; print "t38_errors.draw AREA\n"; print "t38_errors.label T.38 Unknown Errors\n"; print "g711_errors.draw AREA\n"; From 4e47cb5d85393129da18fc29d06374f83573e085 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:29:25 +0200 Subject: [PATCH 121/695] plugin categories: other -> sensors --- plugins/battery/batteries | 4 ++-- plugins/harddisks/powered_up_ | 1 + plugins/snmp/snmp__cyberpower | 8 ++++---- plugins/snmp/snmp__webthermometer | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/battery/batteries b/plugins/battery/batteries index 9df76c4e..197e9b7c 100755 --- a/plugins/battery/batteries +++ b/plugins/battery/batteries @@ -268,7 +268,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") $config{$graph}{'graph'}{'title'} = sprintf($graphs->{$graph}{'title'}, 'Mean batteryes'); $config{$graph}{'graph'}{'args'} = $graphs->{$graph}{'args'}; $config{$graph}{'graph'}{'vlabel'} = $graphs->{$graph}{'vlabel'}; - $config{$graph}{'graph'}{'category'} = 'power'; + $config{$graph}{'graph'}{'category'} = 'sensors'; foreach my $field (@{$graphs->{$graph}{'fields'}}) { if(($proc_data_exists and $fields->{$field}{'source'} eq 'proc') or $fields->{$field}{'source'} eq 'both') @@ -290,7 +290,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") $config{$graph_name}{'graph'}{'info'} = sprintf("%s battery %s %s (sn: %s)", $info->{$i}{'battery_type'}, $info->{$i}{'manufacturer'}, $info->{$i}{'model_name'}, $info->{$i}{'serial_number'}); $config{$graph_name}{'graph'}{'args'} = '--base 1000'; $config{$graph_name}{'graph'}{'vlabel'} = $graphs->{$graph}{'vlabel'}; - $config{$graph_name}{'graph'}{'category'} = 'power'; + $config{$graph_name}{'graph'}{'category'} = 'sensors'; foreach my $field (@{$graphs->{$graph}{'fields'}}) { if(($proc_data_exists and $fields->{$field}{'source'} eq 'proc') or $fields->{$field}{'source'} eq 'both') diff --git a/plugins/harddisks/powered_up_ b/plugins/harddisks/powered_up_ index 634d3365..3eddfd25 100755 --- a/plugins/harddisks/powered_up_ +++ b/plugins/harddisks/powered_up_ @@ -12,6 +12,7 @@ case $1 in cat < Date: Wed, 28 Mar 2018 04:30:14 +0200 Subject: [PATCH 122/695] plugin categories: other -> cloud --- plugins/celery/celery_tasks | 2 +- plugins/celery/celery_tasks_states | 2 +- plugins/gearman/gearman_queue | 2 +- plugins/gearman/gearman_workers | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/celery/celery_tasks b/plugins/celery/celery_tasks index 6a505743..ee65d17f 100755 --- a/plugins/celery/celery_tasks +++ b/plugins/celery/celery_tasks @@ -97,7 +97,7 @@ def print_config(task_names): print 'graph_args --lower-limit 0' print 'graph_scale no' print 'graph_vlabel tasks per ${graph_period}' - print 'graph_category other' + print 'graph_category cloud' for name in task_names: print '%s.label %s' % (clean_task_name(name), name) diff --git a/plugins/celery/celery_tasks_states b/plugins/celery/celery_tasks_states index 1663d76a..e62ab1c4 100755 --- a/plugins/celery/celery_tasks_states +++ b/plugins/celery/celery_tasks_states @@ -103,7 +103,7 @@ def print_config(workers = None): print 'graph_args --lower-limit 0' print 'graph_scale no' print 'graph_vlabel tasks per ${graph_period}' - print 'graph_category other' + print 'graph_category cloud' for name in TASK_STATES: name = clean_state_name(name) diff --git a/plugins/gearman/gearman_queue b/plugins/gearman/gearman_queue index d24120e1..3b8c9485 100755 --- a/plugins/gearman/gearman_queue +++ b/plugins/gearman/gearman_queue @@ -27,7 +27,7 @@ if (defined $ARGV[0] and $ARGV[0] eq 'config') { print "graph_title Gearman queue\n"; print "graph_args --base 1000\n"; print "graph_vlabel jobs\n"; - print "graph_category other\n"; + print "graph_category cloud\n"; while ( my ($key, $value) = each(%$gearman) ) { my $param = $key."_"."queue"; print "$param.type GAUGE\n"; diff --git a/plugins/gearman/gearman_workers b/plugins/gearman/gearman_workers index 2fe602c6..916ae4aa 100755 --- a/plugins/gearman/gearman_workers +++ b/plugins/gearman/gearman_workers @@ -35,7 +35,7 @@ if (defined $ARGV[0] and $ARGV[0] eq 'config') { print "graph_title Gearman workers\n"; print "graph_args --base 1000\n"; print "graph_vlabel workers\n"; - print "graph_category other\n"; + print "graph_category cloud\n"; foreach my $key (@sorted) { my $param = $key."_"."total"; print "$param.type GAUGE\n"; From 5a45d49760d74aa36b5c3bad112010beef01f502 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:31:16 +0200 Subject: [PATCH 123/695] plugin categories: other -> htc (crypto currencies) --- plugins/currency/bitcoin/bitcoind_ | 2 +- plugins/currency/bitcoin/btcguild_hashrate_ | 2 +- plugins/currency/bitcoin/slush_hashrate_ | 2 +- plugins/currency/bitcoin/slush_reward_ | 2 +- plugins/currency/ethereum/ethermine_hashrate_ | 4 ++-- plugins/currency/ethereum/etherscan_balance_ | 2 +- plugins/currency/nanopool/nanopool_ | 10 +++++----- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index bcb50aa0..5b5408dd 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -70,7 +70,7 @@ def main(): return if command == 'config': - print 'graph_category other' + print 'graph_category htc' print 'graph_title Bitcoin %s' % labels[0] print 'graph_vlabel %s' % labels[1] for label in line_labels: diff --git a/plugins/currency/bitcoin/btcguild_hashrate_ b/plugins/currency/bitcoin/btcguild_hashrate_ index 33d72d7e..25e9f2a0 100755 --- a/plugins/currency/bitcoin/btcguild_hashrate_ +++ b/plugins/currency/bitcoin/btcguild_hashrate_ @@ -28,7 +28,7 @@ if command == 'config': print "graph_title BTCGuild Hashrate" print "graph_args --upper-limit 3000 -l 0" print "graph_vlabel MHash/s" - print "graph_category other" + print "graph_category htc" for worker in workers: label = workers[worker]['worker_name'] print label + ".label " + label diff --git a/plugins/currency/bitcoin/slush_hashrate_ b/plugins/currency/bitcoin/slush_hashrate_ index 59533b19..b07493fc 100755 --- a/plugins/currency/bitcoin/slush_hashrate_ +++ b/plugins/currency/bitcoin/slush_hashrate_ @@ -25,7 +25,7 @@ if command == 'config': print "graph_title Slush Hashrate" print "graph_args --upper-limit 3000 -l 0" print "graph_vlabel MHash/s" - print "graph_category other" + print "graph_category htc" for worker in workers: label = worker.replace(".","_") print label + ".label " +label diff --git a/plugins/currency/bitcoin/slush_reward_ b/plugins/currency/bitcoin/slush_reward_ index 28629f97..2986ea00 100755 --- a/plugins/currency/bitcoin/slush_reward_ +++ b/plugins/currency/bitcoin/slush_reward_ @@ -24,7 +24,7 @@ if command == 'config': print "graph_title Slush Rewards" print "graph_args -l 0" print "graph_vlabel BTC" - print "graph_category other" + print "graph_category htc" print "unconfirmed_reward.label Unconfirmed Reward" print "estimated_reward.label Estimeated Reward" print "confirmed_reward.label Confirmed Reward" diff --git a/plugins/currency/ethereum/ethermine_hashrate_ b/plugins/currency/ethereum/ethermine_hashrate_ index ca0bc038..4b640409 100755 --- a/plugins/currency/ethereum/ethermine_hashrate_ +++ b/plugins/currency/ethereum/ethermine_hashrate_ @@ -72,7 +72,7 @@ if command == 'config': print("graph_title Ethermine {}".format(miner)) print("graph_info ethermine.org Mining Pool Hashrate for {}_{}".format(eth_address, miner)) print("graph_vlabel Ethermine Hashrate") - print("graph_category other") + print("graph_category htc") print("ethermine_mhs_{}_{}.warning 20:".format(eth_address, miner)) print("ethermine_mhs_{}_{}.critical 10:".format(eth_address, miner)) print("ethermine_mhs_{}_{}.label MH/s:".format(eth_address, miner)) @@ -111,4 +111,4 @@ for worker in workers: if workers[worker]['worker'] == miner: hash_rate = workers[worker]['hashrate'] hash_rate = hash_rate.replace(" MH/s", "") - print("ethermine_mhs_{}_{}.value {}".format(eth_address, miner, hash_rate)) \ No newline at end of file + print("ethermine_mhs_{}_{}.value {}".format(eth_address, miner, hash_rate)) diff --git a/plugins/currency/ethereum/etherscan_balance_ b/plugins/currency/ethereum/etherscan_balance_ index 497889f4..03c323d3 100755 --- a/plugins/currency/ethereum/etherscan_balance_ +++ b/plugins/currency/ethereum/etherscan_balance_ @@ -81,7 +81,7 @@ if command == 'config': print("graph_title Ether {}".format(eth_address)) print("graph_info Ethereum Account Balance for Address {}".format(eth_address)) print("graph_vlabel Ethereum Balance") - print("graph_category other") + print("graph_category htc") print("wei_balance_{0}.cdef wei_balance_{0},1000000000000000000,/".format(eth_address)) print("wei_balance_{}.label ETH".format(eth_address)) sys.exit(0) diff --git a/plugins/currency/nanopool/nanopool_ b/plugins/currency/nanopool/nanopool_ index 08bfa036..3bc8bed0 100755 --- a/plugins/currency/nanopool/nanopool_ +++ b/plugins/currency/nanopool/nanopool_ @@ -148,7 +148,7 @@ def write_config_worker(): print("graph_title Hashrate in Mh/s per worker ({0})".format(account_address)) print("graph_args --base 1000 -l 0") print("graph_vlabel Mh/s") - print("graph_category other") + print("graph_category htc") print("graph_scale no") for val in worker_data: @@ -166,7 +166,7 @@ def write_config_worker(): print("graph_title Hashrate in Mh/s of worker {0}".format(worker_name)) print("graph_args --base 1000 -l 0") print("graph_vlabel Mh/s") - print("graph_category other") + print("graph_category htc") print("graph_scale no") print("whashrate.label hashrate") print("whashrate.type GAUGE") @@ -179,7 +179,7 @@ def write_config_worker(): print("graph_title Number of accepted shares ({0})".format(account_address)) print("graph_args --base 1000 -l 0") print("graph_vlabel Shares per ${graph_period}") - print("graph_category other") + print("graph_category htc") print("graph_scale no") print("graph_period minute") @@ -198,7 +198,7 @@ def write_config_worker(): print("graph_title Number of accepted shares {0}".format(worker_name)) print("graph_args --base 1000 -l 0") print("graph_vlabel Shares per ${graph_period}") - print("graph_category other") + print("graph_category htc") print("graph_scale no") print("graph_period minute") print("wshares.label shares") @@ -247,7 +247,7 @@ def write_config(): params = GRAPH_TYPES[graph_type] for item in params: print("graph_title {0}".format(item["title"])) - print("graph_category other") + print("graph_category htc") if "info" in item: print("graph_info {0}".format(item["info"])) if "scale" in item: From 34eeebbe374dce80eb99ca69c3e8889bcba3fdff Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:33:45 +0200 Subject: [PATCH 124/695] plugin categories: reorganize multiple plugins --- plugins/backup/backup.sh | 1 + plugins/ceph/ceph-osd-info | 6 +----- plugins/cpu/multicpu1sec | 6 +----- plugins/dvb/femon | 2 +- plugins/fr24/fr24 | 2 +- plugins/geowebcache/geowebcache-bandwidth | 2 +- plugins/geowebcache/geowebcache-blankitems | 2 +- plugins/geowebcache/geowebcache-cache-hits-ratio | 2 +- plugins/google/googlecode | 2 +- plugins/healthcheck/healthcheck_log | 2 +- plugins/healthcheck/healthcheck_process | 2 +- plugins/healthcheck/healthcheck_url | 2 +- plugins/keystone/keystone_stats | 2 +- plugins/network/bandwidth-OVH-Network | 1 + plugins/network/if | 6 +++--- plugins/network/interfaces_linux_multi | 3 +-- plugins/senderscore/senderscore | 2 +- plugins/smstools/smstools_ | 2 +- plugins/system/cpu_linux_multi | 3 +-- 19 files changed, 21 insertions(+), 29 deletions(-) diff --git a/plugins/backup/backup.sh b/plugins/backup/backup.sh index 492a9fed..15bb1b2f 100755 --- a/plugins/backup/backup.sh +++ b/plugins/backup/backup.sh @@ -11,6 +11,7 @@ case $1 in cat <<'EOM' graph_title Number of young files at backup directory graph_vlabel number +graph_category backup autobackup.label number autobackup.critical 1: EOM diff --git a/plugins/ceph/ceph-osd-info b/plugins/ceph/ceph-osd-info index d9c7c6eb..96933ce2 100755 --- a/plugins/ceph/ceph-osd-info +++ b/plugins/ceph/ceph-osd-info @@ -148,7 +148,7 @@ if (sys.argv.__len__()>1) and (sys.argv[1]=="config"): gr_simple=graph.replace("-","_").replace(":","_") gr_pretty=graph.replace("_"," ").title() gr=graph.replace("-","_").replace(":","_") - graphdefaults={"graph_title":gr_pretty,"graph_vlabel":gr_pretty,"graph_category":"osd"} + graphdefaults={"graph_title":gr_pretty,"graph_vlabel":gr_pretty,"graph_category":"fs"} graphsettings=dict(graphdefaults.items()+graphsettings.items()) print "multigraph %s" % (gr_simple) print "\n".join(["%s %s" % setting for setting in graphsettings.items()]) @@ -182,7 +182,3 @@ else: for osd in osds: print "multigraph %s.osd%s" % (gr,osd) print "osd%s_%s.value %s" % (osd,gr,data[osd][graph]) - -# for Munin Plugin Gallery -# graph_category fs - diff --git a/plugins/cpu/multicpu1sec b/plugins/cpu/multicpu1sec index a5dd9d94..c400c49a 100755 --- a/plugins/cpu/multicpu1sec +++ b/plugins/cpu/multicpu1sec @@ -14,7 +14,6 @@ plugin="${0##*/}" # name of plugin pidfile="$MUNIN_PLUGSTATE/munin.$plugin.pid" cache="$MUNIN_PLUGSTATE/munin.$plugin.value" graph="$plugin" -section="system:cpu" style="AREA" cpus=$(grep -c ^processor /proc/cpuinfo) @@ -52,7 +51,7 @@ run_config() { run_daemon cat << EOF graph_title $graph -graph_category $section +graph_category 1sec graph_vlabel average cpu use % graph_scale no graph_total All CPUs @@ -87,6 +86,3 @@ exit 0 # acquire which needs a different pid than watchdog, otherwise watchdog # could/will kill itself when expiring before the watched process is killed. # not a POSIX feature. - -# for Munin Plugin Gallery -# graph_category 1sec diff --git a/plugins/dvb/femon b/plugins/dvb/femon index eebd6661..b51a17fb 100755 --- a/plugins/dvb/femon +++ b/plugins/dvb/femon @@ -93,7 +93,7 @@ def print_adapter_config(dvb_adapter) : print ('graph_title DVB Femon Sensors '+dvb_adapter[0]) print ('graph_args '+os.getenv('graph_args','--lower-limit 0 --upper-limit 100 --rigid')) print ('graph_vlabel Quality') - print ('graph_category other') + print ('graph_category tv') print ('graph_info This graph shows femon output for your dvb-'+dvb_adapter[0]) print ('str.label Signal Strength') diff --git a/plugins/fr24/fr24 b/plugins/fr24/fr24 index ba7f29dc..67924623 100755 --- a/plugins/fr24/fr24 +++ b/plugins/fr24/fr24 @@ -39,7 +39,7 @@ 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 other +graph_category tv graph_vlabel Number of planes planes.label planes" exit 0;; diff --git a/plugins/geowebcache/geowebcache-bandwidth b/plugins/geowebcache/geowebcache-bandwidth index c2a72e62..d2f9c0ce 100755 --- a/plugins/geowebcache/geowebcache-bandwidth +++ b/plugins/geowebcache/geowebcache-bandwidth @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache bandwidth\n"; print "graph_args --base 1024\n"; print "graph_vlabel bit/s\n"; - print "graph_category other\n"; + print "graph_category loadbalancer\n"; print "graph_info Bandwidth graph is an average on the last 60 seconds\n"; print "bandw.draw LINE1\n"; print "bandw.label bandwidth bit/s\n"; diff --git a/plugins/geowebcache/geowebcache-blankitems b/plugins/geowebcache/geowebcache-blankitems index 74cd6d34..a3c80a2f 100755 --- a/plugins/geowebcache/geowebcache-blankitems +++ b/plugins/geowebcache/geowebcache-blankitems @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache Percent of blank requests\n"; print "graph_args --base 1000\n"; print "graph_vlabel %\n"; - print "graph_category other\n"; + print "graph_category loadbalancer\n"; print "graph_info Blankidth graph is an average on the last 60 seconds\n"; print "blank.draw LINE1\n"; print "blank.label % of blank KML/HTML\n"; diff --git a/plugins/geowebcache/geowebcache-cache-hits-ratio b/plugins/geowebcache/geowebcache-cache-hits-ratio index 2fa07765..270c846e 100755 --- a/plugins/geowebcache/geowebcache-cache-hits-ratio +++ b/plugins/geowebcache/geowebcache-cache-hits-ratio @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache cache hit ratio\n"; print "graph_args --base 1000\n"; print "graph_vlabel %\n"; - print "graph_category other\n"; + print "graph_category loadbalancer\n"; print "ratio.label percent\n"; print "ratio.type GAUGE\n"; print "ratio.max 100\n"; diff --git a/plugins/google/googlecode b/plugins/google/googlecode index 02d24f5a..fed13890 100755 --- a/plugins/google/googlecode +++ b/plugins/google/googlecode @@ -47,7 +47,7 @@ if [ "$1" = "config" ]; then echo "graph_title Number of downloads of $PROJECTNAME from Google Code " echo "graph_args --base 1000 --lower-limit 0" echo "graph_vlabel number of downloads" - echo "graph_category other" + echo "graph_category filetransfer" echo "graph_info This graph shows the number of downloads of $PROJECTNAME from Google Code." j=0 for (( i = 1 ; i < $Nfiles ; i=i+5 )) diff --git a/plugins/healthcheck/healthcheck_log b/plugins/healthcheck/healthcheck_log index 247527de..1d36c384 100755 --- a/plugins/healthcheck/healthcheck_log +++ b/plugins/healthcheck/healthcheck_log @@ -89,7 +89,7 @@ if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0 --vertical-label match_count" echo 'graph_scale no' echo 'graph_vlabel match_count' - echo 'graph_category other' + echo 'graph_category munin' echo 'graph_info This graph shows the bad event count on log' for(( I = 1; I < $CHECKMAX; ++I )) diff --git a/plugins/healthcheck/healthcheck_process b/plugins/healthcheck/healthcheck_process index 5c674df8..2eaf40ec 100755 --- a/plugins/healthcheck/healthcheck_process +++ b/plugins/healthcheck/healthcheck_process @@ -52,7 +52,7 @@ if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0 --vertical-label MB" echo 'graph_scale no' echo 'graph_vlabel process memory' - echo 'graph_category other' + echo 'graph_category munin' echo 'graph_info This graph shows the Memory used by process' for(( I = 1; I < $CHECKMAX; ++I )) diff --git a/plugins/healthcheck/healthcheck_url b/plugins/healthcheck/healthcheck_url index 77a0edea..6686f723 100755 --- a/plugins/healthcheck/healthcheck_url +++ b/plugins/healthcheck/healthcheck_url @@ -101,7 +101,7 @@ if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0 --vertical-label second" echo 'graph_scale no' echo 'graph_vlabel second' - echo 'graph_category other' + echo 'graph_category munin' echo 'graph_info This graph shows the site speed' for(( I = 1; I < $CHECKMAX; ++I )) diff --git a/plugins/keystone/keystone_stats b/plugins/keystone/keystone_stats index bed779c8..4ce1d90c 100755 --- a/plugins/keystone/keystone_stats +++ b/plugins/keystone/keystone_stats @@ -30,7 +30,7 @@ def print_config(): print 'graph_title Keystone Stats' print 'graph_vlabel count' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category other' + print 'graph_category auth' print 'graph_scale no' print 'graph_info This graph shows stats about keystone: ' + (', ').join(stats) for field in stats: diff --git a/plugins/network/bandwidth-OVH-Network b/plugins/network/bandwidth-OVH-Network index e3bed44b..880c91e6 100755 --- a/plugins/network/bandwidth-OVH-Network +++ b/plugins/network/bandwidth-OVH-Network @@ -12,6 +12,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel MB/s' echo 'graph_scale no' + echo 'graph_category network' echo "download.label download" exit 0 fi diff --git a/plugins/network/if b/plugins/network/if index 06af4154..d5086c85 100755 --- a/plugins/network/if +++ b/plugins/network/if @@ -120,7 +120,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000 -u 0', 'title' => ':if: signal and noise levels', 'vlabel' => 'dB', @@ -134,7 +134,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000', 'title' => ':if: link quality', 'vlabel' => '%', @@ -148,7 +148,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000', 'title' => ':if: errors', 'vlabel' => 'Errors RX (-) / TX (+)', diff --git a/plugins/network/interfaces_linux_multi b/plugins/network/interfaces_linux_multi index ad158600..2b75c82c 100755 --- a/plugins/network/interfaces_linux_multi +++ b/plugins/network/interfaces_linux_multi @@ -102,7 +102,6 @@ if (!defined $ENV{MUNIN_PLUGSTATE}) { sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } -sub graph_section() { "system:network" } sub graph_name() { "interfaces" } #sub graph_title() { "interfaces" } #sub graph_title_all() { "Overall CPU usage" } @@ -327,7 +326,7 @@ sub show_config() } print < Date: Wed, 28 Mar 2018 04:35:42 +0200 Subject: [PATCH 125/695] Plugin backup: fix quoting --- plugins/backup/backup.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/backup/backup.sh b/plugins/backup/backup.sh index 15bb1b2f..5ee33559 100755 --- a/plugins/backup/backup.sh +++ b/plugins/backup/backup.sh @@ -19,5 +19,4 @@ EOM esac printf "autobackup.value " -find $BACKUP_DIR -mtime -$LIFETIME | wc -l - +find "$BACKUP_DIR" -mtime "-$LIFETIME" | wc -l From 8d48cab1250ee14b9fb6f7530f6336cbcd50690c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:45:41 +0200 Subject: [PATCH 126/695] plugin approx: various improvements * python3 compatibility * return zero autoconf exitcode * avoid builtin names for variables * remove cruft comments * switch to space instead of tabs All PEP8 issues are fixed now. --- plugins/approx/approx | 75 ++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/plugins/approx/approx b/plugins/approx/approx index f2d4c253..5a133a7e 100755 --- a/plugins/approx/approx +++ b/plugins/approx/approx @@ -8,64 +8,57 @@ # # Parameters understood: # -# config (required) -# autoconf (optional - used by munin-config) +# config (required) +# autoconf (optional - used by munin-config) # # Magic markers - optional - used by installation scripts and # munin-config: # -#%# family=manual -#%# capabilities=autoconf +# #%# family=manual +# #%# capabilities=autoconf # # Now for the real work... -from sys import argv, exit from os.path import walk, exists, isfile, join +from sys import argv, exit + def get_file_types(): - """Returns an array of filetype => count.""" + """Returns an array of filetype => count.""" + out = {} - out = {} + def visitor(arg, dirname, names): + for filename in names: + if not isfile(join(dirname, filename)): + continue + ext = filename.split(".")[-1].lower() + out[ext] = out.get(ext, 0) + 1 - def visitor(arg, dirname, names): - for file in names: - if not isfile(join(dirname, file)): - continue - ext = file.split(".")[-1] - - out[ext] = out.get(ext, 0) + 1 - - walk('/var/cache/approx/', visitor, None) - - return out + walk('/var/cache/approx/', visitor, None) + return out -# Autoconfiguration if len(argv) > 1: - if argv[1] == "autoconf": - # Test if we can find a approx cache - if exists('/var/cache/approx'): - print "yes" - else: - print "no ('/var/cacne/approx' not found)" - exit(1) - exit() + # Autoconfiguration + if argv[1] == "autoconf": + # Test if we can find a approx cache + if exists('/var/cache/approx'): + print("yes") + else: + print("no ('/var/cache/approx' not found)") + exit() - elif argv[1] == "config": - print "graph_title Approx cache"; - print "graph yes"; - #print "graph_category Other"; - #print "graph_total Total"; - print "graph_info Statistics from the Approx cache."; - #print "debs.label DEBs"; - #print "pending.warning 0:0"; - #print "hold.label hold"; - for type in get_file_types().keys(): - print "%s.label %s" % (type.lower(), type) - exit() + elif argv[1] == "config": + print("graph_title Approx cache") + print("graph yes") + print("graph_category loadbalancer") + print("graph_info Statistics from the Approx cache.") + for filetype in get_file_types().keys(): + print("%s.label %s" % (filetype.lower(), filetype)) + exit() -for type, count in get_file_types().iteritems(): - print "%s.value %d" % (type.lower(), count) +for filetype, count in get_file_types().iteritems(): + print("%s.value %d" % (filetype.lower(), count)) exit() From 42221b85eb2ed95e63f8149572f1802c359f07af Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:50:56 +0200 Subject: [PATCH 127/695] plugin approx: move from 'approx' to 'apt' --- plugins/{approx => apt}/approx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{approx => apt}/approx (100%) diff --git a/plugins/approx/approx b/plugins/apt/approx similarity index 100% rename from plugins/approx/approx rename to plugins/apt/approx From 62df69c566307c761a2ceaa8f128c8e5998dfb04 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 04:55:01 +0200 Subject: [PATCH 128/695] plugin cpanp_o: change category from 'other' to 'security' --- plugins/cpan/cpanp_o | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cpan/cpanp_o b/plugins/cpan/cpanp_o index f1b32942..bb163fe1 100755 --- a/plugins/cpan/cpanp_o +++ b/plugins/cpan/cpanp_o @@ -7,7 +7,7 @@ given ($ARGV[0]) { graph_title CPAN modules not up to date graph_args --base 1000 -l 0 graph_vlabel modules -graph_category other +graph_category security graph_info The number of installed CPAN modules that aren't up to date count.label modules count.type GAUGE From 8e3f1d88658e9159586e39a26b60527acb523b74 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 05:47:36 +0200 Subject: [PATCH 129/695] plugin ksm_: various improvements * category can be parsed by plugin gallery * python3 compatible * more precise autoconf messages * PEP8 clean --- plugins/other/ksm_ | 121 ++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 57 deletions(-) diff --git a/plugins/other/ksm_ b/plugins/other/ksm_ index 17442d78..f5ecdf1e 100755 --- a/plugins/other/ksm_ +++ b/plugins/other/ksm_ @@ -19,67 +19,72 @@ # Magic markers - optional - used by installation scripts and # munin-config: # -#%# capabilities=autoconf suggest -#%# family=auto - -########################################################### -category = 'system' # 'upgrades' -title = 'Kernel Samepage Merging' # 'Upgradeable packages' -########################################################### +# #%# capabilities=autoconf suggest +# #%# family=auto import os import sys -import warnings +import warnings # noqa + +################################# +title = 'Kernel Samepage Merging' +################################# + def autoconf(): if os.path.exists('/sys/kernel/mm/ksm/run'): for line in open('/sys/kernel/mm/ksm/run'): if line.strip() == '1': - print 'yes' - sys.exit(0) - print 'no (/sys/kernel/mm/ksm/run must be present and value must be 1)' + print('yes') + break + else: + print('no (/sys/kernel/mm/ksm/run does not contain "1")') + else: + print('no (/sys/kernel/mm/ksm/run not found)') sys.exit(0) + def suggest(): - print 'pages_absolute' - print 'pages_relative' - print 'full_scans' + print('pages_absolute') + print('pages_relative') + print('full_scans') sys.exit(0) def config(): - if('ksm_pages_absolute' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Pages Absolute' % (title) - print 'graph_order pages_unshared pages_volatile pages_shared pages_sharing' - print 'pages_shared.info how many shared pages are being used' - print 'pages_sharing.info how many more sites are sharing them i.e. how much saved' - print 'pages_unshared.info how many pages unique but repeatedly checked for merging' - print 'pages_volatile.info how many pages changing too fast to be placed in a tree' - print 'pages_shared.label pages_shared' - print 'pages_sharing.label pages_sharing' - print 'pages_unshared.label pages_unshared' - print 'pages_volatile.label pages_volatile' - print 'pages_shared.draw AREASTACK' - print 'pages_sharing.draw AREASTACK' - print 'pages_unshared.draw AREASTACK' - print 'pages_volatile.draw AREASTACK' + if('ksm_pages_absolute' in sys.argv[0]): + print('graph_category system') + print('graph_title %s Pages Absolute' % (title)) + print('graph_order pages_unshared pages_volatile pages_shared pages_sharing') + print('pages_shared.info how many shared pages are being used') + print('pages_sharing.info how many more sites are sharing them i.e. how much saved') + print('pages_unshared.info how many pages unique but repeatedly checked for merging') + print('pages_volatile.info how many pages changing too fast to be placed in a tree') + print('pages_shared.label pages_shared') + print('pages_sharing.label pages_sharing') + print('pages_unshared.label pages_unshared') + print('pages_volatile.label pages_volatile') + print('pages_shared.draw AREASTACK') + print('pages_sharing.draw AREASTACK') + print('pages_unshared.draw AREASTACK') + print('pages_volatile.draw AREASTACK') elif('ksm_pages_relative' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Pages Relative' % (title) - print 'pages_sharing_shared.info ratio of sharing to shared pages' - print 'pages_unshared_sharing.info ratio of unshared to sharing pages' - print 'pages_sharing_shared.label pages_sharing_shared' - print 'pages_unshared_sharing.label pages_unshared_sharing' - print 'pages_sharing_shared.cdef pages_sharing_shared,100,*' - print 'pages_unshared_sharing.cdef pages_unshared_sharing,100,*' + print('graph_category system') + print('graph_title %s Pages Relative' % (title)) + print('pages_sharing_shared.info ratio of sharing to shared pages') + print('pages_unshared_sharing.info ratio of unshared to sharing pages') + print('pages_sharing_shared.label pages_sharing_shared') + print('pages_unshared_sharing.label pages_unshared_sharing') + print('pages_sharing_shared.cdef pages_sharing_shared,100,*') + print('pages_unshared_sharing.cdef pages_unshared_sharing,100,*') elif('ksm_full_scans' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Full Scans' % (title) - print 'full_scans.info how many times all mergeable areas have been scanned' - print 'full_scans.label full_scans' + print('graph_category system') + print('graph_title %s Full Scans' % (title)) + print('full_scans.info how many times all mergeable areas have been scanned') + print('full_scans.label full_scans') sys.exit(0) + if len(sys.argv) > 1: if sys.argv[1] == 'autoconf': autoconf() @@ -91,19 +96,21 @@ if len(sys.argv) > 1: print('unknown argument "' + sys.argv[1] + '"') sys.exit(1) -pages_shared=int(open('/sys/kernel/mm/ksm/pages_shared').read()) -pages_sharing=int(open('/sys/kernel/mm/ksm/pages_sharing').read()) -pages_unshared=int(open('/sys/kernel/mm/ksm/pages_unshared').read()) -pages_volatile=int(open('/sys/kernel/mm/ksm/pages_volatile').read()) -full_scans=int(open('/sys/kernel/mm/ksm/full_scans').read()) +pages_shared = int(open('/sys/kernel/mm/ksm/pages_shared').read()) +pages_sharing = int(open('/sys/kernel/mm/ksm/pages_sharing').read()) +pages_unshared = int(open('/sys/kernel/mm/ksm/pages_unshared').read()) +pages_volatile = int(open('/sys/kernel/mm/ksm/pages_volatile').read()) +full_scans = int(open('/sys/kernel/mm/ksm/full_scans').read()) -if('ksm_pages_absolute' in sys.argv[0]): - print 'pages_shared.value %i' % pages_shared - print 'pages_sharing.value %i' % pages_sharing - print 'pages_unshared.value %i' % pages_unshared - print 'pages_volatile.value %i' % pages_volatile -elif('ksm_pages_relative' in sys.argv[0]): - print 'pages_sharing_shared.value %f' % (float(pages_sharing)/float(pages_shared) if pages_shared>0 else 0) - print 'pages_unshared_sharing.value %f' % (float(pages_unshared)/float(pages_sharing) if pages_sharing>0 else 0) -elif('ksm_full_scans' in sys.argv[0]): - print 'full_scans.value %i' % full_scans +if('ksm_pages_absolute' in sys.argv[0]): + print('pages_shared.value %i' % pages_shared) + print('pages_sharing.value %i' % pages_sharing) + print('pages_unshared.value %i' % pages_unshared) + print('pages_volatile.value %i' % pages_volatile) +elif('ksm_pages_relative' in sys.argv[0]): + print('pages_sharing_shared.value %f' + % (float(pages_sharing) / float(pages_shared) if pages_shared > 0 else 0)) + print('pages_unshared_sharing.value %f' + % (float(pages_unshared) / float(pages_sharing) if pages_sharing > 0 else 0)) +elif('ksm_full_scans' in sys.argv[0]): + print('full_scans.value %i' % full_scans) From 87927d194ce639b4029c26352775e3c3addcbbdd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 05:57:02 +0200 Subject: [PATCH 130/695] plugin apt_ubuntu: various improvements * fix category parsing for plugin gallery * python3 compatibility * zero exitcodes for autoconf failures * PEP8 clean --- plugins/ubuntu/apt_ubuntu | 98 ++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/plugins/ubuntu/apt_ubuntu b/plugins/ubuntu/apt_ubuntu index d13654a3..cf30c0c5 100755 --- a/plugins/ubuntu/apt_ubuntu +++ b/plugins/ubuntu/apt_ubuntu @@ -21,12 +21,15 @@ # Magic markers - optional - used by installation scripts and # munin-config: # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib + +import os +import sys +import warnings ########################################################### -category = 'security' # 'upgrades' -title = 'Upgradable packages' # 'Upgradeable packages' +title = 'Upgradable packages' # 'Upgradeable packages' vlabel = 'Total packages' other = 'other' total = 'total' @@ -38,12 +41,9 @@ origins = ['Ubuntu'] critical = 1 ########################################################### -import os -import sys -import warnings - warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning) + def autoconf(): if os.path.exists('/etc/lsb-release'): for line in open('/etc/lsb-release'): @@ -51,60 +51,60 @@ def autoconf(): try: import apt except ImportError: - print 'no (python-apt not installed)' - sys.exit(1) + print('no (python-apt not installed)') + sys.exit(0) cache = apt.Cache() - if not cache.has_key('update-notifier-common'): - print 'no (update-notifier-common not found)' - sys.exit(1) + if 'update-notifier-common' not in cache: + print('no (update-notifier-common not found)') + sys.exit(0) if not cache['update-notifier-common'].isInstalled: - print 'no (update-notifier-common not installed)' - sys.exit(1) + print('no (update-notifier-common not installed)') + sys.exit(0) if not os.path.exists('/etc/apt/apt.conf.d/10periodic'): - print 'no (/etc/apt/apt.conf.d/10periodic not found)' + print('no (/etc/apt/apt.conf.d/10periodic not found)') sys.exit(1) for line in open('/etc/apt/apt.conf.d/10periodic'): if line.strip() == 'APT::Periodic::Update-Package-Lists "1";': - print 'yes' + print('yes') sys.exit(0) - print 'no (APT::Periodic::Update-Package-Lists not "1")' - sys.exit(1) - print 'no' - sys.exit(1) - -def config(): - print 'graph_category security' - print 'graph_title %s' % (title) - #print 'graph_total %s' % (total) - print 'graph_vlabel %s' % (vlabel) - for i, archive in enumerate(archives + [other]): - if len(colour) > i: - print '%s.colour %s' % (archive, colour[i]) - if i < critical: - print '%s.critical 0:0' % (archive) - if i == 0: - print '%s.draw AREA' % (archive) - else: - print '%s.draw STACK' % (archive) - print '%s.label %s' % (archive, archive) - if i + 1 > critical: - print '%s.warning 0:0' % (archive) - print 'total.colour 000000' - print 'total.draw LINE1' - print 'total.label %s' % (total) + print('no (APT::Periodic::Update-Package-Lists not "1")') + sys.exit(0) + print('no (missing /etc/lsb-release file)') sys.exit(0) + +def config(): + print('graph_category security') + print('graph_title %s' % (title)) + print('graph_vlabel %s' % (vlabel)) + for i, archive in enumerate(archives + [other]): + if len(colour) > i: + print('%s.colour %s' % (archive, colour[i])) + if i < critical: + print('%s.critical 0:0' % (archive)) + if i == 0: + print('%s.draw AREA' % (archive)) + else: + print('%s.draw STACK' % (archive)) + print('%s.label %s' % (archive, archive)) + if i + 1 > critical: + print('%s.warning 0:0' % (archive)) + print('total.colour 000000') + print('total.draw LINE1') + print('total.label %s' % (total)) + sys.exit(0) + + def check_origin(pkg): - #print 'Checking: %s (%s)' % (pkg.name, map(str, pkg.candidateOrigin)) if pkg.candidate.origins: for archive in archives: for origin in pkg.candidate.origins: - #a = origin.archive.rpartition('-')[2] a = origin.archive.split('-')[origin.archive.count('-')] - if a == archive and origin.origin in origins: + if (a == archive) and (origin.origin in origins): return a return other + if len(sys.argv) > 1: if sys.argv[1] == 'autoconf': autoconf() @@ -118,9 +118,11 @@ try: import apt import apt_pkg except ImportError: - print "The module 'apt' is currently not installed. You can install it by typing:\nsudo apt-get install python-apt\nImportError: No module named apt" + print("The module 'apt' is currently not installed. You can install it by typing:\n" + "sudo apt-get install python-apt\nImportError: No module named apt") sys.exit(1) + pkgs = {} total = 0 for pkg in apt.Cache(): @@ -130,6 +132,6 @@ for pkg in apt.Cache(): total += 1 for archive in archives + [other]: - print '%s.value %s' % (archive, pkgs.pop(archive, 0)) + print('%s.value %s' % (archive, pkgs.pop(archive, 0))) -print 'total.value %s' % (total) +print('total.value %s' % (total)) From 44d57dcb7f65a5d2549c35e2e095978023a19659 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Wed, 28 Mar 2018 10:56:34 +0200 Subject: [PATCH 131/695] Fix 2 forgotten backslashes Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/syncthing_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index 25ac50c0..d0a94de2 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -137,8 +137,8 @@ EOM CONNECTIONS=$("$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections") IBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .inBytesTotal') OBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .outBytesTotal') - printf "syncthing_transfer_up.value %s\\n" "$IBT" - printf "syncthing_transfer_down.value %s\\n" "$OBT" + printf "syncthing_transfer_up.value %s\n" "$IBT" + printf "syncthing_transfer_down.value %s\n" "$OBT" esac } From 29dd82be36b686901c7240b0634afa5b2e570ec1 Mon Sep 17 00:00:00 2001 From: Vincas Dargis Date: Wed, 28 Mar 2018 11:43:28 +0000 Subject: [PATCH 132/695] Fix du-2 to use clean_fieldname() Update du-2 to fix "No .label provided" error by using clean_fieldname(). --- plugins/disk/du-2 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index 41e4adc1..6b8dfddc 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -91,12 +91,12 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { while(defined (my $bar = )) { if ($bar =~ m/(\d+)\s+(.+)/) { my $dir = $2; - clean_path(\$dir); - print "$dir.label $dir\n"; + my $clean_dir = clean_fieldname($dir); + print "$clean_dir.label $dir\n"; if ($foo++) { - print "$dir.draw STACK\n"; + print "$clean_dir.draw STACK\n"; } else { - print "$dir.draw AREA\n"; + print "$clean_dir.draw AREA\n"; } } } @@ -110,8 +110,7 @@ open (FILE, "<", $CACHEFILE) or munin_exit_fail(); while(defined (my $foo = )) { if ($foo =~ m/(\d+)\s+(.+)/) { my ($field, $value) = ($2, $1); - clean_path(\$field); - print $field, ".value ", $value, "\n"; + print clean_fieldname($field), ".value ", $value, "\n"; } } close(FILE); From 167c204db263c9bddffb790cd84da66fa2ab5123 Mon Sep 17 00:00:00 2001 From: Felix Pahlow Date: Mon, 19 Mar 2018 12:19:28 +0100 Subject: [PATCH 133/695] Munin ILIAS plugin: First release --- .../example-graphs/ilias_sessions-day.png | Bin 0 -> 18997 bytes plugins/ilias/ilias_ | 267 +++++++++++++----- 2 files changed, 201 insertions(+), 66 deletions(-) create mode 100644 plugins/ilias/example-graphs/ilias_sessions-day.png mode change 100644 => 100755 plugins/ilias/ilias_ diff --git a/plugins/ilias/example-graphs/ilias_sessions-day.png b/plugins/ilias/example-graphs/ilias_sessions-day.png new file mode 100644 index 0000000000000000000000000000000000000000..f53b11dfc77b3c96074fbce7e85343f46dcb6574 GIT binary patch literal 18997 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

ujh|&HqRQGoRjZh$YHpi~{jr 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 KT-G@yGywp5EY(i{ literal 0 HcmV?d00001 diff --git a/plugins/ilias/ilias_ b/plugins/ilias/ilias_ old mode 100644 new mode 100755 index dc41d8d0..39bf1367 --- a/plugins/ilias/ilias_ +++ b/plugins/ilias/ilias_ @@ -1,119 +1,254 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -# The Plugin needs the following configuration settings -#[ilias_*] -#env.ildbuser ilias -#env.ildbpassword youriliaspasword -#env.ildb ilias +""" +: << =cut -#%# family=auto -#%# capabilities=autoconf suggest +=head1 NAME + +ilias - Munin plugin to monitor L open source +learning management system + +=head1 DESCRIPTION + +Reads session and user statistcs from any ILIAS MySQLdb database. + +https://ilias.de/ | http://gallery.munin-monitoring.org/contrib/cms-index.html + +This plugin requires python3 and python3-mysqldb. + +There is a check for the the filename suffix _ (from the symlink) in place +to decide which value to output. Symlink the file for each value you want +displayed +example: + ln -s /usr/local/munin_plugins/ilias_ /etc/munin/plugins/ilias_sessions + +In order to get precise results, please ensure your MySQL server has the same +time as your ILIAS application server. Timezone does not matter. + +=head1 CONFIGURATION + +The plugin needs the following configuration settings e.g. in +/etc/munin/plugin-conf.d/ilias.conf + + [ilias_*] + env.ildbuser ilias + env.ildbpassword youriliaspasword + env.ildb ilias + env.ildbhost localhost + env.ildbport 3306 + + +=head1 AUTHOR + +Copyright 2016 Pascal Seeland + +Copyright 2018 L + (L) + +=head1 LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +provided that the above copyright notice(s) and this permission notice +appear in all copies of the Software and that both the above copyright +notice(s) and this permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE +LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization of the +copyright holder. + +=head1 CONTRIBUTE + +Find this plugin on L + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 VERSION + + 1.0 + +=head1 CHANGELOG + +=head2 1.0 - 2018/03/19 + + first release + +=cut +""" import os import sys -import MySQLdb -import _mysql +import pkgutil + + class ILIAS(): - title = "ILIAS session" - args = "--base 1000 -l 0" - vlabel = "ilias" - category = "ilias" + pluginname = sys.argv[0].split('_')[1] def __init__(self): self.con = None self.user = os.environ.get('ildbuser', 'root') self.pw = os.environ.get('ildbpassword', '') - self.ildb = os.environ.get('ildb','ilias') + self.ildb = os.environ.get('ildb', 'ilias') + self.ildbhost = os.environ.get('ildbhost', 'localhost') + self.ildbport = int(os.environ.get('ildbport', 3306)) + + def db_modules_available(self): + return pkgutil.find_loader("MySQLdb") + + def get_connection(self): + import MySQLdb + return MySQLdb.connect(host=self.ildbhost, + port=self.ildbport, + user=self.user, + passwd=self.pw, + db=self.ildb) def connectdb(self): - self.con = MySQLdb.connect("localhost", self.user, self.pw,self.ildb) - self.pluginname = sys.argv[0].split('_')[1] + self.con = self.get_connection() - - def config_sessions(self): + def config_sessions(self): print("graph_title ILIAS Session") + print("graph_info Number of active ILIAS user sessions") print("graph_vlabel ilsessions") - print("graph_category ILIAS") + print("graph_category cms") print("ilsessions.label ilSessions") + print("ilsessions.min 0") + print("ilsessions.draw AREA") def execute_sessions(self): cursor = self.con.cursor() - cursor.execute("select count(user_id) from usr_session where `expires` > UNIX_TIMESTAMP( NOW( ) ) AND user_id != 0") + cursor.execute( + "SELECT COUNT( user_id ) " + "FROM usr_session " + "WHERE `expires` > UNIX_TIMESTAMP( NOW( ) ) AND user_id != 0" + ) usrs = cursor.fetchone()[0] - print("ilsessions %s"%(usrs)) + print("ilsessions.value %s" % (usrs)) + def config_5minavg(self): + print("graph_title ILIAS 5 avg") + print("graph_info ILIAS sessions created or " + "updated during the last 5 minutes") + print("graph_vlabel il5minavg") + print("graph_category cms") + print("il5minavg.label 5 min Count") + print("il5minavg.min 0") + print("il5minavg.draw AREA") - def config_10minavg(self): - print("graph_title ILIAS 10 avg") - print("graph_vlabel il10minavg") - print("graph_category ILIAS") - print("il10minavg.label 10 min Count") - - def execute_10minavg(self): + def execute_5minavg(self): cursor = self.con.cursor() - cursor.execute("select count(user_id) from usr_session where 10 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0") + cursor.execute( + "SELECT COUNT( user_id ) " + "FROM usr_session " + "WHERE 5 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0" + ) usrs = cursor.fetchone()[0] - print("il10minavg %s"%(usrs)) + print("il5minavg.value %s" % (usrs)) - def config_60minavg(self): + def config_60minavg(self): print("graph_title ILIAS 60 avg") + print("graph_info ILIAS sessions created or " + "updated during the last 60 minutes") print("graph_vlabel il60minavg") - print("graph_category ILIAS") + print("graph_category cms") print("il60minavg.label 60 min Count") + print("il60minavg.min 0") + print("il60minavg.draw AREA") def execute_60minavg(self): cursor = self.con.cursor() - cursor.execute("select count(user_id) from usr_session where 60 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0") + cursor.execute( + "SELECT COUNT( user_id ) " + "FROM usr_session " + "WHERE 60 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0" + ) usrs = cursor.fetchone()[0] - print("il60minavg %s"%(usrs)) + print("il60minavg.value %s" % (usrs)) - def config_total1day(self): + def config_total1day(self): print("graph_title Users in 24h") + print("graph_info ILIAS users logging in during last 24h") print("graph_vlabel iltotal1day") - print("graph_category ILIAS") + print("graph_category cms") print("iltotal1day.label User/24h") + print("iltotal1day.min 0") + print("iltotal1day.draw AREA") def execute_total1day(self): cursor = self.con.cursor() - cursor.execute("select count(usr_id) FROM `usr_data` WHERE last_login >= DATE_SUB( NOW( ) , INTERVAL 1 DAY )") + cursor.execute( + "SELECT COUNT( usr_id ) " + "FROM `usr_data` " + "WHERE last_login >= DATE_SUB( NOW( ) , INTERVAL 1 DAY )") usrs = cursor.fetchone()[0] - print("iltotal1day %s"%(usrs)) - + print("iltotal1day.value %s" % (usrs)) def run(self): - cmd = ((len(sys.argv) > 1) and sys.argv[1] or None) or "execute" + cmd = ((len(sys.argv) > 1) and sys.argv[1]) or "execute" function = None - if cmd == "execute": - function = "execute" - elif cmd == "config": + + if cmd == "config": function = "config" - if function != None: - self.connectdb() - try: - func = getattr(self, "%s_%s"%(function,self.pluginname)) - except AttributeError: - print 'function not found "%s" (%s)' % ("config_%s"%self.pluginname, "self") + elif cmd == "suggest": + print("sessions") + print("5minavg") + print("60minavg") + print("total1day") + elif cmd == "autoconf": + if not self.db_modules_available(): + print("no (Please install the MySQLdb python3 module)") else: - func() + try: + con = self.get_connection() + cursor = con.cursor() + cursor.execute("SELECT COUNT( component ) " + "FROM il_pluginslot") + con.close() + except _mysql.Error as e: + print("no (Error %d: %s - Database configuration missing?)" + % (e.args[0], e.args[1])) + else: + print("yes") + else: + function = "execute" + + if function is not None: + if not self.db_modules_available(): + print("U (Please install the MySQLdb python3 module)") + else: + self.connectdb() + try: + func = getattr(self, "%s_%s" % (function, self.pluginname)) + except AttributeError: + print('function not found "%s" (%s)' % + ("config_%s" % self.pluginname, "self")) + else: + func() + if self.con: self.con.close() - if cmd == "suggest": - print ("sessions") - print ("10minavg") - print ("60minavg") - print ("total1day") - if cmd == "autoconf": - try: - con = MySQLdb.connect("localhost", self.user, self.pw,self.ildb) - cursor = con.cursor() - cursor.execute("SELECT count(component) FROM il_pluginslot") - except _mysql.Error, e: - print "no (Error %d: %s)" % (e.args[0], e.args[1]) - else: - print "yes" + sys.exit(0) if __name__ == "__main__": ILIAS().run() - \ No newline at end of file + From 6c59a8971b061f6d269c90068af00c1ef1486028 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 12:56:55 +0200 Subject: [PATCH 134/695] plugin snmp__juniper: change category from "system" to "fw" --- plugins/router/snmp__juniper | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/plugins/router/snmp__juniper b/plugins/router/snmp__juniper index 216283ba..da332ea8 100755 --- a/plugins/router/snmp__juniper +++ b/plugins/router/snmp__juniper @@ -141,19 +141,19 @@ class JunOSSnmpClient(object): devices = self.get_devices() data_def = [ - ('temp', self.hostname, 'System temperature', '--base 1000', 'System temperature in C', 'system'), - ('cpu', self.hostname, 'CPU usage', '--base 1000 -l 0 --upper-limit 100', 'CPU usage in %', 'system'), - ('buffer', self.hostname, 'Buffer usage', '--base 1000 -l 0 --upper-limit 100', 'Buffer usage in %', 'system'), + ('temp', self.hostname, 'System temperature', '--base 1000', 'System temperature in C'), + ('cpu', self.hostname, 'CPU usage', '--base 1000 -l 0 --upper-limit 100', 'CPU usage in %'), + ('buffer', self.hostname, 'Buffer usage', '--base 1000 -l 0 --upper-limit 100', 'Buffer usage in %'), ] - for datarow, hostname, title, args, vlabel, category in data_def: + for datarow, hostname, title, args, vlabel in data_def: print """multigraph juniper_{datarow} host_name {hostname} graph_title {title} graph_vlabel {vlabel} graph_args {args} -graph_category {category} -graph_info {title}""".format(datarow=datarow, hostname=hostname, title=title, args=args, vlabel=vlabel, category=category) +graph_category fw +graph_info {title}""".format(datarow=datarow, hostname=hostname, title=title, args=args, vlabel=vlabel) for suffix, node in devices.iteritems(): ident = "%s_%s" % (datarow, node) @@ -184,7 +184,3 @@ else: c.print_config() else: c.execute() - -# for Munin Plugin Gallery -# graph_category network - From 63748dc665599723b7af967b91ca79c73c0629e1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 28 Mar 2018 12:57:41 +0200 Subject: [PATCH 135/695] plugin relayd: fix category spelling --- plugins/relayd/relayd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/relayd/relayd b/plugins/relayd/relayd index 8f1e1147..aef563ae 100755 --- a/plugins/relayd/relayd +++ b/plugins/relayd/relayd @@ -82,7 +82,7 @@ if ($cmd eq 'config') { print("graph_title Relayd host availability\n"); print("graph_args --upper-limit 100\n"); print("graph_vlabel % availability\n"); - print("graph_category Load balancer\n"); + print("graph_category loadbalancer\n"); print("graph_info Ratio of time when this host was up. This is provided by relayd itself (not averaged by this plugin)\n"); for my $host (@hosts) { my $clean = clean_host($host); @@ -92,7 +92,7 @@ if ($cmd eq 'config') { print("graph_title Relayd host incidents\n"); print("graph_args --lower-limit 0\n"); print("graph_vlabel down incidents\n"); - print("graph_category Load balancer\n"); + print("graph_category loadbalancer\n"); print("graph_info Number of times this host went down\n"); for my $host (@hosts) { my $clean = clean_host($host); From 31ee164e760f0ce7680637a47282f5c4ed403ee0 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Mar 2018 01:57:46 +0200 Subject: [PATCH 136/695] plugin percona_: fix python style; python3 compatibility --- plugins/percona/percona_ | 60 ++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/plugins/percona/percona_ b/plugins/percona/percona_ index 3340b468..6282f09f 100755 --- a/plugins/percona/percona_ +++ b/plugins/percona/percona_ @@ -24,7 +24,8 @@ # This plugin requires pythons MySQLdb module which can be installed via easy_install. # # ## Installation -# Copy file to directory /usr/share/munin/plugins/ and create symbolic links for each type you wish to monitor: +# Copy file to directory /usr/share/munin/plugins/ and create symbolic links for each type you wish +# to monitor: # percona_flow # percona_queues # percona_replication @@ -37,14 +38,19 @@ # env.user root # env.password vErYsEcReT # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib +import os +import sys from warnings import filterwarnings -import os, sys, MySQLdb, MySQLdb.cursors -filterwarnings('ignore', category = MySQLdb.Warning) -progName = os.path.basename(__file__) +import MySQLdb +import MySQLdb.cursors + +filterwarnings('ignore', category=MySQLdb.Warning) + +program_name = os.path.basename(__file__) variables = { 'percona_queues': { @@ -76,13 +82,13 @@ variables = { # Parse environment variables # Mysql host -if "host" in os.environ and os.environ["host"] != None: +if "host" in os.environ and os.environ["host"] is not None: server = os.environ["host"] else: - server = "localhost" + server = "localhost" # Mysql port -if "port" in os.environ and os.environ["port"] != None: +if "port" in os.environ and os.environ["port"] is not None: try: port = int(os.environ["port"]) except ValueError: @@ -91,13 +97,13 @@ else: port = 3306 # Mysql username -if "user" in os.environ and os.environ["user"] != None: +if "user" in os.environ and os.environ["user"] is not None: login = os.environ["user"] else: login = "" # Mysql password -if "password" in os.environ and os.environ["password"] != None: +if "password" in os.environ and os.environ["password"] is not None: passw = os.environ["password"] else: passw = "" @@ -105,9 +111,9 @@ else: # Mysql connection handler conn = None -label = variables[progName]['label'] -vlabel = variables[progName]['vlabel'] -fields = ['\'{0}\''.format(x) for x in variables[progName]['fields']] +label = variables[program_name]['label'] +vlabel = variables[program_name]['vlabel'] +fields = ["'{0}'".format(x) for x in variables[program_name]['fields']] query = "show status where Variable_name in (%s)" % ', '.join(fields) @@ -115,31 +121,31 @@ query = "show status where Variable_name in (%s)" % ', '.join(fields) try: conn = MySQLdb.connect(host=server, user=login, passwd=passw) cursor = conn.cursor() -except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) +except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) sys.exit(1) values = {} if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - print "graph_title %s" % label - print "graph_vlabel %s" % vlabel - print "graph_category db" - print "" + print("graph_title %s" % label) + print("graph_vlabel %s" % vlabel) + print("graph_category db") + print() try: cursor.execute(query) results = cursor.fetchall() for result in results: - print "%s_size.label %s" % (result[0], result[0]) + print("%s_size.label %s" % (result[0], result[0])) - except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) else: try: @@ -147,10 +153,10 @@ else: results = cursor.fetchall() for result in results: - print "%s_size.value %s" % (result[0], result[1]) + print("%s_size.value %s" % (result[0], result[1])) - except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) if conn: conn.close() From 2449118cb74c0031eb01cf3a682996d696a01dbc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Mar 2018 02:03:28 +0200 Subject: [PATCH 137/695] plugin multiping_async: move to 'network'; fix filename --- plugins/{other/multipng_async => network/multiping_async} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{other/multipng_async => network/multiping_async} (100%) diff --git a/plugins/other/multipng_async b/plugins/network/multiping_async similarity index 100% rename from plugins/other/multipng_async rename to plugins/network/multiping_async From 95e2c124857cde5a548e1fbe187bc4e8ec79c7e2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Mar 2018 02:54:31 +0200 Subject: [PATCH 138/695] plugin categories: fixed emc_vnx_file_ and opentracker_ --- plugins/emc/emc_vnx_file_ | 2 +- plugins/other/opentracker_ | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/emc/emc_vnx_file_ b/plugins/emc/emc_vnx_file_ index ed5271a4..45bda578 100755 --- a/plugins/emc/emc_vnx_file_ +++ b/plugins/emc/emc_vnx_file_ @@ -61,7 +61,7 @@ EMC VNX 5300 FileResolve These are NFS (v3,v4) Graphs. - Graph category NFS: + Graph category FS: EMC VNX 5300 NFSv3 Calls per second EMC VNX 5300 NFSv3 uSeconds per call EMC VNX 5300 NFSv3 Op % diff --git a/plugins/other/opentracker_ b/plugins/other/opentracker_ index 0bfe0f78..ae424bda 100755 --- a/plugins/other/opentracker_ +++ b/plugins/other/opentracker_ @@ -86,7 +86,7 @@ $graphs{conn} = { config => { args => '--lower-limit 0', vlabel => 'Connections', - category => 'opentracker', + category => 'filetransfer', title => 'Current Connections', info => 'Current Connections to OpenTracker', }, From 21341b5a00facd4c5e4212c8e1050cc398b5a569 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 29 Mar 2018 08:11:59 +0200 Subject: [PATCH 139/695] python3 only --- .../currency/zcash/zcash_flypool_hashrate_ | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index d11fb7fb..fbcd4c10 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ =head1 NAME @@ -7,7 +7,7 @@ zcash_flypool_hashrate_ - Munin plugin to monitor your zcash.flypool.org hashrat =head1 APPLICABLE SYSTEMS -All systems with "python" and "munin" +All systems with "python3" and "munin" =head1 CONFIGURATION @@ -42,20 +42,11 @@ GPLv2 =cut """ -from __future__ import print_function - import sys import json import codecs - -try: - # python3 - from urllib.request import urlopen - from urllib.request import Request -except ImportError: - # python2 - from urllib2 import urlopen - from urllib2 import Request +from urllib.request import urlopen +from urllib.request import Request command = '' if len(sys.argv) > 1: @@ -110,4 +101,4 @@ except: print("No current Hashrate!", file=sys.stderr) sys.exit(9) -print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) \ No newline at end of file +print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) From f27fc67ccf7e0ef3978e061bc7f3d4586b7919de Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 29 Mar 2018 08:37:53 +0200 Subject: [PATCH 140/695] reader removed --- plugins/currency/zcash/zcash_flypool_hashrate_ | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index fbcd4c10..06bb397d 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -81,10 +81,9 @@ try: except IOError as exc: print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) -reader = codecs.getreader("utf-8") try: - mining_stats = json.load(reader(mining_stats_raw)) + mining_stats = json.load(mining_stats_raw) except ValueError: print("Failed to parse JSON responce.", file=sys.stderr) sys.exit(9) From d809d138b1b31c8e85de4830b85788ded8b6e65a Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 29 Mar 2018 08:39:20 +0200 Subject: [PATCH 141/695] exit if api failed --- plugins/currency/zcash/zcash_flypool_hashrate_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 06bb397d..390ec069 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -80,7 +80,7 @@ try: mining_stats_raw = urlopen(mining_req, timeout=15) except IOError as exc: print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) - + sys.exit(9) try: mining_stats = json.load(mining_stats_raw) From 727fc7180de95558f4a0f326ac4149e5e6676545 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 29 Mar 2018 08:41:32 +0200 Subject: [PATCH 142/695] better except --- plugins/currency/zcash/zcash_flypool_hashrate_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 390ec069..a9cba304 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -90,13 +90,13 @@ except ValueError: try: worker = mining_stats['data'] -except: +except (KeyError, TypeError): print("JSON result error!", file=sys.stderr) sys.exit(9) try: hash_rate = worker['currentHashrate'] -except: +except (KeyError, TypeError): print("No current Hashrate!", file=sys.stderr) sys.exit(9) From e2db693c0da7d55dcb8a6b3f7a6021293e6ae138 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 29 Mar 2018 08:50:21 +0200 Subject: [PATCH 143/695] codecs.getreader ("utf-8") added again without python3.4 fails --- plugins/currency/zcash/zcash_flypool_hashrate_ | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index a9cba304..609c1aa9 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -82,8 +82,10 @@ except IOError as exc: print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) sys.exit(9) +reader = codecs.getreader("utf-8") + try: - mining_stats = json.load(mining_stats_raw) + mining_stats = json.load(reader(mining_stats_raw)) except ValueError: print("Failed to parse JSON responce.", file=sys.stderr) sys.exit(9) From e541294f9fc2f6fda52a47a79a74fb72274fe788 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Thu, 29 Mar 2018 13:15:44 +0200 Subject: [PATCH 144/695] Clarify the non-declared env variables Signed-off-by: Pierre-Alain TORET --- plugins/syncthing/strelaysrv_ | 3 +++ plugins/syncthing/syncthing_ | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index c5a9fe3d..5a30f27b 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -31,6 +31,9 @@ Pierre-Alain TORET MIT =cut +syncthing_relaysrv_host=${syncthing_relaysrv_host:-} +syncthing_relaysrv_port=${syncthing_relaysrv_port:-} + getstatus() { "$CURL" -s "http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status" } diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index d0a94de2..7bfd0cd0 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -34,6 +34,11 @@ Pierre-Alain TORET MIT =cut +syncthing_apikey=${syncthing_apikey:-} +syncthing_proto=${syncthing_proto:-} +syncthing_host=${syncthing_host:-} +syncthing_port=${syncthing_port:-} + getstatus() { "$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status" } From e7eb28869c79da262654b14b0328b2daf9c5cc9b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 30 Mar 2018 01:48:51 +0200 Subject: [PATCH 145/695] plugins ssl_ and ssl-certificate-expiry: various improvements * simplify date parsing: use "date" instead of awk's "mktime" (requires gawk) * simplify structure * use the same function (copy'n'paste) for both plugins Closes: #893 --- plugins/ssl/ssl-certificate-expiry | 84 ++++++++++++++++++------------ plugins/ssl/ssl_ | 57 +++++++++++++------- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index 9295380a..7c69face 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -41,6 +41,7 @@ Copyright (C) 2017 Olivier Mehani =cut +# shellcheck disable=SC1090 . "${MUNIN_LIBDIR}/plugins/plugin.sh" if [ "${MUNIN_DEBUG}" = 1 ]; then @@ -54,6 +55,44 @@ if [ "${HOSTPORT}" != "${0}" ] \ services="${HOSTPORT}" fi + +# Read data including a certificate from stdin and output the (fractional) number of days left +# until the expiry of this certificate. The output is empty if parsing failed. +parse_valid_days_from_certificate() { + local input_data + local valid_until_string + local valid_until_epoch + local now_epoch + local input_data + input_data=$(cat) + if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then + valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ + | grep "^notAfter=" | cut -f 2 -d "=") + if [ -n "$valid_until_string" ]; then + valid_until_epoch=$(date --date="$valid_until_string" +%s) + if [ -n "$valid_until_epoch" ]; then + now_epoch=$(date +%s) + # calculate the number of days left + echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }' + fi + fi + fi +} + + +print_expire_days() { + local host="$1" + local port="$2" + + # Wrap IPv6 addresses in square brackets + echo "$host" | grep -q ':' && host="[$host]" + + echo "" | openssl s_client -CApath /etc/ssl/certs \ + -servername "$host" -connect "${host}:${port}" 2>/dev/null \ + | parse_valid_days_from_certificate +} + + case $1 in config) @@ -72,40 +111,17 @@ case $1 in ;; esac -get_expire() -{ - SITE="$(echo "${1}" | sed 's/_.*//')" - PORT="$(echo "${1}" | sed 's/.*_//')" - - VAR="$(clean_fieldname "$1")" - if [ "$PORT" = "$SITE" ]; then - PORT=443 - fi - if echo "$SITE" | grep -q ':'; then - # Wrap IPv6 addresses in square brackets - SITE="[${SITE}]" - fi - - CERT=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" -connect "${SITE}:${PORT}" 2>/dev/null); - - if echo "${CERT}" | grep -q -- "-----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 "VAR.value " days; - }' \ - | sed "s/VAR/${VAR}/g" - fi -} for service in $services; do - get_expire "$service" + if echo "$service" | grep -q "_"; then + host=$(echo "$service" | cut -f 1 -d "_") + port=$(echo "$service" | cut -f 2 -d "_") + else + host=$service + port=443 + fi + fieldname="$(clean_fieldname "$service")" + valid_days=$(print_expire_days "$host" "$port") + [ -z "$valid_days" ] && valid_days="U" + printf "%s.value %s\n" "$fieldname" "$valid_days" done diff --git a/plugins/ssl/ssl_ b/plugins/ssl/ssl_ index 76e7686f..496453f1 100755 --- a/plugins/ssl/ssl_ +++ b/plugins/ssl/ssl_ @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # -*- sh -*- : << =cut @@ -26,15 +26,43 @@ Copyright (C) 2013 Patrick Domack =cut +# shellcheck disable=SC1090 . "$MUNIN_LIBDIR/plugins/plugin.sh" ARGS=${0##*ssl_} -SITE=${ARGS/_*/} -PORT=${ARGS##*_} -if [ "$PORT" = "$SITE" ]; then - PORT=443 +if echo "$ARGS" | grep -q "_"; then + SITE=$(echo "$ARGS" | cut -f 1 -d "_") + PORT=$(echo "$ARGS" | cut -f 2 -d "_") +else + SITE=$ARGS + PORT=443 fi + +# Read data including a certificate from stdin and output the (fractional) number of days left +# until the expiry of this certificate. The output is empty if parsing failed. +parse_valid_days_from_certificate() { + local input_data + local valid_until_string + local valid_until_epoch + local now_epoch + local input_data + input_data=$(cat) + if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then + valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ + | grep "^notAfter=" | cut -f 2 -d "=") + if [ -n "$valid_until_string" ]; then + valid_until_epoch=$(date --date="$valid_until_string" +%s) + if [ -n "$valid_until_epoch" ]; then + now_epoch=$(date +%s) + # calculate the number of days left + echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }' + fi + fi + fi +} + + case $1 in config) @@ -53,18 +81,7 @@ esac 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; - }' -fi +days_left=$(echo "$cert" | parse_valid_days_from_certificate) +[ -n "$days_left" ] || days_left="U" + +printf "expire.value %s\n" "$days_left" From 2b66ed321efad71e2267028171358a641a6a852d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 30 Mar 2018 04:10:14 +0200 Subject: [PATCH 146/695] plugin xen_cpu_v2: prevent "divide by zero" Closes: #475 --- plugins/xen/xen_cpu_v2 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/xen/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 index 75c9a951..aa58184c 100755 --- a/plugins/xen/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -171,8 +171,12 @@ while (($key, $value) = each %dom) # Calc a percentage based on the _total_ available CPU time $tmp = 0; - $tmp = ( $dom{$key}->{'diff'} / $tcpuavail ) * 100; - $dom{$key}->{'pc_tcpu'} = sprintf("%.2f", $tmp); + if ($tcpuavail != 0) { + $tmp = ( $dom{$key}->{'diff'} / $tcpuavail ) * 100; + $dom{$key}->{'pc_tcpu'} = sprintf("%.2f", $tmp); + } else { + $dom{$key}->{'pc_tcpu'} = "U"; + } if ( $debug ) { From 8a72386a3037681c11fe266fa5181d3a52e00dbe Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 30 Mar 2018 09:58:14 +0200 Subject: [PATCH 147/695] print magic value U if error --- .../currency/zcash/zcash_flypool_hashrate_ | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 609c1aa9..4eec991d 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -82,24 +82,25 @@ except IOError as exc: print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) sys.exit(9) +# manage codec and error handling lookup process reader = codecs.getreader("utf-8") try: mining_stats = json.load(reader(mining_stats_raw)) except ValueError: print("Failed to parse JSON responce.", file=sys.stderr) - sys.exit(9) - -try: - worker = mining_stats['data'] -except (KeyError, TypeError): - print("JSON result error!", file=sys.stderr) - sys.exit(9) - -try: - hash_rate = worker['currentHashrate'] -except (KeyError, TypeError): - print("No current Hashrate!", file=sys.stderr) - sys.exit(9) - -print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) + print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) +else: + try: + worker = mining_stats['data'] + except (KeyError, TypeError): + print("JSON result error!", file=sys.stderr) + print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) + else: + try: + hash_rate = worker['currentHashrate'] + except (KeyError, TypeError): + print("No current Hashrate!", file=sys.stderr) + print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) + else: + print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) From 6ff34af50ebfa3ccc4a0cfa46f9a73cec363855b Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 30 Mar 2018 14:16:57 +0200 Subject: [PATCH 148/695] set U as default --- plugins/currency/zcash/zcash_flypool_hashrate_ | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 4eec991d..079f7198 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -85,22 +85,21 @@ except IOError as exc: # manage codec and error handling lookup process reader = codecs.getreader("utf-8") +hash_rate = "U" + try: mining_stats = json.load(reader(mining_stats_raw)) except ValueError: print("Failed to parse JSON responce.", file=sys.stderr) - print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) else: try: worker = mining_stats['data'] except (KeyError, TypeError): print("JSON result error!", file=sys.stderr) - print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) else: try: hash_rate = worker['currentHashrate'] except (KeyError, TypeError): print("No current Hashrate!", file=sys.stderr) - print("flypool_hs_{}_{}.value U".format(zcash_address, miner)) - else: - print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) + +print("flypool_hs_{}_{}.value {}".format(zcash_address, miner, hash_rate)) From 6a0a0c8d58544ec2969282df4e31bde5f78ce137 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 30 Mar 2018 20:56:12 +0200 Subject: [PATCH 149/695] use json.loadS --- plugins/currency/zcash/zcash_flypool_hashrate_ | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 079f7198..8db6effc 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -82,13 +82,10 @@ except IOError as exc: print("Failed to request Flypool API: {}".format(exc), file=sys.stderr) sys.exit(9) -# manage codec and error handling lookup process -reader = codecs.getreader("utf-8") - hash_rate = "U" try: - mining_stats = json.load(reader(mining_stats_raw)) + mining_stats = json.loads(mining_stats_raw.read().decode("utf-8")) except ValueError: print("Failed to parse JSON responce.", file=sys.stderr) else: From 50dd7cc94ef26c08dec5653e76a54a321e43660e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 04:09:41 +0200 Subject: [PATCH 150/695] wireless_channel_occupation_: various improvements * add documentation header * support dirty config * fix shellcheck issues --- plugins/network/wireless_channel_occupation_ | 134 ++++++++++++------- 1 file changed, 86 insertions(+), 48 deletions(-) diff --git a/plugins/network/wireless_channel_occupation_ b/plugins/network/wireless_channel_occupation_ index 71c54c3e..ebe6292d 100755 --- a/plugins/network/wireless_channel_occupation_ +++ b/plugins/network/wireless_channel_occupation_ @@ -1,28 +1,50 @@ #!/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 + +: << =cut + +=head1 NAME + +wireless_channel_occupation_ - Monitor occupation of wireless channels + + +=head1 APPLICABLE SYSTEMS + +All systems with at least one wireless interface and the the tool "iw". + +The wifi channel occupation is parsed from the output of "iw dev wlan0 survey dump". + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_channel_occupation_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut set -eu @@ -35,36 +57,26 @@ clean_fieldname() { } -get_wifi_devices() { +get_wifi_interfaces() { 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" + local real_dev + # pick the part after the basename of the real file + suffix=$(basename "$0" | sed "s/^$SCRIPT_PREFIX//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 } -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 +do_config() { + local device + local dev_field device=$(get_wifi_device_from_suffix) - [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && exit 1 + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && return 1 echo "graph_title Channel Occupation of $device" echo "graph_vlabel %" echo "graph_category wireless" @@ -93,13 +105,39 @@ elif [ "${1:-}" = "config" ]; then 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 +} + + +do_fetch() { + local device device=$(get_wifi_device_from_suffix) - [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && exit 1 + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && return 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}' + | awk '{print "'"${device}_"'"$2"'.value'",$4}' +} + + +if [ "${1:-}" = "autoconf" ]; then + if which iw >/dev/null; then + if [ -n "$(get_wifi_interfaces)" ]; then + echo "yes" + else + echo "no (missing wifi devices)" + fi + else + echo "no (missing 'iw' dependency)" + fi +elif [ "${1:-}" = "suggest" ]; then + for dev in $(get_wifi_interfaces); do + clean_fieldname "$dev" + done +elif [ "${1:-}" = "config" ]; then + do_config || exit 1 + [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ] && do_fetch +else + do_fetch fi exit 0 From 0fad0a30bb8dfaddc192af6d0e6f9c53474a4ad3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 04:17:08 +0200 Subject: [PATCH 151/695] wireless_channel_occupation_: move from "network" to "wifi" --- plugins/{network => wifi}/wireless_channel_occupation_ | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{network => wifi}/wireless_channel_occupation_ (100%) diff --git a/plugins/network/wireless_channel_occupation_ b/plugins/wifi/wireless_channel_occupation_ similarity index 100% rename from plugins/network/wireless_channel_occupation_ rename to plugins/wifi/wireless_channel_occupation_ From 495d96735a1a34adfb235f70d15b3518a144d017 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 04:18:51 +0200 Subject: [PATCH 152/695] wireless_channel_active_: add example graphs --- .../wireless_channel_active_-week.png | Bin 0 -> 25284 bytes .../wireless_channel_active_-year.png | Bin 0 -> 47197 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/wifi/example-graphs/wireless_channel_active_-week.png create mode 100644 plugins/wifi/example-graphs/wireless_channel_active_-year.png diff --git a/plugins/wifi/example-graphs/wireless_channel_active_-week.png b/plugins/wifi/example-graphs/wireless_channel_active_-week.png new file mode 100644 index 0000000000000000000000000000000000000000..472fe858dd7307ed0ab602fdf9a1a5708047b174 GIT binary patch literal 25284 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
$xvip$KS<#GlL!;7v!T8!O`} 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

qpUGEn(y)+fq#R^lJRNpu-5ZDL`_{+O@Gp|L+~Y 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 literal 0 HcmV?d00001 From b182abfd5209d66edd45f07a729dd373d96ac60d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 04:20:08 +0200 Subject: [PATCH 153/695] move "ath9k_" from network to wifi --- plugins/{network => wifi}/ath9k_ | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{network => wifi}/ath9k_ (100%) diff --git a/plugins/network/ath9k_ b/plugins/wifi/ath9k_ similarity index 100% rename from plugins/network/ath9k_ rename to plugins/wifi/ath9k_ From 127f42aa05950f6acee542935035d8e2181efe70 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 04:22:54 +0200 Subject: [PATCH 154/695] ath9k_: various improvements * support DFS events * support dirty config * fix autoconf * improve micropython usage * add support for older kernel modules --- plugins/wifi/ath9k_ | 340 +++++++++++++++++++++++++++++++------------- 1 file changed, 245 insertions(+), 95 deletions(-) diff --git a/plugins/wifi/ath9k_ b/plugins/wifi/ath9k_ index 405b93c2..a43d844d 100755 --- a/plugins/wifi/ath9k_ +++ b/plugins/wifi/ath9k_ @@ -5,6 +5,7 @@ # * rate control statistics ("rc_stats") # * events (dropped, transmitted, beacon loss, ...) # * traffic (packets, bytes) +# * DFS events (processed patterns, approved signals) # # All data is collected for each separate station (in case of multiple # connected peers). Combined graphs are provided as a summary. @@ -15,7 +16,19 @@ # * micropython # # -# Copyright (C) 2015 Lars Kruse +# 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 +# ... +# +# +# Copyright (C) 2015-2018 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 @@ -31,8 +44,8 @@ # along with this program. If not, see . # # Magic markers -#%# capabilities=autoconf suggest -#%# family=auto +# #%# capabilities=autoconf suggest +# #%# family=auto """true" # ****************** Interpreter Selection *************** @@ -41,14 +54,16 @@ # # 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 +# * 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" "$@" +# prefer micropython if it is available - otherwise fall back to python 3 +MICROPYTHON_BIN=$(which micropython || true) +if [ -n "$MICROPYTHON_BIN" ]; then + "$MICROPYTHON_BIN" "$0" "$@" else python3 "$0" "$@" fi @@ -59,26 +74,20 @@ exit $? true < 0 def _get_up_down_pair(unit, key_up, key_down, factor=None, divider=None, use_negative=True): @@ -407,8 +544,9 @@ def get_scope(): 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)) + if scope not 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") @@ -422,23 +560,37 @@ def print_error(message): sys.stderr.write(message + linesep) +def do_fetch(ath9k): + for item in ath9k.get_values(get_scope()): + print(item) + + +def do_config(ath9k): + for item in ath9k.get_config(get_scope()): + print(item) + + 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) + if sys.argv[1] == "config": + do_config(ath9k) + if os.getenv("MUNIN_CAP_DIRTYCONFIG") == "1": + do_fetch(ath9k) sys.exit(0) elif sys.argv[1] == "autoconf": - if os.path.exists(SYS_BASE_PATH): + if os.path.exists(SYS_BASE_DIR): print('yes') else: - print('no') + print('no (missing ath9k driver sysfs directory: {})'.format(SYS_BASE_DIR)) sys.exit(0) elif sys.argv[1] == "suggest": - for scope in PLUGIN_SCOPES: - print(scope) + if ath9k.has_devices(): + for scope in PLUGIN_SCOPES: + # skip the "dfs_events" scope if there is not DFS support + if (scope != "dfs_events") or ath9k.has_dfs_support(): + print(scope) sys.exit(0) elif sys.argv[1] == "version": print_error('olsrd Munin plugin, version %s' % plugin_version) @@ -451,9 +603,7 @@ if __name__ == "__main__": print_error("Unknown argument") sys.exit(1) - # output values - for item in ath9k.get_values(get_scope()): - print(item) + do_fetch(ath9k) # final marker for shell / python hybrid script (see "Interpreter Selection") EOF = True From c67420d8aa80587dc95c7e5f0f3f12764877646f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:22:53 +0200 Subject: [PATCH 155/695] ath9k: add example graphs --- .../ath9k_-stats_phy0_dfs_events_year.png | Bin 0 -> 29157 bytes ...ath9k_-stats_phy0_phy0_wlan0_events_day.png | Bin 0 -> 48928 bytes .../ath9k_-stats_rcstats_day.png | Bin 0 -> 103251 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png create mode 100644 plugins/wifi/example-graphs/ath9k_-stats_phy0_phy0_wlan0_events_day.png create mode 100644 plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png diff --git a/plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png b/plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png new file mode 100644 index 0000000000000000000000000000000000000000..eb2fbe4f50b72b91a8f800298a8333e9f182b1b1 GIT binary patch literal 29157 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

c^8$5P{*{%L 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 z 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 literal 0 HcmV?d00001 diff --git a/plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png b/plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f034005283f33d018c6b485a36c55053641fe2 GIT binary patch literal 103251 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

8ISGRLFws)~3k{axcL{pjDk z@7yGSiKPK4UH9zQO*CDK%1LdQ)h0*1Pfj*`k8a8Bz8>C@&k+}Gub-n7CsrSH&38S6 zG|ULqiLueRJx7KLYPu9DPQP)rggWS`!cGFqYw8&IvR7J z?J+bOL2@&`C4^vydn%3cdOlEx`y7qLI&u157n3rhn^{fCNMx0?xhgzFTeD*xOUBmn z6~2DYH%ELjCznq!zFwOh<=B4eO5eCYKib5a-kjEX1kHh;9224y#${xx(>l*og7@9$ z=oh){>vLquR7zTjW@Fe1*{nVAvL80{x4Z_R+P~Y)i}LmwgO;0uYl8kG0s;s>hR~ zr@l(Gb!PMe=a?xjsvdWqrD$`}seQ-OePuk!PptwdfATDVWNI$)>YGP4m5dh#%jmcl zof*9V6`h@`1oDDDwx0UC3T{WIUIdVxx&q+#sh5W_H+AZ3S!M>0&niq5r#Ae|mDbb` zOWwS`<;z5y?D$Wau+I%mK{UnGqVQgdPka|ZJT;Ty@tLvnjOmN4vu$bBlRnXB|*|#rGh}0SC4PF7-9z$nDRdQ1wZVHaWJ(XVI z3mH!w?sIfbHe*C@W?@A0^3bMX$yQ%WNNon167~M6Hx-LtoA~L9TsZe@Z%US&8NDE= z)<1PYfQ7fbq~zT8NNvk)&xq|gezEew?{V39o-M44<<*=?lMH^YK1aU<#W>Bmk)q*( z29x#tQt`oDI~7Q(-HACZ8^>lRK7BiB!f%I;31PEAZ9!dZiTVJQjz>N^D(454QS7hF zC_ADq=*G&XwJ%yXIJ32O98dP+3qhtc$@%4*4-(*fZlX?_P z=iZP=d}cjnYqQ50Uqk9Ta&@52T5_f48i$XEag%=G=I@OI;M5okibPtfR!d#!t!e$n z@HTgRDI;QV4L~sK0U?SRcmM{0Jg_-CEfGnyLNn=e&D5&o)snYEG;=Z!E}v6m`!Lws zzQ^m^VzV9hLhR)7i5xe9lc632K+cjwjx+$ppmfVm!nvhsfqw2p$?-f>DXW?x9*cS` znnS!_dFFIwL&ylg05FU>&R5p>QiJUrdLSR3Ed17;eUXq( z9Bkx`>)_LEM~gHvx^)0?haQgGz#14vu4dY^APg7)$=U5^&-)%9D`Wt0;aaQ-poaGE zN`vdub~3&=gx;L?c;1tvg#bX)?p!BL*YbE0$cXr1o=Slf3-OvK*)Bdk?|Z!}+)dkR zxp>S)S~M&rni8QRrD7#xfo+baWKo1@o0ac)jubTqc|kI#9OEbyFbp6FS|EGR;KYJm z%FOq1QG?J>Q8CYjzyCD6QRSC&O^=~DStY7ATP0PEkH0eRw@r-puIWvk#8!7XxUHly z*IG&c7D5-ijh3uNynbAc=&5Qk&-p>_004mVX8%v6V62lzm-3k1zyK~R@#IQe^P~;) zwZz+YT)z<|)ubh*R;uCo?Mr_o#~B&VLy*&I3V>S%Dx`5qzI8%Z{J1_jHX~etTk?Pa@DTwBNVh~S617|8p#1;Yd$SnHuJlao zJKH^Xk3DjW%#6s)VzC%SGPyL@W*6DbUVxE$3Nx6tG#bm6Fn%!LH=_pv^xy|SS@U4n z4}S0h8^XY&fw%UwEVZ?CSFzPpbL}F`(VGwauL%cm&)1iElcH!XOF>A%keD)2RpnD4hQOt2Y!SPMaJ6<8cBMJP4;Z zc0vG<^dmWaD0_w`xG9wlH`JvtO@e!i>_(xZFi+Mo3YN_1 zr`LBBPbQJuw7gI_0SN-)F~t$WQ-sgap%^L0#3?u5xWnjZMWg@-5Efe&EC51r0XW$m zmhO6M?H8BQ0!0}r$7v5)8FE3SAi5Q5YLI!bw&{VfT@GrTfiMJd=tV&(r#mrDBz4Ni#z~otX_USLEMc9n z1JEeRsG)>h*jrUtN5)i}>;a%LC1h5Q$vM8Pl8Afzszf1QPu$3MLE3 zY%mtRS;bS-$>r`L6XQI}3K{_BXg*8rv*RJcpV_mjfMi{W&$al<`XD0VLy$1e$QRn_ zZsaERY!HVJMdwfC#O6(!F-nZ%QjCcD091wj!w+o;m&-7j(uA<76AV`E0`l!7+L`pj z1(b(vFH)+^8){WDO)v;=KCPe4ar9ze6OkbT(C86%`V_;R`!A)``LtFv^(4hm*I{cs zrOM>(Fqn*^$($c5svi?CBmol+S09j9vdbE0HI9Q z3nglGf2X=()SnPa7w2yw-_907J@q%6tNzQW(?{WivVgFIZS}ffL$9)Na!4rS`x<+( zAH?B9*LEfX!kSbY^wG@;e^}XsK6wzf;zTT|Y~kVCM#1R`40K{2Imd>NB0`~rAhDD_ zm;E6bL^S+x;y9tN$Uus*qV%TyYXdGKMM7$kp?LnxIHgLg-CWL2jpzrd@(}khQ7k-JXZlw3?=a; z!kNmh3HzMLQyv450!WxARR;iAXzTpJ@mM;uOag#VU%&@2e%%KE+HHv)$s#F+DcxO^ z?speB;=*y8?UKSiZ1w?oa5fc-(fqQf)W!`(0 zC7s3wS^!EY1GoqX)(1d%1b}mVHg`v7BPFIH1V@HOghXi>;{`1z5wftygJvbS!y~~W zU&g+IiGt=LkfwaV+kM_P#FnB@zL(BZ;Vd4X-G;s-()Y~N_h>rqr~@;^g~(1AC~u$(v-BWY`CE+=#cbJ05eWRrGd`8h&=pRP9#Bd z3aQ^Wq^igB8ErXd0w6q)lFV3ol5yz48%0hlGqltwU_n4#O`QntN$vsQaaKUQU=6;z zuv33yo6@c&c1>hNtkPK35ddIvgk1ZL#X|sIWdrO80CGo1_8n>c{qw#2i(@(Y1&c6MK1w^mQQ$pCc0 z&!R#bk}tD|KW3_+CncwPyLuu_!f7B7@8;oP_-r#v7*F6 zcNOlZI%LLH0T63y$cGP96Z}1EB9Rk&f|I2DQbDW{Vt{q;zGy*~1W(ZI6+r>ytW#HJ zAtfFhkFHW7lS(U(!vm1NA}kaOWIio)okfiIQev9$_p06h?!pcgPO+&00O?a6>XI=M z0x-~0&`es&Xny?JBEdwB^8h4ZLF&go78Uja%9KT4YTVUGY@iDu(H17IWk@zntz>0N z{OLtz@Q4DC(JYN9LRE_~r`=MHr)%r_0uKcXLnR6YdMXBUXr*b~Bi$b9=%T^pK13Tp&$hDUAbIn>FikJ!em6`wQ*# zq5pGn@Se2s;rnqQIWsku)l~ppe^e;O23LOL{TV~^dvOmyEc}y5z+G{JrMpZy^`}QH zcxVNPrZ8Qys$`s#{jk|$y8zY;rS(E7X`u7K6kbTeN7(6!r{0a-CFVzp3w26MFd{?7 zV@qd5bqlrxiM`k>c}!E{5J5maL%N#ugm3|f*cS104%e=fI33%fnMZ-eg2h5b9{>}h zwz8Mb{1|{yd(NaxDc6;9ohu$SzL;X+7m>e>I{o*4@AvM5eEE0fIqLfkqoG3Lb5avD z&?>Fu^DO2K#XIXqOT?D)?S-WW`w=`yqx4wn4rnAq&8vLM(%rN18%tGEQtCn^wPCu# zw~kS(%9Ti-QTK{Kcn`_g#;rFjYN>Zn?n?g}XnzC3`h{6(` z-c>O*3uLIC&wgKEMnow9B93)HKi+9&P`HSg@!eheTXg61Ok0R>TGm7>lS95so7c*g%>_4ki{?OcruE`rhN7 zDu*XK1q0)>7y@`qEYI8DPnN5dm2Chz6zyRKN+R``O+r7pK$a$EGb(_Ec>*A1+*2p< z2QjIK40nl`NEi_FjZoL+0s=MnS0EoFCDGq>DyWpz*$7kFG zi)~u7N<2m%qL);`l6-l_qfR*32@e1u*G8_LG|;$j3YW6@5q5eK2OY)Zwhdny?uPqg z)(6mv+97j=677cjFb9T^In9{Nbv4xwh#$b0ila(TNg$+RjUV46w_k5?5}Pc{B4!a| z5mj&iK!{=0o;vZg%3*KQ`DDF%3otpgY+)qbF zTlvOeV=bM!e9*_*&EN6{ejMD#X>d8Hy?l7ni~O%N>j0kLxpl3xc{!|~ZBxy-pT~)D zxqIk@{!7ik-P`JW0vH4PM2f$Z4HvxRhxqUL{ulH|!WikGzbnEPR z6dr%6`5A!aPiktcXg@k{U!iMPQZv~4m3J*c-L)T6MQpEKDHSpI?3Dn(H{RpvUE}F1 z=dvigaJ!)hvhw}_fO)0F0rQ9JYP`&T$m+XQ?MLU8KpDSN5^-F(!X(1vZdtohOkwl6 zyF1UR~|ROp8K?U|DAmNKOYZXKDZqqxE$1MjsJ>&2f+Eg_W*o-e9wJ&-*rFx|684% z=dR4O5c6I<<-gYga zmOr_1E&ljlxCVf8AB;`ry|I0e+=mYx^J_Z|058>cW&`KV{k>+?x^U+vfUh>1F5H)Q zHk(oF)#lxtH2&E^Tc4GGnK2eJMkAHtgdP^sd%dw(o9%=ARd-nm_I_qGgV~=>OaRwu=HjaOG#fPTYFk=< zP`2dSa-VjG3;Bco*tLwQ zyRdcOk(IfIe~^r=Z7qOXTS~D#@J`0FG%tQg#%|ku;XCY!!pCNbcUQOkGB>gDhck~A zxPSg!YL|R=RPL{0u2{a=M*8M_Udc9B*2XdDkB65AySas5xw&G07?t|~X6=1SGTO{u zQQP#-mt`23`HhKM>8))?ARm_R04S6*Sv3D}@Q-fT>BqnE(H!ju)W@E zUcUXA@>~D$E1#79`wQp7(qyR3+>U5UFG$vjf@l5ff;=^+n{Goc%x3!^$Ima*C=^TR zX`4wu!*b?-9gK?SmAu^BnJfRk8@&AH;ALm4WcB;zf>){Epp$=<;@?OQIx95*tXz09 z^Rc=#H-h|$U3l;P+(TI%T&iv;s>!t>=g2jOWv}M03dt5ao{KS#v|A;ER}y; zn6$$9q5Ydu`RVdzDPH=^h_%AlIKRXweJAE&7#E(M3ubTL7+h!^P4BnoUU_!&wf~`8 zO27Y^jm#59VYK~tZ?h5Csz(_nHxx%=BNb)B*ftW`CVNeevbyygK^Vql-UiYS&eLIct5Y=w29}?T4M8*$lUD zrj+8WzE^hd9*sY;LS)<5Q%n-Ip3>T+eLYQ{z8j-Z@O zC?ORdfUG6fyYFA^d}OPFs4f6Y1{(JP2xTHkY_m`bKk+Rp!qGr922D`fZNUwxkBQxD+?Jw0lu0Fr01DjD>#sZlowoye0cN@+di46>{$ zKRSuo7 zdk{?BIF=Na9;J^sQ~E`wziRh$s%!9x6fOWBMLZ^X08+>k z)}iuF(i^EQTG-7}b(t3d$YX|+zaja%q0@gJo})eYaL+#kkkGzJ7MVx`(15wh+li0W zK-**vRqrngVj_OKGWTGglKTo8ui(w(ZIVAD&t$kW9t#DEh@m(DqzsRAs;g8}DF9VD zRh?Y;Lh&q3*g8-AeVW2{!~Jv`rf^in2f+DsU_wmu4OBUih&4b3zH=Po4HBJBLcOvt zQ-?;nwz}l~?yvqKHz&jB{@dkyyb{F0B)UhZb8#Bx?V__AJc^rX6u5nVd^e;q4G)|i zfWALHOHzl@4X5tu6F+9!VqWM2V0@CoCIB9~B94_fTA6uAfl>uHcL2mRJo%qUKOFsd z*iny>;R`9Tm{${2)%l!cG*Du$I-B9^cQ~2=3VfVC2IjtiuAO`cWJO!F+xY z12E9m_~A4|oO;3Pk0o|#&<4PR4hbkmF$<$Spu`}wMEtm7Z6rFKh12<0qvdp@SFOLY zMe`zHF#OR!xRIt)m1a0i0P~nn-d+SQ0yAjjDjU+9=uyP9J^gb^;64~TPUt5WND;)r zG#CiLImSnPv_IX{AA)IqBDN-gX|36Y8`gtuD1H!!K@>?IWVNn(2HT&>zWs((P@?U{ zzZmh3t~-i;>#F$k^ZB?6%%E2|&lfDyn;Y4w1Ebhp$WC?OC?5CX zX+l&=k{Q#D%MDF+a)Mdv4V#6wIx)or01rJD#z~G>|MgJA&N}&-vO#Xl0P%$OaK5#g$fQ$3xqxfAf;c50;x<>^iB}0 z0C3?;)hnhHO-@O!r9!3Qca4TFykz~Sa;h8^F{w&cG<8KxBwTq6XqUsHkP}}S129R% zcGAM!6RB$}<#zzQu%bo}04-e(YCeL2?>c8{M$X#BhtbjFsYe+OUKBW?KZl76x95*h zx$7J+4^HUkQ`4juoUzk|3=`TAacq+Cps$3Xh=TfVRgLDWfWmzM2`aaHCSe>btQs-I zbhIgh+ZJ(R4}hvgoQFIjJZPFfdNpZKV4XCb=6V(Y3q=Jb05eq0)-M7u(52J^i6o73 z?W=lN)`HU@d40FagC+|)09X_NP%xT7eFk+4krL+>?!A0_UyX?jIRL|p^~t6ZFG#@V z{g?#+Ja%}lR+XSM2B9SYB%m0$EaGX@dB{mrhU3--Rq6*M2w4~vy-RAmss*|gvRCDW zC)krFuGtj7{Xr>%shZX%y8sG^0Ti%m;+lzTD(&jC=W2SbNt_-qN{LT}f z&J|#3-m=wU>F#S;Sxo}Pnh$OKq{?hn;nII-Efo>IPbXE$RDG1U#{eMwwe<8(E~Psr zb7kb&cK34kFr5YzS4w0|nl(XwrP~dcd%Od79 z`o&aezli+Z;5o7^>x+4gSO_4U#vmL6PJdeR zKUvMX}b?&Qi9FXZln1L|&2e8E7Rsq9nd zkSGmBX#jX^q4%yflb<#2?4Kq}eUDczM6MepNh8w_6T7%viR!2W*bhzM3o=AM^&;zm zJ>w{Fa%r3-j-6s#QrC@>oyT+}+;2)gPh!6~zi|+L2^5;*16q=(Gr4zrvEha!6wNs%5DJh79dYQl98 zlJ+x|4H-R2XrF|OF&YR6Iu7@_f`*8f$QZ=+EBSmB4oIvZqPg}}jM_f*y-g;)Y=n-kd(%Ej;1poP!FGPr&AswLMuE13ftKpCfM^^Nvfy9-Ksi--obIE zKa(Bu`iC|y0VvpEN)4Y@7rwpdo|DrdrPMKiLroNXUDe0Te~6J`>0u^hT~$-my_9_f zFqe&=$aTI00NBs>^2C;rYo)jU=auu&8ne2kgn^_{7@RJ9iirO4x%Y1LUnTj`Yx*MP zle&8JhaVO0JD05nCF_tD4}wsgA;_R0K?uO$1K{BxqB4aJfbbGk;@Ju50btRX1`YrX z_pC|9$_9Wug$a@c5&VlOJAM)Q+i0i%BGN@C@ee3G&=3JIVm|)tq=o|mW0`-yVBM1~ zXPWLCyF2q~{e&QKKCREEbxqu3(Wh!!9?Oax0w|@-qN!`TqiepddYNn=fU0=2K|pw% zq@vR^<}jC@qK@LTIdbW@3N?$Wnac-B6EX zIw+IGYwxEenlIwg3>qin?A?8Z$3+&B3}!6t;g4`)UyqWxqG8V)H=U{OANp>RJUX@0 zGo3o>SxYI{N+EyR+c28o^X>GcxWA7cvTc8`3!r;^mrCxc*tV@x!$OrkaiwG+0FX=f zQ`$HW{R#WyA{zrx;)Rsr04S3%t&4)?X%7Hm14x5ngucAzic}FFgof5BLEt65@O#}WSq}_KiQ815a^^+Kg-;U zJZ>6;Pu9(1p3J7EyYgb56bnRE?y+8Tm5oIZE{6ch1!FFwN^PX-6V(_M@`o8s8mjN6 zgO~;hf%4g^PRd6@>rxShbc!uwt4gR1cI@jm9cXNXwVPDw1E>PFh_3Ngp!Y2j8BweO zYP<@-LQj#s0Y9A4KUL#!sl2Z#&687j@n&xfp-!y1F#qA zX_L?89|cbKv$1>R-d`)>h5li5TB~(pr|*utP7na#VyK3N`~WNl3r8#*m*+Qxw`cfi zRdH}!cy(XT8*aEinQmEJINRfP&zk^f2YrQkJRAeiU!3Z=OkhjE@M0O70HizCCUpQQ zbQUUaC+=^R`)(M>BwFh=M4%C`0Dw$LrPu=inj_&7i^f`@YyNQnGg36?Q1bD{GnNcJ z;qT^-7n;N?VgibiTcpNzzjae`NP(e}s za-c-hT{{g2G;)gGB?X5IC?^ivbEsFACu%sCW#d`qXW)Z;0blViD8Fnv9X!C4qIV+E zT|ik6)cS5!56b|QxTwZ{Uii6;3!q$_qCIHV;g{>XEw*bOv%$r7%QFuIzsK{{u2pAH zXR-+(mv=PNRkW^Z3>B@XrM70%nkK!16Z!~}O3Uz!PFLhuRVESjeKJk%PD$w~{YA^R z$;4peODju6bY_vR0yA9B#7=B?%{bD+zz8$8Ujm?Fo5>?fMmZj6QP``U=0c^Aj}hn^ zYQxrgWomcC>7#!_?>wl}$Dw1Gr@CN43~YmGMz}U>o_zN{S>q3bbtLgw*XN0y-tk5W zJP2n))jtOO3i~V^)OV}WYv=l1LnPcI{@Sd0U(Z#^dfqsJrs)UcwmY2u1QrVB@}LV~ z{LXZdmhKpUHrbN_0iZgY!fOZ=RbW)BChl*^LMQgr6G(%|B)tOww70jpJqLhbku|nR z(3kE=y3<8bL=Yh~;XM24p&B_X=;)(s(?cN&ld^%aBD_*gBXpSfxDhZiY=9Jr8zBU7 zi8ZVOVm#Z zKeiwY{d2gh68oCOIZN@${cUW60k|C0=CaexnWhp$qr3iTB2BV+O5!?6hDm9bpVXK2 zqQSgrr|nY&a{yUG1t6KLpfsQEmWxxRtf;N*4@we^i$EzoOlh4YXUT$Ngk{5NrnNDE z*}x09Q_XjE)tyaGtM*T4$?VNq<4iCVsXLJS$hTLr{em~LhIIfMc0@E5VK0q^wJW9P zE8|>lTNo{FG!64qhbm_CMO>PZ~OJ7=OtriXen6l5&?$>5b z;-#r?V5~+^V?yDo(INmE_5jSGWn2x#@l(zs7eKCkH3jRjGx>7eTXULEEHWyPI1??; z;(oA)zh3z}uhZ}0#5hK^pHJ&-r=TNRtW{*_129~)YgIj%AJm>+!g_@d0X&}RuNC#G zWJ+r5;4yV4JXnAS;8Mm&&+V_sfy##o-&p_dn-})Lk+*@=kp^JRI1u(>)^VhMl$k`r zymdi^CLJjPFY=0X0H8=u5vfvRq_iN5o=+zJv$FsSo_D~vX8Vx^O1I|z^4$NVY|ULj zw_U3ixL3?5LH5Ak%5Z;pbbHYrZntYyj&^Qu?r~jwtyblcFXI5mk=tBty@0tgbpb5q z4$76N`uOIibMUiSV;p$*q5k*%Nt(4!K8Z*q6%>`u!aP~b9pDTn2JYQ`e=ZY z%$&S_!IC*cxJ_Sm59_;C0FA8~8ukJ32(5b6apXK-8sFDXA}=rc05Wm!>`{ez z+gn8d&BYXe`fgQqHiKLLf$*CLcd}-*-q(52a@M9>Wj7tAapnP$Y#<$ z@A}0YUtkV^i?fAgJ6jFxl*X%)>B8^ddAVeF*X-$eRoR&oQbugJHGkFg&7BlS^J}%L z6{RK{hBn#ddp3Y^>=5b9wGB(1RJC|EAHJ5=EvZx`yHw2=M^Vx0fdzVMN2yi6z23Vx zjI~L9Z>y@t^X*!dg=A~z$$89dEvJ^s@1@5bBioqqJ!!R*F@Ggv7*^A)9X_sTDdH#d zuLQyB!6>qCRcD7U6}DeM1VB5SE_D)4P%Eft^=bt*4m-6aC9>KlU7B$>+>oX|JAZf+z|X`UfG}rf=RQe2 z^Nt}q3E$Z2LI6NUMF93{18X;xXaApcVb5Lpl$!_c@@*=by_~PfvE=?ZbZ>Y7a3`QR zsk|=^Q>yog8xVC|8hpHvGS%7a5jmWk`>7GDbIXkvw zel~-yJ9&F}y0Z=uG7{yYwWy^@ zo$~w!fXeI#^caG*JilRbFJW?c@5~;*CFBWm!yab<-XBlSo<*TvW%dIA#qD0w4-cA8 zn*RpC>dZU2A(C^I7a9Q6ct!Zl^8ALu{M!*!;!j6nZ4e*lx*G&#;&=cm=}iEYnHCS4 zD>EAx_OP(r>Ad7^Ud5707Kg6|Vr zdLsL7u3JB8neWzF&?AFr)jBZH9p#4rj!c%d@8OX+xn7zPUiVMAQykMOn)*Ie0Owk} zP`u>xT_R%|Zn$BbKHBKWIDK@|k8Zhrljn5pdV1T)?@hU1X!EA{c=Y5uzxifZoIOZo z#~Zva6l}Pm!QIuly@KLA_LbfFwC>rO#{A-T&vuKs%El6PE0ux1YLru*QPdpSZL3OePke{-A|e;e~&esPJ7VTKURCj$oI z@0RB~@KE$FX_L=yDT81dWE+PLj`Pw~ z#WAh^f!oa+Bz*Hu;g4UTY8$|scBqF77J^r;K^_I51Hi;){8XXBh#EPH-%Z@1xo&-w z!y@O2yMslemH9h~tLOr{I^beX>I2{&FXTh9FUFyp5UwV2JD@5J+LK}19RfHTC_9m# z2lwN9>g2m^001BWNklHLdTUfZV4zBilmG}Tjh!0#>aD$j3_upG0#Eye)cFt{^l z&RUv%zA?VFTu3uFh&Fa60P4rw$j~&iYM~h3F(4C zJynYg5{pj}tft9$0#SnBK$}+z?SQV6(HOi;d>J3di7f|;FG-l!xp(s=lO6X=q5{9( z0uUC;cIc{dRCo4S>?F@KoEXE9^mfT;9sm;3l5raV23}>nf$mF)C0s`6Dqz9yk})Ey zZa7_#G1b;4ufN52zXbqy@8ack5}Af~|1YhS9P(LAuwCwoRLf9i=N(O7t(`00S}1?) z?|%y@?$7=BH`ZY6^2S^Mu67yhe-?8Jzv{$hiV|CY)ZbZOYx;||8OzR zoFjD)n2V>5Y-`+Vs-V6gu{j%}^?Kt)T*{&6*_03^IF49q>267P0I0ZAT-1~3eo~rn zIduc68^&PXK>Vd#Zk8JP0lutm+>XyjktgW98aOTy1is==BRz(P3wyxxH$pE3K!zrv zr8xEwH?>Lqb^iz;cnI9}U#I+?x6>b#f}zi5kd6ZB?iyj41)XGv zn`>Vcep3m202i|O(l`Gl0JC^=esx=u?qfe?Z@lz>zw-3Qm#TT1-doqxiy#cWT(_QU zUsaqt09v;hfD9ijKJ(uIF!P@aL60HG1WHvQ9RJ*k2jeFi0z$iR@mNaF&Vzs!4 z414tIoo!D)=<6SRcVXu#VT&NJq6wGpWPyVF`iy&tqxoug_MG8s!H7juCSJpX5th>! zC-Eij4|X?m01EEwi}9)f*w__X@4neMi&0n3q)uX7XJwX*T$~Q|Y@qB!eja?j%R&9i zU;Ves3q1h&Tt8oID*XPU-}-Kq9mXWg=fr7T^zjA$QY9!C3_II5>_Kv_#;8+2>x%oWCAV-cK z8K&+6(DJ*5+-cKdiocSXu1>pdyYHW-Z!KSVPuN{2b^%-tYWvP~tr~~UIJ&=X_M{fK36(fqsp%71C6 zhpHaGwth{)o9Tt4GSTH%lMqvFuXOM(ssh+DtbL>GRUoB$@*Ng8mu%NL7Zj%1$op8+QNp>kaLP zpAD?K1!U0`L8y}mKqe0JM^^y!W>+icc5h=t^JWNL1pwcV*;alK_tKk6yr@NG8aB%_ z?{&OOxuYv2cZqW=ARX1{1NRifp0(3os-)_}!=3xpK0a{*Nl%~Wvw^Y`d9wAB=*cv*E z3kM4;e{%Q6+RV&Be<0rbEOTG~<)ZGpbf-7eF$O@~lD*bDo6JE6d9LkB0oU> z__|wclum`xCjiI@KnAS{_5yDGq>|>?zlPvx?Dol0e1e@W1F(S)w`JSo#+NA#KwAJn z`z-)ygJeCC-Ls?vfDvs~*$x0qfUO=WrZJoVKzAZO1sYqJY~e6n8F#Ul17pbLXg44u zV7v){^og(lK=J$j>kWSGJOKE>K|uN==pb~Re{wB2Urv7D3VR>@dEyOZK(lH9fD}K= z`vI`DoV7S%3-gBn(BS}*y;mYnl+<(2{%?ov|Mbq?5nyy*c&gL+oxOEf)!()~ylJEb zq)U;I?oL6FMnph5lFgtPl^AT6V>>e!^lBrD5G z;lwEKV%FeGCLzA$=s5jFHK?yxi;VBQKGpu zJBhQEcQW&Gu)6$Ie|txNbbwz4@V2%dqa%weTacDMR*jQ^YNAQ@%eolClHl$3>&g@j zQF(c9SoMOeNNE-gHw$0In=~(2Hgw=jF4A5)V*N!8`r`JdoG&E4N7qsuZUN8w_c=4^ z58<)*;2z3&C+sGGzq#6l8_+=lS)Epw?mP+^tCVRJT7YCIeG6v?J&48hWgB^Kx1E#j zk81q62(Dif_4-a#??QsvgckX5k$lOY+c`nZet~K5UfWAP_iR@1Eq+@4(IkAYoQ5cD zvfF(r3&Yu}1&bh-HMXR`LGEVtNE!2l3>VWW@El_kI zEm%g7r%V&oZz>{7reg6#jWFWZP0cP=rg{Y-BMu(h7WBpA>4PEx*o%3rjp+JorP!K- z8A5cUm7=2jzs{t}iR*YA>WVdACYf&z$qk<8?YOecm7=AX)F;1iA9#2HhbB>BF~*>b zL{NqRsp3P7Mwfi#`d;x{eF8}f>=R6J_J6ArQIY^ZQd*= z2rZ9-`z7yOZ1w} z5J<*F6Ymb6r2JrM7aVN2_|@~hNkt41|7LBU-U&k5g0Y3BfkohJvSa%gx;gULcF$2G zNm0m-Cl%q%CM1z@kMXPs_WV{58 zS?a|3_@JKGv9-gG8cIe@4?G_(JBrdKQMC%s#n6UJnJre9_j_PW0(bpjWJ<81aXY<*+(t)VILC6nF0n01Zurf+VHDA7R}Bd z!raZnkrXhBDv$8U`fY0_IF&H1P87AOH}#KxjDMiV&d(59dx*LegJ^%3nEWjXU84iB ztrx~g7Ri?6P{`kWG1$}FtC{Y^iAK*hV_&xlvWvg6t))bE7e;vpH*!`QvR#bGYUwZ1T+_uL z#@cx)m6$UCftWbRwAcFK+Jq8AmebS_#7tFlRFfS{&dOnAHop5%|0Pn15E~O>#p<`OROCVw-{^c$){z~hg3ghTG~rrII-(ch zC|b7R50GnZ&a1yYOqzPln8OmXw*B?RJOaeu!H~giamgYaugw>l)rkGUvmY*Oy-Ef_ z3YS9n2PGYEj7Q4PVGNs>Sdb4z?62t$E(OK7ejEHkV!DmtJy`W+#i5Q~V|$A6P8`{9 zebI}^Kci$D(y}7@Amf;*>5-xGIR?+|(fILK-*rmzTWZLGRF=3YPf7~nH;T=J#0)t^ zeV#!0i_bRa6RbQ}mmgaNz-0AQAp!jM3C#rnO|J9a1h*~Jsf{SYuo&m{qypm?oG% zTzpTkQR6<-gUwsfcxO$R!D(;)1Be0oAjCJp77HTAi}4HmX?_1i5iwrhT==?0zpR$m zaBZfoUW+b$$s}HG?l_J?7n>Es;s{S1h#cW&@g%lctnL_^gX+dL`+)oo;EAQJ+a_J;TdQTol!iDdf+QTkM} zK37NK;(Fd5WxL756$NfU1Py`++r!ZPxaxFcZ^3fyi2@NL!kohv=o)R`y$aLMAcS;~ zf8dWLWxf7JjW#F(ci?k(bq8z1V`g{QUPt+ub+x7?`V%{13&A&+;H>bXdVk`224+BY zCEUWHEIBmh=Q{b@P3+;rF$ZcNc6XFMEhDO@N6sCyF@BOVHug~E5OMLyZafG9)(a1j zYd&6OMw+DSM4$ZSOYujjOxNjosjQw>!9j}a->RbPZ>(T$ zA{w;_5^m<)u{Z6ua={R(?4nQ#CrYZ{y?mJMaWJI=MQVt72|?Wv*=9fLlX;{ossueP z8Eh16n148LS^>pj8a{TLQ+Vg%ne`m{o}z}h$KvjlD*A_@a!3oO-y(?2`BKkm1mE8& z4_1%UzY`RQqQbWWIV~H$sS}z+!m;0V>tbc7FBd7)5;XA!^#RI5q_0 zRy=N#5&Gp>oNsf2HPz4jHO5Bik*%W(_Uim1rizn;LZ8vDQ*ZR=aeNtGW=}>Bv_rbqYK^cPF zin!?GpQr9N_&GxsF`q|2&ERi1WWTGi@M>f`5hK0Xn=U_I&Gj&3jBR0s2xLPHHxH8J znjdQ^4AAFOlEj}oGU<8mEq?~4G1%MDph=WmR+gUI;%*~%0!f~}-?La~4RI`uoO&!2 zM{c%F94fT8x3aG0Qd9Q(lN!W5D_$^kTKR8jfFAH2A{FNIDvTuMWrgFe71$F0E3 z@TjqMac&j%^%Eh2PlM|y^M=wrKix$MPCyViKp~3!aETQyRP(K;IRd*$$(QYeO#@>n z?3UMOzcy9Da0tm={52OwUn?udQ;xeZ6^Am#C>V+()5&Vo;KfFkSav&V0mzjbM zd+X6DhOD?t-_A;!t{&f+$R;%_U5PgN*v9Wn5e^m{6=FW9Asn087w(Nq3itL(I8Q@s z%y`z3fx5SaH}~tqf^7L>QU7`?!`c#e9o08?>wK9>R>Wd83T!E1DnzP|>!5^Nbo+HY zn}b$S1H6MP%8p5HtIYXTh%m}hR1#*tQCb4;@jG>Pl=N#EhUwb9Dby63>`9xxm&K5 z34DC~0)~->P}xP1_wqY$a<(RGlBKeoZdibOR_>>2+gl9}e>O>*txsqYCtX+QnYo|W zt?;Eb6qr@8SsP207>-7xFqlso^sX`Fp0GCB zDKi{RkUFWn%4mw96nR34-juujAbo$>8V9>FIj4)gHf@3S6m=wJCWqSLY@VclSet-uQdFNzc+7v6g+KkKXf~tmW!uM4 z?6Dp}yE*wigq2OxCh2yejMe|8xh(W}Ca$BpnfGJ%lhO8ko3L3=6u_m-#?DOvNy)CK8o0kNW(sRvb-H( zgbjQD%w+4BND1+AlpBv4$%RTx?mYdq6S+;JoyL#T=!^5gN4(d4IK+$}ucsziA6;xk z%&ELS@rQNJvg{s5Ul3P}?>}^dxf!?{gq=83aoyoF!bXsTA}^7u&zxmuRxfMf-EgK| z@0u#C#7&{=Zj_?Bn#GGHcmF^kYOA@F#PN}X zW9Z52Zbu{$?J5R|^OLOHFE-qE(QXSZJe9KORc*-PKX$kq?YIBbD09*H@wxXJLzCkw zz~J&>>L*H`ZqNO_4eiG0{$7?mHHKqOix*Aqudj-$SBiPfbG|-o9M%$Gz>g7nO8ovJ zoswrkdqRKIgkyB`4pRdC3d0NgH`ONRh0o%|KA{oC2i2z@Yyt>21`99K`%i3HJhmg+ zN3`bc9NbjAUxv(6M>E+y_v3$Q9P6s+-FcARAIS}Nq@S~CJdH0dw(;?~Yx26PvYIh| zohiqN78`M;A6)OUKbXX6(y)Ll#iD(n%|Ek6Ke`<4;>Z=e!p}!GQpLJ;}_Mj-Q)+X@-;r4+|fq_ z1hLz?JXX_^dN7VdI|0%zclUERxTrFuS;9YuCEFQyCyP8o##@m1$wdAFCtzlEZEYBP z#7@h&`*bsoV*p*i?Q0s+*@dK@sc35s?DdZw95OzZ@~P`Nmj#_V$GrOb8A~258*_q) zz7goiYHqqW11szP-X0b@x}1^{T{34|0foAimDSs~ZMGx;uZ2dFKKSR%P(%4o|52#lK&Mi0aLMLR=Zg4%s_Bod@HvWA)Br0n6 zK7l+TCo|`bo7+VYCaKEn*MZLs%*^sm-&dBqYhS_khx+?xYwV2l^)sZhUeSBo#mdjr z)YaExB1^n`w>p-uzP-JT01y9!n%d`blTJ@>;o^8*2?}+mdcAva(rhtk?CdGM)vH0t z7&D=xqm%A+HU&mm zYB!;@0c4Zm8dF{Gi!~@zIp!rkA>sPc5}Y=1LUD2N!1gBP#0MmF>$y54$k^CezFJ|O z_l*E#w$907sV(qCLsL^zg+YNqP*=g(7?CIkZq?(%LHCh1=I{!1Fmb?pZ{Bb#--VsohMKm26H~Q4yB$dQF!^?kKsC zE1R$|c@0NV1Szlg>6rS(#YJFXU{X>N8{W#tu&{a$*d7!LZS;VpfoJ~x`}fk)(%|6W z_wV%XOnN>yryn0NI`2#_t*@Jur@j^Lqacs`qU3_q{~HD7TUk+o|LFb$2T8xnZ%)?L z)pc*2pO{cDp73yUV~?LypF--c)NlPzQBe^T6cihayRC(0JX}Lm>j5j0&2e7}BwSuu zDeN;jt@&ksN_l$_>~{Q{n~Mva^lf@`T9w^{z&3DeA3uIfOe9iL8v6eIyU*=$y2JY5 z+M(--#`Mfg0*ejTz7f?(b z9I&leB+lD)c6Z;cP~QFg`4i%cgii43^XJCvqrPyX2k7WmOTmnq=C|-k5vnc1f4!r0qeM1XWBqpWPil1E)lZO~ELn9|9~ zNr)m!BPQnAlKIwcgJsCyqWoKB1Cj-Z$?0?oc=(d^Rw(c87d!%Lj^Q$wDRV zfAN#$cmtZg_Z$T3 z>Dd`bEThdNq*r4S6>+h#i12W36qtrf;PLJs8*UM#X_Df>qqV*Z&s&hmF0Za~OD$Li z3bm>+klXj>>ftg^*- zpmBP5cyY7ugAD;G6D{yL69FSW+LL54UmcAN0U~*LsUd4NM`zBJOo!fNYih9!Zd$Mj z%$*1yAE=>qAh~XBnL~)k$Ue#D_*~5_WUFj0E@p7LbCdq(h3&oSA|QU_iD0@EX;ouE zKtU0`yFPvqM&Rb=wt0F92BomMd7-bbkGL^8qT~U(4vN1_%f=LD(4e)kkmIP`9C{)9 ztNbx?a&oDx(#FQr*;5gl`38_gBf`Ty!M;q#zXwu+bO+ET0|JZuTc7Hm>>x{+@N!ZJ zbT2r3N=Psnei=JK8yBh#wYt*g#|qg4bzM13GgXx zOwM5o>J(@TcVbIK3{RWW-`Fwkk_d+V$!|;0Dh6Q@oOiCP`_+?`2J5&{Bkj^p zsj$+xYwlq=NjQaEk#?>9<%wCL8e>A==;$bzkcH`Oob#Uw^78oIj@|*x=j!ST;EAg0 zCnrW$*5VHz;7wq&NCxGf6mSlt^&mQ(u;&z2yWxZV=Ek$+?D?%5rK^{x+qJc|f-ZaD z+&Qv2PXc>`!zZ?;D=k=?ukVxoBQzwl_hzx5K2>;L9GPu3hP4_$%R<;|L4v;VWJRUs z+>1;c(?8KDd*2sdQo;tX6dqpI#J04wG+ahThJJS>ISLlp#`d<6WhFoi0Bu3olH}9U z(&CRYGc)tmi)m?S;KX!w=WlPWAr?tOPQ43XfG8zHXi4GZB$_`w4j|DJhU~ot>Q(6e32u z+44)UY7lt08JY-99Q_gcYXl9|{D+N{SEEZIp}39*4UDil#IT^ME2*DX6w_(sFARJw1D3BGrXSK zn43sAUiHlz04eC{=;&x@0=rk<_R8S={3`!iTf1P)96(TI`U=mCydtotIxH$Yyh4?l zboJ2HxO^(fKT#o^&^CAx+~u}SKU}f5+dob@+1~`Pg3U}7CPYYB*vQau9KZ$~9Ej=$ zkRF_`&+Sqb3CYO5#K%kB-rlUS`n;mYk5`({4n4ga<^4hY!R}c(@s%rFwb^^h4G;1A z>hC)A@c%>xuMZpXm75aagD-SV;o7po&DF$6DK@A8a9{ZuLw$2|wJ=9_88yu^^;SL7gU1oi&y}c|JUj%1gf85m(q(03stj*{zSQcEQeIXD@h$yKYR(IV zwfzLZw5_czp6RXOv&`UGkbl(GtDUyTu|=;Bx*|xKz$Kp4=qyhqf4^M)!SOb$4;N(wmh^n)>rmObo#Kn$u{ zh!D%CX2DxM*%EAuRU2}Jdiw^s|09aZU+n=5+!^#38t*LC+n)GyNt{O z$j(A@I@eUAM|E&;uvdKwvqlp+4eKXa8u9=DDGyzRlH}tP6KjkINaVb>i(i8m`!ii0 z=mcPYvQX6wt=h+Vd3j*CK)HXC;1K;hWnrEve!?$m=k24i|em)7RTFrE1R4ag*a=AAG>U05SACN?5KzdH*wkXnT1}r&h zf1%mubZd;CpFfO%`4UhfTwGj0Q~=6KPfp(2-PKW1DRVzHuMg(Z;`6$6G&VK{kS#ZN zeL~Nt(fxGm=*T`tCFzesAiq`eppSRK6AuUHU@N~EBwAoO5IO9?dxyTZwidrzL+AKz z@_uHiNzhRUuiisnPEH5#2F1n2X=rHB0zb@Sr5b<1CMVrE7;)NgOTETv)TJaRzoF-q zs`oWT1KDucSHy)SA#6#JF1~KuXBI-V-O&A#&0d>T6?XxGcE^@~Hd$Ns=tl)kr&FAez0xohwJok43F!S#IJ|!{n zMF@G~$Nj)f211^a!$u~JlEv~Nk@Kqam1_X3Ca;G3OA*n-}x7(K9xO=@~oi@V`r z5b(EWb4<1qakSJ|Ri6row9`!&bcsBy-^lyWMEHjy!pI)~4<`Lr`pEWZp%44h{@OE8 z30}@{nhbv7vujf*8U=-*D~4Jcu#5Hnq`1^n=D41X*k@+Ei%T>t-{+_@_5&Zr^_+lA zqTA?RSzSG0$;0?rR^agEKwXXtWJl|ScR&Q_bn|!r5YHG2RAoBtY zGw*qEp&Qk?^JdBW)f{2Kom6guI-NIrUXhNyOloOI!!>g;S9UUEj^N|TU zTY`uQr8K7tFXm)lMa9J?%r9JV-cLEj2HUz#z=>oQ= zprBxFU8K!IN2oj+io*08+~KUB4B`7rX8qBRG+^ldk;w-)qWTPL$yd&HXToWiDoJ_b ziGEq*+^1u}=m07My9yWXhm^_)#UI;^sO=@W@*Kwm9Wp0YJd)GygxN25gaVw@Cqv{y z#mV_PYj|g}Gz;idvvN`iCBxRxzn|>Ee2D@$e=J-nMJB>DC)qg^KSY!+EiW7wFWh6{_W{GJt6c#4q^Q z%k0nTC;)^&7N+z=BVW-Ht1iPnX{5T3(f=2Cex?mu<~H5-AU?5A-0$?OLtwd=7(mIe zsMM_02HBPZkSSdSi=HmmCs-~BUaZbWNl|$6QdcLEWd2zADbV32LX@DvKu^}1Povt+ z-om`RyliYGb#$0h+dFcqh9Ln1j$QnQ!|s)pGsC!#Sjkkm277sAR*m6QUYe=hZ`dlNufP;~%w zeX`G$wZ8@=4uE7e3wc1Q9RB)ZHEO1%MGS=Kt5?IMHVsSRkpn6x?v1iJva+(csE8*v z%(0l2nhLTZAwWW8)~X7R>%xuK=w}9~KJHSN$!W6Ux$m<95b`2`FlA;w4wXE*+%6t+ zMA0E!_TaZ&^aYAjozpgu_sEIvZ$H{Tcms=1>R&){Oes9`RK^Rky$6@NUwX4PLTAaQ zVaqMhT9AzvapvF>zW?|k;JBd-u$Qdt$4bIB5~&_!t5`b;C~VnsXB@AVzXFU#rb7d1 z%e1AKZi+mu{T%vXqtJ0br+Sh0Gcc!{5`O*q)lKO$tLakWeRJvS3+X;Hk2gG4=mShw zSV*Yrc=c^>;R~`MVb~YtjZaEOMM_|X7#L)(p%K_8kI=e1l6R@?1t~1QJu(+sK$j^` zPEH0=8J_Skfbk#)^Ud9)Id6!SK1fx|rxj87Or{nwP#3;6nAW$ zZ2_HG%r)D<4vA+k1Y9JJPU-CABp^6AHzx-Sk^$R;4UbY%p0AGu4rig$l0(Wo zKq>>Wm1F3fjy)g=%wl4`se;RYkt}J>mrX_Es}ds zS&~;g{pa}jkDZ2Hxb=2_9j;OKFKo)U)$>nCMwo^u3W-^asv0iu z3WY~BaSYFoZbt;g(C}l6J}wk?6e7<=0?%*=oko0}vhqg6y71J3$qA?<%c1ad9}4}nsuA@C11S$s*)O&=3|I7 zXG?(hX!`n=WY^Sa3S}z4jAZ=h_kze<%~Sft3rO>~x3@7dF?gRQgY(Y;N%>&p z!7XR75?SNCI}LPFCjlK>TU$-d=?1rBK*Im=Uad{7zM9al>|~KE_+0eq;iCn!t)3vULx;j6Gku$#oB)tw3Rhyk{4>)_@zW{)_>4#qaPNP>#NLRgB26 z3={g4>%9NqM-2$&gMi**{UW!0aOKGg)Dn}XN$z6}fE>j1tLd!^Y z5|gV@QBm0GLnsg+B3t{~>W|;g+E0R#E{j2vro5;`@+XZ#PhOukz5_UqizHRC+H#7c z8k0Qb8u0#}(C=4}``liv zzf+*A^Hm=4$=J_}3%$5kze{a3Hzn}gKW|-Ih?Xw_sWaMZqK@UjW=beOlWW|vR z%QXMOOL9xgSrAjMfo=nmV=}LeF2Hpl3(nXy@3s^;fSJIj=(1?dX3oo&2vD4Ki}eB! zlNFE}#&_}Iby$=X_&$}`J zBi7SA6zLs%%^(RM(lRZ}ioSjO4dmj+#zruzpvLOd*k&S>V|lUu!T9_9&;O>O{|9z5 zK+}^K6~E9m2a%GAgc`-gROH(gD-D-r!fV;jT%m?4fwDuU;7ER7#6PW^^!Cb@Psv~D z9vvU|yys!~Y!1LDNEkK^hf+pH)c11n$;k<9!AF#txiCAs<6h*#LGJ(wW6j>}?OWnL z0Z@7oh=l8hO}T)OOHWU)0gyT%z(m{hlz(YA_AOAjmi7|)N~{z$roUwoNCZt3vef=7 zeKYIQakSw~K!#_UeSBnOdZa16R8>{EgoRxkfanJlj8p+9^UjZtR;8sD`W6@UL8l{4 z*yBfjJ{(X@<#JTk*4HUP_llA6kN&2?grkz@sA3eF11`1)0Wx4n?fbv@EFMa$CSwOt zU3hr-j~_pFc6LAt-Q1{~S@|>V7eR3ofNC7k1N|*1r?yk-{#QdnP>-5h^=-YoA13_C zV~F54O!ZGFka&I*kQU+?Z7{9d_8T8_Ig0s_fJ68ZM>w4*>G@&L?geqOhHY6pvulnub^dV@GuM{-{IhW{)U zrR>#EKl_aR&Sx+(0DX%zD?gq!hIB52ngZ_&3OvwtL1z_Ie;{wg(#ZX%xUqBpz5uIB zOGzR9yBq?V5K&0jho_6ISiGauxsy_X>fBeHKm6n%F_a^GQRK&2|cJYAi6;7+(d=?D7m!{z|RT&*h0q#0SBE{WoaeAOS>U>{I1u{$0hIDvLE zB?(FF%csT7w*rt87;Iu}jGWK5rMuf?uFlDtk)>)w8fZ9lr;Qq0C1z{1@?XGMz~qJO zZFkq!UKmr7f?EK^vE)Rq<}qko>4A>$a%Y&H=i!Skv^0^u-g~fI!#%tHnhoY6%vsT0fe=Q%Qy|uMB%F2sk zE*%BP0IEZ+X*7vDx?s;yKF$wTU|avrT&LCvuIk`p+eY-c98K3yyD2$lOos{%LxUTR>ou$700(v zhlhvnA9=KwTtDu)Bl#fRyb0)(jL5NCO_sEFcdy^~_`siVKHkht2XY1|)a>;1-?OtN zGnK!HcnNTFa&mKzjgB5~7dHd4$_+$G2cLpI9PFiZpo2`%Rd$(Xn(Mt{$H*{{J9J>pjqL0fZ<| zC68$P-)cT0#kYLWHkFmtZ1TJ~adtD#KzI=x7*wX4$m{OnE zD&1pJOE8z`0vFY`9t$Y11a^|tMc!jLYv1hbY@izh!8Zmea2RHPdwl`g-i2x@Nl85$ z$YL6QX%reBnX&mYVV!UYELtNvZHRO5{=0Ep$L@&SPVNg+Dlj;7GRyo|D)URlTpouVm29S=* zFV59pJgXnN*UnbBDu1bZm`2SXE%Uqg4I=s8lbL5u3 z(48cr<3lD7_|N)1T(}{YSsKvN6>sD<_rZzV!Ut$R4(!Ds5V|3$H9B3ydQ^IQ_m&(9B}0#Jg1TWE0OhnzUd(*MF6H`N?7 zLIj`i@>YXh z>iGn)=Oh50eh4U+mzVlvK9Qsxc^s%-tS$HfeF?0&xw$}^kE+h#tg>IUU*Bp<2#vto zgYdfpRgPqcT)YGepG0m_0*e_18JU5B0r-jLT~{!t(^rB@ULj}hBPD)-{%Lc#EVjT= zQl^7u2Ya1!H+rf>ykNpvqrCYSTa!uL@ zmu`jvqwTCcFS$zu-a;SnuYo31-s@a|wR6?-fpP`3ZZ^ODoF6~708#)N`rg6rhAVK8 z-S?yC=jWei+BUS)2q<@uAI>@q)s~mOlf@a5l9WV*?9A0C;MeVbP-IIP0KIcOdVeSk zUS2#TA_DZnvZEUry8Zg*3Jx1_RsxLbx6Gm9|HflU-ey>hlL#mSL}1DU=Uhdm(mguL zNlR;db9oBdpySQ9M4(%Dz@X~vuP=nCzOWn!uihRhz@21gPC!w?m8db?-P;ZR700p#R zYcgJZ%wa#nEQ~VNMKU1ibL6pxV6l?QVaNfpG1`=HZ}ixJ1KA5WZB{OHLwJ6^<50*D zSRy)!?tX1P30m&@JSjjm03D0{_4V$uu>xZtx2*ZZS}l+SdAS(ZQV6% zE(>0~0s;bjd^NST{2Uyy9*SH7%8nYQJLr7fHJ%st01&vk*5q_7qbIj8iw7snN6yt$ z><8v-1;|ZexCyZaa95NDC@@-!U9nrr(kf&g&hqih0HtHH+GaruxL|-O?6Zf3fU=)q zF-Ed^3qP`zi@^%|*Y>_H+;&_Lm49kV%41IV$3&$(@g+9g`I$FFjy>7WIySm8t0gMn zz*8dow{9Y&Jg|iCR3UyR{jcYU-K^OQJlA#i@BmC4i&6vwVqRI7mDO&_rMUQyj)D#R z*n{qj;QsHw^gF;XHI41uM<_H?0z@b$K4ZhL8qJMtvvT*!BJFI{x)&5!W$e zqXarhXd5X$qjR=52L#w26_I)MR7ck-4a5JY?N+xo^Xlk%o+`^<%?RoraiFYBRc}ur z39o`YbhHlU>s?NQ?vpWEw)p5#`0zqLslPgPcZ*Y6z>oV)j_CDITKE6yys1;MS-T|% zaXCQ!zP7`wZfGEXnfW()I8xneq`;__oG0VI>bV_RW`S(36%_V`Y|Z1be1XCuQ3Z=p zLy3mOgdNHPdaHo2>CI1|I!BW6k1Q;Rf(8fRLrL;dHhw%d_k$M>+BBBN3mrCo$5$fl z&?=g`fFJ-Bdfvp>6agY+ipPgs2n+yWn2zKw2aS-=6cyh8)|LpLt+ht5n%B<)F&zGp zl&9Qgq`IZ{#u;-X&|}0%d3vvnR{ibrCkNJweHwu8L5h141E72zQk5RRqhS~dZP5VU#A=_r+t}FH zD3MFR{!9gecpIUaMx0j~O-NsI|Jj{ETHF!zkL;|qpvz@eRMzp#!d*pW1f)K-0u6ER zcPEV)SXlA#@q1I{xpj5PcGLt`A2{7N%78Bquzx_=M0%M?f%BN*GZ69b7F+$n_gFh= z+ibe-8r#yq6v<685J@fwigI^-;jrO+v}{f`mJU4RpCPIu`2>a`xd2aj)PUAv|7{&2 z@W%pwQmV^5@D)(`>~G?d7`jXWwK|y6+jXT26QUoN&twPS52+PA%r=Cjo!pZ(eA6I{ zQs(9T2jW{-SC_(T!(koFW5f1Ck#FAvhyf{6-M^V_@d~WuRz7P;%;-$6GTi5#86a>1 zs0N(Fxr(Wp)z+z+g}T62CMMQ`*pvWlow0;_Cr~6=nLN8rt^@5T7V(+* z4cv_9(la`slx}EO{x{tc0FEj4OP>wTV`SW8<2I?3MOyiSxx^M+1jt{C_2{o*vj9b+ z=}hqi=nOzs)`_lo{Y?*^h|#5y{;=PmDv6ngE;$h{gr)R<4^2|7XHTO+hRFT_5EYIN zb(wfU&ZbZru5f2U7rBPR)z^4oK^q+o5^u1`5qU;PrKIDsJXrw=GF#}gC~lNGf$%gX zgoK0~da^?PEy=Hk7uE3*{3<$q}i{S*VD*TmR3C^%S2 z`T6MN|{j+UPGU<+$G6rMmVgGobY3@-Du1X9{oJB|KUGhP+Q3acIYBZ7JeMNT+fC ziKl7S3uZyFFu_SXOMEcX;R#ORi37g@1KK=oWYbw| zLk5~c_%tHocLe8Y5Luva@R}Z<^H=Mp?_ZGxL0Yvcdn zK|p&-{;;S2e-v7^j@~lRss%32AF2#0-#t;Uyx8}=M!W!rxH11Hc{f6vvIkd4Q94?N zs#_V}j|x~#fszIE4UlB6fhn{?v#{qKd_H;|op*B4X)Cu|YC=M;GJVtC?G-TG0YMe` zNx-WIhI+8j0!V8S)h_OOz(H`oKmgn=AnCwE1mah}%;*9f@a-FIrt=j#UyqRR!H=Fu8h)th!#H0T~8J5~L&~m+5yG>6TB1x?CjXjES_SB>k((AaVM= z-ySux@_Zz9*R#~TI6ptQ@Pq)9&ub3PMLU5H^6>mY(_qDklvEdRFu^SXJsq6?7vU#~xRW{V)aK zeP;SB`#C+`=U}lF_@EzDSt3y6QvZCas-~u1maS%uW-sejwpnU=? zGQg7X%nCXkd^odkt45DcOG^t1C%hQ&%o`ehRi?k*wej)TfIjYO9IdFTBKY%xhw#^= ze2R{a1`&nJ3f$s1l6yMY)e^Aa-yn#9>;h~&5a{IO%fajE@Gvp8ivE6i9$`f1CTdNN z_L0~Q)AvalY%li1a+bS{!6@YYtD!5TQU4KG43HF%B^_lw-@wnpQk0pwWaD#13IR@P z@JSi?oenxUVXRG57In(>JG;A;uc9?qwzswP^eFN0j@z+CHP8T+O-@X7J>7a^pG`(W zA}b3FsT}k3n_e#@CF$`$0!v1iIv|t*D5FoBK4$ zBrOks5ujRG)D6_2GSLJQ0N^R7D__Ee*VFGXBy1AXUt$J|s?*_eCs^B&VxlI`l=v0) zBB5l8Fy){8W8qFjtzboiWoa$q^Gs!&_U|t`4EVc&Q6@bmMjXutR=?l{6gTkV8l0Ze z(7Y&Ai;9SN3zh;pJ6kU_3G?xt15^s{3t(S7u(HL+gGY%~&44NHZ^N4JZkF1E(b3UC zt6l4Ev4^W?wG81dy$MhO?(8h$_-)lTQ>tnyFv>F=K+n za^H^N`{!jE2X%xUmB3y32o(|NNnm*rB`K*^jqT$UT0GVK!a^iSrZR9)TY69r1&6D2iAJ90QN2I;!>@_WPAZR4JA}N(rVOI zvUaQf1AS?oQwiXCor2e~gbtcm7aZ8xMHh6WV8HJS;8y z!PgH0O-)T8rGef_907a_XmEh)T-zMfWyv%1>DTx;4>vcMk;9_*d2ES)xpy%WW!WCA z4gl84?RImKbWtH8%R-sQ&6O;~#U)@ZRert&NCG^t0S{N#Xsse}S}Oq@O3LDiZ7ogB zpFITIl!u4{%gbQpOAVP2#gNp8hd4O4>x1M;^2Xic)6>(CMKHx6;0PapDa%*@&C+b5 zM#^W^WM0VA#Og2fsnF+N^3N9nFX3;x(Z5*u|J5$_zpl2q^(kg_PvH;mGKPSU%nL=y JqUZV_{y)7(jK%-} literal 0 HcmV?d00001 From 8c05462d6ee39ed5f6232d24b0b636aec4664e49 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:24:08 +0200 Subject: [PATCH 156/695] wireless_channel_occupation_: add example graph --- .../wireless_channel_occupation_-day.png | Bin 0 -> 22662 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/wifi/example-graphs/wireless_channel_occupation_-day.png diff --git a/plugins/wifi/example-graphs/wireless_channel_occupation_-day.png b/plugins/wifi/example-graphs/wireless_channel_occupation_-day.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec542277c393098bddb997dc97c9e57c4ab2bed GIT binary patch literal 22662 zcmZs@1wd6>*DkyO36T&)x)cN@rKL-{8<7wY>25ZS2uO)YiIhmUbfdI%=cc>6u+I!74=IHS}GelWY3LBFY6M`UY8R?g*5QMZ3{yoP)2H$zQG|LbELo&>B+6q_**FvZWTXd)weoY`Eswz2n(_a;zl#_ ztlM2ODY7`Mb2Rdm2O^H9dQQfA*3~zWA6FEn8a*+~`|hmuMFAP3gOK{s<;msZFUt<> zPV76Sr9Y<+% zx+8l3#Fvdi^!|InH}X>V?-=6Zpui#U-S^L0TU+^FONNtXT+^?t$0|BF{8U)iVbajh zAR!^4a}F){adE@ZrcU2MDl=1TijOD`5(*a$!lhD_mhR~2SmVEY$IxG=z{bpM&hCY3 z&Ty|H&xghF(2V}^akr(8sHsZZuH}QKrl$3wEa9VmaTNGdK~7Fiav{&NlkF*JuL%X0 zmX`Jo8XAte@#pC1@&Kz8wCRhB3v?>a;@vCD^Z~PjgM*JBKSFN?f5<2a9uovLuTz<~ z{5H3=w6w5raB;!G!CBdN6A}{QLn9ty_Fq|9*=@WPVqsxnV;g|O>kA7Di;Me_jrq)1 zkJg7$+*e4TD$nx+T;Z#|)5cq`j!%zJ;AKKmbkV#oj)qP%va_@E^S4rNujnDG*#`L7 znCn7I$Tdy4_KWT9?dBj{R~HvFNJ&Yl2C2;{lv-HW=g*gCk-|4G&K+BV@mTBjkdJpJ zN=@%!V)F6veZZlpF&p^q>kC0<78ZOCOVSb&fjR{d!NJ;9c4>);WDqSqJ*#%Hugxsn zC0x=G+{tmZS0-aHK0f}(5Ad_Bok{nrv;EF!df}6CZYKqUFw?9cZ-hFBws8ykF%)F6TR0MnT!u{ZfP5~vyyYA^~M;YP~Sn=B*fmce~ ziuzAu{!C9#Gint=O=|<`5AgAWQ+D`7$?7i$%P|tUtv$|{<3ghE5wV0(2~$F(q@)$r zQ<;MI-);ms&;Jg5Rr)T9z-l!A6}{rO=UkbN)w)SGS0_`p^h(JB@4AVN`bS4kj>ild zFL!Eb`IOte*ZR}C;#jAqrqoQ0UAIja?z8bfdi3Z6ww(a{@l#e-Dt;F$*WK7JU+VVT z$Tmj{$ao!=X6j*`Z4~@2@(Juuo;)!*-Wbu>*LQb!|FB&El%KIYQ=a7{N1shst`oV~qqRZ~+F z74`EAcv8YeLlYphtU{Ed-F@j#fHs5JYgq^12&!uA@tst+dSct;#dcVhg4oiDoJOr zm6iL4hmUrBZeh!RVk&Lm?9<+KIC!OM^5X4~ag78ueu#j8Kn=|Gmp|&v%naDJp-hEZ z$5ot(vm&2sMgf88U%!4$Oq}OrB{MvG#%47OKl%Rs`}yJO<=I(PRaJKa$LhuL=7*`A zL&v_ZE;(BKs4*fI-FRW2hN7aP&`>!eqwjv%bdez!M@~T@<6;0L`S4+e^}CIrCvjalb88C=Vk{5o zM0eY=v!4R(1pA62;^^ooBhxvYqjpSLo0Ub+>(czsCQGi?&y9yl%&n}@5n!%Y@?)r1 zxjH$iGsG+%C-J*FI64CReS&`%GMl{_wFLSvlyciipQ6nDVYjuqrUvs4vVnntg&YjV zPa;WOv>ue|nvV3;n2L(ZPeeqY?1gr{nQLYR)mnpN{FWU3Km}Fz<(`yqXgzq#YRU*t1~>IV2qMR!zO(hm)YiWG($29!vLo7!t-no*gzx&KeyL~ zla!QHQvDrdzmo{bDcj*kgGAXQ8=0AO-%gDNUcP*(+u(T~LZAGmp{qBEe{pd!#plM| z)YLRVzQP7xlPm*+!5U8{Ex|rwO}T@WoSnH@AmmI#L(^%=%E~H~Qp6!v;F=CDu{7#O zB2Nh{8YM^$&pkJIL|xIR;14^Ro5hQ$=;F(Yz=9(q`+!n{Z2-RxtlWFC4I2m~35K8P zt?lRM$Hm3<_G=Fi`QeGvdJ!5kekEX24Q3G)7AMgyd$shKAH&Qt8_~8^GrN zvmN)TsBYV-e7^BH=hoNj<*1Ji4

%9*aY@XMd_F&_HWzYhGSn!PsO0cL(sLxOjkS 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 G6Z?Oo`5`+1 literal 0 HcmV?d00001 From 97a1b23287b64e6bfa522b7f8fc6a61e9db83c09 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:26:29 +0200 Subject: [PATCH 157/695] rename wifi_signal_noise_ to wireless_signal_noise_ (upstream name) --- plugins/wifi/{wifi_signal_noise_ => wireless_signal_noise_} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/wifi/{wifi_signal_noise_ => wireless_signal_noise_} (100%) diff --git a/plugins/wifi/wifi_signal_noise_ b/plugins/wifi/wireless_signal_noise_ similarity index 100% rename from plugins/wifi/wifi_signal_noise_ rename to plugins/wifi/wireless_signal_noise_ From 42f09647f26187ff8c5980f7c5caeb67f39fe029 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:29:25 +0200 Subject: [PATCH 158/695] wireless_signal_noise_: various improvements * add perldoc header * support dirty config * support "arp" (tool) and /proc/net/arp * fix autoconf handling --- plugins/wifi/wireless_signal_noise_ | 152 ++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 44 deletions(-) diff --git a/plugins/wifi/wireless_signal_noise_ b/plugins/wifi/wireless_signal_noise_ index e4c2b946..970399f1 100755 --- a/plugins/wifi/wireless_signal_noise_ +++ b/plugins/wifi/wireless_signal_noise_ @@ -1,23 +1,59 @@ #!/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 + +: << =cut + +=head1 NAME + +wireless_signal_noise_ - Show signal strength and noise for all connected peers of wifi interface + +=head1 APPLICABLE SYSTEMS + +This plugin is suitable for wifi interfaces with a stable selection of peers (e.g. infrastructure). +It is probably not useful for hotspot-like scenarios. + +Information is parsed from the output of the tool "iwinfo" (OpenWrt) or "iw" (most systems, +incomplete information). + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_signal_noise_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut set -eu +SCRIPT_PREFIX="wireless_signal_noise_" + + # 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 @@ -34,6 +70,12 @@ else # TODO: there seems to be no way to retrieve the noise level via "iw" get_wifi_noise() { echo; } fi +if which arp >/dev/null; then + # openwrt does not provide 'arp' by default + get_arp() { arp -n; } +else + get_arp() { cat /proc/net/arp; } +fi clean_fieldname() { @@ -43,7 +85,7 @@ clean_fieldname() { get_ip_for_mac() { local ip - ip=$(arp -n | grep -iw "$1$" | awk '{print $1}' | sort | head -1) + ip=$(get_arp | grep -iw "$1$" | awk '{print $1}' | sort | head -1) [ -n "$ip" ] && echo "$ip" && return 0 # no IP found - return MAC instead echo "$1" @@ -54,55 +96,77 @@ 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")")//") + suffix=$(basename "$0" | sed "s/^$SCRIPT_PREFIX//") for real_dev in $(get_wifi_interfaces); do [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" done | head -1 } +do_config() { + local wifi + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + 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 -r 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 +} + + +do_fetch() { + local wifi + local peer_data + local noise + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + peer_data=$(get_wifi_peers "$wifi") + echo "$peer_data" | while read -r mac signal; do + # ignore empty datasets + [ -z "$signal" ] && continue + fieldname=$(clean_fieldname "peer_${mac}") + echo "signal_${fieldname}.value $signal" + done + noise=$(get_wifi_noise "$wifi") + echo "noise.value ${noise:-U}" +} + + 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 + do_config || exit 1 + [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ] && do_fetch ;; autoconf) - [ -z "$(get_wifi_interfaces)" ] && echo "no (no wifi interfaces found)" && exit 1 - echo "yes" + if [ -z "$(get_wifi_interfaces)" ]; then + echo "no (no wifi interfaces found)" + else + echo "yes" + fi ;; suggest) - get_wifi_interfaces | while read ifname; do + get_wifi_interfaces | while read -r 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")" + do_fetch ;; *) - echo >&2 "Invalid action (valid: config)" + echo >&2 "Invalid action (valid: config / suggest / autoconf / )" echo >&2 + exit 2 ;; esac From 9a6d3e449f43e64ac4089ffe37651f3267a0f488 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:31:06 +0200 Subject: [PATCH 159/695] wireless_signal_noise_: add example graph --- .../wireless_signal_noise_-day.png | Bin 0 -> 37079 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/wifi/example-graphs/wireless_signal_noise_-day.png diff --git a/plugins/wifi/example-graphs/wireless_signal_noise_-day.png b/plugins/wifi/example-graphs/wireless_signal_noise_-day.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4c46b87d5b2abb3fc3213aa3841a5854f96e7f GIT binary patch literal 37079 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Ν~bO zZPY{(&VB6xkukmQuBOj5ADyiuO^4b4-5R{VL@})l75D!$AvP}KKkT1{D^yA}5Cer~ zO$lTcD2^q{dMjmC@$~J%uMIn6Y~PQyx&}QOwdul&SBamz zm+!I5H;sOxpPR*O`7s)+w7Qz~7Z7#DIn|JglrqRMt8cc8Asd zzwUiZ&2$k%!g?9$r~IO_6?4sM=k=w{(w%y+>*nAK#ojjOPLwUl8e9Qm9M#dX!1{nJ zQZWZOOe8$a%-3lySo3g(_AbrVxD)_SI8Xi4vv^rLOjHzaSIiXJrf|sAf5#~E%MkBK zk9?ZdtaRi3(Nj7+ut+?N&D!JJMSSuW9aFTvyyNx;EVn6!?#-xwg?dggBw#$kNYhDmNNm<~t%LQ?;F@1}r z@wNwQhd(NYoAG`qs(|G(9<({b@)PR(@h&g=KaoTd5_@P<`RY(Bb5UuN%T5&nRcVq& zGZkWj&PnGpW3pK}zhB$+%6(Slyu6d9vik_cod-{(#MOC$!3D{)p%X%Z7ooyu(X_;1 zQT(5Rr>@K1QO+gM8M;O$Nbwf(rZLwqd@9G+6bRU6)yCWQR$pZ4@EoeAfbtE}SiqEo zJ!hdmIkLj%?23Vb7EeZVZahdpZ5Vgd-c6SJ7WebmJ8o@>OG)Zeh#~-^GBP^q>EY4d z+Nz93F;R1eb?8R-NT`Hc*s*?}DtL&5#Ka4l>EJKW?KShR+|rFyq?G&}sNcitEqA(&cSU6 z5_jXf9%p`0XIXTtmS+H0@;{ti#CPM;KB0vGS*!cZ6SfgW%Lg9d4>t%(8`_8978zSx zS%HO-CClu$1aG=;r$a=B?{gE(lpK@j@-pf}#y%C-w;}YhrFYE`7(aZMC9hTsiXjNI z7ohO<^$n8nmnVm@$r;k+DiSoSH62N0Ed)UP!W}Iu<^EeXI(OJ=CtR7SRYYc4W&zph z+xoRFs|+f2bgu9Jt!6}_oAnk&-iD}+aBT<&QFHQ;su!|u)6*#(3*%ueiWZe6uYb36 zujOp)CO?uH7o>`#X$y{R!rM)&2nNU`vnFLIQO(=2Ly+A(Zv> zY%DB72%ovnRk68-hmh%J`hO)1!zoEP%GPuCyZ&sX;*X41T!Q;qS=YcJk_VQ3avb%h z&p&Q6=}Jontg_yHNa)28JWp8)TR+_4GNx-0HtwI%tYxPYuLh`&Pgp)!KBoFSxWW_% za9vd@A@lZl1PejtuoXBzGsbR0cx~-(@e4_UDs_@(*lUA=0$<*k{B(>6cekU`6clC< zDbPTD?Y>KW+IIzMfE=YXNRwnMbidVMf_uPpeGgi;>u-#)%wo6@OUpr29UBv~0Dk!2 zzYGNE{rYM{p+YIxGRF0A_2!yG5Pm7OVTgtvzdW` z09#RRBtvz|S%kG^cI^Kw+LWam6Lacq2Ts*ya*xOZ25A)>szga{yw`H@2Y zG;r@ZER4O6kBHS7(dU^LQ7tisS{W40e{(xI=k`mhVRUm1DSCH!MvDXsImiBvX+qiH zG*#kS`}9(2Bp(v*&iCFZ*v?Zt@6%GM9@~m@ByMO{Z;NH>F zW>!>;bauXgv8n{Idjxs(GLzSb9~YTc=ZsORzyHlL(3A)ZDLLe?r%d!^#}bcYDm z0s*?i3I(L z-&Wb{@lGr8rET|IMr7-Nt^Zr7EG-?Jle1Lkz6aIE_tDWt@5_JEO+&E>DX@u&iHXx> zFmjh9lT$w8VMBm*cHSjBf_;`OWZ9F zxnWWX8`?aj5 zu4RgSf?lk+$YVsa;jqwmKLN6Q3UO_lh=|&k_8>NffM>Z;y$|(zcui0?!azrtzid$9 zo92u%+Bgp6i0|Uit@0xm%`3k@^p_@|LbAvyqMPPE23{kLh@6@lbP%LVt(7u(pN_$8xh8Cem`oeTZ?ctsrD!;PuY#JHs0TR*ETCqctxMXF=_;%p#MM z5<;`|*_+u3Db^Ul6rx0;xD~BO+)$&0q6R-N@9i7Nf1LjH`dh-oEPy2AX_-RC8KAU$ z@DZwj*AewAJW%jE)!Tn4UA}-qL`F6-H5I`a^vRv~TuRyV0*kGD{%-$Jm802HNOuye9dgXGT}kdny;^u8=|| zlneHlZpDh5lGxO8uMJJSIH1e|@KyBx^q_)*#5c=mWhQkd0t_P7cb%qHc7t>g%h>-@ zwg?pSX6STScE@}PGp6 zJ0iPkH1CDdx3j8)*cxJQ*Htu0CW0`jd!lJkJl{qr*rnLkx92{pznuIx&Rr@O-2WOU zMqsb6DphY z^Yf4{3JMBhun`i0Tklv+A!3}O7@`&n$%an5==__4h`t+{D$@6(s2=F(hoK@WDOCoO zVsoOzZWE+n5Sq$VJhnn5oaK2pjNyxCKOYIclq}t`gjJC3$Nq4Y4%B$;=S$a9`o9fz zndxf`?=2)t3eh+*T)oe?QyQc8AwdTfKybey;SNB}b%2}7Gswxye`W~S!OCKLdZ!fv zLLjjhKw=LPj#}#K*0T+iJS5Wx;}&V8iy5Oungx}B6osn8{+D4Z7d^dc_|0;i!bddQ zhlemEd4{hLidIx;7HUE|6e9RiZ7*nHf;!f}s0mtg06bc*j*1VqzwLF==$iXOsDN`-*$ukW0^z;CpZe~WG=8pDN!K)lhTj+fRz8lm& zqxWfrgeG>WQ3^f;c2Xr8od5^h#nJIoK|PjAinBcF@+(qqVW8_`OUCoPzNUsk2?_-j z^Sdvb@4XsX{8S=agq5$D;%n9DJ7IQR=RPWRkYo7$%sJRc-v%1F{Td zOOm2e#iL|DzwgrG$NEs0xmAO6iytiQf7eIJInIsm#%GC`5v3y-Oad%MRK53+VR}()~z3Bv091YSIA=>@iK>m2(z`-V}c1l(Y`K zR3eYmx6#iMU+@eY@(gMG>!slgh6JsP%K_Xcu-Z;Aw~)k>VUGL+;lamxE&b5Z(10cLAUO-edbwbRjo&SMz z(T*n+bhWj|pnqZI_it{XdP7YLGP;mffC|e!_cxsrD=r#ZT9EUog%hX0-vM%6KhR&y z#J_TIsCHQ+N{#nCJGA+>vhTRO@uDl6c4Huc%cR6A)5plz)b!8JLdW#<^sM_5r5qj9 zjqvgDLCPC4P7^=X{!{q5*Z8w?)<>>B2-;uu>pkEJ!->hs!7P7yqbiG=C+_lbCeFkn zQJu9B+u+IlCJyUrdtWP$w{TtN?z{ax2eRr=_jw6;PH3?0?C!?Iz<~Nn+SjiB}rlpnV}MrSLGo+z;OCc#dvGBaaLlU0(u0X8w#FPF}D^< zF=(SpLf0dcK7Sb3r`o@#-Sf)y$Kn8&F$I*^Zwa9Z3JQWXxxNe2m7kEi3oteB{1;7;{rBg zh3#}0U&=4@g}9@NpV6{(U>Biu6D+y~wgl=(kl)_=`xg}fmA{1dNlBBM8~Zg-3cw{I zA|fGqBU(mp@8U8HY0;du1CNOMzOIbQ4%4|H#b2gF9IY+3iI*ViAm9E7v!AFj>~0b| zrLzx}oSV+FyO6Z`u+e`_A#!FAK3IZU*9~NZvw0Iu`^NIJz>5Joc?!@X0Fp6u15K2P z;xouYf6BlLSOaejjp1DopVQOJQAk2XKsEP?f&%z5Te&KkoGA6M03e+>TWM!tZ2U>5 z5H(Ozw)?d~a!LxMx}$;r-*6oKs zvLz^=7|}(hr>9f<93!2$Z?k&{?$<86%g>nINS>c6F+#blg&JGafYZp*Ps6pQ z0w=hEvF-L>D$RMbpF={Z!bWkX)y*C%z|~g^f1kSox?dZ3nd+ZEe}D^*wh8qvxc-`z zsWGw3Z8320AZcU7;)^rBmCM;kTz+=$qzY*2?tqqpST}I~Ekeg^vb)m6A3uLyy>@Ne zYz_wCkQ;ExlfAn zhc1A(hEGsqT`#q4%hU+qtVnDua2;dzmi&3ApFWzFz2vE}OF_W~r-d580m6jthx=6; z{6I@%4ju+hB}hz6u&}heyQlK Date: Wed, 4 Apr 2018 05:13:00 +0200 Subject: [PATCH 160/695] olsrd: various improvements * support dirty config * add perldoc header * flake8 and shellcheck clean --- plugins/network/olsrd | 270 ++++++++++++++++++++++++------------------ 1 file changed, 157 insertions(+), 113 deletions(-) diff --git a/plugins/network/olsrd b/plugins/network/olsrd index 97daadf2..e74568d0 100755 --- a/plugins/network/olsrd +++ b/plugins/network/olsrd @@ -1,62 +1,88 @@ #!/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" +: <<=cut + +=head1 NAME + +olsrd - Monitor the state of an OLSR-based routing network + + +=head1 APPLICABLE SYSTEMS + +Information is parsed from the output of "txtinfo" plugin for olsrd. + + +=head1 CONFIGURATION + +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', default: /usr/sbin/olsrd) + * MICROPYTHON_HEAP: adjust this parameter for micropython if your olsr network contains + more than a few thousand nodes (default: 512k) + +=head1 USAGE + +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 (e.g. OpenWrt) + + +=head1 VERSION + + 0.4 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + + # ****************** 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 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 +# * 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" "$@" +MICROPYTHON_BIN=$(which micropython || true) +if [ -n "$MICROPYTHON_BIN" ]; then + "$MICROPYTHON_BIN" -X "heapsize=${MICROPYTHON_HEAP:-512k}" "$0" "$@" else python "$0" "$@" fi @@ -68,19 +94,20 @@ true <{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 {} @@ -248,71 +287,48 @@ def get_ping_times(hosts): return result -if __name__ == "__main__": - # parse arguments - if len(sys.argv) > 1: - if sys.argv[1]=="config": - links = list(get_olsr_links()) +def do_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 quality with regard to neighbours + print("multigraph olsr_link_quality") + print(LQ_GRAPH_CONFIG.format(title="OLSR Link Quality")) + for link in links: + print(LQ_VALUES_CONFIG.format( + label=link["remote"], + suffix="_{host}".format(host=get_clean_fieldname(link["remote"])), + draw_type="AREASTACK")) + for link in links: + print("multigraph olsr_link_quality.host_{remote}" + .format(remote=get_clean_fieldname(link["remote"]))) + title = "Link Quality towards {host}".format(host=link["remote"]) + print(LQ_GRAPH_CONFIG.format(title=title)) + print(LQ_VALUES_CONFIG.format(label="Link Quality", suffix="", draw_type="AREA")) - # 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 + # link count ("number of nodes behind each neighbour") + print("multigraph olsr_neighbour_link_count") + print(NEIGHBOUR_COUNT_CONFIG) + for link in links: + print(NEIGHBOUR_COUNT_VALUE + .format(host=link["remote"], host_fieldname=get_clean_fieldname(link["remote"]), + draw_type="AREASTACK")) - # 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)) + # 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)) + title = "Ping time of {remote}".format(remote=remote) + print(NEIGHBOUR_PING_CONFIG.format(title=title)) + 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) +def do_fetch(): # output values links = list(get_olsr_links()) @@ -353,6 +369,34 @@ if __name__ == "__main__": print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) print("neighbour_{remote}.value {value}".format(remote=remote, value=value)) + +if __name__ == "__main__": + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1] == "config": + do_config() + if os.getenv("MUNIN_CAP_DIRTYCONFIG") == "1": + do_fetch() + 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) + + do_fetch() + # final marker for shell / python hybrid script (see "Interpreter Selection") EOF = True EOF From 4bd1dd52e7b673fa058dcf8f0f0cb1bb0642cd00 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Apr 2018 05:14:17 +0200 Subject: [PATCH 161/695] olsrd: add example graphs --- .../example-graphs/olsr-link_quality_day.png | Bin 0 -> 20739 bytes .../olsr-neighbour_link_count_day.png | Bin 0 -> 19804 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/network/example-graphs/olsr-link_quality_day.png create mode 100644 plugins/network/example-graphs/olsr-neighbour_link_count_day.png diff --git a/plugins/network/example-graphs/olsr-link_quality_day.png b/plugins/network/example-graphs/olsr-link_quality_day.png new file mode 100644 index 0000000000000000000000000000000000000000..512c1cf655edd36e834d667168a2d381ade4c072 GIT binary patch literal 20739 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 literal 0 HcmV?d00001 diff --git a/plugins/network/example-graphs/olsr-neighbour_link_count_day.png b/plugins/network/example-graphs/olsr-neighbour_link_count_day.png new file mode 100644 index 0000000000000000000000000000000000000000..223a784a1fcd0df10492c62b750959cfab80b891 GIT binary patch literal 19804 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 Date: Wed, 4 Apr 2018 05:38:05 +0200 Subject: [PATCH 162/695] add new plugin "wireless_signal_ranges_" monitor an overview for an anonymous (and maybe varying) set of wireless peers --- .../wireless_signal_ranges_-day.png | Bin 0 -> 32260 bytes plugins/wifi/wireless_signal_ranges_ | 160 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 plugins/wifi/example-graphs/wireless_signal_ranges_-day.png create mode 100755 plugins/wifi/wireless_signal_ranges_ diff --git a/plugins/wifi/example-graphs/wireless_signal_ranges_-day.png b/plugins/wifi/example-graphs/wireless_signal_ranges_-day.png new file mode 100644 index 0000000000000000000000000000000000000000..830c710f4604bafdaf1003d5024c387efe55ff85 GIT binary patch literal 32260 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 literal 0 HcmV?d00001 diff --git a/plugins/wifi/wireless_signal_ranges_ b/plugins/wifi/wireless_signal_ranges_ new file mode 100755 index 00000000..f17029c6 --- /dev/null +++ b/plugins/wifi/wireless_signal_ranges_ @@ -0,0 +1,160 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +wireless_signal_ranges_ - Group and count all connected wifi peers by signal strength ranges + + +=head1 APPLICABLE SYSTEMS + +Information is parsed from the output of the tool "iwinfo" (OpenWrt) or "iw" (most systems). + +This plugin is suitable for wifi interfaces with a variable selection of peers (e.g. mobile +clients). + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_signal_ranges_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +set -eu + + +SCRIPT_PREFIX="wireless_signal_ranges_" + +# thresholds for signal quality ranges: ascending values +SIGNAL_THRESHOLDS="-88 -80 -60 0" + + +# 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 $2}'; } +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 == "signal") && ($2 == "avg:")) print $3}'; } +fi + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_level_fieldname() { + echo "range_${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/^$SCRIPT_PREFIX//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 +} + + +do_config() { + local wifi + local lower + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + echo "graph_title Wireless signal quality ranges - $wifi" + echo "graph_args --upper-limit 0" + echo "graph_vlabel Signal ranges" + echo "graph_category network" + echo "graph_info This graph shows numbers of peers with defined wifi signal ranges" + lower="noise" + for level in $SIGNAL_THRESHOLDS; do + fieldname=$(get_level_fieldname "$level") + echo "${fieldname}.label $lower...$level" + echo "${fieldname}.draw AREASTACK" + lower="$level" + done +} + + +do_fetch() { + local wifi + local peer_data + local previous_count + local current_count + local fieldname + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + peer_data=$(get_wifi_peers "$wifi") + previous_count=0 + for level in $SIGNAL_THRESHOLDS; do + current_count=$(echo "$peer_data" | awk ' + BEGIN { count=0; } + { if (($1 != "") && ($1 <= '"$level"')) count++; } + END { print count; }') + fieldname=$(get_level_fieldname "$level") + echo "${fieldname}.value $((current_count - previous_count))" + previous_count="$current_count" + done +} + + +ACTION="${1:-}" + +case "$ACTION" in + config) + do_config || exit 1 + [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ] && do_fetch + ;; + autoconf) + [ -z "$(get_wifi_interfaces)" ] && echo "no (no wifi interfaces found)" && exit 1 + echo "yes" + ;; + suggest) + get_wifi_interfaces | while read -r ifname; do + clean_fieldname "$ifname" + done + ;; + "") + do_fetch + ;; + *) + echo >&2 "Invalid action (valid: config / autoconf / suggest / )" + echo >&2 + exit 2 + ;; +esac From c81c20ab3b60512ac86beaa0a0ea4dfafa76e1dd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 7 Apr 2018 02:11:05 +0200 Subject: [PATCH 163/695] unify DIRTYCONFIG handling in shell and python plugins The following issues were fixed: * comparison too broad (e.g. "0" misinterpreted as "true") * python: comparing string with number (is never equal in python) * unwanted non-zero exitcode (compound command "&&" as last command in shell code block (e.g. "case" or "if" branch)) * access to unset variable (shell style issue) --- plugins/bind/bind9_resolver_stats | 4 +--- plugins/bind/bind9_server_stats | 8 +++----- plugins/bind/bind9_socket_stats | 8 +++----- plugins/nginx/nginx_error | 2 +- plugins/oracle/oracle_sysstat | 2 +- plugins/postgresql/pgbouncer_client_connections | 8 +++----- plugins/postgresql/pgbouncer_maxwait | 8 +++----- plugins/postgresql/pgbouncer_server_connections | 8 +++----- plugins/postgresql/postgresql_active_backends_by_database | 4 +--- plugins/solaris/forks | 2 +- plugins/solaris/fsstat_act_ | 2 +- plugins/solaris/fsstat_bytes | 2 +- plugins/solaris/interrupts | 2 +- plugins/solaris/io_disk | 2 +- plugins/streaming/packetship_ | 4 +--- plugins/weather/openweather_ | 2 +- plugins/wifi/wireless_channel_occupation_ | 2 +- plugins/wifi/wireless_signal_noise_ | 2 +- plugins/wifi/wireless_signal_ranges_ | 2 +- plugins/zfs/zfs_arcstats | 2 +- plugins/zfs/zpool_capacity | 2 +- plugins/znc/znc_logs.py | 2 +- 22 files changed, 32 insertions(+), 48 deletions(-) diff --git a/plugins/bind/bind9_resolver_stats b/plugins/bind/bind9_resolver_stats index 86e410d5..80f20373 100755 --- a/plugins/bind/bind9_resolver_stats +++ b/plugins/bind/bind9_resolver_stats @@ -72,9 +72,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/bind/bind9_server_stats b/plugins/bind/bind9_server_stats index 4d495982..3c211e3c 100755 --- a/plugins/bind/bind9_server_stats +++ b/plugins/bind/bind9_server_stats @@ -69,11 +69,9 @@ if [ "$1" = "config" ]; then echo ${key}.type COUNTER done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/bind/bind9_socket_stats b/plugins/bind/bind9_socket_stats index be742f33..f84261ed 100755 --- a/plugins/bind/bind9_socket_stats +++ b/plugins/bind/bind9_socket_stats @@ -67,11 +67,9 @@ if [ "$1" = "config" ]; then echo ${key}.type COUNTER done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 9cb3d7df..6372867a 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -156,7 +156,7 @@ case ${1:-} in config) do_config # support "dirty config" capability - if [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ]; then do_fetch; fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi ;; autoconf) do_autoconf diff --git a/plugins/oracle/oracle_sysstat b/plugins/oracle/oracle_sysstat index 82159721..85ddd1f9 100755 --- a/plugins/oracle/oracle_sysstat +++ b/plugins/oracle/oracle_sysstat @@ -1119,7 +1119,7 @@ autoconf) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/postgresql/pgbouncer_client_connections b/plugins/postgresql/pgbouncer_client_connections index b179636e..0ba6e0ec 100755 --- a/plugins/postgresql/pgbouncer_client_connections +++ b/plugins/postgresql/pgbouncer_client_connections @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/pgbouncer_maxwait b/plugins/postgresql/pgbouncer_maxwait index bae7e8d8..a257ec6d 100755 --- a/plugins/postgresql/pgbouncer_maxwait +++ b/plugins/postgresql/pgbouncer_maxwait @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/pgbouncer_server_connections b/plugins/postgresql/pgbouncer_server_connections index ddd95964..6b63d2a9 100755 --- a/plugins/postgresql/pgbouncer_server_connections +++ b/plugins/postgresql/pgbouncer_server_connections @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/postgresql_active_backends_by_database b/plugins/postgresql/postgresql_active_backends_by_database index e4a53382..78a34745 100755 --- a/plugins/postgresql/postgresql_active_backends_by_database +++ b/plugins/postgresql/postgresql_active_backends_by_database @@ -73,9 +73,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi while read pool sep backends junk diff --git a/plugins/solaris/forks b/plugins/solaris/forks index 5edf0a81..1744bdf3 100755 --- a/plugins/solaris/forks +++ b/plugins/solaris/forks @@ -98,7 +98,7 @@ autoconf) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/solaris/fsstat_act_ b/plugins/solaris/fsstat_act_ index c9113586..3332e0a8 100755 --- a/plugins/solaris/fsstat_act_ +++ b/plugins/solaris/fsstat_act_ @@ -163,7 +163,7 @@ suggest) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/solaris/fsstat_bytes b/plugins/solaris/fsstat_bytes index c95bfdfc..4fb5e7e1 100755 --- a/plugins/solaris/fsstat_bytes +++ b/plugins/solaris/fsstat_bytes @@ -160,7 +160,7 @@ autoconf) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/solaris/interrupts b/plugins/solaris/interrupts index d421004e..7f1f49cc 100755 --- a/plugins/solaris/interrupts +++ b/plugins/solaris/interrupts @@ -118,7 +118,7 @@ autoconf) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/solaris/io_disk b/plugins/solaris/io_disk index 3701d16b..34ec68f0 100755 --- a/plugins/solaris/io_disk +++ b/plugins/solaris/io_disk @@ -334,7 +334,7 @@ autoconf) ;; config) config - [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi ;; *) fetch diff --git a/plugins/streaming/packetship_ b/plugins/streaming/packetship_ index a9ec0964..b7465498 100755 --- a/plugins/streaming/packetship_ +++ b/plugins/streaming/packetship_ @@ -108,9 +108,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # If there are no pumps then output fake pump1 data and end. diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index ea232028..ffec4ea9 100755 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -83,7 +83,7 @@ then EOF # Continue if dirty config is enabled - [ "$MUNIN_CAP_DIRTYCONFIG" = 1 ] || exit 0 + [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ] || exit 0 fi TEMP_AVG=$( expr "$OWAPI" : '.*\ Date: Tue, 10 Apr 2018 22:25:49 +0200 Subject: [PATCH 164/695] p/if1sec-c: handle absence of state file --- plugins/network/if1sec-c.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/network/if1sec-c.c b/plugins/network/if1sec-c.c index 47f7842d..b7ec5bfc 100644 --- a/plugins/network/if1sec-c.c +++ b/plugins/network/if1sec-c.c @@ -38,8 +38,8 @@ char* get_ifname_from_procstatline(char* line) { int config() { /* Get the number of if */ - int f; - if ( !(f=open(PROC_STAT, O_RDONLY)) ) { + int f = open(PROC_STAT, O_RDONLY); + if ( f == -1 ) { return fail("cannot open " PROC_STAT); } @@ -120,7 +120,7 @@ int acquire() { /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return; + if (fork()) return 0; // we are the child, complete the daemonization /* Close standard IO */ @@ -139,9 +139,15 @@ int acquire() { /* Reading /proc/stat */ int f = open(PROC_STAT, O_RDONLY); + if ( f == -1 ) { + return fail("cannot open " PROC_STAT); + } /* open the spoolfile */ int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR); + if ( cache_file == -1 ) { + return fail("cannot open the spoolfile"); + } /* loop each second */ while (1) { @@ -213,6 +219,9 @@ int acquire() { int fetch() { FILE* cache_file = fopen(cache_filename, "r+"); + if ( !cache_file ) { + return fail("cannot open cache file"); + } /* lock */ flock(fileno(cache_file), LOCK_EX); From 52905275b8bab419a7348f645f7b6924d4de4219 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 11 Apr 2018 02:47:49 +0200 Subject: [PATCH 165/695] p/multicpu1sec-c: use stdlib IO --- plugins/cpu/multicpu1sec-c.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/plugins/cpu/multicpu1sec-c.c b/plugins/cpu/multicpu1sec-c.c index ab8a8890..42aaffd4 100644 --- a/plugins/cpu/multicpu1sec-c.c +++ b/plugins/cpu/multicpu1sec-c.c @@ -95,7 +95,7 @@ int acquire() { /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return; + if (fork()) return 0; // we are the child, complete the daemonization /* Close standard IO */ @@ -116,7 +116,12 @@ int acquire() { int f = open(PROC_STAT, O_RDONLY); /* open the spoolfile */ - int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY); + FILE* cache_file = fopen(cache_filename, "a"); + if (!cache_file) { + return fail("cannot create cache_file"); + } + + int cache_file_fd = fileno(cache_file); /* loop each second */ while (1) { @@ -142,7 +147,7 @@ int acquire() { line = strtok(buffer, newl); /* lock */ - flock(cache_file, LOCK_EX); + flock(cache_file_fd, LOCK_EX); for (line = strtok(NULL, newl); line; line = strtok(NULL, newl)) { // Not on CPU lines anymore @@ -154,17 +159,15 @@ int acquire() { long used = usr + nice + sys + iowait + irq + softirq; - char out_buffer[1024]; - sprintf(out_buffer, "%s.value %ld:%ld\n", cpu_id, epoch, used); - - write(cache_file, out_buffer, strlen(out_buffer)); + fprintf(cache_file, "%s.value %ld:%ld\n", cpu_id, epoch, used); + fflush(cache_file); } /* unlock */ - flock(cache_file, LOCK_UN); + flock(cache_file_fd, LOCK_UN); } - close(cache_file); + fclose(cache_file); close(f); return 0; @@ -172,9 +175,13 @@ int acquire() { int fetch() { FILE* cache_file = fopen(cache_filename, "r+"); + if (!cache_file) { + return fail("cannot read cache_file"); + } /* lock */ - flock(fileno(cache_file), LOCK_EX); + int cache_file_fd = fileno(cache_file); + flock(cache_file_fd, LOCK_EX); /* cat the cache_file to stdout */ char buffer[1024]; @@ -182,7 +189,7 @@ int fetch() { printf("%s", buffer); } - ftruncate(fileno(cache_file), 0); + ftruncate(cache_file_fd, 0); fclose(cache_file); return 0; From c043a897656b40f3f6dbe96a74266cf934caf5b2 Mon Sep 17 00:00:00 2001 From: adrianp Date: Thu, 12 Apr 2018 15:51:08 +0300 Subject: [PATCH 166/695] Renamed plugin name to allow a broader range of devices to reuse it --- plugins/tplink/{tl_sg108e => tl_sg} | 0 plugins/tplink/{tl_sg108e.png => tl_sg.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/tplink/{tl_sg108e => tl_sg} (100%) rename plugins/tplink/{tl_sg108e.png => tl_sg.png} (100%) diff --git a/plugins/tplink/tl_sg108e b/plugins/tplink/tl_sg similarity index 100% rename from plugins/tplink/tl_sg108e rename to plugins/tplink/tl_sg diff --git a/plugins/tplink/tl_sg108e.png b/plugins/tplink/tl_sg.png similarity index 100% rename from plugins/tplink/tl_sg108e.png rename to plugins/tplink/tl_sg.png From 83a8cfc77d37d6fa2d39513c497243c08debbef0 Mon Sep 17 00:00:00 2001 From: adrianp Date: Thu, 12 Apr 2018 16:07:32 +0300 Subject: [PATCH 167/695] Cleaned-up documentation, added config option for number of ports, renamed switch ip to hostname, added optional TCP port, changed category from switch to network --- plugins/tplink/tl_sg | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/plugins/tplink/tl_sg b/plugins/tplink/tl_sg index d76d4212..fd333106 100755 --- a/plugins/tplink/tl_sg +++ b/plugins/tplink/tl_sg @@ -15,8 +15,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # -# $Id$ -# # Magic markers (used by munin-node-configure and some installation scripts): #%# family=auto #%# capabilities=autoconf @@ -31,19 +29,21 @@ use WWW::Mechanize; =head1 NAME -tl_sg108e - Plugin to monitor packets per second and link speed for TP-Link SG108E switches +tl_sg - Plugin to monitor packets per second and link speed for TP-Link SG108E/SG1016E switches =head1 APPLICABLE SYSTEMS -TP-Link SG108E switches with web management (http). Tested with software version 1.0.2 Build 20160526 Rel.34615 +TP-Link SG108E/SG1016E switches with web management (http). Tested with software version 1.0.2 Build 20160526 Rel.34615 on TL SG108E =head1 CONFIGURATION Add this to the relevant munin-node config file. You can specify switch address, username, password and description for each port (the switch management doesn't allow port descriptions) - [tl_sg108e] - env.switch 192.168.1.12 + [tl_sg] + env.host 192.168.1.12 + env.port 80 + env.numberOfPorts 8 env.username admin env.password mySecretPassword env.p1 'Link to PC1' @@ -55,6 +55,9 @@ Add this to the relevant munin-node config file. You can specify switch address, env.p7 'Not used' env.p8 'Uplink' +If you're monitoring multiple switches, create different symlinks in /etc/munin/plugins pointing to this plugin and use the symlink +name as a configuration section as described above. + Requires WWW:Mechanize module: sudo apt-get install libwww-mechanize-perl @@ -80,15 +83,16 @@ dated June, 1991. =head1 VERSION - $Id$ + 1.1 =cut # read parameters from munin -my $switch = ( $ENV{switch} || '192.168.1.1' ); +my $host = ( $ENV{host} || '192.168.1.1' ); +my $tcpport = ( $ENV{port} || '80' ); my $username = ( $ENV{username} || 'admin' ); my $password = ( $ENV{password} || 'admin' ); -my $numberOfPorts = 8; +my $numberOfPorts = ( $ENV{numberOfPorts} || '8' ); #populate the ports and descriptions based on ENV my %ports = (); @@ -99,7 +103,7 @@ for ( 1 .. $numberOfPorts ) { } else { #no description - $ports{$i} = ""; + $ports{$i} = "Port $i"; } } @@ -112,11 +116,11 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { foreach my $graphType (qw/Packets Speed/) { foreach my $port ( sort keys %ports ) { print "multigraph ${graphType}_if_$port\n", - "graph_title $graphType for $switch port $port $ports{$port}\n", + "graph_title $graphType for $host port $port $ports{$port}\n", "graph_info $graphType graph for port $port $ports{$port}\n", "graph_args --base 1000 -l 0\n", "graph_scale yes\n", - "graph_category switch\n"; + "graph_category network\n"; if ( $graphType eq 'Speed' ) { print "graph_vlabel speed\n"; print "p${port}.label Port $port Link speed\n"; @@ -138,21 +142,20 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { # extract data from the switch - uses web scraping (tested with 1.0.2 Build 20160526 Rel.34615) -# print STDERR "Connecting to $switch with user $username"; +# print STDERR "Connecting to $host with user $username"; my $mech = WWW::Mechanize->new( autocheck => 0, - requests_redirectable => [ 'GET', 'HEAD', 'POST' ], - user_agent => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0' + requests_redirectable => [ 'GET', 'HEAD', 'POST' ] ); my $result = $mech->post( - "http://$switch/logon.cgi", + "http://$host:$tcpport/logon.cgi", [ username => $username, password => $password, logon => 'Login' ], - "Referer" => "http://$switch/" + "Referer" => "http://$host:$tcpport/" ); my $response = $mech->response(); # navigate to the page with the network stats -$result = $mech->get("http://$switch/PortStatisticsRpm.htm"); +$result = $mech->get("http://$host:$tcpport/PortStatisticsRpm.htm"); $response = $mech->response(); # print STDERR $response->code()."\n"; From 38403565c4998d04c4de0333861449d0cf7d3935 Mon Sep 17 00:00:00 2001 From: adrianp Date: Fri, 13 Apr 2018 13:52:17 +0300 Subject: [PATCH 168/695] Documentation for multiple instances and fake host, Link speed labels documented and drawn as different colors --- plugins/tplink/tl_sg | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/plugins/tplink/tl_sg b/plugins/tplink/tl_sg index fd333106..86b0ec11 100755 --- a/plugins/tplink/tl_sg +++ b/plugins/tplink/tl_sg @@ -38,9 +38,18 @@ TP-Link SG108E/SG1016E switches with web management (http). Tested with software =head1 CONFIGURATION Add this to the relevant munin-node config file. You can specify switch address, username, password and description for each port -(the switch management doesn't allow port descriptions) +(the switch management doesn't allow port descriptions). You should also create a fake host for the switch and attach the graphs to it. +Details here: https://cweiske.de/tagebuch/munin-multiple-hosts.htm + +In /etc/munin/munin.conf add a new host called tl-sg108e (or whatever you want): + [tl-sg108e] + address 127.0.0.1 + use_node_name no + +In /etc/munin/plugin-conf.d/munin-node add the following entry: [tl_sg] + host_name tl-sg108e env.host 192.168.1.12 env.port 80 env.numberOfPorts 8 @@ -55,6 +64,8 @@ Add this to the relevant munin-node config file. You can specify switch address, env.p7 'Not used' env.p8 'Uplink' +The name in host_name must match the name defined in munin.conf, and the tl_sg name must match the plugin instance name (symlink). + If you're monitoring multiple switches, create different symlinks in /etc/munin/plugins pointing to this plugin and use the symlink name as a configuration section as described above. @@ -94,6 +105,17 @@ my $username = ( $ENV{username} || 'admin' ); my $password = ( $ENV{password} || 'admin' ); my $numberOfPorts = ( $ENV{numberOfPorts} || '8' ); +my %speedMapping = ( + 0 => "down", + 1 => "auto", + 2 => "10M half-duplex", + 3 => "10M full-duplex", + 4 => "100M half-duplex", + 5 => "100M full-duplex", + 6 => "1G full-duplex", +); + + #populate the ports and descriptions based on ENV my %ports = (); for ( 1 .. $numberOfPorts ) { @@ -123,8 +145,10 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { "graph_category network\n"; if ( $graphType eq 'Speed' ) { print "graph_vlabel speed\n"; - print "p${port}.label Port $port Link speed\n"; - print "p${port}.type GAUGE\n"; + foreach my $value (sort keys %speedMapping){ + print "p${port}_$value.label $speedMapping{$value}\n"; + print "p${port}_$value.type GAUGE\n"; + } } else { print "graph_vlabel packets\n"; @@ -158,12 +182,12 @@ my $response = $mech->response(); $result = $mech->get("http://$host:$tcpport/PortStatisticsRpm.htm"); $response = $mech->response(); -# print STDERR $response->code()."\n"; +#print STDERR $response->code()."\n"; # get the data my $data = $mech->content( raw => 1 ); -# print STDERR "$data\n"; +#print STDERR "$data\n"; # The page stores the data in a table, but internally it is stored in 3 javascript arrays: # state:[1,1,1,1,1,1,1,1,0,0], @@ -171,7 +195,7 @@ my $data = $mech->content( raw => 1 ); # pkts:[0,0,0,0,14141090,0,10461386,0,14226,0,12252,0,0,0,0,0,2872063,0,1402200,0,59764503,0,34619246,0,4913873,0,4393574,0,44170456,0,68499653,0,0,0] # state: 1 - Enabled, 0 - Disabled (administratively) -# link_status: 0 - down, 5 - 100Mbps full, 6 - 1Gbps +# link_status: 0 - down, 1 - auto, 2 - 10Mbps half, 3 - 10Mbps full, 4 - 100Mbps half, 5 - 100Mbps full, 6 - 1Gbps full # pkts: every group of 4 values represent txGoodPkt, txBadPkt, rxGoodPkt, rxBadPkt # parse good/bad packets @@ -204,8 +228,9 @@ if ( $data =~ /link_status:\[([0-9,]+)\]/ ) { my $currentPort = $_; my $link = $links[ $currentPort - 1 ]; print "multigraph Speed_if_$currentPort\n"; - - print "p${currentPort}.value $link\n"; + foreach my $value (sort keys %speedMapping){ + print "p${currentPort}_$value.value ".(($value eq $link)?1:0)."\n"; + } } } From adbc670d0f31b7a12244c54c40be5dbef759d7a5 Mon Sep 17 00:00:00 2001 From: adrianp Date: Fri, 13 Apr 2018 14:29:13 +0300 Subject: [PATCH 169/695] Changed link speed graphs to area instead of line for better visibility --- plugins/tplink/tl_sg | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/tplink/tl_sg b/plugins/tplink/tl_sg index 86b0ec11..f164c517 100755 --- a/plugins/tplink/tl_sg +++ b/plugins/tplink/tl_sg @@ -148,6 +148,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { foreach my $value (sort keys %speedMapping){ print "p${port}_$value.label $speedMapping{$value}\n"; print "p${port}_$value.type GAUGE\n"; + print "p${port}_$value.draw AREA\n"; } } else { From 8be3410942b7085a69ff93f6f1dbada13576949e Mon Sep 17 00:00:00 2001 From: adrianp Date: Mon, 16 Apr 2018 15:21:03 +0300 Subject: [PATCH 170/695] Sample graph of traffic --- plugins/tplink/tl_sg-day.png | Bin 0 -> 299266 bytes plugins/tplink/tl_sg.png | Bin 240473 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/tplink/tl_sg-day.png delete mode 100644 plugins/tplink/tl_sg.png diff --git a/plugins/tplink/tl_sg-day.png b/plugins/tplink/tl_sg-day.png new file mode 100644 index 0000000000000000000000000000000000000000..5567089cce2a08f0957200ca2263b0395c9659ad GIT binary patch literal 299266 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

dOJVq996*H(wq*Ri9yN|*lw-P^ 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 XWwL@Yu|s*vc#< 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%0ArcC#8N$LcKPn$7HO7BL$#6^$83$ z=*f-P{W>ACIZyOmM!;H34{RFE&CJa7961uxE__{-G~x!iIPR#Ymy@kb4RPh&ayKoV(|K*EvRp`?fHu>Ji^ITcEAok+-tT@B!Poat%1*wgc;&WBwZgn`~avVmyn z&)QiF629p?(`UHWtbY#AbZ#l6M>0B!E0%ZZ%D8|rb|EGcvY?4@tF`F6O)9PUUqKb6H2pjlU2uGkCxNU-*kxjqfhBWsUd}=r%b65gipI$^uxB> zbROx8Oh|!tkr&hHZ1_cDxNCvM{f=vVo(y6jo*sh_eRZh5_K`?a=c}J*5iu}REVU5t zJRBHV`E9rRM@Bd%ApxtQi6G=_x5r>*U-pF z9j|bCf2Tv0PraBa(Upn$G__(VVej5)cj;oS^wZOslzg2U(IT1nI*zx`zBYF1_R5bu z!EW0AXD&^go!R_HZ{M|}mrFkFp1m{=^L+L(Y9poRHxlC8F5jj;rT~Bg1M@R8eXUm7 zGaU|RpeIQygPvp27*3W;9O)^aNX#OatXLUPS|?0XTpcHtXP{qi9#(++`wB z2t7npaMqrjI>MG^WH)(*;*2L6kZ z4BYZOEao2i#Ux zuypcFFNB`&Y~-7XhLEMhz5toD9W1nZ5|517aQ29KruX#Ba@le{OP3a11e*sQYOt!N zp_#&-!1ai*k7aT}Ix&Q_tAEO3d@^)2zm-=7ZmcIhUu@l!iZ>zVsyB|jVyk}n;R_Vv z)2Dx{+eZu_L70$xVi566=;c__Ea}{qhA9>@FE6jm%|SFJC=DUenXv4wzAH+UrO?zB zMs9rqN@i!dBFQ|{Q--se3{R5p2g40PT`|7m(i(*<@_Caqd~#+6_f&Ox^K8$g&o_|N z1n|?>DWOntbdN_3L5Xwi>{sLHg~X_vpR3ZTM4QpVGE2Ur~jl|-~UE7kPuE!@RE?1D@kSsfNAKF00W>H z4?5+lCG%hez~bCGHwRGdU(my+VF(hUk9t6PlDZRvj>0u{dD*Uhc2l-_=&7`H_5E*w zj;|mdP*TWnr6~-z(&HG*D3o2-&VE-2KsA)eD@y8!X!%ZhTzLQ%Ko7_; zqcJ;c!0Fh^0O%neJhEIK9gV<;jpJAf+~gAzyM3To6MBLSlw@F)skm%V8T70{IYEUy z9oXg@sF2|vjJ%~As;~IGSpn#30kL!lsgV9=_Qs7wpfyjbJpYQd@FoO&NUA5V*Yw}+tZdM zY|cfCygCx$qX`&h#fTlp54G4<8&?RO3BR`{fEZd95n8)(gSD`L?AENV1WZND&7vx2 z$E{l&^BzWq&m~GkRh^=gvc0kSAD+GOneUE$=9Aw;zvy#ce&tWDY<~9VpN#QL$7n^l z9&>C5s|qpKV7ikW(9zn9R)-tKX*eW{9W0!p5b>a|kQ3$xg3I7mQ1))xVtT{_UYH~C zYBQ{)&GGRaej{mE5uGCxLUSkiHgDdv#dLS6M_8rq2t(GcU++J9G>iu2qNjpVjWjWM z%bHR}hXif65Kqu4#nBAPd+)ZTqdIn&13Osa7Sm}LEFJbPMpid(-`?E0GiEWJ z9tdR6DGDPav2^YTg{CDaFGxX`BB)KX=hFw)Azx5H43(i1i8}t==RT(@v8M3xkAIvV z=a;|yWyR1F{r>O&evm-_`k7~*VY>Li2OoUwvB&rmE#sbI&vYAwbD}8@zj2=F$Q%d^ zQz!yI$8OSNuvv@#rH87=K1bv5h$ zC>g`ejrBFQJMm{PHwi7wuW#<;x1R2$q*90yfEjE)RRXtg(JppHc%1Eo5@c0nHmV4T zm5bRP)JT7vprYT9Qe-uN5@lhA6^n=8QW!&22#tb#(*F)1!x$pZ^qH9fZ6R&S&{TRT zkq!x@M4stDhEqp6>tl_`(RZ~6MkO@Lo`?r$Ffnb$Hgq>$4K4@dLJ8f?Fa@HzYzQ(q zD%8V%VSVXRpxy%|#6gusMm*EwEgh@~vvf(*Fpq$vLSG-VR$($G{vfFEgr*5AM7fym zWqKy;AS9K5rDH}TOu(!nE+>N8%Rw#;&tTCr_mIZcRHmq7YIWrq(DnbhpC73G)IAMi|cm`PXtHMgKTy6_C+(<@yP4!wT zJ=4^3*0VChn#S&o7);uwc?s4!uFs8x*jc3N=QS?}0@xWtIZ02kc?qHf*W+*+91o$v zC~@^tnRP<-GRc4zaP8a6K-`9B?nRvv(rMJ1h(`b=zD4*!*9^x}iwS!i<&eSdZGK2@ zo0gP72A@#3$6tRoF2q5`<|TCcLDT%5?}8Twip_H;IUs_6AXlTrK+riYY8q7oyyoIz z)F7x1GB!%optpHBM*qf!K}L-mR)SL?0YaYC!qH%!>AEJdISrm0FvNOz!751bZ?(2T zra3*3!Nz*c!JE{ogoZMQlp?FvE)6p(xu9y6PRI=_#O0!26=UpFeH6$RWSSErCYX=B zCME+K#Wk+sMXYIFf~Oimjj?piYhRq>Zq9CsB6^KT`NI!C%wU2u=owGITM+ojBablH zB8Fs4YJU6M-~Q1b{ZZyMnF{^uzy9lA{Ka2nLn~^F?7aj1z5RndGuE@DeRQe|Cp&T) z8AV}y5|iV!f8x2;?YHG(Ou#l2WeK!e3GUb{VU&aw;@HHl9OklF3`^)2?`npwvUcu$ z3m@d563Fft!U`oY4k;wXaJa$501ygqnw}nZ!wkDqd;=QS*yefw498LQx_Ma#XVerA zE5X%>Id=Q0%Ya6K>x90HgIfSf^ty{qXb^m{(5o(RI6`evuCy6mU{b! z2Kttl=6f+1J-z)cSFwhWVTOZU`7UF9O8i$OW9+&XUXpm@SChxl%s`d}5pGOxk~5W7 zg>bima}x}t#Ff`}huwq~B8>qz=#qq$V61R;L633JWq&Z}T<;K#%3;x4lr#|!VYX+Zbb~Pou|JOf-hjOif5o+6268{A2%#uTGPu zQRA{DcQ8G2Zc>6#;?gIUPHQZ+#a=d|m$_BeMqsJa>&-c_mE$gZrdwOOjjNX~U1HAi z#EBCu2p&Fsn0d|1moF3Z(n~Mx+qaKF#<#xptsncbAJY=*H^2GKkA3W8{I;1kl;EwK z%O_uL5AE5#bbH6pNc-wl7_@fb0vn#J$2l;tcI{fv@bFeRG{l}8y0l4n;|3|&#%C!z zApS=%%bIo@ zv!RR~yNp+u+2&6R>0B%1gb{udqQdYiBIF7__7@YfG2h+R(8qbvq`}Ds=O^4C2-(QY zJ&^p<4@DBbue02; z$;PSOyH~GV;Q-sUba?r)#kg?w@@1xEEF@(*ymYCxZy$O44<01spvTpV7pp=h)Jd~F zWCv>d!iB+m?y-1EBiy?;AjFpwel{*a^tkUr$-1LtB~NEzy+%h@;lQ3fi{~z!S?pUr zckbg)J-YbXnX5}XZeG0hkN)mkmd;)1-!m%iiIYi~;r7m*Ygezbt&p&faBPflV{DA0 zo>z(YZC3_B4?PD!106DhC-~$fOFsriB&Q=q1<)%2psk0nw~r2%^A_5Y=Wfsg7Z|Dp zHSDaXyU9t#+;}b=<)H`e1icafa!LgzoO96V=dmHxmS2IMLgBp{fDAGLFNL$~10g~4 zcs(PXQv^L2Wo#4x(ONfea?13^&6@)N2hhRN?QA#g*A0O7by!L*0Po(t2DenxmjRHe zoh<0~>}g-RG`N31;*`M6i6RQY!a^8kL>h=)x`Yz2E5Y{fUpaq10cL&ZImmE5(hkJ6 zey(&%ft~R3Wj23r(U4IQh0s$SI{;ihe;!Wu-n@y;%Bm^HJ$P6FKm*;iYnJBb&DMbf z21chOx2eq4Y17TVF-{Mc9Wy72|H@Mi|ykSali}AG3e{-X5C~s2mzfpD3t&N z9o)dz!|_8{_RY;UVDu(T<}44uyzOHK0XsW0V?Ct)eoQKVCdSmEyQb~4if}?Zml0r; zA=?{wnRvo-0E%hMCURYc*;y{FphL2~gOPMODxoJ!$C39v_!uA_YTS;#O#oPJ`^W{X z0PNMP?1l%n3DDA@tArlSI{)o&+o+p07S~&g>2co8*-hxCE!Z3JGe7e)SP14azxK7S zefG1T{mtL}&7c11pJqm#4&~ENKmCO-eBmd5@+V2@JIR?{s|Y1W)7c0Q3ESAD`(^UM z;VJNFXvlWe5z`4tL*J`3bkkino#4icaCwdG&z|?eaQ9ze!Cu_jY!n13r@)8?4NaB z-GY?HF3Ar?67tVs9>ZpZxOq)u2s6R_iOBFFeNLgspm0wZo8Cx62rIBv%+BIxfP)>* z%N+CMnUG-x4Dl>VGubtb8c4luuHqmiO;gwqzOmGlNvQ7IMjK2F;2_-2l4P(Sy$krUjp!xVlF=$w8*06FM{m=j<1ts{}4D)fxjE-y#l-9)Q)*Q@dMtle*+&Bau1>H^8_ZfTY9| z+w=wEEvZh)a}rYqJzyp1@gaJg974q1Bo0A_sSNbAyaif?#5@359&w{YF0vDHJ;t*D z6!Zwu4;|>)ZyC#WyOjo=^o*~#7*Y;B7=j+_hYY6)4dyxa`j{UHZqcnGNTmr*70A$9<5mO-WwLyf7E(Rv11R7n_3RPe)1fc5cIJ3UE z$lNApm~HVS&4F$(v|LWLT9;0obaI=3E(N_jpwcp6kXc;Bzzm~^BpXuN^=%(r%=vjH zXzWS?_OfF#ocS;#B6;p+`^=WP6Cv<$A8@&-ky${%C3BVsAPF+ib7s-zR)}YJuL|*@ zGp||b5wqR)nQfwvj+kNfIu5}B z;KR%%F-&TH=}TW?>HK$o=XdCXvbO%kFMjdWS6}_5U-~8au+xrxL;b5gLkm;u4;&p} z#aY`?F~AJR?LynInQ)hjd2G(?+7)yIm1i+hNJBE0=3@eWaQ+aQWhBDCmBVFNtYMNb z;Ojoi0?v9vL*Y)gt{^Vd0=o3SW141eetsuAFJmeLC2p#QL2wu)e0+9gWta00%j6i0mZD;KZq{Pjy2r^-m2r_KY@+YD+Ek`9-CyxoR z$iq)|?7(ATl=aGpb}|DS-Lp}0Q9`wgh=Gm z)TTs!Zd-Ohi_&4;{L`QQG~VCLWsZ-J|G*FY!0p?&KmYm9bC!x?KK$ViGpqN$_q~r~ zth3*|dGn(m{V1EAYK8YU-D1T5<_9Ne1dtyhA{gUiKXbvABuVq;GuN?ZvxySabA z1TPt9*QY@#nH5-AVD8FjXqwlA+ey4TopBR;Q=1zSX^xbnfRVPH$Owp9t4jlRjNs<_ z+InAqUn6?NLyBc~6Ufx3#D66L9cy8@b8?&tb)iLkySOIm`j~k4_5xem-3xMqY^N|{ zvzu%(4)#oUpQhK#&P^P;11J$0=rLHtNow6em*!4KnAOlUik(WqxJYRliMkUo+QO!< z==kFJSX<1tiIB^Mqx}X>vTjS9JxwVgi(9;EVgHzNZcd3HV@C;Uwk!g4$xY?WY49q@ z&LKy)JMjqQ2K;cdn|7E(1SNL1D`wQ4U9e>_(^TnqQ7F*S<)vL1o=I+t5>~|tAe>Uk z{_ikLN9i6i^zhhM&Qo5Rz1BYq7#BH__LS%tmVl;_NUY)S&x}TpM%@xK#Dke8 zR~LKgfll1paOanU+&A`Ox++eqOioVH-Na$&`@Zk{-v9ph^C!2Yu%Q#@!Ka>j>b>uM zFOxSP`p}0+&#Mo9@PqGo$2)Yq2r)pTuhrKY?Cag=wb?rr3P0(JxLd-vwzhiHC3aVa z$^}UP87?QI6P3{UMA!dS2);GeP-#lK=1e8J{UyxwxmkzFo&JH);8y@ebe48^>`uQantta(iMm-j5=P@C*wPu9jy6VC zUZ>@V?H-{*5Y#MWx@YaA=m;v1VeGx7&FVKwrvw_sRGHU@O-g{8#L}@p7`@sl)2OUd zjspnBnik=4nZk5o!j`+~Dg^1&0Vj0kFX?4I`Q($wj~~~u>L2;YM-+4P=uw8D5KF_q z=RNQFp6~e{V(?*4Y`m*HC;d%M&bBxD_l>rurtQi~oYrx}oVavp_|PHlCnScY6Sn>4 zLmj=82`QWyLVCBl&j3=HPer{j7n=@=C&q`QfgY-mgzl7h27C)FAMf71)Okw}n<`NE zvXIU+-CQb8sNc||^P)KfOXu>R5|H}WiLfl0lD-#oZ4i{H0O5#SJvQKXjpRR~q zLTgoH;+g966@F`i*;hZ$9AYxpb}5E4cy+axnnTRbH;rQQH@PY1>eZ|4!5}Y!kx#=$ z3&twC+OaIO%^0(@Gwy0H8X7q)OQ{PgH7N2S>G@3q!2t|L<}JP9nlY4f^U@Sn*T`L)Irv{sU%52h{=vs~vcP%byC=Uk)&G-E?%n8V&CX&Q)-^K(Ud+ z$whPI*msc5v_BSxyly0-YCw3g{t1s3z2c#JC!CzMMQXt^=8)N12uo z3=2V#&y|kBITa_=>NH*6j*s`x&(FA%jPfZfpr?AN?r`$Tl`DJq?(LeskmrKr^sQU0 z63)BhT{FtCj_m_LQDZvVCdQ-4%d>9(!QsaCsaZ&Dz0^DJe5L>3HY|I27nD zU>;d{^ljNYXF3;e-2$_1JQg~aSW;#+^rSyk5y227^3ZKaSEoMok}kx>i%j&`)y+-Nn%;*x!u40zXE5RlU+~Zs58BN*3rLgBNFQ!ug zgaZ@B4=O-NtK?&dM;d^%PeZkRg{8{1OhjJdEo8vQ(zDa@NsQuCq23mTe2#b?cE4jA`WZHuIr6&>t+EijqiS%yPN z2mTeg%9>m8XwbgN&o3`dfu>Rf3u?rUdRt@(;0?`{dt#Ng~EtV=Yof5p7wfcZn1U$F+0XwkGW+r6?fl#F`t0Z zj7avx-D1b>k<8ZghF+E^`ZfNGx$A75Q!Q9wjPqE68)zjvAvxmVbdLe{*SiC>gCE9- zOljyC-he^?Eihg=cW&^ZhinC^`J)mhCMFKVIklKd;<%Fs9%xR&wj82FRMja;DOcAw z{^;A+|K{|qfA>@G#gFnUUwiqNpILtHfBxWVdt;y#$QB0W%+}PQp*Rg5vJ-*p0D}Mk zAOJ~3K~xj{amZyjj-rgZOB0(nImzD5J}zsCcr+b?tOl2{&JijssKsd^8imyWEeZuw zvcdUBTpbx&7l|CR2K(dgIfVX=OP6}?y_bCg^#wjtYo?Sq?NYWpm?0}n_rB#VVKgWg zCWg7gV5g;oa&Fxl8yk4=LEBpvy4=ZNl}B#7VGP02F*)BCZRy}iEOOnEVZCvh854u0 zV?rT}8_LD0a68zk9C2?2WH6P0*$pE@V#ZlrgNR2%OQ*bOFj_hBgw=_5G9G!c4k{cnC5g4 zGcE9i;YFBA5*x(|!VogZBW=hejan;+A)-XmA5MvzUgc0RyuoZgcqk>B{Faaeo31rn zNkV1JbHk2^bL~=-CrYMEES+2+Bs!f zm>5CEZOa4yVUS5z%Glfd5%Fvu*Dn1qGE|7BYJvqdY24mkwo4lsp%3NIdx1_?1rZjlj;D;v6Vz_}4V?n@Zh-jK9N;-JK*vVc7E5n#} zF|ImFoWF%F{f0qCtwRCvO~Wk_?7IvW577`TU|!6^ES<=9ohw`6aU6|0cQa%HLv?ta zhb&qrXKqCh3UlwkOfr+|I{z|`65%ko3_C>Etv!X?ijHOsrXi7jDCz792rfzs4Z+!^ zm+dITI0bzN#Df;mv2fv)v7CXFB%KEn3PvM+c&f)SBpxz`+~`9$;>my^hL>pM0^3OB z+R=k|9WPEeO;F>+BV14&0se`HOdwwFG#Ph>A&7gl4xx-#K}AXyTB402k>5mlHj^{hA=G=NCreWO4!iO zRrLH0D@VCl<_NBfiH+MRY`Y6WDbYBO=^`yqhSkLh@Fv$*1=mI2g$5|$x2WnO+@04h zJ<}11R4^^TuJ(;hJ7O$=9#H~hf+a*HARCD zmtq;TB`%DNgdvQb>#1*yg+#N=&1QAwjH=aMCo|0~13DkbwkBXe98WTHj@X zQY8dbMJ1S%erLa9KwUiLP~w^;+)kNiy4;^#l>%x+78FQ0WTe=!bkTK*vUHv5bB$JF zUqZP-NeJp4fDB?5YB41e;f>7|?hwJLf z^2FrKy7Nq*#Dh|v={-FYmoE<=Jc#=(zA0>7f@_%&$(H+AS;#gc7|o6rxGe8HHZKCp zvQX}^a%8h`lj|oJIU;poYKnW__zlfCyX8Z=lJa9M3!zZ{dUSuGhcNG*M~q(i5aP9o zjc|2p%3NOu2el1UAM(zyd6In0R&{plV1pplr+iwW4ns1GB`+~Ob8{T^jOp^8=>VJG z`36!<#{W*vn0ThM^@?1)P24k`^Q?Y*Uvl}@FFMOL%Ul?=a)mL{NI~G#{&UUm2EtM|6x<e~ps#h} zg;)N|wVqFYVBbbh|IBpa_Up@mtA*T7^B+p!3DT8ryTX>0A9`9sBo8xOz=#{Q^BUCK zGPq^p0jZZ&upqzA3eZWyi=nr?%zh6%(>e5x`>OyOr+9kk6#_5=JrWCIREpnxMPN*a zL>6j;4D@EX8?e<{a*iOXU>)emvls@7jgUuPnlBAH&wMmtp@#I(LyK~h(rtEtbv)Da zpz|?+3(c7qKY@-~Lz@IOpQbP-4-JFN(W4XY1ik#*Ix+H0uamMg4fh)_ElqE16w*-0 zfqd=Y395{}&Rx@!GSBnG(HPJ4s>MUqL1;*hckZICEvWLktgrZ!8k4ew)&4R{1& z;IU~65(d)1@E04hF%Tt?0yLs1jzo!MQzW~)$nLJLt*g4Ka;e<2GP81fx%hYD|6b(H zyS;lO(ooYQO2i&=`qotxi|}cQ3_1FxI+Dtm}Etw z1VaYOSTs+cqP9BZJMk!KrKEQrTl|MGNw!tgmji7<2NP6xraHlxE}pWa#oV`6r~QL9 zzFpGjem$QF4E+pFLj8+srOUV~W?7y3n<6`KNoegB@*7fN zmG{p9$XH!>m|_c!c6aR0gTEAdskg0;xLiu+5(gaWZMC!dt`F^f-9NIcVv zAnZ#;TUEWMv+(ddnk1_f!51}ruT?AsO=VK4seW;|Kpdw zr>nX3)xW#^4?j157I;+QVl}+yMx<7^d|2mEzms7p=$BIVb-oSU5ppNOW9{D4qQkK#AZ1_=#j zjlxWlH;#C$Kr}$mVFDE(tU(DPh-xKg@``(2{6>9%EX`(2WI7GtFtBW4eGz`mot;QQ z&3+{p_q_F{O#se^+twTau9p?$$jEdw(eXJ%P(?%NX|su;f~xLO-Qc)i$dE!R<1{WZ zGTn8MQUWOJ67K1Y3t zF(86qk?BtH9Z3QOUVNdtV3wer`%1MeGF?+>L1=i^K$AdBq%Ur6H9j&u+|xNuKngV9 zwxx%vj3-Ep4H0V?gvit7vadX|1)r&<~p|1p@7ri4rMyMj)$NjMW=TDt-L=ZzQofc9>U?!>o5oshKUXAy0~QA&GE3| zg3+_vUJx_q6H8S&iLm}8YQ>^q^oSZ-d0b*KiMAfDAF(FLx`Uub!#Ole;w0oy(D?QN z5*hv+N6Q0R7YPcI9s*Sl$%aJsrKK{503{Yg{82Em7`Xc1!Fx9~X@Oi3l}oI1Ix?Mo z(=J#qWNyQ4lr-uPYUd&`!+>aA0|+r8;bL>}WPnvy*q{sGw~hp@AEgKqD15GIrUYye zJd~=!ge~yld~qWQRS%2TqZimjVWI`fKnZbh;sw>?;IXR0AaG_XpsS}>BF-Z#M>a9w zW}xf#NabVrA_zoKH>2ahoz8`y!yp~uR?ikagH*KcdL*+M*xW=NQ=1< z7S*c^ptJ`jh9;@ ztBh;(P&^$tFsB$s4daf(#Oz^&%81K{qB2;sNKY5e6xWTGr=v4Ln7{~3IvmjgS~y=E zGH;N`^x3(^oxOe{)92g4v$&eM@$tb^r+hdqR3m^ZnZig#;U!ZAjwy$jEX*03TA;F` z@O-2v>?4PKwlfx`(3iK|jDBxvsr$$gAGZ$7RMZ~4)SeZkKAfD@4aM%!7HCj08GfD$ z(4%m&;{J29xk=@_cQ-aSIbz1{OiwhILPn9i$u!wP;?(Fkkh5JTKS><_jOP1TJe47S z`0mCAk?9JM6sZ*$n_^IwW#ovV#Oty3idu@iOQnju16%gY6{VDEY;6&)KXl{>A(=i@ zHn%))DZ^espDIT^O6J#n2F}!#E2HPnr{LFu!)3?NSpmOE40HP;n$Ye7^cfg@?&8gv zdwW0nIQM5XUjEjVe|2N~x88kXYp;*E^&`U;_UmZ~#d4`Fjwq#dWCQWeq`{Q`y*9 z#m{Cn7vYz&mM)j5o@_!^_3m=35EnPQh_oVWleJDMrK5G~LXa%kAyh|ZiMg|{zdm^E z*j=}$jhrH3C|5dqL5mXaa;5WdvZ{f~jGR3?=MIa=zO9mT>((uHT~{etSY~EspxIp9 z-p<9LMD@G^s*EDjJC-k^Hu(!PGsx%)AFlW~cLqyGLz?w{s1G>67b#;(eo_jU_I*w= z_ck_`7Z-_4zvnz-)|KSOs;3l?)=@6JQlUF@XR{RgzKsoTU0hpOAmyu-GbQkSS=Cdc z<-QMHrV9|WNa=ij);f_RGA@pf`^a?VWYZ$wC#9R3n$mFro~NI0 zpX2E|idnL*1RH>mLLWr|Uw3Yt+4_A4&u%8W;h&iS*w1(CKKdK!x93cn3nZBdeLI%f zZ#@HFEvi7s{6-t+7K8-+T&w~*m9++?{>Pr^4{Z{HIT`u)JU#ei9gGnYU+ly3@Y=KnM0hqMZ#I=-!pTVwk zMn{5_wYw{tglkZ}|QW)Y(92X}+By~)YQBNXVc zFi8#dN(iBn8_k`6`K8;JCRhH&fAHO1JKL{*^ZJ`VdGQ~8_$l_4|L}L6`H$Xnz63(< zE5I@@5_Jdk#)d_vpRe6?5+BVqMn7YrZ=I$;m*y2d^F)VmG<`k=_Mp2pF>FKVKk(kKz zL-TZq$KOY$7xAfMzSI@vVjSjzrz|9nP*Tmu@XVZhWWX9bG|e91>fE- zI)I7L=t!2F0?r2Q&=+>2hW14ifX*0bo^x8MlI1WVqLVcaE`A83s%kNwz!t<{LLa~+ zGBO?U@S(@Su4`*!!)Q@|7aAm2EzpplDyJGN+`Q&q7qRH>%JjXR=FS5{-T%|iKYM3& zYwB+EH~#MzfBD_#Hk!Lb{T7_v@h422CI%j%AP74wrMaN;7~cs~yvl^|#R)1>%DDrP z@vYj3X5Fd(6G5EN>~Lz0FzzzS$UPI3!+_KZq)Io_IB8P zPv;~g1VY=Cd7m5D52v}<5WCAKRAtn(YzQ+NQQg_v>W#C9hvVrSckg>-`WtOAJt?Dv zqy&m=qFRWrO0^1zXiVbK@VC#!taIS5;xVyrtk6$zPg>B3$AsCDNC|-;kr1dJj|M=( zFkxE*9$O;84GG6a2mi@WJ`A*_^{vPMyMH4ZyS)!JMoY1LK{lfKaya7AKISHd#fN~2 zi+hN{!DK92;|$0lX>Md+4n<|c^d*J)ebn3on84h_kdRoKfJ21lN<M#fr zFR1a(E|w{-4~HVC@znSZ9&SoH`GCofOlKx9BB)%z`La=1OrWuLCf*!{bST2&BXfM8 zM556eEC!*WrUfAo4Mzt{c6V(N+|eXJf>kRv9^CPSDS@zfJ9~z2UudwF;+HhP5YlTvREaWxZ+<56%Rs zJqL-0z*0R*R;9VnM+3DuOh6`vAib_~FhP9NC|x8bCMd!r%e)FX5>8U>fr+6BDr1(! zt+dpIqw?`M*i9`ihB^QTZ$NlJc_aBkWIB<PSy+oABrZ_F|XH_ouZMJ-F&eUtKjh;If17T)n24RNgN%%#5@%fGpLtlC2#^Pqr z`yXv|_cms4-8y>i?CR1&v#asBZ(RFN{=u8>OwSx4hP@r_sL01`igJlBgz+LFNKsbZ zQaSKW)Jvu)-69aU&1-M3*GUUGxw1qnDG5xG8{nIDc$nS42O|eWV*8_Y0Dc_&%fvE<EUSIK0EMZzzO=+PI1ZHYK(a2i#P^|wph{bcRY-Oxt9nJguQi02oa{StWQEI? z+@K6gL=T%Fs$`e13T;g z?&s{Lu*do&gDubm^7(N}1t~0-)KZL_@q7+{A`|K_nfyu0pFFfuzIxrK=YCG|ZRxWI z$OEmEK=37n0DoqG>@8(J&+;9TB_(}bl}VPQQ-0*lGbM|7tyezvpwL?(ki{^WlWaPP zfSyHj0Z)}YK>P%GeU~W@-lS~ZViSq)h2>XcN3um>;&aL&6usqGsUC-kz97pnQJGW< zRg<-H%Xs)jZxK|FVNsc)eBU~lWXqKINN-JNY$mbCW-;A?^IC^uZQU^M#2sPoxy%Eu z!qZdvMf6YQOAbvMMAf#k5 z8<1*0m28z)Ett~2)g#*-U!I0igrcy2YwF+rWlI+2Bq3OB1l8F?W3CKrl54f# z$i;a6mUH<)D^9e$r<1Iw;J6~4e<{Q6E6@>ZqLc4a({|{+G`E_=RVyiky~9<0ZW%I5 z7-v;b88C5s3`$BCy;YZ!Z_8UOf@2|%?&|3P9H^pNRFC}GaEgI*B$Tk~G>vBR0r6*l z_Gf$|V1^h_{=zT(f=$J3ZGGh{U*S{lnP;AP$2;DkPn<7*`O6HnpMCb(x4-@EnsJ2m zx--4G-MhNpc;x*4UEa(o60F-3cYiDG#)l2=GDX4s=*g2|R8lSa3%U4e2?nym4f3p} z#ofFNHDDQ^S;1&f(Sj(sXFTrW^}~tR8z1lIZtmi-dr3ECjL@B^`@t1Fapz9t5encb zOoUYl+WwVFu5a!9i@&*jdtvocKmXR|#%Ax*V)vOdsMWW|@BZLF|LW@Re3%O*8HakS z*AX&c(dDQ+pqZaoTI8BoqvUFx&29$LLHLgu_x9TDKVa)2Cx1(=i~-N>&%_+%9k3zRl4V z)s-#@M71>A7ZVR|Fo{fOeS^asT%b%YsvR>mWe80=mMkh@o{lqEPMs<$P$eHv2TdY1 zcXx5o23FC>urLQ48=20r5=V~oL{L>LMcVsOg@CCt60@x!Pp7&dfGve70j;O1+|Suf zI)g}H2S5M$&wt_*pU|oenYfDQo_p@nrAuedoMF(30zCfsPrx7Ha9l<**nEoqk~zCxh4$I0U_DgAgI&sOy{E+RBK`4Aix)! zt|TO^{Was>{>IU8=>{v_R-02XNRQ(i;)NB-d=x? z%@Azwu(i}aiaWw=sSL|k?mmJ5z6b(>%FYt*^Dzi;#8b7J#2XNI4g)7a4|4i}s9LSA zFC~>>4+@T)FIxRs6Gkfri)DC>0IQq{Y0Pug02K~= zz%1(SZmj0=jpriM>#0l|H(`S2lCdwS#h9nxIC8aAW^Ik>888V@8KHrA7!LQt)0vOx z?r!2GQB}95(UOgHhpYa5KZlzhM+Q=ipy|wGk3FUV==0A%|FMsK?D5AR|J>(3_qTuh zw+}w};5WYUjbHxdUw-6~NB;cJ|NO82`mdStWXA&#=zq`PK+oV{_o7=YZhIFd1K4h9 zV?^(7yz8>8wi_#crUcW0z3vOR71*U(RcYG___Z^)fY4r&t6aNPP~WPUcexC9ZZst4FXD+gkWvATi0`GDrxT>8nU>SIBB=#CnudWvj>@Y6Z4f}O0Q!VTmD|%vXt2-4Knqm!t+KXWDi%>e4i;<5McvBf2 zh!}v0QyFHh4p{YC)R3~{dN3fNqAnWp)oqx>8ACe4T|SYR^bHL$Cz6ASL|gR95%c{l zGa7D&B}QNDV>PO2m zTBY1F$1`b9kEMJNu)Cinj+q(U)z-CZjHc}?oVn7uIldUS=jXv~n;SBfG9kn>>Ea?& zyE{uu_O*KVuF^f0(N|WQtE(*S_0pr|W#&p*wg6@;<(^Fcr(Th+tXMG$GpWgG)iiqN z4qM{vBbdd?r6of~je0adMu>qVvt_fhd|ie3Z+^{_4?zF`AOJ~3K~#Q?wx*~1 zAsg5Fj!Kt?x4erofs6vy5dBU+MMh z_6&1JMz(L>T<1%RGnS7Z-?(;-MU%~BdiAR1xOC&{)jrZISNbS<`La(pFJ0<;;DNOl zUT8e{puIhD!rqdSpG{Am+_-qL@6@RS>8r0cPMx}U;lj}Q^Opa>17Ydr#fwlIO4f#u z#D~6IxG;SF{Q9e}_9e09Ik!iykByTjmcMoJrPbb(6R*AJ`=45S@q!8M!b0E4W4E4r z@&CHG^549aGsYa3|&!ot!VdR6ViOh@ZTbh*<5oR1 zU0Uz$fK-nZRZSpF(>t@X-_E2I(5s#Zl~6r~yz8&M))*e%;X;9dftJ#F zyXP$-tT}Q73)!-kUiEI=V3f(iF+GJr2&yrCbkwuHNCC|bqkzYbN%c0JirstZrNPsu zw~|U%qIw7_BArsb%a<`_c3Z@mGbmlJ1iq#MZZcp+%xcy;Q2x}ZwdbE7cIXHZ`Kg+Qo}ogM+=}<3und4U|2Tb2XuQGHVCT9ZH8uG=5=@&Yw4qI3t-= zJ<62$J}z3kcl9a*iU!fK$dAl5%9vHaJl|)|1-ZI%g>i|U0LtRNa~ua$52^0t`%L53 z*7lZ`SllpWbj}MsYPqwN>Y;ZT-$#rOweZm>9rgrkw(m@(dewX%u6%Q1f)VQ;Rss*m zHWF0WyZEf{gDie{&>kw#ee`#GYO446aZ|d1f&F2Ih8$k?Qohfr9!iI&BQ|i4>qrm= zSya-5)h$xJmhU@u3^7AE*DqXxW)mjCiEEy%$|&U^>YZh}4Yn(RmX z$)Eg5<7Kn?wXc1R>C9jM^g2FGHN<9o!!X4@s`xPCvze3Nixtx0=*bCXhCAB&h*df~tt zVpURFRM#{U&}eYtEeA5$q*psX-SIYw4iGs(aO(?GH6mS#_+mtxO(Oe#O1@(N2|@P0 zkaCz3fWJK36qI9YZmPGfk}5BTx?QlE60o<-WIG7ilC8HYlSs9L4KvLUZ6AW*)Jgfs z0TlBsEgb69_1IDwOn@M)_Hi!mk-Hnl=!P-f>9diKHT!%?sh(G~tO7dz+AKb^n@p1=mDD}ABrU6GQoy!KN-~)bkha#4c9e_R9Vws` z4Inh_l~FpSb!4xAMz{jia}#fUm=wYVq;3wlX>mmbbWIymSko5~yyfuVz=Lxw#oK}j%yN{}So>rTcE2u4X#WWa6h zHEJh}r12%CfJxa%$bcy>qG{Au~===&r~nsSz>vx^CUQ(4YUZP5|@3kcw_cy>;c zPMN>URXrz)Y2Qbupav$$IZ7emovFb@0W0`E$Y?f1s(R4Ew1xpGr!TuC3)WUTT8AbC z8}6&5AAX4VDR!h(&#{u(&6Z#!nVT$G@VxICou~6kYWZ1Z$rC>lQD}v~Mv4yOXT#O| z6)NOJ#kOzi$~(4fvM=+CU;N^4{nl@N>QkR0W{9YuKmF4`<(!D0`?;U{z2E!2U;M>i zeER99Kl|Cw-oAbNH-Gatb*uoBhP?xQTU`V9?(RK$uHW`+3p*xPH#biN#kB|m6N*d;^mFA}#BYPWn;RQrL1_49ZL)uRn<=yiM7mbl zCe+(|#F_ZIzZOX+CmGf#0=g~)$_vOMp+f!rgF&0W4Tw_^W?cFodBldT5x@DBm7{ER zh`X#sDiel<`Z6~^e=67*I%?P)9-%^yKOTmLYT>XJ=jTtJJQ=9|49;+NYimr~!FG9a-fWyb%RndlLvOLNUQfem6Td*Vi4(ElP$jHzq2oP?*BZiLwK%7A8r^hQFKGu!kyr zkpw1FR|_e%g)pAOegfjgRr{FK1?7O)Y>f*xYIUa;5USA^=23Vkflx}Q77S<2!j;a^ z!7M6DS4|jl*=N0R0Z)+vZz{u#FLF{}ZKX06ycJYe{c6>PHEpWq_P5+L869?ZSg47j z2Hf{5Lyv;E115M(3rTTe*7BjHa&;U%x+Eh(Sp#h^sIr?a_LrfHcIsp+SjFX&+>gH;5GBdLi>=GSO@x>O@?2XY%X=ggOREAXr zsZ5spopJ=hz8*E$b<)y-zbcbVT`Q)!Zf|>-eUz5jRn!bygH^`qJ6kmMVchCS7UANY z78>YF`GjIQVIBT3g+*IF7xxKLUhCjntHmBxw#(~qnL2foOcW-jbjjJxI?#lPsm$54 zJa-U}JjkK|QP{2Hy7I~-an3#q2Q%z!Gz`K)Fr~2O%aWJI)xk%7F-)W~b?T=0!XOV! z>>hd3IX9%Jk|w0rYp&!r@ut$F`V#5syykXLnR1L=$59z5z^Frr<<}@Ck=1e&&wRsI zCFVhBY8R$3I+Cb_!J(FUI(LO$n5PS{UAg4CpTkXLT4&>Z{No=Na+m;ufp})D`6EB_ zBaB3S4qX4h2R@)2Keb6ol%1^_7}B1mjlOkz75NP32ME^VLBpfm-WO*|-)IyJd2Xp+ zaryi=PI_3Y<(n}Wh{r_27lMgH!*&_+i4)ovwTs-l!>61v3i!E-?96NJhz)|H);XBX zMnpjnz(FRJ1xhyP@MnOKh#g3X#jC;PATcvc_VxzYg&l;0VJ?!o!%%rHvy2&jx$@n6 zTw>YX67m7dr9WvB9DkBKB1YV#8K?h8R^+mNX zrwTd_zyVCWJr^cn21J-}TF9O|yTCda${p+&Min3-G;xjM=~_o91mU1|F(5HMS3nx< zrj>UH!bEZcCU$5*7(v>Wxu2bQ&h2&(>%7;hb!eW>?J%zE>A(RZz!5$Xop6P8#CPs~ zMy5;OND1F$(VT%MGXdf*ndLd^fI7dqu|6}qu(Q{H{Almo+yYWEPbg?l&&=fH@afa@ zv$GgDUE7sSnLyPvYBKfrN=;8sLug^erCwNsWXd)Ga#ATM>4Gmyxo(cQ^7Zu<>W+?b zm8CkXgP&5VuJH0HjY0vX)Bp}XbS3Ye4p)hy%O--%yEj~KB&JG)Q zee?>dlE=aOSS2ul{@LzlNu>R?x~|YtF7hHYes*?=OAg1zmgeU@ z*|V%sjxOw?U1MuIMFS@%C;1Qurt(wQc5z7;IVC9v-OX{9_~Ih>a}AG;%}h^Y^tlSM zD5WjlCR0js!51{7*G!WjD%mm>b0)4`;}T17s8~LQ3AOIrxg&lFf59p0i_f2%o8=PA zFTZ?!W4HI+55m~S^sQS*&YZbBKgSKbpMU<^um0#WQ+J@rkSJMx!=BzsWspFW$X~Vw zm$UrMRXzN~JW;Xk5Pv?>mfF|8rBqLnR-}4JHIk3)AsduQ%5zoKLpyqVS0^XA#%`OC z-i?ivW@QyHTL3koIS@cd_)&!%9+H}#QoxkzaqTU}{x0#beSP=b^&~6gI17`qJXzJ# zC4>+ezCunKB?rY3BeKNLGKd^3r&qecBS)rh+~5++h2>?IqDq9JMD-8}v4$IHOVlkZ zx(8HaWW-eF%$b?l*hvx`vUtL{pH2C(N=NjjPsjTSxP!=SL*5M0$mj_ge{A8soNA);; zWov7Bagq7&RW6Qk*E^@=IqPmxUJtiIzfdK4I(fjXW~JSp`@2;Pr^#)Z3k#fwyw%&w z@f6;Tma87Bn)H1@1Ak|S%!bI{<*J@YktNYGwhJFLF+pq`UXRb;tkNkbSM{)&C`;1Q z9Z*K>q+LOrlcq$7y{B_v0xS-iJ$iH&F)Q_SDG^01X$TrZ2f7bKV@90!d3ICdWyIQ# zfBiU8lSV$DPNs6`x!xk0_PNa0OqY$sDQPLQn{KLxPw7^U^6R9K7~J@&iBw8HkdrCx z9w@_ZWfua*b|og_1c#K4fP6Bg7JvFwB>_$jT_6P$J(ROt(%@4n8W5y}|3*ew?zPP* zt~+Wji;PE-GRcmsn@B%>w6!qzHrai0(%pAC2Z&D&G#MT3H)h!!b{K0Qg}(Vb1af#z zrsa^ahLk5<2^}^esgUNvhl~tGg`jpNsMu5W+P6~PFWPczG`6j$ zm*DiCox7IF*aP=K`(MqQpk>P|k&%xA1~E5*Ck`MN7y}R=Se1 zr)OUVj*x>b_W53_lS-#&uM5%$&rm=xt;i+=i_mup3$$bkDU!cz0U+~(HEM$DlutI7 zi%d!Zsl@~dP_P;(Eu;oQO7%oW^7YoIKA-Y5EwUt;Rs9(&3Iv;mcQ^=gM`0*}531BH z7tZ8c;P|$bkIEPhPHmHKt2_$=82<@=vSgVs9Q3Ltob(h==ai_USZ2$pQ;x>5KjY$kj7*P3pG^t7cFIBW z5=)64$L8&EmZ1x?4gN79!6y#4w+m)%IBr|d4)Lw*B7OMb+SL;k^j*lOXD|>>|3dB~ zZx};VkXKoy5KJ6FIR35w^l7g43W3nB!xtgcKoi1PqgtG}If#UmZ;UIzQV^J*c;JBt z3W`+92F?y=6F&1yrP9TPgh_R3sN}uPolkz@#^%=cZ~yeOTWjmxlM|>6cJ1ZK<@bN` zYjgkW2U$U}75B<(C=7WK5@u_vW0LOEy?a|TGZygccg3YkbpD%J6O>m+UUV*9P=ebd z*#X*j?p(q9O4*wH(2$~1g-oGsPV8gX2Vu5B1Q8>=zGT4H$k^u0zjNm}WhRWds>STf zsknO%!Fc=1mEI?wU?32N2@sg>!LU~s>|8Q&chDfv1Htt67U6Q$5(4rF65Ql z=A7@7Cv{*}-uupZXfDe=E#v9`vQ#;Urq3cAYo*o7FNK%UlnLyze_;C7-_Co-@)}^SdCqGCe;2W z?mAGwL4pA$wW!f1nnj*F@wBt<_T2Xj&q&)qk0C`60$V$)V?^CZm9cpMG*{vgDkPe> zX!o%?`t6cVRd<;WXvgaEXMHGEBPpo0AviQ_Tb^?^Ry~Lyo+(iTL1hsS5hTkmJ8Vc0H9QM6 z`3Dfur#5S79VsQ!hzBRPwIR3)5)Abv1cFq?-P0C{NtEV-x-en7SLf1%bjIpvaxsRv z)D81=YLwkC@@{f8F<)pd%J_}=!%yWyzi|T;H{IsPt7!x&ZDifpr zPIF^=VRbqDq^&Q(pfF;>B!(~E*@a0+i==I57t$9eRNTd7CkDjn3(69#FTQCD#Ex)w zqJVdHfvj;*>=YsA>;isj*Xv6F1gj#S8|~==TR&LsJ9u_eqil#-7@5vm30^a3sZ_u< zW5jddh~9nSSroRLn+yY@S}~XiLSt;qwzI^c!7{pbfCUPl2y*nOU4ava3F`|nm|z`g zzxUWNf;#C--FQ8x#WqI*UqFLDV`ggxPo6YE4T^0SbHPDd6@^tr?zp<#M8ys##|?nE zgOPc(Q&DWcG5db(j8(N#4pwEDyJDzUrF<5eYW1IgGqxKqlkkk z)U@~#hKV2$R2Lta4jLYlEaf}q$chfI<{KJj>zmO2Z;4sQcC!UqRON*B_JU7c7OT9L zYwOk(KB~p66$XUoT1w}8q*~YwZIKEyAi{)AmwWDxK?5E-t%wc5r~)K}CazJAWo61a z5P~y106|LqTw0d<9diVv!8E+^2&INF(QeBSg0#ES&u(}fE71}4OaZkEQ?$03d0#0b z($j$hMBqzRlRIIg@QB{e$n;9oDlX)eoXGU)nfYB8nLax=KiG~;pT2#2_{^Dk_JQ^H zvr{LVLPtg6B~yh_D=L$KUwo(L^VpGwLMBu8c0m1P%04Y}_ERF$iB4TxTV7h?>LlXz z;5|Db;9M$&oyizIK4@rEDf!TV=rA4-J7y{P@%=}d3eY2yD#^43gs44czsARDv}<&9 z&4tP)BhypJK-grAo+8r~=*k8`h#-_w-3PZUiO69|LL{}6=sd1Rnx4K(Wctygi*s}A zrxx*Z86=i5g7M+8JhS%;k3~4Af+#Uh*>Fzqq)d5gk?C`D1l4?VMV_BrAUypYNu zoG6z+RUoT+ZmPNH1DIdGMhO>f`0Pqw2NPkl7L7 zkB@Jyt~R;neSMwK^rU8GV`HV2DFviC&_GCdpn56}H;YVK^{^)#KkPQ5v7^mLre`}| zqIxJSg29$J%D{S@25d zaykZU9NvwloUFnja(y3)NiF<1KiLrP*0UWaC*Sws@0>N`WH&{o=UY5?f=5>Ml!Hi2 zOiUmu)b+|(PxbJRV1lP}k?Av?JY7oZ5KE0c^syTDW#tEP-@&t+z7NdKzzMrjoU5Mu zEnFW~5qLIq;sl>dyh0j^QY&e<0q3N^f%GVwez|3MMZQlpr?Q_C#mVgE&=5F*9C4JC z{BqIzZCNy88ALI6sNqTA(==QIe0}n<`8(@f8#^X}QmE<_1`{v9a8~%h}G!0CzSx_naOBUc6HK8F00vg&WNT!J6(_Cw?j~?BG ziL3??GMtD^FY0~~xkMI;M^Tw9gT!>tL`X5jG(4y6w8c!!4M;_e65#}9@@-P}B=D$= zwHu^#f}k+6lu}nzSV7>L2|B{PpiHD?;Z*6JC@f4=zPh6Am&7!Uj4-m{lt^eIe)%+4 z^~7s0393b37`uy`MM{^-*qf~PbBeE|oMLD%GH07Xv9 zwWYN0KmqIn@!&2p9S@2g3PP$OHC==x0Kr5E>^t;g%afy4csIoO_;I`xSH6{yRP4x2 zK?A0Mx!Aq0jO`HqN&+KK$GSuXPe+ShbRiMXjP*spti`LFV_f8WrEaxvpYkP<>ARDY zji;ZEK@~#teG?vLYun5e1F z=4-F@pE-jXg+OT6;foMzUI#$U)KJOQjqQK_mp8b2>wo--H(6x*`0c)@pT^?6GP&}>Pkn9bfB8|jC#%w8y6|UA zgOCK@0d1|Vu5L|F+tuw(TEfV}d#GEP7ym^xa z*It&7g9su&d87$YxFH$Tq`OC#FTVM8L%l-Qn_ zz^eD1JsU$+#zhR~^l1?cXZ!kfi%i#DTtQIKT&xFfH;lQeWuES-r|b})IzQEA>;Xr3 zu2VI?>>FV*o&C65TiiC;dvvsqEe3p27@5drX2Rb_?T96kRZle^pl|W&wGNbYWakh; zyKcA?hkX9Dan@&+zWItu7Uj3vWnj+duMcq*Vx=f$5e5xLc(T#iw^ef}#|)+J#jej3 zwE}C`dxBt^0eWJN-lbqFIdX)*|3;pG?HJg@j6Ia#S z&6X5FviaE@rwRvtDq|}NNIP@DL8jINK|=WD0?8ti%`xUejYK4MwoJzy3NS^%uVcx4 zvO@|ls8F_!Nca{HOJ%?iG2=!bActWh2y)^1{jFk5T+|e^=wUdh7AgFV#Kcqv9d`4A zf&6VtxIW;+X;Q({B{58Do$_Nm-9FlgD@!W}on5l6jvb_cF!7ojMp#o`w=~hQ#Ul9f zxh82r%x%?zzxdoB_19Jh;CP8mQL_4<^+vnZ;r?4FN#`94ZIy}H;>~TFa_8h$F^02| z*s|1p`m{E=t8hTdcOxsvzB{!cU>Uh1;oiOO_Lp=Kgd&QmJTfOou0M+k=jOMn+`9*v zx>+Ag%qtWjQ9VZET1!C@2d*|WhA=sPx1m{g|*C24V&qr-X{JzX}}ySKB& z9a&wcPeadL!c2EVy$eA-S)Tkw>MZCl3Z7(b| zSh+%F>Kj9GnWenyDwBhW^K{5+M^Bfm>%_VBQ+|x6Q%5W%;DPb?L1&k2YrkZH52Aoy zM9%4KL9J{_ZhKTvwPPK4P6^lY)fU z9L>5$GG%W*i|N`losCP-9{iG&{Q$hR^zL2T_1_NDNp6%*J~Muz3~th=J_j6<`E{Rx zGdVFacIHe9el0kJ3Q-)tH1QaPdFMHt;*vVSaK3~D9JO*0ylktS1xC>s;3-kH3kOmZf;6ikhhAVLMhcl zlBt`LXAEd6IG{xJOjEkMxrq|pNksxx2^Et50)$2>pKT~<0rX7C0|+~d!EJbEJzU73 zSH_eAW~~!BbYH3`0#a9u$lARk2JI`|>@3TQ+_O4#>=;)B!{V&cC3A9BPfCX%c|k1~ zQ7^h#)j(yq#PW{2_ci;rN)8biEF4!USy*Or5~Z72S;@trMD^4!-*@1%>m!$9&?=c< zNOLxy5Clg2Wjm_Uo5~{2W@SK%MvGFzeWQIaKsG;A2q~46&lTY=)IedPfu zq@&i(`1qiUIp_#M<+CXU6s)rOwQ?G6hz}0IDL8;h3PO3mXp2|}wwUbo^!N6#yMK@s zN-&`oH)#+mw7zSnnoy=3j(;n@AfniJFcCM!yCTk}@>_DnG8Vh09(<71inRPB)G$|} zB&($OB*Y`POjg=K((8hrX&N1|sZD6ozVU@uN&SL=$zMwKC@k$u3Tn2YEHc>~tOo*0 z+>%8vISZ35Q$i>j<*giNb)`$P&mg!PL9wXJ3Q7$-stn~*WhCB+heV|ogp_KS%IxjM zs0@4wR<~BWgM(bvHuTU#MR2kdNam;(Vgn}SB}?A6Ok!{E?!v<0Ll0SOisO_|E2vBTAyO(yn(Ay5(bi8>+K!9X}~ z7WHMPQ7{B5@=b{vXktGY&JKrVjE=G^E*Jt8;R|SBK>~rW38TB4A$VVC?3Anp5b6pOVO0f`O0H~f|Fb{8ex%X!kAM8B=2o-s-fEXyJAQR)=CGU4U@|q0$}q5q!Q|V-)0ueiHG){gS>{+5c{&VajQEWjSA7M#}fRTvQ4Sjw1& zOF>pwIhr95h~qKdf^G&nEr&*vF%9*Z(25`k%L;sEMGo?sgzEFH8OIvdbdDW67VbWs z@-$~=xcp7AAYnqn7qGrN!>B6#-I$o*yo@jis)cNTi3WM(R51%G*3E@;Tw>|w?U+^A zn3@_qc8o`+Ng7;YX;UveSAl2aW@yOh%FnY_s+K4u2#hti=jJ%KOX^b>h{~h6{8%j& z%-TStGPsIn(~gXGf7S-#DlqsC{z4P-n{#vAL1(u`1fd}yFa>0g)i8pzFu8N5@$kcZ zZibOUwZMeW0KOGtFfo-G8$;lL37lH^s9g*QXG+wqtA+%DD*&3))4lHOuwXc7E?XUh zNtg+u7B}c@&&>4l$y$>XNxCuW6>CEH;v{pM5a#7&4l)aY;Alu+qCB1Bgq$6-rVT!N zgH$FdsLUqBbZ2vVy6^O9)+1_(Xj07Bv__o%A$U4YxpGmD)x}IwtxtDq@vBum+vO<3 zkp=hdlS5RR3_%P^*hIPT>Kr=IoU?}P@z;Dnpuchu`REL@XdWM1Sx_8R1Vx}m_*j#6 z9YOjy2E{Ev$NaDY@tCm9UonGKFp;LEQg+oOWh1wY*V3Z5eW20)6_qK#qQNjp<=EhBJ0tE4>>pPQzEkc>fw5K++Vqcyk2$ItBl0|P@ z%;J;G#R=sqcrHV;nUEqhtzHP^AGfp`S52Jz0pDKzUlc(^Gu3N>;sv1Ey&o4XA{ z4|wxlijOV$Et?WZ=oyc@|yH@}Z5rnxbW+uYdDBZW_ z&iwoUUwdwkQ6wgqgD}>p*7o!?(~6)}1rwF=DG+cK%qhxSq~6l6iaCbK!h#*4;HVLt z3KL=F;gpk?wy-5^dUoe1cQd=$v$#mWFVC$@EPJ~9-04Wdsv|TCK$GCbbhW~{`J40e zeVVEXk`T~f^@rJ-@NF<^_uf6u-R$Pfyihd21T>&Y;2NdcWig$vn$Z0TVjp&l@r1EC zs4uF8MP-Yz&X);*BTW2adUXh`){_dtV)}^_F_Gz*GC%OmfnP-#+1bxkKo2~SQo4%$ z5;YK1FiCy2mMdEFZ42|gy(asDq8HP_1ZP(f{~RRQHpYHOzK()qOaQf}AL!`-A;uB6 z#Lh837b7rXc|_leVclufT3XUEh&klylqCo-1hfXVxyU9P68z*$-i!U05J8oiHwJy~V1#P}yV(9dpBDnFdf?9&f43 z3Np-AbZ0VUIR;0POqutFvipH-dwV?N(N{M&ISs*{UF0d8!{(+^oHR^T>VxTgU>!S+ zc*~y%A8MdfD~3KzzS1+U&OniJxh28xicDw8!^PYIFpCB`Lei(|j*ml!I9izVmv@N> zxg^THwu?I_EQpc|zKGguvmlp2Fw4iDqxrDXr$pEhnXVW;MMI}jh8iWoo>*18cW!ia zdTGf=rV}EH2g*(H2a5hqr7UxR1DNE#Evl8upSW{p?DXjv2$PeOV4@g#52vUvKHmb3 zEi(OT-@wp!AK&R49GW4@{^ZHU`Pto`{=a(h>My+eiP_m1F0u5Kg#NkI_Iy!5E^30} zh)k7~#>6D^$#J9F1 z(@RuOI+#*Ge|sKH^9ry@9nV$3eAVMaQIYA+o?u~8ss|}@NksR(0-|WtDzbRF4Dw;H z`$|XXD`$NWl~ODQvg&{0ICj9Jxasf;@;BKx*V4l2W5iz+1x%S=v=a{uk! zySX^znvtUZ5}7W=DWP6D%?CM{Vs+Ikoi~-Wd>>LoD_NtRm9CWUYpEUzNQ{oVIhGt0 zFeT4f-kLi4071fhFP9lt=fecEd?i8t>LiMvG zY7HT{7-eK+(Vgg06q(-9_aR_XMp@J%3+(fgz7KUXrQ-+%?yzlN#avxDQ4^YG6oFvC zQ|J>j;=KREO?7}2e)Ge0@~p``{^VIX@SfojzvK{GJ@01w85_)chff^qr-CLD7?3w7 zCx_3U2ZC}mCzncrppX7!N*VG=k#c_62xJ8`vL#bFD&tckS?mTpw2mDuE&`hJsWQp| zK4R^Elu?U5MT?*Yn5ayud@@t7d;vh|?H>E#I8T`Uj_y2gWfmcmLcn(um4htMvm!x6 zMxP2!*PS~SE9)XKL~8Q&TqT8})IxQ2kEI}p#Xc3&%TjHo;ijcy$G8j{+M-Tn<=tim z;HOeX-spB%T@P*Z+O;E(Jdz7P+hT5xK2)$Q$+zWLIYnS;pz*~k^TR{^KlJ2r4%=+r zx^=`w+O9Oa|L|)Q|MB~tfOn3nEhS3PnUcx@n)rg|>fB8S-}qaSrqo9z*@en18s~26 zt(TBoRn01(Boj1nNJxlRIxn$lB|WHm=!I9hUA8-O`8(Ipp+w}ml3sWA)wTVUEZ~7) zslfpu$yQXEq)qU(q<}(xo7>S?E#;!Z_C1zaQm9kee4m5k1UM3sR675*oK2p&Td%)9 z$oawi-M*gG3qVpZR6bWw5fU&#G9gH?(gAt%>Qw}lFZrTYI_Bd7jcQd$uy;Csk)L|)_OQo zVvvOzR&Skzq~tl*-FQ8c%*)dus6|dKt4CfdtvS1ow;^ZO;6_dN%dL`&OcG&fE zLF!(pm1+^b$WI4i;+FNdnNAD6K2D;G@01G+ z0wNY^OJsW7ZL7@F018>H!j#-HOlewVde}yqHpD{-#I`V77F#3w0y^<3W2|9WA3?1z zoSwoM5br^A$zCN>2BfZn2(oH1=01eTbS?qmxIOj-IkQ#;^K2Q-=4gbGeOo2RJRO@) z;!bkVOj8URpvvL8HY^I=f1=JJAsVMv>3dNbJ63{u!Jtqf!Nlik&|K6+hkSXug$1^N z*Od*4D8nsegXaznRd{_r_im~)O6kv6zVemVUVH81AOAQry}$qazmHWy4yHwzs|Q2Y>JfHSTev(1OVHjg2iuF9uEj3M*_h^Zy#}TT69+;`H(| zhYy(VU;R$SIM1#DFc0t8h8L-IoJ-d3o zYrDCA`}WXV-omp@5-=IQ+qJ#Nt+182EefER5|T=F4i*vwi%jRl06r8Wd?&5~<<+E7 zu;y}@gGHuuoJs^H`ZmUI1rvneR%Rypm}3q?LsZqK0!?fL5JW(T2k(F6k;uq&FhOl0 zh+x(wi)xw5a9O#t$tJpWeuBU(#u*prQ>SXHtRS!k*JRSj9bpy>2Oaa61oMThQD(|? zm_tB+Top7IXUBE+A$)O?xxKQ&sF%sS5D1QjPykQIU?9q{tza}nkR!vxwqDb*#nKLfGZ8s|hNQh0< zgb9gjcg&qv9|%F%vcuuQ2ZJGqFW>+sb*TaQY`02Rw_Re%DhcZ~Y(B9=r`evuHJ0v- zq{_S&Cbl%8 zDOo^dSqv^GB--Cl(yt~QTm<#7W9GucM7>?Br7wUBQ#w~`)U6CgVqz+zUwup@sH~L* z_M%M<^u=6E95ryjhDjV>h(zlLqXBV_0(=+fpGE0KWN-s-H)|L9AZna#y4K7Iz zR)6yaAzx+t^lv|fd~;`^gyrDr_+bS3sQS!jKJ%`3y^B#MGn!DDoUeWDYYa5s`qsDN zN*RE@{PN46_{1m9o;{28{_>Z<%mgPB7><~GJ%a;qw0m)fwH5Q44jZ<7Zr{1X7z;WX z+xXQl%|rY^6Eix3Zv$ zb3~AP9D0vU$wi~M?mqF|v?yP|e0nll6T_P2)zuSxH3CjktyNl2mJudIoQ#fQtnHge zDqS6pgRsKE$a{P2?F)gRwt9P;|H;7SheOPy*jJQcG+v?7asZ6Kx`%=*^DG1yMf`r)8PZ(z%G>piG z3oOmHQZ)zj&N1)X+YCB8^aY4To$cx(PJvA z@2wy(yUD&FJ3FjC4km=I)?Q!V%KSVVasv6{xXIp4!-Rc7VVJl+Zuj=yb1Ni#6SQWb z^7>w)J{$(Y6&`lhb=wQqmRRTMux8w1RjF>S>Y=&pKf&4gV_0f-L`@S}%N|%ZErzoO zsMNXK`O*Rk{v~)3`FP%>nwD^b;`z7Q&X&_wYsd1ds2{p^QK5dmmyRu zg_zf`Gm}qD44bAQEQ1MB#xyPBU%lF5%IGMpVF)N@8RN!a=p{>Eb!=67@nYlT$(>7= zfU|z_B2!QsFTadS-cL`SwC9s2*I#}aE4=>FOH44Yf9qTO>4gjU33B=$ebn-K{^mCk zn04~A>6tU@FTB9~K{nld{&{Y(TYvGz*4uOEtW2%+`R5ytKDznptF<8{@ZsZiOPHka z1oPY;(ccT-Y@9v4@Z2}PzS8)vx!2zEw#V;%(>FGM_0%fY z9c{k+5|k7@({zUN(Er#mOU0dI$F^U6bpV=+Q{r7)0paYv>Y;}4-OIphubFI+4i6Kt z1k6pIk({;^wNwuUM8>3gDLG6iAUpI4tl68H!MqY`W0EYXmW1aj;MSI1K)~hZ^Yb?3 zMPWBLQ7BIHZt0O%J$Ib76mVx{rKO2dy@UdC%!Fw!mYTh}&J&yJIUR55dsg*8OsYpZ zHDzA{s2exX&do77!S0}zg~859^+-J_+NyUz0Z}?f*yd}m5uDdV>15?R*|B@rKyMG7 zg=|?%uX-@iF&9+=8j5C0C#I9W@=C7iAu6VNqz6@R^PArsI)8q{E1gu&1Ts(cP)4s@ zNr8X(;mwN|52$2yrE_ow9(t&W%2%(IEGbORo!hu@q5rYRvSy)`1Lj#{LJsXFdOdLN zToZ2vl~QKPQLj+-Fn3U1TtG>$T;VKI78j_%&PDc^^=#zZI0 zc$x2W(#U5sb#2#{a~U`eb01#yI{7|RJ+xzU(@ro%U*x=$%AHm4eQf*24>CN${n__` ze4om6=Z0TaG&XJUenB3!n7-@fX}&ZK-F>e(J1h-h5&X+Cab#KUH#e2$h8GrI*+S)AEK{)hqUW42PCK^BE}C zLyTbbV)}g^ZZfsW)`U-f@{@dPe*NoTXKUuon>XM4-uIq6ckc1WAAjbVXV@6`g)e;J z-S2+)SHJqz4}9PQ$O%j8ue|cg2S4~hHgvj08HU_@JB`7eu?LQy80Z?dI5IwK7^|@< z1`TxcG(9$}*G}24XU7Bjlz2~O4oJ!Isms~!>swu08(}Hj)n%}6(3BDc$7Tppa+H#< zoOTL?UIxv}%j}`H6=W{^CSH%X?n*jpq0;v3wi5ei0^oT@Sdm(vGC4u45Ig)R2^<5} ze!E{xnXZ-n>aGc_fO#xp^bi`Maj?-ANc+#O@w{C$3)NYa86Ti)rB(n}Y^R?>A(2=FhO}>Ep-OQ7|Vslf30aeeYRtwL1 z?OOlC4;%aP%Iw!7z?m=pgDJ)o2*kIJo;}OQYYReN$!y(Z4sf`6nek#E1cy0;b+)67 zxurd@I?!S+1>AOiqrmp=-%M^E9qxa}v*+z2hoh;^ob4SQSyB`d1YnHox+{vW1;Fv23%@iVzaZx7s*3gmQ3DKs+UwiuXIS}&=7Nq zn|pi1+{PgllhS3My%w|;kn*NGPRkfm;-9b~ra@jA6RJnaq$VDCZtL$Vx3~B9qB2s$ z1N!dDv~p6a*H&B9O=C^XUC}iY*SwDJTcHFoDQS%0U@5kbvnod~l5aDrXUgRn$6L=_ zM|{qyMP?LpU?Sm5x@n}WkhDbF#rE>7GV==yN0_aFl~Ou)dVnccuAurdUIASWqi{xS zS~5xtd`4B%3g{p#Zf;t@Z+(R|Xu`@3*_rOmQ99vJ+9TECTV}c09AS;6zyxKfx}PIs z=xzyTOf&HK<0+M_sDM(mEtc|jcZVSe(v>pcUiAumpLsf_MfvpNcvm9RowI29xUA|q z>CO5+sTj+N+}AwJ4W-U)9q@zR!FBR|+k_YI?I9s)AbAcq$M=EW(9jA~JZyxa@2)#$ zNwQ8qnd5z2!t3p@4V~RVxLt3}G|b3Ty?uppswZW<>#khycc=dzP&y513RI7hrciDZ z4$lmB*?(fx$SPg2>Z$G)+pF-`(7{cb|d2d?T)UqI>m?`l>yw9BeFk|0TA`?4T{ZZ|VwyyZ4~5aishNzc`y()6 z{Eeib(!n@TakJTE*K_0!3|pf_?t^wugM(ITkPS}mjNE$6+?829rosH`PfhA@v>Sm! zXrQgHwFR#xj4q6$?fM9UpjxDxoaIzsJXW^5{>i6bdGzeqfBwFQ*{4asFQ3uie{FH= zCw}KE-}oQiKh(XiQ04s+4dYdYNL~Aw#^6T$wo!ksq7IcYwaoaNxf$F{1R`Bq#J1aw zpy8s*gpCbkgMy7b=B!cn;jTi_XffAW)EH_|7nNaa0nRi`=_K${nJ`QQA?EyqmeS#S zIDsg+t+qZw5MjfeUL~4fs03ZNKL_t&yXSfc<#i0I8_Md2vQ~i!m12j;X1N*h= z!-@BFw8^N~hHG)o4$VbrStP07iTxU^erVJf0Ho#VI6{HpbwK?@l^9RA!mRVwR(JIM zYM>K2a)_b5XC+O@$qy@?0J1DN%GuHD7y>C?=h3qsg_Y8IPr-qg82 zh-y6?Txn`qeCzb|Sr;2uQO2mnFnM>Y$>Km`XPb(RU}dw(zPI zN77)=xynn6i_oMxCaEr?ENto+eD+z}8Xur^jshF^*k-)hAAB{D1H!&5&QoKxG_WrY z2>am>4~2tNWX(Q790XP*?ap+rp$j62Y}fMqJQkJXE&~LtC1h-;xqZ8z&bWY*+I?}7 zO0A`tnUn3y-a^FP3B0KcXfOm1!UPBy5DVc7FvxACgB|So8G8HM!*Eb7uI^yHPMeGa zsln)qV_0t77<}tn!z4tQY_6{pi#^~X(`%EZ?dez$=y~d?7=qyG@cW!P5(5MS!sl7T zg<#h1OIuJ^78e9O{WZMko~ewM%Od*l(1LwU)fw=@JM*|&~!g+G%gvmh=d;VkioA65yNJts@>s^&wH8FM_~ULh}>+!o=0Ji=T)R4@WaQ;|60L{BxM_dE6ST*C&WtwjDt_ zw=akwvR&9Q5ky3;+b3C3ENW}6H>@wN7UJPQ&svn}giThkO=a2_*k<2W$+1J_+U~PT z`J6HY*FE-FPJTs~?5M%UT>BKMW`eSGYN-s1t!S=+3z`LUnDFT?zGPeJoYVGntU-4! zTvWoL5#{OHknJcKks?A+ZHGey5UEVx`SZalBjzF=99(Zb3+s#Pf_XXzL@1rFd7d{~`$!E6mTS4Q67O0W6m!->}9 z?|$*!`FUHCV~sM1AOge+6gb$4Dq%k-yM%5G@+AN=L71qP0>vab0dO?s!yL&FQv%el zfQicZ6fr{xnIqx*2V$h&R?jhgCKAhS#I9Z|TT++^EC05fOSDMiyPU9Tag*J4GXhiK ze2BXPlA30)Z2%=f+P?Xb4+rXF3Mt?QhS; zp;RdBrKo{b*SYuAsgZ=-!enP>V|<)TvBUTxfT)bWtFR;T=>kqw&C?Oj>Q8b=7pn9( zYRic<;hs(^Lx>*t`NeI>k%R<0yB0%bdU{JuF z7Vt*7GhK8f)#2?turm)cEy;uE{PdPgWYKG;9#{mJnws6&>pyX{Z+g0Yt@iNn?8?dz zS6a`^5L3d&P#Bg<@d3KfIhp#4n=#F75@qa*edL+Lcp2OzQye@9=(1*|I9gbhN->8k z;Oy=!)?505_19QdDeTnQ{YsHADZfwYIJM+NA&$n}WTt+4<<8c~I?BzkOx%{F9DX)2F_BF!%`3F>)W~N4(2?n-#gP*y=4WTR`Ud{`#j8L0{f{iq&knUI znWeMOVPGd7!4V+Ccb}81dg66f0gPbGmit$z-Bxz;r zqNv`XElOD@NR(7M2u>{st_zy0sGb4H# z#vIj<%5ZCWfns*dNBvo`&4HY!=K0XZ$B&*q&6U!*Z%d3xvhUPIWuQrku@XJYXwIeB z+-1MK?3FHQFmn|!t9p_UAf$|TR<6iN`@WtYlUuGA=N!ZS{**F$xy8ApWIm-4_(5GA ziN1K}mDQ}Y+jD=nimk7so9o1+_w||jxiK`UPLHqX`%ptrrJEY+CA*VVy$-(5h3GA~ zAw;g~i4i5f5B4D>q)8iFA`B@(lG0(-rxzBuIbFA}%l=E+0XMNpNt!&fD9Nh?ltS8aP>3D*qB65q=jv#wPrgOVDlM`)b0U?(` zg6!F$@-P(T#G9go6j0q%8I>$c9fo^1Zg8NI#U47CW#yd+Wn014z?87M9vbIgaA$fh z{A`Q4IWp)7YVvJ4R-)T(Pv2i&o_}C$_?=Ij=-J)l*e@_aiB`6{{_yKl|KSflHq_rI z-sQIIai%TNkg*yz8cdSB%jIuY(ge-!Y@D4PIC~ZuMOxIZpe0jbmCaX<#(AKTZ3WS# zj89Waq; z^UlkFBn1D0ta9d{_#!x2Imyj&ZpJ*_-s0krJe~8x>Z4C{b;Rq1%HwdcAxTeHUQ5;Y zl>#y53Ya^0aCZI3s`Gt%>l^Y9zLv_Q2*TKu95grCkyOc42a_dPY-Pjc_Uo^6F;Qb= zWFNx}S#niRY?7yg8t_zY)fNv_wYPUS$kPc2NhZ%cxZzg#QqTR2Os_a1E-%IuFT3xy znmzY6`X4#Jzt&9r5T{gP=F6*>ztw~y3@$HL{+2mgyE8p*VxP}GkU`eV+bVf$C$+LV z8nGM?a7`_vR3!~{e8mi^PGjjAS56^~j**X78N+{dmEBhuwCVz%>NN%gro{M80JaW4iP%~I{zwR{31T@E1YrhM%& zE5Nrn6|2-uVXZ2hD@%wgVF$vbLeCBq5fG*X{CJ`^1V@3*+qZW(rDAk61`XC(XdPQn zksv9eiHMwbiTh-IunI}DWiUptm=!3^-UYDL!`!@lnV4Z>7zH!iR!NS;jD-fx(*!^e z=D=Bd`DH@&!yu>@u7cGTKE#Dpb((OZ!@s#WbLQya_dPsDH2MZdJ)b>Gub0-hKmGjV zFTLlXetlU~P#q z2Gs`Qh{pUk4g*U=ojOHR6(p*oa8gLv1);Tkr&wzH!K?s~Z zcB0z8CO#Bn1m0AJ8_(TbOO0MQj;LK$6?)H@Nlf%yDRr1&Q3=9g;42~rFbGel8O?yY9?#87mx4VVE341}ah1f9 z)@6z>{|1@eWT(c;T3>p0lO=|@ITFaktU&DSCb0sXSrs?Ci3vb-Vx|?@4{NiVadRYx zGrPIEYO|Zc6O&GO@8dV0lm~X8c=1y;24{7(%bmL(^9!5ZT)fx($Dh6Oy$_9k;@^8D zIlFmfX7#6k|HUu=m+v}0-2ZQTb`x3Wn6o&|J)GH1)+O*6adRYxpfVgpLfk{lgz;h6 zF%wSa6y0IhF@hR9`K`8zN^vLBOAQ0?c|*-^x@#LbDnEb@zDm5xaJ=-PcsdL~%xZx3 z$jxp>YVM&(NEfr4EJH`QT?`1{X0hvd5wn}x_6k#Cq)T<*2(z2(YxgFnW_B%9sQ1pD z85TEZ=I5DAn7DY6y_?f)YH<-Kj2g3PYRf!j6e>hYEsg?ZXs_k4+|Bq10r@`Fp5kP> z%tdy0?=DSF_8dRHyts&QWV)2JdZ-Z0raa%`+Pn-t3@jA8klHbATcov7x}Bmhwo#gG z)O&mPu3T9qUhmkkdw1_*S+r?7H&n@$vE8G96TV2v@x`nE{yQFCU7Sr) zGD~OWz$5rVNvRBy31`K%BF<)2&vJ%`r>|ULpD}9+L>(`zP!PN&Dhx1+8SDy)UTB4`ACJ70&3Qow-{@x%W(U61v1MO z5Y=Nsa-IniMtXd*?s5mMMeLmu~OkdwNwvL0UF&0PgJH>yutzHLS?@8wPO!GG%+UJ9Dtyxjv~WVqUe1N3TpO=Sd>?mvuiU=9xweK4BKAT+QmThyma87M7z3a+kew97YN?+2K5l3m z9-eyTmHwfj#r1V|krsst6HvUMxw5Q1%(a1dZSMhCDR>ry~J3iAU8VUo<$E1&vT9{4^8_SDmn zqemNqgZ^z5G?Ku1#&|KXs0$%ko}Js=-7JD+$)1HQMr6|@CfT=Hwx)8RCpjD)(g#`0 zN*~x?&s&UXD$Q*LrhthrnOjC}m83?E;bAtSmXs-n4BfPyJOv@!3!eiwfw|Mw`}xZY z=Z_!x{>M-BHu{O*Y77mLzr5A;|9oTWAHMt1(LvJ!@ueIG-bz}$%J}lRlBV+6l#3Pz zPn@uL6eqsIGZ#WiTIjpafhpL>WKI5||mHOOV2$E-Bfk4vJK)l`()7Ty}05sf?62S1EF9C0mqukW(lC8Sw+AZ%efXIi|X@r%U<> z+GUgl5MXM!r8I1&^Np~W-rL6sLA|T%jYl8cKRlf44YqII9Qf|<4x@`|@fF10GG}qR5u3R4Yp6`K2*Jf6K_77hC!hiP8Q=LAi^g50~ee(l=O_k54cT3tB8 zgc&U68Xs^QKx_Y;f^e4)$~(Y4Z4tjHPe;4LT{!X0$3PG*@B(`r;9`N>cI2=2 zneo8xKrEx;gMwROM9-K;gbpM(m~in7dzRz4#b=gNrBIeb9gbFRZ)p!E2p)@1pKvr> z7wm?EpvZ|N+VBPLgtNwxV&JK#*cE4Bg9r-*1~jBvWeBEf12jT$@Qn(S>OvyY*&xbo zX}4ZLP_9~)n00seb?xr#;=gvmt-9DD9()vq^M&S_%zO0FCynKi3887e%_7C4kJH@Rq8==$=nEsfD1smok3E)(@T*H(5I|5P34*wyGP+f$y1=JU z)u($nLEPuZz|&912r6}@FTuTNb&3U+C!R1&LM=RX`E+M0E1V#N792m$mg{f`t~0To z&XKY=>Fh!^mwgnG&JJ99IajXj?7}E>IG*mwCpp)~=v#*xLYTo7UI3ky+QxjLg`OkR z67z50|G2AR^ZIooZ_pN35t`XeCMO(^syp0$nyA+H!UDIqM`FTvr}j_=U?K`^O-?pv z*=Q6+5O%S!sU?yin>TJS_Z$g?PfsS&!sX8~bo1&}rmreuBFZOIDl#*HHhKuzGF5W8 zc{vh-_6Uj^DGbX=?h< zo3Fo4et@X^T1ct(S_J}eMuRY0V&n~GgbiT>G}8lN-Dzp=?c3OpFbKXDb_{D44#ZIa zi@NL7Kjw$#YBzj!)?}5)RK|S>`5tA5LK5+^MPUnHm3QQJ0JI)g257Grk5S%=9HbgjLCfd(Hp)PcFUp$>W@PwB6k4+uE?>3~e#}-p~Ha7eDt~ z-+%V#VBN^{*uxxHqu?H$IL$o-eR0ZfQ@?TiJp`3ugvT6wL~!fj*fF3Gw#cb2agph< z;vqdEFooEKh{$vd2zIQZvJ~iU*A92j#BD7=4UrJ06^ojvm`~7ILr>Xh#Ox;aktqs(nC$ZCJcaTx zRyH6MCKmqErPYOnz2nDM@7@Kt8q&6m6f!iYnDlI9I-!e@Lf?F;@K|t55=YM>=6*^U zBGa#4oxgi`@Z`za>1j`V9|oIc6APosa7#s|6VAzE2aIF!B^w?Kx!7}4mU1y8hoN4- zKE1v^dg268;fm=lN@s=SoQ+k|_m*4$N)} zAIc=*!A@#O=T`mrHo16 z$1`$*n%um3lcif~p>*D8BR^RMO!+=jJr2oQUY@^wyYJMgyYusm_Og-bDQQP35C-pz z&#w7;*dQX)y&Fw7#B+)712G!KG@)n^oD{^$Bh#}a znrCQ%<^0ySh+A4%Unh7cZR^?tj}qU9NL;yMmkd+aD`RO_3-UCXG zu+rXJ*6qtGYA2z1=-C-Bm+kC3A>VobBnl zb?essSGaX=l|)y$Q$Xeakk@$#GLb&1|23Yt-;@{R$z)1A7o6aWM!+vbB)T(@R<4Jr z8k|Vg0*iJYx_NM@DuLA0h(Ht@Q9kZ!qH}!0=ME4EhX&NuArKX+?-DgM{SB0AN@-A6 z%FA@V$V52tmZG0#%A0zwn~w-+LR?k3>Da?exgh8%Mc$-Q#7)r8L-;49GiRzJ6l~%j zONef-5k+Q=9=UDttm>4I zom_!@I%V<2feddd*=BB8X+Cd?KxfZ}4M*nQ7c&3t@P;VR5QnSGIWqqO^I)HVUy$o- zn!s#+4dJz@ka1I%o`@J*b(n*eq!bZYdf-E80#c;m2*m$pre=~7ou=pD0wUAlN<~Ba zWa2z=#O{>tP=Fxn;qF5`?_|k4St2jdhp-^Y%!4DVOz$8KlqA!yxu5G!qr9Pk2F_Gg zrn-2ZGo=ttvcI3H6xEtcG#eV|L)`773`8RFo`!MdI#Rb^eL#zybd|{wKmPt63RYWGldCL*SQTGaV0j zt`zb_rjHVte(qfP`t{ML;FCEpOR%P4>B({jp+_YpOMzr(C(FK4yLdg=fCrn%bo{P} zOfOluP&o^S+i&&PmxT`6&wydJ%8|gy*r7wEixv@V>!%<733fH40Ss(78Q!$Klg;!`e(%_8R@A@mu7$~=VHKIaVFMk? znJdGOd};rWKD4E2ReU(E3iX(g4y!+x$cK8{|B{Ak z;_O*mK1voZwn45K6dCONg@k_+KYX}o{(PIrbVfQ%&LG!N&EyOgPR!*DmWbX3PG2e> zLE&qi>CIo@>88j+Yu9)@Q7{}Y;~=C-KQqFq;&E<%ixi6G5feQ%f@Y4NNqk|R6M?}l znC1Pgut9xU(1a;5@ErzDBxQ5G0c7*yN5V!03x%sYpz(!`*hhP~7FQe_cW3ScLq%=ezeG{t!C=ii zSsnUW6qN~U^o6%uCV4J<^eC9K7yix+=wHcj1f3Rpy`N0hB=!RT4alzvD9AS zjQB!I*q}{RDFzaZ6G~}}`U1%{+S?nya6u8Vi*L1!OmA!BTr^8IaM=;ZO&fu5XS#8) zp3@fCS;3*XQt1ZeCfV@udQxR~3I+U}Waa1oHT;v2qeq>3h0o=F6CU71q~YN(yND(V zROmU0Vu0JHw3{Fw`h|iS1>>Q^(p7d5X*`Z~Gkc+#eiaJb!WGwHw(@7Dmoq(=Txh9; z*%}g&_|~p2E?-uhSRfbJT1KW5r%xYZDG>TGdNXq(5}AJTBs0!Tk`MVD!}SRWvO)!DZ8GuoK9fIWwwn0yC(NWWmUqhsbnB zIwSZSu$@JDeu1r~Gvszi)D(y%+B-5xSZwgmjz#V*h;^kX)r z8n{sA-m0qbpmVFb!vMa@-))rK_J??X^&j{b|FTdO-Y-rJlm1Z zLOn!9nRCN~!6L-1?ehJ`hYGn-N0sOTcmi`$L-|##xb7#yy>u2trnh%o=D-r}Oz-Tx zjA!j-!nnrAFSWK-FJ9c$-p<8~fiZe)Vfcf^B$kus_yrPXE5W1`6d3y{F`-V%dWbhC zQ^MJZOvkWr>5|+eOU1-A?TOK&YVN328kjL#Ql29OJdIfMq5Z5u`FL)Ob^er}oRpqj z5s}FBqelm@kr0`F{rYISIDd4WgcG_`%5|#mj!c)}L??x}fap9O7%L)Gp^iE-ef0S8 z9*Iov>cXp8njlkj9zJKdt+-QsS@5SJoRiQ|^5RQMEFO;k?i6w1<()n4Br^TfDTz!c zDqLdEGo}78Ro=v15&_cO_}17&rk~liZB}!$O=P;ngQHZ!zi^wWF3$6=j&|-$f4=2p z)9jf|WwElNk_&rwb9?)h&W@t8iWgc>KCpH{vcHGO^iC~Lznq;Xv;ytII|R3)u@ads ztriG9z!exGtztQk9Yfvw2}#kB>FAGNuXy5t9(w3NPa@Od2dxkrr=!pGFxjC8^FVKC zaBz$($d4W4=Jn*vnS{uCkmU!U{{@0uDURy%695kxneKre{?=Et35}C1=o7D}!RW7k z761ZaB*q8e1d5%WFtC{>q1V+_QCoX)_wEYrOxKa=BHloi$OJu_1C>DjZ~|nA=FX|2 z&O1{8SXXyu`}SExrnk5Iff@42!Gj06%f}y49`*^5>F3UsoIih=$aL3n5bn}*){YJY z;2b@A6i(y3sH||k?DX1 zC~*c0iXo1q(xYa99!la>A;!nv-lCHyRb=|?+3l#AK4m4Ra>_fXC2~N9P?l+!OuGD2 z$uLiDuyxa!Yr-oa0Ru9*p-PB@wv^Kl_|x-5W~I9R7$A~^L!-&@>Sc>685W4+iYF2T zlQT#V(HW1$fE;xEDaDB;CNSk!R9x0KK+gY;7z{iq?=!SarkGebS&cK^%FD;NYSZ&; z`b}4LQdiVCO^b;5mC|`4%!tqeTi21NJ3HU8YhqJmvRhPHIj+xjk~d`?IfY?2^HaYg z1cppfCl}w{)%S}i;0Ydei3GdgL=GqPaXIo1l>H5q=?SU=0O$w;n^ki04$YS>B?^Rt znz_fKsw$?b(ZxhcMLm!JP!xWmQjqtD`}-^F>dbg>N~A4lN9r4r2i{bD^mx%HzSHvh zwN39>*HBbkk{BK;uc@JrzC1kf8~?QXpMG;oQ%w#A>1U-a6>7pKiV$a!FswsinzyPp~ zCzEAa8#fc^L`e`k{6qqpSEYelUteWyt@n8p2s86wEH|f6SqWeXRE!rEGUAxgp#woC zbREnmqyx#)Wo3jOKpq|@ydcSi4$gRwT0zx$-3Y*vC86h}7SQc%Ubu2=1wL zZ^j`xNg_akrVu&DJ{jqxQT&aV>B$rTmX(bplk|f!(lPtyCisx*Ds)&RAaKfZI>12r zhk1A-r<;x*$aHGw&Yeu;AA9UEfJY5~_`@GEm*22q!|PxFdfsHk^MfD!fQj~|O`Bf# zy4TT8+rPb`xuG)BW;&yVZDcyPXtcFetXpRjW=jXc z$ZdE5G*Z@(;W984na+KQ7RVJtEJmYDZI|u2E_dYEF^+L4F%9;Yz6*;8k?BnGvK5!> zw~+$}xaQO{G95*wN0s?Rt^zau9^1W}zTGA=9X6Vpnrx8EB4lXq-m2Sg4-YzbeIyqH z8J#MeWUOk4A0{}5;pEARO`B{WBf~Dou!Jbk`-+sy87v$!UC!Oy7I-q2vCBvd*-k?DK)R<2ze)uT^%#th8NJEZut zzjh-x(`B+O{2|jPo_K;0;-QBgqJw_snP;f#ZMWUVMg!d?!^Tg4`co!+JmGj=)N$Fe zWisPb*(b)6iE*w(sjDtxXp{*Z#tAi{ao>oI=Bbw>q!3}{s^+!AWt6;5Sy)Htpv^g< zP-4A^)Vb;qk zQ%&DZDUi3Qu1?vm%qXzXDJVca)WB&*L2|K@sjp`_?gKOZKPe3&#~iunHb zzyF3eyaBpQCE4b{%=xA_z3KMbZ{NIm^WMFCnOWkiCr_|$?q+GLkD=myH32UxRWlgQ z3SnL-|1hMFjg6c*p|lnlXgg@6R0)@@d@g_<98~g+PQfXWC~R($Rh*=Gi^hr0H5iNa z4-cnm=TtNN7fdx>U86lc=4@zzTyMiKoS=&;6SH1iP$J2Qpv@|QS6dNZPv;6 zb>76eb86egOi46_EBbaQLkc28{BaK*GNvHv4G(alY8P52%m~%{mF1vltW4b)LGMh! zuRNDqM%kO^dFstvEiUbn;2c2>8KtWcQ=x!vBraZzn%DC_T6(>|-(1HuvcSt|S67O1 zc&w%06ubl5!Fb%PIzjx zOF>2yIJCk$JPTylC<_l3Pkn^~vrJ`0;}e|+M;e5)bziw+E)A^}#0e$OpSR?MYq@)I zfX400RMS$(wI1oXsyrnhQ_!UbJJX6ta}$@26S`^|BRZ)m#F;ZH@X><8mEK-l6U-<$ zDq%L#rN#nnl!25|ds)DnP5^JDb(5JQ%Ump)d-m*EzI^%Cty?i_zU3`%!P3cu^5KUc z#+8KGC*PTOKL7ml?|8>M*s9~RYuBy^AAAtwzgi{Zq;=}_@P9o!HfvsG=c&TlnIi`e zV%LlvK3vw^%sy1{%$b}k&g`Biq$7t9GdGPhtm4*5T3yXy7OAwVDt_Vw@qH@AQR(E# zlB%i#e4i^SI7F9Yt2m#9b1PMfiwp6y;Tm(lCjQ$Gz2jb6P<)Fc@dDTv^>$q6R7qu~-hGQ$21yM(DiVL;dz z9ZfL?lfvr61pA4oR;oG|FS62_XlpB!%92XO-J;;T77Ua(-awUy&@V=XO&VNFR794MMIC$@h- z>G1yjC8YcIl{7X^N%!vM+-&$MUA%aB@805h^VnEtzjAo@?trwZY51j=N){~er{kQ^ z4uxI2iurZtPEOG`NXyr)i@`(GyqX_XuI6oG|NgQ?i@Y4dd0>sG@9sUN3!1v0-Tr)E z@qGhlmaJYfL?Fn`H^*99OBc>-e`fnnx+dOT++N)@FSc)gaax`+Wb2l8eSNI8wMeJZ zE+y=O9Y0>mJMOkiw<&A=_>m(i=m9_ndT0WNnQQ3L;-j6NnDp2mRGOomKuA;2^8ipZ zkW`>Ydf@`l8@qG~T^((2M|;(tIGQY4Ezl!H+ePyQdZZu%=(zx>*0S+9w@)e@38n;k zq+m_sj$@nTi^8K|Q=pdyAfC!a#7qcTfgUMH7t{;%Na+ovXAtO->NA*FsUiw`164tK z75WzGXPk-yLkEB&9MZJV5TFtx_LXzzxDzPBcj@2g>m1=rPe@AB=pHf!KnHq<4oTyd zN{=cE;55AlfDH`-JyO(00dOkx#t$B>KxrM&3G|dg20;%(I9dNbWgsu9ouV6L%`Q4(`t^2CX%`ugOlQ$>30QzIawp3{wo82cDL z0569SG3qh)d7+nU>?4R9J;$+^OIN|23|lH?1Qaxd8T-H{XBUE8ZjN<`q=(cYQS4_hlI#e}p zUh?o^0ibGDn%LFr<{10Z(4!N;fla9sYNp3NHQu??Kt_-$}QC_Cm%C?9y>0XkuxVAtHTWebze`|rP>C%n&- z*S_|(5|qHnfQ98{6ZLb(s%O_X&B8l@tLTvt6QE^*7(KtbTSa zG?mJUR6XtPYZqLquZ@hX_63vvg8Qru}KP?15)>10x5sQ9VklPoJb5TUq?AfX6IB=6Me$Z)A z2sVWcrpqu;j7a$@Q$u)!BO=w%5H@;-hKg%yKyD6IchhkF5BIXsvs#A&sPfv3=d}?S&<97G=!vlA63g_O&)fJqSgG*rqdPtRDS$~!; zSO6!&Rj?ZPxq@3gctN!$U<&n#$Z^ z4HzoWd-e)b9!t?Jz)<7R*kX(6JY^~e2!g>aPw&&71IpRR^n*D^nZm_K(3A)#C}Ci? z^uSP;Pq0g3%84{+QlE!vE)FdL#R>Zq>>rsb$tV_jy~SX&>|=tZ1|Fk@qvRsRmphW8 zN|k#@#vl8a)_2~%;GOFlp-@09HUr^|8RxIZ-}j}xPrQHg%7$_v5T?b2pgm+5JJ|~(xtLesLH1jXtAnBw+Yg8e;HIu;T<$I62_-Y{m%{K`hD) zcB4l+#YsK}JL1ym35_8m%87+lP1KAuRT_c`Qqc_>>4brP?8F?5gN|8$ZlrZn25C0L z2~_0LSb_}x<3Ij`c_iEAT!;OmAN}a(KmR!ueDj;%%!w65Lldk1?6c4EJ{#^5maeDU z1dEK15-K!WR9C}MecY3RlxC>ANlu(7<}O-sj5U%JM_pX&?NwKVi!*psB03k_WNLGe z>zmCu(YgA#;b3S;1(q5R(OKBwW>-SFavM+}zs}XeF8ug7p$~>cv_RMnVDilbE2=o% z@8fX+862N(dt_eHBb{=Tnlr~n zAZnx=9OSHD8zS79J~l9b8Qz8ngB^ER6CG`WVi?o1~`BB9pe`VBLFp#d{$PGj*=D=n-wc56O+PLw5&_O@FA z(>c;omP3WQ(3a(zomADDPdiXsw9E`#dDwdy3}hRlo^7q7EqyT+;|#^-=4L8J1|BY1vgGEQZ{`RjIc=4>1y5G5Ufs~p zAYG?qr%jptFuaMqlNG#VXTdi{WV)uVQlk$Y0W)AJ$b^8d$T;z>`1RX;IQxoLx1oW{ z0uy!{3gl&Fg3GKq5iJmcLJA~!*_==Thda_b5Rm4MN{K}XS#ZQI)IbvDi9|%oWl1~?)Qkrw7p23Afi!^67{`ZeR+M}Gv17uO8hi9UlKdGy1)H2#XTXV5#EgLf z_9fJxT3gIMa$>GoUMJT$hdD@$yagf5tbo^oKmonSxW1;vOdz7D^kae-Lr!!rN{>p! zp=QLSX(Sg`1NmYG8Oa4?)SASI6QMu{Vpa2c*+cOt|C`oxj%9Pig3OL;vO6n8;#tC*V^5A#lSq-sz`jM5!=s!u27 z7m;%#XiB6dCF}1u}a#a`mSy@ul01snIbW(YO69{zN&!c{aPW#YMU46ZWepEWbiJMQp zXRlFbM_YMC^$RV>o98#oF6b+-uDv`mQe9Wq-PKuKRk;un-8?6`-vZVJmDQb0NL&B>F+O}JC`$AI44qIh!%OEho+%#9u0Iu4?5^7 z?|Ae%C-guESQFF?V|eBQ8yDytWPvpg`aIABq3liya$$q-kaa_k?{sdSegO9T0PJUj zrMkMWzuyf;x;}UH{m_$M3jr#T_Vl3MK$!3T9*-x14MbDYbGo6|9gkPen$>mrawau! z?$FBuy~f5yV4EFoo?rdYXah104LtV)GbC_OKQ_wzlp{T;pK^2Tji41$LZKFYMUI{acK8908FY0X@*8&@5x0zz|SS zG(dA#hwmQfA&M+x-&L;QqKWksA`p5AJp0%OebmFL58a~A!z((X)S6A}t)v8->g)4Lp3TUS@!*MiKm{W1?EEA9kDVY8u6*dYEWg!=@(3E5256y{NJlofq|rA%+SMMN&Q z*zrRr*ob2t3g*wRm^Y731igydD3a^kxpVkkAOR@wgsvgv?QQZVO2P?|qb)6kt5#La zoXOOIemNQiT`o|hQ4oj$g%l^HrPrQ+zW#v+%qU1Mw3Im+19^C9XvrrAlLe1`7ouQ^vS`sFkA}#Y zm0jyZGub_h_w-b+TgN4q>e^M`SP3Gs=)d3v9{A!O>HNs@4}3h4*Mx{V{0kjjx-<~E zeu7S3r}s_ye-*0gtTIhPyFoN3TB(hyu^FSl?x6e1~YYNTsz zEnK-W>PROl$|6C=S*Ps+p^?%h3nt^xG0Ya=1ZL-|2)BY6E4Fo{ttBUDKRhrR8FPXonhI{U^O&?} z1OC;?>T)B9abbUKkKggsU+8$eULN5+DB7 z;rHLQ;GtXRj*gBMkB?30Tca-Y4*$kiT0i&b+O6o#g}V=EKNvp&foFNzBI zRX1YShvb2iWKuPN9dh%~kI}ktqEB)NuMGY-ZPi04CK95k;T7J)iERg>$)yfIv2nYk z0(zO@7gU5Nkf2jF?aHJe&4?8zcwMXbZ8w;h#DE=5x5wJ%!e2uXANFkMYR~| z&T1>9h`+Jsc?{);4AlwixcIeERpHXC6*k1 zuFiPZ7Y!TmB)PDWu-GyT)}vHiG?D3?d`rBZ87IQL*jWv)Jl1TCoIcIDXhf!)u_3uI z=JAC^kAX;47R?DBlXOXP^p61@;RHJ?W(Jm$1{5YaAu?UZ>zT;}I+Lg zuRAxWi{t{WD$s`TmiyAGUVBan0Puno{1> zemglnLJ%6Vb(`bupr3_8Dl&cEJkge*7jr+uv9V-6wUd}oHETp$~K(PfnbeL zk?GMtsWv)xjMbHjOt;VwcE*|0V68C|NQx5<`R6!5yJ-!IDv{|noXDB*WVFL?v)KLvhQAVJAaT!QQ1P42Gqkxu_CzFxs0|QA3v$cp!7f&Nc=lBxsqk%Wp z-90K->qg5wf_?nd}&K3;i;M0 z)p&4{%2cWpnV$#{rHG(3Vz&nqu&b$V%X1zJ2-%@>r$T=XF}FLgj3JpZQ_o{YuoE+k z%m<$Tv1CA<;B_$%i%pu6F%yt2WK@o!nwLhK>Cu}_z}{%EVra9Gqs)y>WI-S4^l*%6 z=Sr0V<7jypUrU-LnY+rUI8+td{EtS1)Pnes_koq^(* z3_q4J1k8Wm_Zds%o?^NB>z=t9XRS!oeM?x$$E&;Ih;W+iiSiu)v9#>q;UyVXqJEMqt^qRk5MeZE+kMY47K$Y*69}*^VT? zkC}jw(G?)?GWRsHlc{uoU~Cbk)V zVf=Q+0@{@-HM}^bXQ*9Y@-n%(_LQd9E|MuVg(@R;mMOhK`$|YgDy9bvhF_lsoCLC{ zXL*2g{K$h9#amKQm8y(3WD#^*!VGlS1O!X~3iqCt7BD_`KMWQJJ_=#rPQ4w(9H$3C zDH7jlW2kTLeBwN=OOP=sqCidBf}rHllXB!N&RO&y`P|I!l*&hhf3tPdn_%!e}ssVMU5ru0U}Y z3%UGwg=i8czpP*m+2`1F42hm1gY#lLBz(a@_T1=1GP;#SS2H7g@mh_mt!ghE?iD+h zCnrD z$*pwf@{{cS7?~nmLTLn5fZ`Hb$+{v+48-Akd_23>ezB{74mB_!D(u6!Gq=mm@S++6 zgGF>$ysI$bEb!Ko5(<%>NCF{a=5{N&C4h_?FQXsaN)A~yj`%@VooKx_98Z*pU zJ?7`lbrjNV%rMjk!6Zeh`IE<2tuPDaD-&+5{z8j zctsiz1v@3Z*#pY=APb*kL$ufX&hf8}4K)<(0{PVQk&>5WBp=($_iqP(mNx~u3(F%` z{*hp%Imzp`&3tQw+!q?%?7A>4NtG24&$eu9yq*+cxv05*||h_U$Bxf4!6LO@8}DlQTJg-*-Wh zYQ8^UYKwj@`u=iVx6>jP*v$W{6m^;BjP@JV9=}Zs^E#g`?E_KNq3-#g4uH23|CCRS zvnwe{psUDZ&$As^BjYqHfR$-Sjk;bkJe~JxvrfE?8{UiZ4-tKVC|l3OXe4+VP499g zT_iZv8NPC<7F~4iDV(76w-6Wc$-+-DY9Ja-F*`eVhXd=j7#)(MiMuVU5gn1_1%?CK z`#y!$;7dSo0-h6H0-Q+tp)=L|#w3w(PEsOT?U`29nB`PVu|-Nm;amx|PFJ`7ywZN( zk@Jt2b@I1a^4=d!t6OgyTfW!HKcrwNXe_l~qB;{kapZ2Dzj~L4mS<_XMu07!t%ebm zC<53IrKDd>(s<&kXJO|t#iVBIAIk|mi;ZOm(y+gTuq1hxf&24daON2F5F~4}B3v4g zk3t}Kq*{>r<2zP2M6Y}8N!~}G6~Fp(J}|`K11tcQ+s+l@7a+0heX@45!BOoyaL?(D z^boe$fG|+;tJw5t`+X>uKdZUUGuEw8F8>BX^krnVVmb^yevm68JT68NecchxACYXI zAwNYM$bi=wi!YRbpQf6x0n;SID_;ku3)NAM(EbVrf8~Pt_eEkT4OMt9vm%_{jf{Fe z7!=1h%28n%8~|IdPZVj8tRxxO4ByOqs$wQSd)x0*ATOnq%c?UJjz;3}LBkpX4n>2* zTG}0wGK%hqoSIs>+B=lu8rI>}y$?0pe95v19=UxiQ5>?30D6{UPaU`YAJzPXA|46j zS<9$y_bdyq!W_qu zV*f*vo?hRV5U9F7XHJcpo})fQ@2?(wFkJ~Z1*&ol?#{;v7WA;EIfQSl`6UD(E>vnN z0qxgaX;srv#5&jxKeT=!%){__QbiY6!bVD|L%;q<+sdcU4U=U0MzV zA$NkTwnv#hPkT|&U`2|YsZeIj-&ND0V96*gD(I>bt|311wi4-Cu6egfRN~U}MFJ-( zY6ETj|29BWt*S(pPmtUz1}#Pm7y=lpUwO|Y)whK`5{h`50;v5%pai2Px~MGkvWz`G z{H98FGQ|x>s9IrId2TubCSBw{PJX;I(1C})N67Aj4zXn2g_TK>2G07r(4>W z#*#AP`?+Q*b@agL)ZPL3?#<_wZ&`-zZIHsB2$Z5?i~pXE*ncRVeLVa-os2&7AbVd| zf>suK!#C=UvWvv`=F!A2tmaP3*@tZ%%eS)Bn20Y_4);Z@X1Pxo4Nk=gED+1HWJSL$%-z*~;gsEwAS_KJD zdDRy#a!bkhRH5Q~`d>((`JBA*x{{@C277uSp?QpDo35smnU|Wex@>7#v$o93^Uze@ zr$)i@AT3zsDw0fN9YInLWOVq1jYG77#0qH{Ty4 z-0i+Hxtx|r6#$#t*EKd2JA{)TMvMM$_cL+Q-=1X|b^_;@@v+n~Lb@gxq2gmPg1%h6 zP$=ZFceY+!hDM9jk&394{mcb9j5HT1QPgdRW=615wz+3pCy#sSb zSI#ap2$pFZPam!|VA5+GGL(7*#khd^|81cB82qrfe)aquNt^R#8K^(rtK6bMBKUb> zfyOd1>qKaSL2oidkhn54#^vQf+ws)!7TbA;P^0L4TaCJrkq;FIBXi20i-)`8?f#E; zjeg|&&eCK`bdp)*x%~$5^+2LX;>q{F278=iEr4aH6B{1FwSrz-G(PV0XYCsUyZ5^? ztiL@R_vwSbqR|&J85hffdda_A)pS4gOuGE%BTp;Ku>yxmKd;F56 z_T?F6%w<&F5>{(dxYz`y-z1_UfB>NvPP}iv{OuY!f^-I5mhoVGSyRSPjttpSUhXIA z`CK*~70y{7u)`aJntq5ghYNt5xz7Qa<3-F_$kXy^!lt>o${ShVr|8%pI={Q@^o3%j z5It6Xy!C*-Q&V%EoqeZISe}-@Ct^7HWyq-EUiw3)vACYYv%0~}LYqoW5)j6ETYE14 zUP>m;B36{G(V{T^KXo#cz?Ueu;s+z`DbGH4fTB@{fPNc5IdeuxP;O;&I?FqPh%!kv zKdaS~!O5`!Ej-hUVnhJZprwFdZ-PQ8p6?}`s!E(}o|j0$W< zxRbF*Zh%+u>;1@WkHxjvr4xrda2eX#6vrgyfcRJpo(9osZ!%}>WIcQ;+s~?L(8RUT z^5sXkKULz%&K9~)qw!)T^7rq7`EEBq4g@_<lSOopHA>P+)KNPcO#;ik8wv-c1 zom)j5kZDSR;3<{Rj%TUoYn8$KG2m&DdwH|Athr)h-uZG6#4UYNfZfvdK~ArFHRMP# zGtvnpU7&)?xZIt23^@ISdh9CJ-Um8T^!;(~ zsx%xww1WXE(%Ju;hQ?Eer-Vib>lUe{0~9*I9d!d+S2KpxPe=9`2&#)m}aDE zU5(FrOk9l}mu0L6bI2H>O2cMi;#Lx}RP&QSkFZqG1qp$sgKK~Cj2fH#_4GadJswvt z`vo>7xnerr z(sFbHw0EP}@vIIPOsY{IXrrPat9NuoCrB^Ut}8Rro@NFS-Q|P}PvHQ8vF-u+hHb?i zdzNC1)&FUH9dvv7@qX$5@!;?O`grx@{k9CidH4q)P_;&c8uuOMrVDZyuo?9iw6Pi3 zKmEZd=p0q{&c^P=RDunFfe#O*$%f|{sE4y&S&)Wr)x*>2)k!!i=OQ|igM!|B*+Up-;y#AW+?K&|o zP%Nd{5mY%?2OdmD*Ob>KG|V+RT$wx9xzz%6FHn~xG=>#F<|p$pqWRL;c#jf%9>FL%aNk;)dKP7xV180awd5i3+SVMhu}#tuG?FqZ!Ar8I$5FR*oZ-mI^=9}XZ`9j zWGgpYc*Jwl4sk{&#J&O%?EgCK@3V3C_2Cx)x&Q)rxt5XHN8ftjMS7h7q=ut*cXfc< z>w{@zqv1?xqq7&@4l7hdK7UD&nE8D-i4B@#5rGWxgCG_-rgjMnz~2chOG6gH<*~AC z4+?*EFfrc1Ia$fJ(QG`NZX=wrAS8ur_iqv-)h;?X* zxFedA+)qptnsQ@CBfW)B>(0~0G%LTp;cr^o`=05^d)o2;VKe5Wk1s;)QZ0y5?qC7T zQ0Hq}f7aI>Pt-l7rSqwM(o`uw^JwD1i(lE5`XsAT*8Cihd>TvLR$*M>G0IiDbLI z#_jHCm6V+cE|5@vUTw6*guSgmP#LJuRPRuKrRr-6o*p*4)lySK4^*hG)~+$A%HTY5 zl7&Yz#sWG^25WymSa^v^g*-4=y{~us&e4rWPuvZa9+9=Z$psb&qZgfsH$#zEP4Qo6 zAv#owX0n)vuP%H0hdz`uKm&!judCA*wK|TyDNrT(tABW=191V)s?4w9uB*&Pw%HTZd&`-&E*9WYynYGpJxQ1fNqmy43t34X4~2v)?UJh zZsTrnFr$&2?1$WGp+*RR43 zA;k`fQdP=V?nJnW`Fy7uXJy>f3^@CYxG=D<^G&E?x`jo=&NyOcr}qOE`b7yz-r7Xw z5wMepI2Kmb)z!4Eser&-aHI$u+KP(N4peHSy2VGA#|zj3g+;QKIsEf_x}&H@Fix$| z9ep841?Wu(j8URYN7MO;7s|wMSKrb>$|(%JcY%TRRob<$LP}oN5!aQR}A_wf9L2TVlF-5u)BkjTWN^p5)&xsP(0j ztZ5@p%h0*4iJre%+~>_?A}oyPeJ*7Yo^O85EE&Og3a^E=r+TWYzuI~q>k{>`x+7Jz zK|;2UR$GJZ!DhVOsU?3LAlBae)#N5a$&zbaYZ&Yg$!tBfga|>QFCHk##x+%Rnq3rF zxC6!Oo@H=T_KNf`cmBnN)%0Ir{LL76NUqhZC84cCE04l)6!`efVAfuHyYkZ2y%xdgjFx!pP~Lm%51k zw9cPSl28al1w(ytM#{LprrQ(v4HFK3{s5N6{%@eE9_NWKO7wLoH}vL|FADrUJoAF0 zkKBB3)k$?ftgn zCw64Rtgz5TNa7TF={;GAy07&cqc#L^32-*1{iEB_te+jd45g(tt;^f20_*UO(u|3v z#4HJTe%o%E^VF|BZslafS{3+T8W5l~3kfdLj zCx+3;m@8{prHf@oQoE2j3EW&_$ir+(y=Qj-s-$?Hq!emNC1tRAC^b$-8=+HSBkK39 zub#uCJV$mx>x0qw<4r>e*vhqgSWn)UzY^j07(R3HvG0pp63hOHCxdOZAfkHZoC+d3 z{|UD!zf)3;3T-*q<_d>X9=dN*Y)aJQNFFA|C9^vm@BGPO_4ifw3$O9kY-QX5E)vvp_c z*1hx0AciC*oE$Xluz5V%-1h%k0QW6l3+&n+1qE}8k)h*hzkANG@NB!b6TosI^GD<+ zbGirG!dZZ>h7SsHMfZxl?mCKI)Hwj(Y7M{aHOikQQ1Sj>bQVX6#>A$j}?Ic*Aub%AuIso68 z`dJPn-H4FLUx4Dwvk|d8CJi~UpN|$k@!IwF*PdM1>-OfyhY;nd`=)1Eip}yL@L?(Q z&s08n5)q%rSy^&gw8%SNc%P`uXZ!a<`~Of-=kERw?t`qSq8~S+?9^|-!BQX&jkV(+ zRI;xzhqN-BcP>Y+_HL(Jtf1*aK(FX>t#S9aeWQ)@^Wz@oAB;?bq(AgSl4%mnW#jl_ zo;Js>#M&r3TD-iya4>f$WlI@Ek%@EKGZT!!gi=MS4r9`fZzQ~b>kXl_FvGpRu>MOTSKrF1GXSjmu(VG{<_NColBmM+Wg=Yixpk-w=>|)FD(6m-Q&M3q z1ICCvUm2sI%{U_?hN@3d$%Y4`U{s|)(nn$;_R;jPN{*@&GX=D*E@QSM)xi)Nx$^I3 z?^T3q#>)9T_F%u5O)x{u>pTiWU;g|+Qc~h!bAmHG1vMUYtI?NGMl9bsn zt!P%FUg*;@bc2q$sl;M%cX6>AMl&UoB6`4o#EEv3Nw0TkcT!15u^*FieacP%8u zA9t+h@BUk@_fyh~l`PgHE>06K+7JHcy{YKTwf(UI9VN3F*s?C3YUqm%rQfp*m2X?V zmmBm&W}0|tvWt%Vx=`D(!QzlHk!R>oE3Vedo9K)6HB!$FgX@x=g7aTcsw?a<~ z^_xVSle^0eefbh)IknFwI!BjHyNxaJpq5>aQF63c1ET1@qIh?d6Gly76>V{nEZ z;+eRsERlnCE*yxTPmbx&9phw@il(p-U)@`BbvJ56I5@^88;dtK_UZ|cscn^Wq?eXg z3HK^-udyWLP+k_RG*Le^FFJ_N&XQS+mTOAX=$j4Y~*ZBbjx@N%t-+`AMIMoM75%s8-3U2u-{B^IhvXqjP?ddrcqi| z`Kki`BKJv2u*huMFvZCDyG)-OtP%xDfjm^(t6k)+55JAER7ut@gkJ^U|Fk4+QVezl z<|w8!N2Y1tXfWzKrXQmFu{qzJLLSLKdpg7>{6V9st2-0@@nWqq`zPt>aJ{MNqx&5> zSL#6FX!@_mVdzl}Znxw9`uah1>iXREKm>>Mni%vEjlAV_94Q~0n%d+ zKP8ZI0=ru(5ueg!7}*vvG2mQ7@+wBV{pwZW(i;@5BCMsfcdaiL&Il!3k{~_< zoy6L897ZPclb%MHQAuNI9Y8*){-&S7&UbBSb~{cWw8e!D_-!P9iLIh(w*0;n8CiFowO`DJV{#jHPHM0w+eOPH6am(L2x&=I$qpMV3ZO z6}ODJAj+Yxq_#Guc6Y;r8VN4!x5SIJlb-Q$=NMnhshLi6kq;pq4Gm?m>@)Jl8qMv@ z=}%MgV}i>Q!WJj<$6aY^V=?QZPKxQ+(;vcvmEj5EmW z_q{@i!rHed-LO4rNbLFJ+bj%hI1mD+G&=^(ATsCW6q8=PC() zmJ7Pg{9NvSQbKtOjg7VWZPPq({e_njWc&eU&Ctb)qe;{dvb3?nt4uOiW?t8TseikD z<#XiNGlS0Mr*$p5#Re(+TzYT4i~u;K|KLde???Vqf;mln(z5HedXu#efM_ACUk z@hDEcR}-|((L>RSNPOV>pD`l2uQuO%1t%sI6*)_v{|zo6+T>lTip|i{?g=e#^_Q-p zP#kK}b1rK-yF_M~ZdPauWvZLETL>33mTm;o;8M1ATVhtdfJULj9jRtvIC1~_UTBK{ zBqG_YItjs*$Zvs?&g~5J>7J6Ki%iK*lw&J(>WQ(rIA<6WQ=MH%5y?XA{P27PRo+sJ z?JyK?;$%MZ#VCNV0E|_6Lt)X`v%SyRW7$@d?M;+S-))qFqM9qz!&+O7=>7hQXiM|S z`!A)@6*)&~&$Rlj|x$DUywM*^>T!)U+N?IVs+otcz z2PJA8cdohx=|m^0cVJ-~<>Sbt5x#?WGLwCinEZ8-&lL1By1MUW*|*8y>*8j!qbzH~ zwj-E~gBFf*YmGdS+d^AQqqX&V)8Ri`?{4Q91gg#7|J(zU%Baf;@l_W8=aaI`_pVFd z%a>jJZjWUHcjIfbd&KZd&uQR1I1@4JK2$awix zfUbn^*eJU`#$KA2;7$Rhz6(m^ zaUpv;M{S>#nVgiOm~<&V{_=E|ITgl+DZQei#~)Y z!kp`|GE>EecOgmR_rK8R`882V-O6V3dBe9k%O|^%5FUv|w~Z_0D}024hY?huSY`8n z2gpPjC)=Mz)t}}vvFyS^u)*iHP~{^o$Rj9d=nn?%7H%lfDcMH$#bsqqZ(iAyLl(sv z>Wf&a?xl)mZJ}aqEQS_S{k=6|>bUAst;Myi{4Cd zp2usjw7moLJEq&nlyOLy*EZYL27rGzNkZLM0S4AVpz`f=?fYqMA-w85r0;(Q>eA>; zwgzyiSpXAr^2o+((O>Me#FI*wdKaP_aW@gSs!+MHti*YtU(r9AVzOnHa(VtjL796d z*OyZIk0gsxF;uFb_XX9QQD43s;gx1*<1afBRQ4c_y6BTXjku$IY`!UeZT6b78qf>1 zrA!8O?2BvPKoaV$6iS#B)Qer*sx7=a6;d#N$HB6yS-#FEYqMTRSx$j37Jo=(Rc3An z(xR`&L)SZgA&>lmsI8-{#4?zi=Z^^^V0Z>5bIWPsAw@=Db|S zKAsN)KDe>%*y&)(g_DN_aoabkDN=YLr|!_SfXcv3GqLg^ks5@4z8+Z=ubypX*E?Dw^HOlf?BbX4DgNmJucs>6 z4IL?h^Q+(=I7sS;KMYhe`U_Hj-V8+IqPNjmWq-n~zRje2HjbRPSo)ikURp2O4sp;F z5}afyg2T-Snu{Y{Hobf*Tk7`SPeYBi;s1z1aQPcUwKPdh-sXNqY?q0BlJU^dq&Viw z|5F6EBOU$|Zsn6$>?h9c!6~t%A2NXoB7BjR<&f29y8|-6c|0}ND9NH^+H#ydXg^LHkmC^+8v1fiqR z4g`zdEokI8kN&vyITE}&+=uNtDzRmVNQnn?JyE3`sAO6xM83O|C_BuxEiCb?seU!o zUSC@3{l!b_VGt8*u+rM-w%TwXjc=SiDExAnmy#W%+v}lT!!@`)=In%-Ht-xq$fI>w zgG1MQk)lIeN_3HuN@&09l%>#Wm0AwSFuaw2u-MpkY|cyGwUkD9MFy_PTPw#5l$TSD9QbVNo0jdU7G4)#JQP=XnL&FTs~v-Q7P-7Y`>0x7Wg49Lac$z%{mE@<^$g zmDDAT`(o$+#Ky4Q?{rZfr3VCXjicnWzG%9HR+93~`2PK3ZfdIS-A?V(+OWC>W^U06 zZc~r3TXSr#iWGK5uVBwJbLGzd%#u^74|+DyTQXz)yzsle{JWV_*xXl<7@dGxaa;e zN0fa*LrdT5aP`OXmWzh$AL+dA6(=W84atp_yK9Au9j)uz@l{VM-sSP!lX8qQ`?-cW zn|Gd6P?HA)L{-%vJ-y*x10_`VQul9f;?Y7g&UR|<-}0e__aWT&FO0S$YXqKiu)QEK zj!~T;xwNG(x27l0MxVX11?SbYGYZyw1m}ezT*T6Y$T0SJoTtzaB`k-k*_E+B`$ofi zA@rgP=ONr;kS>TJsi?+_)xW&RcuJ6G1d+KrJ6k?;PCHl%@XBj{)-3C-* zLN;9hcf*C`UTUePjE*8alg3GK*wmvy44z6{oKtXh!~6(t z$y7-RVGWaX{qaxElHFzn@ytze9BUp;P8gqMWWzgslU=B-StWu5_fzLnYlklY$pdx7bN>@xpK7xfQ>LVy#ZPoJ@oQXv?-7ypmA1@1VZh zOv9POTtFIdL4c|o33#JZXZkT@8=y91Jg>cO@QK&$Wo&t?2U*W!M@yzc<2JLw0GIAKGjPnu12Xiw0 ztw}WhJ=rk9tfO*L6{kc!@p*&h8-DF@A<|PesT8fPLq$y#pk@cZ$E<;aeDJ!Mb7N~0 zQwtaQ(-qSXDLFdpXYZ$6?7nVxWbSFAJadoseIcOV!+)atL_JuL++L|G1(ELSph6lj)bkXbc1UDH={Pb47s8+?zz3RHLD z5`owid`wMiXsyqW%V>W_T$nw9+hW;(C#o{e_~eX}_#9B1=w(NNx9WPsP-O(tF4Ejf zo$bn_z}+Ww#LCQ<;PgU%P^u{_f54L1AKK_Bu)UQLorPGhI7`=5DlxB&aImxj4rd*q zx=_o8(PyR_z@ZLCarX7>j6-FJKxZn*I zlk#JiTR}#d2)teQWXFgZD?- zZr?}VA~0|6=eV)a@d7e9sH=to`SNhLkzc7px5m)Qb|HmuO^PCXqmu3W!KfP@bn_Ps zCZpknhk?n~eM7AHWC2nyXfrO>nFQ14IYYDf|7i?YgVME+TDf?zhrL_|>atoz2 zuuXli>uWv~ph2~2T4($a7%|W+V-EKiQ*T(T2;va_{S6;Med~#yUfKeAi&#H;1TPi4 zthj#j>lBMB4wp}%U$K4@PNT^}NAl%a7HIm1Ww?|u7lu?8UL9_@B6i@h6aO+4a>j0- z9L1dbZ2ahEN-KEV?DA_)`!AuwZ!!Y-OGGhOJhln=BNz+cClCp*1h}gPX^!+a-gl#i zgyc>#>;ZKyR3iK-BG5N?3lFTtd{myK!RhxLAR(TR(dc)wffi!>ZjxV`J^jM>aPZ;8;#N=-BQ#h<#`78B(^ zD|fx++^w(vnsU=Y8Hgi7?l@8;|pHprqgrVcpFLBVBY-WOcHMLp&G(yE1?x0)*( zMkZ?hyamjzcUhG1z8D0B47g@pS{1zX-;VETGIU0zNikEo*XijZU`kw|`~hMH(je-)FWR`6yu9Q0>Y#!Pm1E=AE2Q_$QAAuU z_PKa*jUb9Q%rC8yp)NnaJ`6}{`F5TfaKa!0VVeaKSV;YDpENqz0CU9^G~~#_;<7i6 z2o_4Baq+{OP)@gl@l;=m56Zlq<6m_08IDpLWbFDp(&zR`B4`QU0%zW;ax^e zge|2~4e;PoB%;CjNMe@v1>s&0mFtu0?*xjE+TG4*nYoplqiKA)!EkP$v8XWJLwd4L zOI_K=T;NTNEu|3oRA~y1PRVbO1ws$eEaTXiaxvfh)0r8v1+}d#JVjL(lA<@sr-j&T z2S?*b&53z%w6%#Hs17(R%^!>Xx$-G~1yA)jPcNSC4T+^?a)~i1HF{j_{+mnSWksji zdMgqBc-%y-I{X|rEED$dx4QFE#G{3S>-n)6z4K@)w7VfJ`e({2P2H#Ub)*lMD-@v- z8X*vxc>%`dDN(0Og~^S@Y+BBN7Y*G3lpY^s>ztz0U$cQrM%J!uls_;*Y=1bnVR$0e zi3A+-iwg<}5>MZ0D#eN~z|Vo7sACJVPQJQ8px1GKL$djXN|bJqq>tjqESIrOZDYQwdMfe-y|o3FnFrUQHR zs&uutMuxk*Z#$b=J#N}sDJ4U5;3k8?E)g-Y|EbEZ=l(M_3sLeGo0wt0xa>;dEb6RY z&#;`-Ok{-n+?hsAxdedo79`!n@u+k0s?<#dsdy#@5UIW?UzeA!=9AuL%k^Aspm@=) zH3CKoYisqxEHI011%t3;nyb$B zEe(};MGgZt%D4~ClM4iMhRH*(<^<6@}cCu1I`rA)m#F|_hXP$F(L zW&~Sl{))#A?8PE?5k+fH#ra8iZKxSZOuVnV{m~zuiChCM0kUXEUip*V!OB>yJyAg) z^S^yrsj*tmg;VPxp%j0{=n*+ab9*1cR0;lG4EKts{=XLB5bkGEDIKwNN%Vkb|9gII zf&+S!2+dT^&dhpY7#uN>9Iey5H;rAhU14v{V^UoWBaTm{PW=hD!sK;`8FaM`mxF*w zH6OjQ$`;nmLLgm29!y9a3eTZWW$z}Fn!=R52UkcDt(vbEX6S>tpM{yIpMh0x*YM4n z8kNMxG@}Qfg=-#TXTYXzJ}cAq+a49mOCp*x(O1GS^~Ib4AX2-ugi1d!hJsppZ!7fE zj7MEEoHPHhQk*O0d~?Yo0~v*z>B@$M!A=<*77-2#b{%_6}vI{dFp2) zM*QRmc$S`d7?saafoN~)*7pSmHkwVZ31|3qWL=Q3$qRcO$tH#$(A#Zl_dUxI);K_d3WAE9<%|P^geFBea{rbI z0j^uqG`UV7+L%NZZ02QP@D(6K^_9wseFz0Uoqv1jHx(9-s~EX0QMC6l^HPchjz^7$ zg_}>P*fNam_R5SqcmD`hn$o#Kfn z%TZBw4rXm4sU`1+*?(9Jc>q^4Q|W`ZCu@Ci==;9P`#$UYE^7uATeFy}$lHmE{~_lO z(t=}lIKvNz13QetAax2?wkx->k`(`Q3wLj5x3W_1%Bu&GtoWjPCh^2J&g~ww`Q-oLw+Ee_WtkrD zm^Izy%ed`s(qZXvySF9$dLMg3zR4Cpt^x$sE4I(D*P|ILP$H4YONK}kRw0jQ+O1M~ z5l2F`(Hxkh8D;F;6G4*(2VrN51Utx|>4oY<*4uDnMqISha0nk{hVucG&mh)>aj3|v zAu$Rhm#pMoQb9($){?p!eE+7Hl``*^sxl#O6K(`D2Qxtf(4G{!qkkF5) zeY{gW!mY@Y0Lcm{QF5eC(=`4&JW7@RL1Dio)A5NM1r`-tPPI zXUC7Y5Rahjpmv+dJGKcV_Ami{SOLjq4P;L;j=`S_KL|Jlf~8wZSaCD>CbGFvTcLb- zrKe|Ff4uI4z|6nxHWY5`|NM?cq=$Ei!Un+-<7aNg)XJ2Gr&@}v>zeU3+xn{2)!ABl zu!`pBggCdg@1Y2VdHp=5r&xnxWE8#j`?I*?{<`;m>Eoc`ELkGR@#bXsioGTH41BYj z^^Sg;TS_nYlgO~2GzBY^u}AhJQT41TW=;ko`3Q-t6vzZIOv#$07NJJFtZh9K{20-5 z_T`1IDH4PnO}75?|4Fu6adQG`9AlNx|W z3(2C=O7@zJJ~sZii&O%swNcbRvm3Ve5(D!~)R`aI&YU9hDKS_2put<2U%UJ|y-RSl ztN_x9jm%w_1@sx6duck0o<%?0lrX{K-n5R86d>?Rk zcKCR&tk&aJGM{zvbvny+=wso`X3g157Yx2cEeNQ>2Jvfd0>GZaIcXEi!S}yRH0#i) z0Kl!4lk}_<=u-55-$2p!dHysB2eJin;7vDTWGIE_j5f^BiT*_;Oh#<4jiX+hY{V$^ zdXj%rBD(;?b3Ckb7WDrQQC|TRM-y#L5(vTF6Wm>bySux)6WlepyDskTlHl&{F2Pw` zmf+#d_y6_kRZ+FYY)|+0>}>a)bI(1;twNdHLhK_kPN&!u9Sj@{;7(*Jx#ahNcm0Ps zh9XI?xoBnON|z_D82CuM26l~RcJ%>6nD3|LIwdAKR>fVg3a=(> zHdV>BjC`u?BM19$`lXrhK8#&q1ebNmQUbNBCHi+i^8D0CtlHY%vbX2ajrXhED}Tqk zq_xz`Q3zddq@w@BsP6KN&vEJK@|0#PX&v^%xbzuSzS^MfoX^_S($#ht}FV$_>)q-)YG%tK!J3E_L>6xt=-mtn2q)(`SKDk=uu>89c zd!x}-3=!Wb&J!Ce+LWNJz`iFx4UR`zNVmjtSnR7<>D8Jk5okJ-2_+G{@k<_X0@=OT zD6$DX_Jb_&vHBI}C%gmxT1$V$)rJ8drs=V2_b8xzi$JZ9Ci9PmIc39s$Rl@eRfHIh z1$)iLHnXIve}v4Q?`E=Es*@393184vMq-?7w2p;3n?X4Kq`H!6Qp>;9RvbB)iS#NOHe$N2f z5i8q#z-E9GJ?g3~Z8<-L)1){&>dI3gM>L*`%%%_?9+L>@m|+C&^?K6sJu0 zoALk_AB-YIFp zDpsng;o!i}?eKTU2qg1hI6msJ|@v&?N6f%0I$w_R?+7ab?p$P%j$Fb5E>}G8R^Ia-~KRPggxg(#VS?Cer z2W_4`9;|UfT% zQ{k~8)ON>FyV-}&Pa(+NS?nl$M!laF$#ML?H~naPKnVJ$@~RjT5a$)}c;}P*dYuNt zHZgLd26)e5bcv3=zjYO%)LQ$R-T9I(a0e|Yk|>D+gZn=@-|mS6mw;<`+OQAZia4=2%;*h$d<$ph-FRk zPcd^UU|a-HuD2#ZRPjtwpHz`55F3S73T80c&;=GS3{W`?V&zaazOvT*zC%+x&2l&W zqFZ~#-{e0iY2nPi_fB(w;iGU)L#=VQyMvL6;q^YhwENrN|4KD}Yg z{(U!jB*VVjM|Pnq5lpf~bKSxxM5#FH_B<&hAjyt)OnzWl=poZl{T3T6RmC*?+mIy`MRErn0#*QmHmyrsY9&zy%7+CV0+OpQa=?=D z3hBHHZ5NRPdQ+DHinau{0>AEmT*>YHlE0MWTsHoWty^ba7Xq7cI5kl{Uf=(AMYepOH&TzyHJ!b9f@RQJ%L(p-9OVw>fEgIPI@z|$(JN{ZVf#p%yQ(J>BBsL%$8*3OVwh%J z8%ukxmIg3pfyE95ta*xLSyus$%|2#cdc|9)qoc0Dy?#rCtuez^jH7*o{8>IPQ_?Ed@l*nXY<(sk|zPE4F4W<%D$(_?kGPTD|Cit9?~e;ZOlG2X!D^-M=8 zca)yNp$!(e9VOdr@52#jg_Grb1Vn~<2R7_GE=Ci{=s(=>x4-{;aSGg%m>LL2xyzhv z6(_ap5|tM~m*ZVtNl!QGa&Kef_g1UW>XeG4ZHZq2%9fvVopeSjtVfyXn>+!3KwU$! ziMFh_PoJV<6U-I;;y|4LTP){XQZa+-CI#*X$R(lb4w1WdLPvP*rp=*Cm z5wo6jFy1hFYHDA#9rTOnzqJPG-2_qd33*1H>0^agG1kEc{aoLgRh_z9WH(n|PEpqe zX>+6Oyqb3#4*p}kUF1F{P#6pxuS~a8m&}V{3j4dj|`j^r|KSvP=65#HbG@sbTQ98)M|8a4-_|Kg~~)kCj;G< z-J#viwpGc{$or8$>}fm>F;$igIyNe~$e5KWkuY#_NH+aCzp+e=?Zc=ecJnt1QgkiO z&|E3`gh7>$wKpZa3k*>i8U>*yXfAbRob~o;pLyxv&;2oC5Gp_R9iVjS_n3ult*4oJYcSc9~c}6YHnFVE*x|tYLAF3cg1yiHNYGd&3 zDb3Uarzfr632q!qFqSSrg-KJ!dHVb9VdQxprfPD1KM*B)MqQAf%wA;z#q4#}1L@vN zn}Dao^qsrh2h@Sg+it&?T>oclK%W;jG%hak*k4Vr+umSh?0HDm$-_#kJ;l^P_m{nV z(}X{Gez7kYDH^AYi@|G-^tzX2X!2bK&?Nu?(wI2(O1rO5AWg$m)CV*TNan@{0h6J^ zuV2pfB3;PZQscP-Q#y{nii$8fI4ovzYizAXoo_{0EBc?Gs|B1R*j?Kr2*~t&MpizS`_@&4E6I%uE4xqSEJ`BQ%T<6 z_xx6HLxX`euw@?d)ryIUR>dEmz;uEma*T_crLRI1v#{AkR<_|c&Q-XtVmy3_$`@2t z_)v6XRBrAHsZgU7XVQYqB7z|m*`VZtBx$!vX!*3TJZ`oD)QlqS5Z$D9>FO5s(7U4w z(IYq~%}hZe9`3)`8cocaCjaJ&N-njUsa+l&o##lw^oQgPW|5}?B3m`IdcR`8qe%?9 z&adsh+@7W1hM-^_N{4V*DtWXT0_01gd!n!Khl1fDawuqT9=Y%A zf!g(|UvZVJK1veHDwP(%UJ4n)MSkQLNyc@6*sgFDwK~}$(&O|wQx@e*ewl~42i|H9 zwE7H;K4|;x<6Q+lRAunw&w>*?;K;KiE-MnhR?6J7On&;(tnK;f-lYW;wOp<-uQv*A zlz_X^;X6D*>Fiw z-ud}87m}BF|7k_`UK$o)j4?mAl_Ad@8No7u{3!`z1|~OJ$@3RHCdzsC}mjF2&)xkV&moWYufK1pUGFc}0W?gC2k~8GN z=wgXMWqV`8!p|~x8KlAe--OM&mtAzT+nTcm?}-2*h|=} z=DGEJzh|RqYM_dMUT0yE&PX!@dAX`nkj90jScxBEJs2H*79HxWOrbUL{2Y z83q42nu^_|Ee7fNlu5~qxm8rz!gw$^!dL5wS&PFl?rs_k^x6qWF8ci2Y(pa8q-=oE zX%W9Mb7pP%LEw?Rno8Wpm9Ptx9Ar6k)VSLxGzw@3R0!U)1$17u#rGI{`5@Tjvr+<*}8j*ST8gBEC2`i8GyN8QiV1<(?8@;eUu#`{kH!B?!GDed%W` z0f&&H+6S_(lwKSJ)q^JouV@&gFakuMEzLTSNCZx;ansE5TJ3oCej#e4@HR66OlXlUYVP=rG6yhOFk7YyHsZRv_~p6T7(LZn3-Pvwd_x z3czWF)0BscDzV2`XA`AE4d0HGS7N7_m*|+)*GTo`s_%DA@L3rYA49UN7|mcNPs)&X z*yhOv+Z1V$_F2aT+-n95IdfnAN8W$T?ZrE3JI?j__jJtdbgF;lje~GWr%~)MVLfVN$!O0~1Y>M7qg9+4R~**IKV-ad&e&!`O=?r#s1qrtva%0qi;~WGGp-#X zVp3XVrr@+CtqBoyIQO34^MY^=(x@ zW%*d}E!+*^D4<9w|V-#k16w zNs&&*l@SKiVtc{2P+|bgg(i?+>gy&!x@9{8ClDI@lx|2zsQutuVED{ik&Z-SXKsAI z6Nu^m7h;gMAcZw7^_SyPOs6 z?U3Z7OPUfoEFNh<)2R!Ch$M7UyN5j=h0v;^o>J&-^rs4se}X=SPg5gl&FpED&G=y} z!_Iln=?n1=29Sc0WywbS`4msjz2O8cX!Y+crYnO&(sHA2AvGd1=S71$t0pJu+NK#X z2(CIP;v7?x0uZf$I+&>O6F`|YMNXfo3Hx%y;58{MzLbl9c z-kiB_Cv!9-2br^Se7v7|d9JbXFRzXV_)aO(aM_Ik3l8`Zu0R->OK_!QB98SVDTx_x zbvsgwZck~c#KRTQ1z4KiP=JFgg130HO!I{zZBgNFCYQeoH*Q~)Xwr@OkZ z0(@S7&qw=1eW&+}q7I0~1x`q;N3&3Vo_T&l`@F9*(TDRphy!Hs%~ow zUxKRbI2dS;KuL9Weh@~%{ahd!^AmY_&bL(_aSv_$uhPk!{7El5q*l+`)6Dq56souY{x@M7O-@Tz_e#%kob`r)&6w5+scRnsY^z^t+VAm!#haraZ zy9CGe9EeBT!~*ZUg4iC9_2nsX7_?9))n(6=h@p=E^aY=1q_`z1b${UB+clv6y@Acx z2|2BDD^s}t-O|GGy&hw+irMz-%THX=^|`q|O4H|WE}6>f!Y61LANgN*YxU0n`?wjv zqaEzEC`&I4tt6gEdJw_*QzaOO@&Ir%f~uci z@*mp+$?*=n)Sll4&v~s8Y393j`Q#e-CQ8KSAohoWudt=usLEF6G0e;Ib+I#XWj1FA z^3SuL)GG^9XN06rSzTGD)T}xE>G^4bC8*uCk4emWwG>61v1(J>Q=ZdX&HS-)qefR2 ze8=|VeLk6E%0vYrU0FBT9W-T$+)Dl1@@|)N$rEwyE7d{+uu zL_kt9`B&Y=K#hs2R;P($1e5!#M7nHUh47-7vp_7TV{OS?IC88Iusi*&_y+!|r)GwD zZ13*g7`>Q??sj?fC!a!eur1Yv^Ngdti8uDEcr zn{oY}ujM8!|1$*B1n=ot8oX(Zc#95BTxV(OLyi3hpTwy1zg&P(+EKL{V8AxjH){$K zm6i!3O?)t;VchYi{Ex3KfCn{{52s5u+R-p7rW7#cbq-0c{OPSpv#)rC3K>V6^13V- zec0Zvcxf|8-E3P)Hi{hVTFZvNJzTsy)p*B^ldU$&vE;K99709xOMcD9Hx+X?B#=zC zkp$5~48O_=zP0Fg{HvpC8&nxaB(X5!d)V0Ff4d(3Yv^wx8oMm-5!RZ{>@!G)Epvp6 zzPNG8caw@*U1$@-i_{P?a!z9wM`SHi++n1=Qo-@p&(Fk80|ok73*L-SnBhJH)?l1k4^MKN`za}FGO?I z3f<7gwMIeBH(^zv;Goi)j{I7Ola9RVHJNp(mEUBmnfg;G>sX@AGlqP17F*~E23(N< zkg>^}$O6JK+Tp@yaL{18wPIK;{)}sNRvggnLK3=sSiAbkt+_4hiOFpgZ~(Cp&bh^H zp&K%#?*Er4^E8pi(0Nm zMFyHD0+b3Kl|p+gXKMvgl4W^GU^do}F@4jKf5v|AcUV&RnwfU%E)6@LKN1EeU`Ck60+O7}*7>ch za$D{8@((kRU%&8XAb%Nj+dJC167!g_zIOPM{L2uwWD%$tWSTh!#eLG5W&4)?y?a=W zYd@J$0oV_nb;&U9joe6!>MJ~jX4@~R zℜ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%0DniTE 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 From 0a40358f9e1fb1145d0e46c59ab8bfe2eccc072d Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Mon, 16 Apr 2018 19:48:06 +0200 Subject: [PATCH 171/695] p/*1sec-c: improve error handling --- plugins/cpu/multicpu1sec-c.c | 10 +++++++--- plugins/network/if1sec-c.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/cpu/multicpu1sec-c.c b/plugins/cpu/multicpu1sec-c.c index 42aaffd4..c1f72574 100644 --- a/plugins/cpu/multicpu1sec-c.c +++ b/plugins/cpu/multicpu1sec-c.c @@ -95,7 +95,11 @@ int acquire() { /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return 0; + pid_t child_pid = fork(); + if (child_pid) { + printf("# acquire() launched as PID %d\n", child_pid); + return 0; + } // we are the child, complete the daemonization /* Close standard IO */ @@ -118,7 +122,7 @@ int acquire() { /* open the spoolfile */ FILE* cache_file = fopen(cache_filename, "a"); if (!cache_file) { - return fail("cannot create cache_file"); + return acquire(); } int cache_file_fd = fileno(cache_file); @@ -176,7 +180,7 @@ int acquire() { int fetch() { FILE* cache_file = fopen(cache_filename, "r+"); if (!cache_file) { - return fail("cannot read cache_file"); + return acquire(); } /* lock */ diff --git a/plugins/network/if1sec-c.c b/plugins/network/if1sec-c.c index b7ec5bfc..4786d956 100644 --- a/plugins/network/if1sec-c.c +++ b/plugins/network/if1sec-c.c @@ -120,7 +120,12 @@ int acquire() { /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return 0; + pid_t child_pid = fork(); + if (child_pid) { + printf("# acquire() launched as PID %d\n", child_pid); + return 0; + } + // we are the child, complete the daemonization /* Close standard IO */ @@ -146,7 +151,7 @@ int acquire() { /* open the spoolfile */ int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR); if ( cache_file == -1 ) { - return fail("cannot open the spoolfile"); + return fail("# cannot open cache_file"); } /* loop each second */ @@ -220,7 +225,7 @@ int acquire() { int fetch() { FILE* cache_file = fopen(cache_filename, "r+"); if ( !cache_file ) { - return fail("cannot open cache file"); + return acquire(); } /* lock */ From a720b6c9dc8639b3504573384590c3cfb7063387 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Mon, 16 Apr 2018 19:54:51 +0200 Subject: [PATCH 172/695] p/multicpu1sec-c: fix stackoverflow in acquire() When acquire() cannot open/create the cache file, it wrongly called itself. A fatal error is now thrown instead --- plugins/cpu/multicpu1sec-c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cpu/multicpu1sec-c.c b/plugins/cpu/multicpu1sec-c.c index c1f72574..497d1a28 100644 --- a/plugins/cpu/multicpu1sec-c.c +++ b/plugins/cpu/multicpu1sec-c.c @@ -122,7 +122,7 @@ int acquire() { /* open the spoolfile */ FILE* cache_file = fopen(cache_filename, "a"); if (!cache_file) { - return acquire(); + return fail("cannot create cache_file"); } int cache_file_fd = fileno(cache_file); From ccebcb7b4fca97fc2b7cb71b2d7d71d5cfff36fe Mon Sep 17 00:00:00 2001 From: Tomas Mudrunka Date: Tue, 17 Apr 2018 01:17:57 +0200 Subject: [PATCH 173/695] Modified ipset according to #734 --- plugins/network/ipset | 61 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 11 deletions(-) mode change 100644 => 100755 plugins/network/ipset diff --git a/plugins/network/ipset b/plugins/network/ipset old mode 100644 new mode 100755 index d931deed..d9abfc81 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -1,12 +1,50 @@ -#!/bin/bash -#Graph number of members of netfilter ipsets -#(c) Tomas Mudrunka 2016 -# -#Add this line to sudoers: -#ALL ALL = (root) NOPASSWD: /sbin/ipset list [!-]*, /sbin/ipset list -n -# -#%# family=auto -#%# capabilities=autoconf +#!/bin/sh +# -*- sh -*- + +: <<=cut + +=head1 NAME + +ipset - Graph number of members of netfilter ipsets + +=head1 APPLICABLE SYSTEMS + +Any system with a compatible ipset command. + +=head1 CONFIGURATION + +Ipset has to be run as root: + + [ipset] + user root + +=head1 INTERPRETATION + +This plugin draws number of members for each ipset present in the kernel + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + 0.1 first release + 0.2 added docs, munin best practices + +=head1 BUGS + +None known + +=head1 AUTHOR + +Originally: Tomas Mudrunka 2016-2018 ( github.com/harvie ) + +=head1 LICENSE + +GPLv2 + +=cut + [ "$1" = "autoconf" ] && { [ -e /sbin/ipset -o -n "$(which ipset)" ] && echo 'yes' || echo 'no (ipset binary not present)' @@ -18,14 +56,15 @@ echo graph_category network echo graph_vlabel Members echo graph_args --base 1000 --logarithmic --units=si + exit 0 } -sudo ipset list -n | while read list; do +ipset list -n | while read list; do [ "$1" = "config" ] && { echo "$list.label $list" echo "$list.min 0" } || { - echo "$list.value $(( $(sudo ipset list $list | wc -l) - 7 ))" + echo "$list.value $(( $(ipset list "$list" | wc -l) - 7 ))" } done; From 0bb9b58803356c4a804f7707cb70ba174c39a8d9 Mon Sep 17 00:00:00 2001 From: Tomas Mudrunka Date: Tue, 17 Apr 2018 12:28:59 +0200 Subject: [PATCH 174/695] removed exit --- plugins/network/ipset | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/network/ipset b/plugins/network/ipset index d9abfc81..ef6ba902 100755 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -56,7 +56,6 @@ GPLv2 echo graph_category network echo graph_vlabel Members echo graph_args --base 1000 --logarithmic --units=si - exit 0 } ipset list -n | while read list; do From 919991eef3719666f3ddcc9bc5378cba5211a527 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Apr 2018 04:04:15 +0200 Subject: [PATCH 175/695] plugin ipset: fix shellcheck issues --- plugins/network/ipset | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/plugins/network/ipset b/plugins/network/ipset index ef6ba902..c7bb0a3a 100755 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -46,25 +46,33 @@ GPLv2 =cut -[ "$1" = "autoconf" ] && { - [ -e /sbin/ipset -o -n "$(which ipset)" ] && echo 'yes' || echo 'no (ipset binary not present)' - exit 0 +get_ipset_list() { + ipset list -n } -[ "$1" = "config" ] && { + +if [ "$1" = "autoconf" ]; then + if [ -e /sbin/ipset ] || [ -n "$(which ipset)" ]; then + echo 'yes' + else + echo 'no (ipset binary not present)' + fi + exit 0 +fi + +if [ "$1" = "config" ]; then echo graph_title Netfilter IPSets echo graph_category network echo graph_vlabel Members echo graph_args --base 1000 --logarithmic --units=si -} - -ipset list -n | while read list; do - [ "$1" = "config" ] && { + get_ipset_list | while read -r list; do echo "$list.label $list" echo "$list.min 0" - } || { - echo "$list.value $(( $(ipset list "$list" | wc -l) - 7 ))" - } -done; + done + exit 0 +fi +get_ipset_list | while read -r list; do + echo "$list.value $(( $(ipset list "$list" | wc -l) - 7 ))" +done exit 0 From 4b9fcc0bcc5526ef167db1c0f848c7512685d631 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Apr 2018 04:05:06 +0200 Subject: [PATCH 176/695] plugin ipset: use "set -eu" --- plugins/network/ipset | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/network/ipset b/plugins/network/ipset index c7bb0a3a..b777351f 100755 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -45,13 +45,15 @@ GPLv2 =cut +set -eu + get_ipset_list() { ipset list -n } -if [ "$1" = "autoconf" ]; then +if [ "${1:-}" = "autoconf" ]; then if [ -e /sbin/ipset ] || [ -n "$(which ipset)" ]; then echo 'yes' else @@ -60,7 +62,7 @@ if [ "$1" = "autoconf" ]; then exit 0 fi -if [ "$1" = "config" ]; then +if [ "${1:-}" = "config" ]; then echo graph_title Netfilter IPSets echo graph_category network echo graph_vlabel Members From b745a75ced5b67592464d9ace58924fc2cbb35ac Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Apr 2018 04:06:44 +0200 Subject: [PATCH 177/695] plugin ipset: add support for DIRTYCONFIG capability --- plugins/network/ipset | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/ipset b/plugins/network/ipset index b777351f..8f8adacc 100755 --- a/plugins/network/ipset +++ b/plugins/network/ipset @@ -71,7 +71,7 @@ if [ "${1:-}" = "config" ]; then echo "$list.label $list" echo "$list.min 0" done - exit 0 + [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ] || exit 0 fi get_ipset_list | while read -r list; do From 890e971c2ca0814ad569ac5d5eb98f17db4df8db Mon Sep 17 00:00:00 2001 From: "Felix Pahlow (private)" Date: Fri, 20 Apr 2018 21:00:36 +0200 Subject: [PATCH 178/695] ILIAS plugin v2: Use bash+CLI and combine graphs Combine multiple graphs into one as suggested by @sumpfralle. --- plugins/ilias/example-graphs/ilias-day.png | Bin 0 -> 32597 bytes .../example-graphs/ilias_sessions-day.png | Bin 18997 -> 0 bytes plugins/ilias/ilias_ | 254 ------------------ plugins/ilias/ilias_session | 236 ++++++++++++++++ 4 files changed, 236 insertions(+), 254 deletions(-) create mode 100644 plugins/ilias/example-graphs/ilias-day.png delete mode 100644 plugins/ilias/example-graphs/ilias_sessions-day.png delete mode 100755 plugins/ilias/ilias_ create mode 100755 plugins/ilias/ilias_session diff --git a/plugins/ilias/example-graphs/ilias-day.png b/plugins/ilias/example-graphs/ilias-day.png new file mode 100644 index 0000000000000000000000000000000000000000..130604cf259103e2a73af5a9d070ef57abcb777c GIT binary patch literal 32597 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 Vu2i#mJd+Xdl9y4GE|E0w`#;rU;}ie@ literal 0 HcmV?d00001 diff --git a/plugins/ilias/example-graphs/ilias_sessions-day.png b/plugins/ilias/example-graphs/ilias_sessions-day.png deleted file mode 100644 index f53b11dfc77b3c96074fbce7e85343f46dcb6574..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18997 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

ujh|&HqRQGoRjZh$YHpi~{jr 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 KT-G@yGywp5EY(i{ diff --git a/plugins/ilias/ilias_ b/plugins/ilias/ilias_ deleted file mode 100755 index 39bf1367..00000000 --- a/plugins/ilias/ilias_ +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -: << =cut - -=head1 NAME - -ilias - Munin plugin to monitor L open source -learning management system - -=head1 DESCRIPTION - -Reads session and user statistcs from any ILIAS MySQLdb database. - -https://ilias.de/ | http://gallery.munin-monitoring.org/contrib/cms-index.html - -This plugin requires python3 and python3-mysqldb. - -There is a check for the the filename suffix _ (from the symlink) in place -to decide which value to output. Symlink the file for each value you want -displayed -example: - ln -s /usr/local/munin_plugins/ilias_ /etc/munin/plugins/ilias_sessions - -In order to get precise results, please ensure your MySQL server has the same -time as your ILIAS application server. Timezone does not matter. - -=head1 CONFIGURATION - -The plugin needs the following configuration settings e.g. in -/etc/munin/plugin-conf.d/ilias.conf - - [ilias_*] - env.ildbuser ilias - env.ildbpassword youriliaspasword - env.ildb ilias - env.ildbhost localhost - env.ildbport 3306 - - -=head1 AUTHOR - -Copyright 2016 Pascal Seeland - -Copyright 2018 L - (L) - -=head1 LICENSE - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, and/or sell copies of the -Software, and to permit persons to whom the Software is furnished to do so, -provided that the above copyright notice(s) and this permission notice -appear in all copies of the Software and that both the above copyright -notice(s) and this permission notice appear in supporting documentation. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE -LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR -ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER -IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Except as contained in this notice, the name of a copyright holder shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Software without prior written authorization of the -copyright holder. - -=head1 CONTRIBUTE - -Find this plugin on L - -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf suggest - -=head1 VERSION - - 1.0 - -=head1 CHANGELOG - -=head2 1.0 - 2018/03/19 - - first release - -=cut -""" - -import os -import sys -import pkgutil - - -class ILIAS(): - pluginname = sys.argv[0].split('_')[1] - - def __init__(self): - self.con = None - self.user = os.environ.get('ildbuser', 'root') - self.pw = os.environ.get('ildbpassword', '') - self.ildb = os.environ.get('ildb', 'ilias') - self.ildbhost = os.environ.get('ildbhost', 'localhost') - self.ildbport = int(os.environ.get('ildbport', 3306)) - - def db_modules_available(self): - return pkgutil.find_loader("MySQLdb") - - def get_connection(self): - import MySQLdb - return MySQLdb.connect(host=self.ildbhost, - port=self.ildbport, - user=self.user, - passwd=self.pw, - db=self.ildb) - - def connectdb(self): - self.con = self.get_connection() - - def config_sessions(self): - print("graph_title ILIAS Session") - print("graph_info Number of active ILIAS user sessions") - print("graph_vlabel ilsessions") - print("graph_category cms") - print("ilsessions.label ilSessions") - print("ilsessions.min 0") - print("ilsessions.draw AREA") - - def execute_sessions(self): - cursor = self.con.cursor() - cursor.execute( - "SELECT COUNT( user_id ) " - "FROM usr_session " - "WHERE `expires` > UNIX_TIMESTAMP( NOW( ) ) AND user_id != 0" - ) - usrs = cursor.fetchone()[0] - print("ilsessions.value %s" % (usrs)) - - def config_5minavg(self): - print("graph_title ILIAS 5 avg") - print("graph_info ILIAS sessions created or " - "updated during the last 5 minutes") - print("graph_vlabel il5minavg") - print("graph_category cms") - print("il5minavg.label 5 min Count") - print("il5minavg.min 0") - print("il5minavg.draw AREA") - - def execute_5minavg(self): - cursor = self.con.cursor() - cursor.execute( - "SELECT COUNT( user_id ) " - "FROM usr_session " - "WHERE 5 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0" - ) - usrs = cursor.fetchone()[0] - print("il5minavg.value %s" % (usrs)) - - def config_60minavg(self): - print("graph_title ILIAS 60 avg") - print("graph_info ILIAS sessions created or " - "updated during the last 60 minutes") - print("graph_vlabel il60minavg") - print("graph_category cms") - print("il60minavg.label 60 min Count") - print("il60minavg.min 0") - print("il60minavg.draw AREA") - - def execute_60minavg(self): - cursor = self.con.cursor() - cursor.execute( - "SELECT COUNT( user_id ) " - "FROM usr_session " - "WHERE 60 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0" - ) - usrs = cursor.fetchone()[0] - print("il60minavg.value %s" % (usrs)) - - def config_total1day(self): - print("graph_title Users in 24h") - print("graph_info ILIAS users logging in during last 24h") - print("graph_vlabel iltotal1day") - print("graph_category cms") - print("iltotal1day.label User/24h") - print("iltotal1day.min 0") - print("iltotal1day.draw AREA") - - def execute_total1day(self): - cursor = self.con.cursor() - cursor.execute( - "SELECT COUNT( usr_id ) " - "FROM `usr_data` " - "WHERE last_login >= DATE_SUB( NOW( ) , INTERVAL 1 DAY )") - usrs = cursor.fetchone()[0] - print("iltotal1day.value %s" % (usrs)) - - def run(self): - cmd = ((len(sys.argv) > 1) and sys.argv[1]) or "execute" - function = None - - if cmd == "config": - function = "config" - elif cmd == "suggest": - print("sessions") - print("5minavg") - print("60minavg") - print("total1day") - elif cmd == "autoconf": - if not self.db_modules_available(): - print("no (Please install the MySQLdb python3 module)") - else: - try: - con = self.get_connection() - cursor = con.cursor() - cursor.execute("SELECT COUNT( component ) " - "FROM il_pluginslot") - con.close() - except _mysql.Error as e: - print("no (Error %d: %s - Database configuration missing?)" - % (e.args[0], e.args[1])) - else: - print("yes") - else: - function = "execute" - - if function is not None: - if not self.db_modules_available(): - print("U (Please install the MySQLdb python3 module)") - else: - self.connectdb() - try: - func = getattr(self, "%s_%s" % (function, self.pluginname)) - except AttributeError: - print('function not found "%s" (%s)' % - ("config_%s" % self.pluginname, "self")) - else: - func() - - if self.con: - self.con.close() - - sys.exit(0) - - -if __name__ == "__main__": - ILIAS().run() - diff --git a/plugins/ilias/ilias_session b/plugins/ilias/ilias_session new file mode 100755 index 00000000..5f85a7c4 --- /dev/null +++ b/plugins/ilias/ilias_session @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# Munin plugin for ILIAS + +: << =cut + +=head1 NAME + +ilias_session - Munin plugin to monitor L open source +learning management system's sessions + +=head1 DESCRIPTION + +Reads session and user statistcs from any ILIAS MySQL/MariaDB database. + +https://ilias.de/ | http://gallery.munin-monitoring.org/contrib/cms-index.html + +Requirements: + +bash version 4 is required for associative array support. +This plugin requires mysql CLI or a compatible client being available. + +In order to get precise results, please ensure your MySQL server has the same +time as your ILIAS application server. Timezone does not matter. + +=head1 CONFIGURATION + +The plugin needs the following configuration settings e.g. in +/etc/munin/plugin-conf.d/ilias.conf + + [ilias_session] + env.ildbuser ilias + env.ildbpassword youriliaspasword + env.ildb ilias + env.ildbhost localhost + env.ildbport 3306 + +WARNING: Setting env.ildbpassword will possibly expose the database password +to other processes and might be insecure. + +=head1 AUTHOR + +Copyright 2018 L + (L) + +=head1 LICENSE + +Licensed under the MIT license: +https://opensource.org/licenses/MIT + +=head1 CONTRIBUTE + +Find this plugin on L + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 2.0 + +=head1 CHANGELOG + +=head2 2.0 - 2018/04/20 + + first sh release + +=head2 1.0 - 2018/03/19 + + first release + +=cut + +# Include plugin.sh +# shellcheck source=/dev/null +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" + +# Shell options +set -o nounset # Like perl use strict; + +# Graph settings +global_attr=" + graph_title ILIAS session and logins + graph_category cms + graph_args --lower-limit 0 + graph_vlabel occurences + graph_info Number of active ILIAS user sessions and logins +" + +declare -A d_attr=( \ + [0,field]=iltotal1day \ + [0,type]=GAUGE \ + [0,draw]=LINE \ + [0,label]='users logged in within day' \ + [0,sql]="SELECT COUNT( usr_id ) AS C + FROM \`usr_data\` + WHERE last_login >= DATE_SUB( NOW( ) , INTERVAL 1 DAY ) + " \ + [1,field]=ilsessions \ + [1,type]=GAUGE \ + [1,draw]=LINE \ + [1,label]='active sessions' \ + [1,sql]="SELECT COUNT( user_id ) AS C + FROM usr_session + WHERE \`expires\` > UNIX_TIMESTAMP( NOW( ) ) AND user_id != 0 + " \ + [2,field]=il60minavg \ + [2,type]=GAUGE \ + [2,draw]=LINE \ + [2,label]='sessions created/updated within 1h' \ + [2,sql]="SELECT COUNT( user_id ) AS C + FROM usr_session + WHERE 60 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0 + " \ + [3,field]=il5minavg \ + [3,type]=GAUGE \ + [3,draw]=LINE \ + [3,label]='sessions created/updated within 5min' \ + [3,sql]="SELECT COUNT( user_id ) AS C + FROM usr_session + WHERE 5 * 60 > UNIX_TIMESTAMP( NOW( ) ) - ctime AND user_id != 0 + " \ +) + +# Read the environment and apply defaults +DB_CLI_TOOL="${ildbcli:-mysql}" +DB_CLI_CMD="$(command -v "${DB_CLI_TOOL}")" +DB_HOST="${ildbhost:-localhost}" +DB_PORT="${ildbport:-3306}" +DB="${ildb:-ilias}" +DB_USER="${ildbuser:-root}" +DB_PASSWORD="${ildbpassword:-}" + +# Functions + +autoconf() { + if command -v "${DB_CLI_TOOL}" >/dev/null ; then + echo yes + else + echo "no (failed to find executable '${DB_CLI_TOOL}')" + fi +} + +config() { + local label_max_length=45 + local i=0 + + # print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + i=0 + # -v varname + # True if the shell variable varname is set (has been assigned a value). + # https://stackoverflow.com/a/45385463/2683737 + while [[ -v d_attr[$i,field] ]]; do + field=${d_attr[$i,field]} + echo "$field.type ${d_attr[$i,type]}" + echo "$field.draw ${d_attr[$i,draw]}" + echo "$field.label ${d_attr[$i,label]:0:${label_max_length}}" + echo "$field.min 0" + ((++i)) + done +} + +# Join a bash array $1 is the glue +join_by() { + local d=$1 + shift + echo -n "$1" + shift + printf "%s" "${@/#/$d}" +} + +fetch() { + local i=0 + local query=() + local query_string="" + declare -a results + + # create an array of queries + i=0 + while [[ -v d_attr[$i,field] ]]; do + query+=("${d_attr[$i,sql]}") + ((++i)) + done + + # build query by joining the array elements + query_string=$(join_by " UNION ALL " "${query[@]}") + + # obtain result using CLI call; don't supply password through + # command line; note that MySQL considers it insecure using + # an environment variable: + # >This method of specifying your MySQL password must + # >be considered extremely insecure and should not be used. + # >Some versions of ps include an option to display the + # >environment of running processes. [...] + result=$(MYSQL_PWD="$DB_PASSWORD" \ + "$DB_CLI_CMD" \ + --skip-column-names \ + -h "$DB_HOST" \ + -u "$DB_USER" \ + -P "$DB_PORT" \ + "$DB" \ + -e "$query_string" ) + + # initialize array + mapfile -t results <<< "$result" + + # extract result and echo it to stdout, which is + # captured by Munin + i=0 + while [[ -v d_attr[$i,field] ]]; do + echo "${d_attr[$i,field]}.value ${results[$i]}" + ((++i)) + done +} + + +# Main +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ] && fetch + ;; +*) + fetch + ;; +esac + +exit 0 + From e6a18b5a5b529cc5a397e676a90303296f4a2193 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 13 Nov 2016 19:25:31 +0100 Subject: [PATCH 179/695] sshd_log: use DERIVE datatype and support logs from journald too Now shows the number of events per time unit, instead of a counter always increasing until logrotate. To use journald, set env.logfile to special value 'journald': It will read the sshd logs from journalctl _COMM=sshd. To configure the source of journald, set env.journalctlarg: Example: "env.journalctlarg --unit=sshd.service" --- plugins/ssh/sshd_log | 89 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index 608f49ec..dc57e3df 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -1,12 +1,12 @@ #!/bin/sh # -# Plugin to monitor auth.log for sshd server events. +# Plugin to monitor auth.log or journald for sshd server events. # -# Require read permitions for $LOG +# Require read permitions for $LOG or journald # (set in /etc/munin/plugin-conf.d/munin-node on debian) -# On busy servers you can change value type to COUNTER and set min to 0 to avoid minus peaks at logrotate # # $Log$ +# Revision 2.0 2016/11/11 15:42:00 Thomas Riccardi # Revision 1.2 2010/03/19 15:03:00 pmoranga # Revision 1.1 2009/04/26 23:28:00 ckujau # Revision 1.0 2009/04/22 22:00:00 zlati @@ -28,39 +28,108 @@ #env.logfile /var/log/messages #env.category users # +# config example with journald +#[sshd_log] +#group systemd-journal +#env.logfile journald +# +# config example with journald on the sshd.service unit only +#[sshd_log] +#group systemd-journal +#env.logfile journald +#env.journalctlarg --unit=sshd.service +# LOG=${logfile:-/var/log/secure} +JOURNALCTL_ARG=${journalctlarg:-_COMM=sshd} if [ "$1" = "autoconf" ]; then - if [ -r "$LOG" ]; then - echo yes - exit 0 + if [ "$LOG" = "journald" ]; then + if journalctl --no-pager --quiet --lines=1 "$JOURNALCTL_ARG" | read -r DUMMY; then + echo yes + exit 0 + else + echo no + exit 1 + fi else - echo no - exit 1 + if [ -r "$LOG" ]; then + echo yes + exit 0 + else + echo no + exit 1 + fi fi fi if [ "$1" = "config" ]; then + if [ "$LOG" = "journald" ]; then + TYPE=ABSOLUTE + else + TYPE=DERIVE + fi + echo 'graph_title SSHD login stats from' $LOG echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel logins' echo 'graph_category' security echo 'LogPass.label Successful password logins' + echo 'LogPass.min 0' + echo 'LogPass.type' "$TYPE" + echo 'LogPassPAM.label Successful login via PAM' + echo 'LogPassPAM.min 0' + echo 'LogPassPAM.type' "$TYPE" + echo 'LogKey.label Successful PublicKey logins' + echo 'LogKey.min 0' + echo 'LogKey.type' "$TYPE" + echo 'NoID.label No identification from user' + echo 'NoID.min 0' + echo 'NoID.type' "$TYPE" + echo 'rootAttempt.label Root login attempts' + echo 'rootAttempt.min 0' + echo 'rootAttempt.type' "$TYPE" + echo 'InvUsr.label Invalid user login attepmts' + echo 'InvUsr.min 0' + echo 'InvUsr.type' "$TYPE" + echo 'NoRDNS.label No reverse DNS for peer' + echo 'NoRDNS.min 0' + echo 'NoRDNS.type' "$TYPE" + echo 'Breakin.label Potential Breakin Attempts' + echo 'Breakin.min 0' + echo 'Breakin.type' "$TYPE" + exit 0 fi -awk 'BEGIN{c["LogPass"]=0;c["LogKey"]=0;c["NoID"]=0;c["rootAttempt"]=0;c["InvUsr"]=0;c["LogPassPAM"]=0;c["Breakin"]=0;c["NoRDNS"]=0; } +if [ "$LOG" = "journald" ]; then + CURSOR_FILE="$MUNIN_STATEFILE" + # read cursor + # format: "journald-cursor " + CURSOR= + if [ -f "$CURSOR_FILE" ]; then + CURSOR=$(awk '/^journald-cursor / {print $2}' "$CURSOR_FILE") + fi +else + CURSOR_FILE= +fi + +if [ "$LOG" = "journald" ]; then + journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} "$JOURNALCTL_ARG" +else + cat $LOG +fi | \ + awk -v cursor_file="$CURSOR_FILE" 'BEGIN{c["LogPass"]=0;c["LogKey"]=0;c["NoID"]=0;c["rootAttempt"]=0;c["InvUsr"]=0;c["LogPassPAM"]=0;c["Breakin"]=0;c["NoRDNS"]=0; } /sshd\[.*Accepted password for/{c["LogPass"]++} /sshd\[.*Accepted publickey for/{c["LogKey"]++} /sshd\[.*Did not receive identification string/{c["NoID"]++} @@ -69,4 +138,4 @@ awk 'BEGIN{c["LogPass"]=0;c["LogKey"]=0;c["NoID"]=0;c["rootAttempt"]=0;c["InvUsr /sshd\[.*POSSIBLE BREAK-IN ATTEMPT!/{c["Breakin"]++} /sshd\[.*keyboard-interactive\/pam/{c["LogPassPAM"]++} /sshd\[.*reverse mapping checking getaddrinfo/{c["NoRDNS"]++}a - END{for(i in c){print i".value " c[i]} }' < $LOG + END{if (cursor_file != "") { print "journald-cursor " $3 > cursor_file };for(i in c){print i".value " c[i]} }' From c04acf95ac533b443690d8902357b141f487267e Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sat, 12 Nov 2016 15:00:18 +0100 Subject: [PATCH 180/695] sshd_log: fix shellcheck warnings --- plugins/ssh/sshd_log | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index dc57e3df..ac7d21f0 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -72,7 +72,7 @@ if [ "$1" = "config" ]; then TYPE=DERIVE fi - echo 'graph_title SSHD login stats from' $LOG + echo 'graph_title SSHD login stats from' "$LOG" echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel logins' echo 'graph_category' security @@ -127,7 +127,7 @@ fi if [ "$LOG" = "journald" ]; then journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} "$JOURNALCTL_ARG" else - cat $LOG + cat "$LOG" fi | \ awk -v cursor_file="$CURSOR_FILE" 'BEGIN{c["LogPass"]=0;c["LogKey"]=0;c["NoID"]=0;c["rootAttempt"]=0;c["InvUsr"]=0;c["LogPassPAM"]=0;c["Breakin"]=0;c["NoRDNS"]=0; } /sshd\[.*Accepted password for/{c["LogPass"]++} From ff68f64155cc111010bab3682e2814743a6a0516 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sat, 12 Nov 2016 15:02:26 +0100 Subject: [PATCH 181/695] sshd_log: cleanup autoconf * always return 0 * add details on autoconf failure --- plugins/ssh/sshd_log | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index ac7d21f0..cad2e8ac 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -47,21 +47,18 @@ JOURNALCTL_ARG=${journalctlarg:-_COMM=sshd} if [ "$1" = "autoconf" ]; then if [ "$LOG" = "journald" ]; then if journalctl --no-pager --quiet --lines=1 "$JOURNALCTL_ARG" | read -r DUMMY; then - echo yes - exit 0 + echo "yes" else - echo no - exit 1 + echo "no (journald empty log for '$JOURNALCTL_ARG' not found)" fi else if [ -r "$LOG" ]; then - echo yes - exit 0 + echo "yes" else - echo no - exit 1 + echo "no (logfile '$LOG' not readable)" fi fi + exit 0 fi if [ "$1" = "config" ]; then From 26181ead335223b439948b3b7436d3c29a757cda Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sat, 12 Nov 2016 15:38:29 +0100 Subject: [PATCH 182/695] sshd_log: POD style documentation --- plugins/ssh/sshd_log | 103 ++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index cad2e8ac..f8555db5 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -1,44 +1,75 @@ #!/bin/sh -# -# Plugin to monitor auth.log or journald for sshd server events. -# -# Require read permitions for $LOG or journald -# (set in /etc/munin/plugin-conf.d/munin-node on debian) -# -# $Log$ -# Revision 2.0 2016/11/11 15:42:00 Thomas Riccardi -# Revision 1.2 2010/03/19 15:03:00 pmoranga -# Revision 1.1 2009/04/26 23:28:00 ckujau -# Revision 1.0 2009/04/22 22:00:00 zlati -# Initial revision -# -# Parameters: + +: <<=cut + +=head1 NAME + +sshd_log - Munin plugin to monitor auth.log or journald for sshd + server events. + +=head1 CONFIGURATION + +This plugin requires read permission for the logfile or journald. + +On busy servers you can change value type to COUNTER and set min to 0 +to avoid minus peaks at logrotate. + +The following environment variables are used by this plugin: + + logfile - path to the auth log file, or "journald" to use journald. + default: /var/log/secure + category - graph category. default: system + + journalctlargs - space separated list of arguments to pass to + journalctl to get the sshd logs. + default: _COMM=sshd + +If the "logfile" environment variable is set to "journald" the sshd +logs are read from journald, filtering on program "sshd". The filtering +may be changed using "journalctlarg". + + +Config examples for /etc/munin/plugin-conf.d/munin-node: + + [sshd_log] + user root + group root + env.logfile /var/log/messages + env.category users + +Config example with journald: + + [sshd_log] + group systemd-journal + env.logfile journald + +Config example with journald on the sshd.service unit only: + + [sshd_log] + group systemd-journal + env.logfile journald + env.journalctlarg --unit=sshd.service + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 AUTHOR + +Revision 2.0 2016/11/11 15:42:00 Thomas Riccardi +Revision 1.2 2010/03/19 15:03:00 pmoranga +Revision 1.1 2009/04/26 23:28:00 ckujau +Revision 1.0 2009/04/22 22:00:00 zlati + +=cut + + +# Script parameters: # # config (required) # autoconf (optional - used by munin-config) -# -# Magick markers (optional): -#%# family=auto -#%# capabilities=autoconf -# config example for /etc/munin/plugin-conf.d/munin-node -#[sshd_log] -#user root -#group root -#env.logfile /var/log/messages -#env.category users -# -# config example with journald -#[sshd_log] -#group systemd-journal -#env.logfile journald -# -# config example with journald on the sshd.service unit only -#[sshd_log] -#group systemd-journal -#env.logfile journald -#env.journalctlarg --unit=sshd.service -# LOG=${logfile:-/var/log/secure} JOURNALCTL_ARG=${journalctlarg:-_COMM=sshd} From bc1e78ae5a8f835bc68ef6d22f8de162977665d3 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sat, 12 Nov 2016 17:00:35 +0100 Subject: [PATCH 183/695] sshd_log: remove env.category: it should not be configurable --- plugins/ssh/sshd_log | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index f8555db5..66061a84 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -18,7 +18,6 @@ The following environment variables are used by this plugin: logfile - path to the auth log file, or "journald" to use journald. default: /var/log/secure - category - graph category. default: system journalctlargs - space separated list of arguments to pass to journalctl to get the sshd logs. @@ -35,7 +34,6 @@ Config examples for /etc/munin/plugin-conf.d/munin-node: user root group root env.logfile /var/log/messages - env.category users Config example with journald: From e178be6432474c904a1531767840b504ef3fd454 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 13 Nov 2016 20:43:46 +0100 Subject: [PATCH 184/695] sshd_log: fix label typo --- plugins/ssh/sshd_log | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index 66061a84..3c38af1c 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -123,7 +123,7 @@ if [ "$1" = "config" ]; then echo 'rootAttempt.min 0' echo 'rootAttempt.type' "$TYPE" - echo 'InvUsr.label Invalid user login attepmts' + echo 'InvUsr.label Invalid user login attempts' echo 'InvUsr.min 0' echo 'InvUsr.type' "$TYPE" From cdb82255e9151f42176d36da1d7817bf83d51b81 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 29 Apr 2018 00:58:41 +0200 Subject: [PATCH 185/695] sshd_log: allow multiple journalctl args Useful for multiple filtering. At the cost of not being able to pass spaces properly in args, but that's uncommon. --- plugins/ssh/sshd_log | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index 3c38af1c..5026d607 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -25,7 +25,7 @@ The following environment variables are used by this plugin: If the "logfile" environment variable is set to "journald" the sshd logs are read from journald, filtering on program "sshd". The filtering -may be changed using "journalctlarg". +may be changed using "journalctlargs". Config examples for /etc/munin/plugin-conf.d/munin-node: @@ -46,7 +46,7 @@ Config example with journald on the sshd.service unit only: [sshd_log] group systemd-journal env.logfile journald - env.journalctlarg --unit=sshd.service + env.journalctlargs --unit=sshd.service =head1 MAGIC MARKERS @@ -70,15 +70,16 @@ Revision 1.0 2009/04/22 22:00:00 zlati LOG=${logfile:-/var/log/secure} -JOURNALCTL_ARG=${journalctlarg:-_COMM=sshd} +JOURNALCTL_ARGS=${journalctlargs:-_COMM=sshd} if [ "$1" = "autoconf" ]; then if [ "$LOG" = "journald" ]; then - if journalctl --no-pager --quiet --lines=1 "$JOURNALCTL_ARG" | read -r DUMMY; then + # shellcheck disable=SC2086,SC2034 + if journalctl --no-pager --quiet --lines=1 $JOURNALCTL_ARGS | read -r DUMMY; then echo "yes" else - echo "no (journald empty log for '$JOURNALCTL_ARG' not found)" + echo "no (journald empty log for '$JOURNALCTL_ARGS' not found)" fi else if [ -r "$LOG" ]; then @@ -151,7 +152,8 @@ else fi if [ "$LOG" = "journald" ]; then - journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} "$JOURNALCTL_ARG" + # shellcheck disable=SC2086 + journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} $JOURNALCTL_ARGS else cat "$LOG" fi | \ From 8f68d6e69719024cfa054b3f9d7fec0444e746ae Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 29 Apr 2018 01:36:32 +0200 Subject: [PATCH 186/695] sshd_log: allow both types GAUGE and DERIVE, default to GAUGE In fact for journald mode internally it really is type ABSOLUTE, because we use journald cursor to get only new logs on each collect. It results in the same value as with a plain log file and type DERIVE: it's the rate of events. type=DERIVE is best for servers with lots of ssh traffic. --- plugins/ssh/sshd_log | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index 5026d607..f9b19341 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -23,6 +23,9 @@ The following environment variables are used by this plugin: journalctl to get the sshd logs. default: _COMM=sshd + type - "GAUGE" or "DERIVE" + default: GAUGE + If the "logfile" environment variable is set to "journald" the sshd logs are read from journald, filtering on program "sshd". The filtering may be changed using "journalctlargs". @@ -48,6 +51,13 @@ Config example with journald on the sshd.service unit only: env.logfile journald env.journalctlargs --unit=sshd.service +Config example with journald and type DERIVE: + + [sshd_log] + group systemd-journal + env.logfile journald + env.type DERIVE + =head1 MAGIC MARKERS #%# family=auto @@ -71,6 +81,10 @@ Revision 1.0 2009/04/22 22:00:00 zlati LOG=${logfile:-/var/log/secure} JOURNALCTL_ARGS=${journalctlargs:-_COMM=sshd} +TYPE=${type:-GAUGE} +if [ "$LOG" = "journald" -a "$TYPE" = "DERIVE" ]; then + TYPE=ABSOLUTE +fi if [ "$1" = "autoconf" ]; then @@ -92,13 +106,6 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - - if [ "$LOG" = "journald" ]; then - TYPE=ABSOLUTE - else - TYPE=DERIVE - fi - echo 'graph_title SSHD login stats from' "$LOG" echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel logins' @@ -139,7 +146,7 @@ if [ "$1" = "config" ]; then exit 0 fi -if [ "$LOG" = "journald" ]; then +if [ "$LOG" = "journald" -a "$TYPE" = "ABSOLUTE" ]; then CURSOR_FILE="$MUNIN_STATEFILE" # read cursor # format: "journald-cursor " @@ -153,7 +160,11 @@ fi if [ "$LOG" = "journald" ]; then # shellcheck disable=SC2086 - journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} $JOURNALCTL_ARGS + if [ "$TYPE" = "ABSOLUTE" ]; then + journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} $JOURNALCTL_ARGS + else + journalctl --no-pager --quiet --since=$(date -dlast-sunday +%Y-%m-%d) $JOURNALCTL_ARGS + fi else cat "$LOG" fi | \ From 1bd6a6411dedc00ac9bfbf750a877a7bfba685a7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 6 May 2018 21:47:38 +0200 Subject: [PATCH 187/695] add plugin "feinstaubsensor" --- plugins/luftdaten/feinstaubsensor | 205 ++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100755 plugins/luftdaten/feinstaubsensor diff --git a/plugins/luftdaten/feinstaubsensor b/plugins/luftdaten/feinstaubsensor new file mode 100755 index 00000000..e9732bfb --- /dev/null +++ b/plugins/luftdaten/feinstaubsensor @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +""" + +=head1 NAME + +feinstaubsensor - Plugin to monitor one or more environmental sensors + + +=head1 APPLICABLE SYSTEMS + +The "Feinstaubsensor" was developed by the OK Lab Stuttgart and is part of the +Citizen Science Project "luftdaten.info" (http://luftdaten.info). + +Data is retrieved via HTTP requests from the sensors itself. + + +=head1 CONFIGURATION + +Place a configuration entry somewhere below /etc/munin/plugin-conf.d/: + + [feinstaubsensor] + env.sensor_hosts foo=192.168.1.4 [fe80::1:2:3:4%eth0] bar=sensor2.lan + +The environment variable is a space separated list of . +Each can be either a or a combination of label and (separated by the +character "="). +A may be an IPv4 address, an IPv6 address (enclosed in square brackets) or a name to be +resolved via DNS. + +Examples for : + * 192.168.1.4 + * foo=192.168.1.4 + * [fe80::1a:2b:3c:cafe] + * bar=[fe80::1a:2b:3c:cafe] + * feinstaubsensor-12345.local + * baz=feinstaubsensor-12345.local + + +=head1 AUTHOR + + Lars Kruse + + +=head1 LICENSE + + GPLv3 + + +=head1 MAGIC MARKERS + + #%# family=manual + +""" + +import collections +import functools +import json +import os +import re +import sys +import urllib.request + + +graphs = [ + { + "name": "wireless_signal", + "graph_title": "Feinstaub Wifi Signal", + "graph_vlabel": "%", + "graph_args": "-l 0", + "graph_info": "Wifi signal strength", + "api_value_name": "signal", + "value_type": "GAUGE", + }, { + "name": "feinstaub_samples", + "graph_title": "Feinstaub Sample Count", + "graph_vlabel": "#", + "graph_info": "Number of samples since bootup", + "api_value_name": "samples", + "value_type": "DERIVE", + }, { + "name": "feinstaub_humidity", + "graph_title": "Feinstaub Humidity", + "graph_vlabel": "% humidity", + "graph_info": "Weather information: air humidity", + "api_value_name": "humidity", + "value_type": "GAUGE", + }, { + "name": "feinstaub_temperature", + "graph_title": "Feinstaub Temperature", + "graph_vlabel": "°C", + "graph_info": "Weather information: temperature", + "api_value_name": "temperature", + "value_type": "GAUGE", + }, { + "name": "feinstaub_particles_pm10", + "graph_title": "Feinstaub Particle Measurement P10", + "graph_vlabel": "µg / m³", + "graph_info": "Concentration of particles with a size between 2.5µm and 10µm", + "api_value_name": "SDS_P1", + "value_type": "GAUGE", + }, { + "name": "feinstaub_particles_pm2_5", + "graph_title": "Feinstaub Particle Measurement P2.5", + "graph_vlabel": "µg / m³", + "graph_info": "Concentration of particles with a size up to 2.5µm", + "api_value_name": "SDS_P2", + "value_type": "GAUGE", + }] + + +SensorHost = collections.namedtuple("SensorHost", ("host", "label", "fieldname")) + + +def clean_fieldname(text): + if text == "root": + # "root" is a magic (forbidden) word + return "_root" + else: + return re.sub(r"(^[^A-Za-z_]|[^A-Za-z0-9_])", "_", text) + + +def parse_sensor_hosts_from_description(hosts_description): + """ parse sensor list from the environment variable 'sensor_hosts' and retrieve their data """ + sensors = [] + for token in hosts_description.split(): + if "=" in token: + label, host = token.strip().split("=", 1) + else: + host = token.strip() + label = host + fieldname = clean_fieldname("value_" + host) + sensors.append(SensorHost(host, label, fieldname)) + sensors.sort(key=lambda item: item.fieldname) + return sensors + + +@functools.lru_cache() +def get_sensor_data(host): + """ request the data from a sensor and return a dict (value_type -> value) + + The result is cached - thus we do not need to take care for efficiency. + + Example dataset returned by the sensor: + {"software_version": "NRZ-2017-099", "age":"88", "sensordatavalues":[ + {"value_type":"SDS_P1","value":"27.37"},{"value_type":"SDS_P2","value":"13.53"}, + {"value_type":"temperature","value":"23.70"},{"value_type":"humidity","value":"69.20"}, + {"value_type":"samples","value":"626964"},{"value_type":"min_micro","value":"225"}, + {"value_type":"max_micro","value":"887641"},{"value_type":"signal","value":"-47"}]} + + """ + try: + with urllib.request.urlopen("http://{}/data.json".format(host)) as request: + body = request.read() + except IOError as exc: + print("Failed to retrieve data from '{}': {}".format(host, exc), file=sys.stderr) + return None + try: + data = json.loads(body.decode("utf-8")) + except ValueError as exc: + print("Failed to parse data from '{}': {}".format(host, exc), file=sys.stderr) + return None + return {item["value_type"]: item["value"] for item in data["sensordatavalues"]} + + +def print_graph_section(graph_description, hosts, include_config, include_values): + print("multigraph {}".format(graph_description["name"])) + if include_config: + # graph configuration + print("graph_category sensors") + for key in ("graph_title", "graph_vlabel", "graph_args", "graph_info"): + if key in graph_description: + print("{} {}".format(key, graph_description[key])) + for host_info in hosts: + print("{}.label {}".format(host_info.fieldname, host_info.label)) + print("{}.type {}".format(host_info.fieldname, graph_description["value_type"])) + if include_values: + for host_info in hosts: + # We cannot distinguish between fields that are not supported by the sensor (most are + # optional) and missing data. Thus we cannot handle online/offline sensor data fields, + # too. + data = get_sensor_data(host_info.host) + if data is not None: + value = data.get(graph_description["api_value_name"]) + if value is not None: + print("{}.value {}".format(host_info.fieldname, value)) + print() + + +action = sys.argv[1] if (len(sys.argv) > 1) else "" +sensor_hosts = parse_sensor_hosts_from_description(os.getenv("sensor_hosts", "")) +if not sensor_hosts: + print("ERROR: undefined or empty environment variable 'sensor_hosts'.", file=sys.stderr) + sys.exit(1) + + +if action == "config": + is_dirty_config = (os.getenv("MUNIN_CAP_DIRTYCONFIG") == "1") + for graph in graphs: + print_graph_section(graph, sensor_hosts, True, is_dirty_config) +elif action == "": + for graph in graphs: + print_graph_section(graph, sensor_hosts, False, True) +else: + print("ERROR: unsupported action requested ('{}')".format(action), file=sys.stderr) + sys.exit(2) From ee3b1cba8c49ff76e127752cdd7c74ab4e462bb4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 25 May 2018 23:52:09 +0200 Subject: [PATCH 188/695] plugin's readme: fix broken sentence --- plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/README.md b/plugins/README.md index 6257568a..1bee0c21 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -14,7 +14,7 @@ This repository of contributed plugin strives to achieve the following goals: * simplify cooperative maintenance of plugins Contributed plugins are maintained primarily by their authors. -You may file bug reports for plugin issue here in this repository (`munin-contrib`), but please do not forget to notify the author the plugin, as well. +You may file bug reports for plugin issue here in this repository (`munin-contrib`), but please do not forget to notify the author of the plugin (see the plugin's documentation), too. Please note, that this repository is not supposed to be a dumping site for random plugins of low quality. The related infrastructure (e.g the [gallery](http://gallery.munin-monitoring.org/) or automated tests) require a certain level of quality. Please see below for details. From 8e37e4802a6dfef5d6220cc7ecc9439b97783d33 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 28 May 2018 10:58:15 +0200 Subject: [PATCH 189/695] add example graphs for sshd_log plugin Contributed by Niluge-KiWi: https://github.com/munin-monitoring/contrib/pull/770/files --- plugins/ssh/example-graphs/sshd_log-day.png | Bin 0 -> 22225 bytes plugins/ssh/example-graphs/sshd_log-month.png | Bin 0 -> 31168 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/ssh/example-graphs/sshd_log-day.png create mode 100644 plugins/ssh/example-graphs/sshd_log-month.png diff --git a/plugins/ssh/example-graphs/sshd_log-day.png b/plugins/ssh/example-graphs/sshd_log-day.png new file mode 100644 index 0000000000000000000000000000000000000000..525c9c03ef45c04a68e0ef2c363cfc84c8254e6d GIT binary patch literal 22225 zcmb5W1z41A*EKvyN=QfxA|WbB3`&<;m-GY-v2wkZ|>uk0fw3DIhnekrmwLmB9_c7n^UAo-*SXGtm`c-c7 zK#B<7E<@EfN^i2U$fK7=nM~D+(=ySQqpi`I&m>7nRkmk~l`BRwe{ZzRW-cRV=jNo~ zpuFxHTFl_U6GR-Ai3ohZium8Y7(V{vMPD@T-3!(J-5Qg2L^!%7`M689qb+0IIm zG|PB0H}|ja?D+1}8CgfAwqnnk$`fwB>tw(-L3h^vkc=!aZTI!|mSdy1b?FqMTslMJ z%a7t%Le?BoWA?mc`fI>yZ#JIY+;Njvvd-e=j#YP>FQAgeR z3Sal^*|S7MM6$B7OiT$8`Yz3VefRy$L)<$)h9{zEZ`#|h50%w9c&E{vM%!6!X0Bqa3al%XP1)Y(hQicx%1*~KH#e2>0W zS63hIZ}aGuc;Q?7M#$hCC!INS=1Xz0=kek8l`B`O4i{|}7Z*1Pb^LF~2yVf+0p ztLHcJj2m9tPq&dD6Oxe%SPg3E>JI(2a83Qw z#KiT-cg99b5@5w&ym+y{y-Z3<>gwt$D=j@RFrZqOu9va=ijs#qpsu#|+qZ8nE-n(g zzs@SrHyE!r2Axw-P=E`;z{tqT!ZP^tXVu}>&!r`6Qqquq1JB`x1|OIDhkQTm+r=Z^ zytzOfkp1bCt%;}SG1Ku*Q-$mH*4CEV)R=oln)g0)2JK?0>azD1FN?f*aDo~skJQXy zk>z(&`>W4Be}u=UPl_2D+`1*a*RS_{|GrSrE}?^Vn6338vb&+F*>7;X*MGbda$N1n zC@ledLh@kc<&6u@vndg;90yHmQ|nHD{T_8K3L7pb9i08b!opNbgi4G+kcJ^&`E~76 z*x1MORYw8LV?S-GtgNhpf`erU?xb|Vej6Jz^zLW~peaM6k5>F7FL?9u@tGB8=j3>7 z4?9^sc_Oc%P*70dxgIn6=F~^E*}cvA!-E4CSJ!~oui;%+y*?bJ#YacepFdxTn!oQO zWp{M2dvI{@^{bo(Z4u`tL)qtW9!DnIrhf+W}Nr3zR?M@Pp99)qKwqelX! zZLyfsgUbUyk$}Bfwp^*8_EGin+}zw3>*2kb&g8YVwQx?&i7oi*Lqwzf;DP(m?zDz* z&%0M1jZe9IM3ck=3r&NzZ1aUV`YT67U<)IE-aljGWnWmCTDTTrkrIO?`O?#Faq z#9?OPM*%iAHfyCf8HKvxKtNoao~Nhet?Jeo!MeJ-ODgeV&(?mCoD&!-e|EN5^ZtEb zUtdBWsU%feYHFFX#Z;x3g~dg#t5@&m=C)6LlBs@$KRHg zh9DkG+uYpjv7Vow*VEI39SEB|N53LdgM(a3K4lFyS&7ucH_7o7ogpsM$>KC~ad~2a z#DUHNMQvf?5PQz+D=W4`*}1v7K0Xbkk_TR77B)5!%@125c+y&BFH=sq{k_Fr^;g`8 zli&+uS900x)iiM5ycaINxwAU?_U+rk!a|5zzAEpT%9Mw+eN#_`kC=w(Yig3x z()_-}IqHatiXK!C4e2JSs$x4X4A_m=`h57nVre-K2QawI%OvjNuryTK*w~n+lEC7f zZctLNEFX78JvA_$JVS{wIwq#zu7Me3>+A*$7JQAq8b#BNOgUEeQz(nn3$NbaMYo~)r|D?#9+p`fge9m4i00@-NC_U zj05Wyoux{XlW&z(sKkkEYF6!q%cr27mh~S$eypp@AVqN8u3$S+?>{y+X7%&S18way zkcpa_CLPsitu%?#ZF24;Q-6Kr~e z$jYt!MmD>=?D_k96y$Jtbf>AD9cA3z-=ChE+T6fAeoal8r>*{K4$IEYhK(<0MV6nR zZ*FCE(HIXO|B_PlLnEWk-rn%IIHkg{TC*}^fA*l{DX0dJbFMx9UajKp=7xud2W$VL zD)jODx{mf&&${A?)GkGDi9+DRKB}pyfyMQ$Ye8&Q-8if;eQIyN`n$BWG=Q3`Hi(^_ zJ-2*K-rRh4t~blPN`Gy8`;vf|{cqyVoRcevgJZDG=f3BVq$=dJq&wG(Wg98;cyq2- zGgn_6HTO;`DX*X)M%0Ph4n_S%A z;0xaR&f`821|CsnlJ>v9!xH13s`>n=#lq6Es6)tRq`d0*a2Z0bt*xzo`+3&=?7fX{ zl>`a**9u>tIEV`h3i9*!wzVmIW=?GDhXK`NA4z7Z3jj~Xv)ifTUl9w z%AlF8eY-FwI$A_n7#9b}?3>>}t{hutMn=sd@$`#QCWG2vAT}eS@{n+~$+>w(~67y`4earDt^1VBRyYG3iz%c7KH;0aRSph!%)hdwY8* z&Ned+#3738CPz=DJP%ddF@MM<;@O_AkqJo|mY{Rw1Jat2o@Bizv62vlM%wPKO?7p3 zL4w&`pZ4?ftDC~Y^X8eBntt}W!PwPxA9AbxV4=B4v+6V>Jw9L56Nt?E3KPl0jkB{y z4{5ux5D4d6CML-@F4#Ic$`BM46-{^}3+oRP}Q< zq3yo+^mggNB^6oe- zOWt#6!FBAbdweR~NF88q$JtxAZo#3zJ|bW~ckY~~re@cnv2Sj-&1j{bhQ`LP=Cgu= z#d&$Wko8M-xx?ilTbq85B&VW!WNIpF#Vla6m^Z_;QFzIN zB_hNwF2n;5NL^ison462NoSrg$d^(lmpFx}?|N}RzClxET#~9l1OXWv8;f%at1?Ez z-H1PMylDg8d)$)=@_>nv(Q~r5%@0{Zzjk%0u~9TXQbV9c0=uK)!BnHrR zH009e%yykpP-p?f16O=}+^Ad#?@INGiHHn$M1qjrDW0H#(a{i1yMp}un&N6T9fEfd zb4|fl%FsCB;bE6l?jIixRU&WLB}p_O8OMve z-q*oAM1_Rp_*`%5MykjX@L#|FF*P;kK>;35a^KCsZ-Q%cTig4VDFct) z6s@b&0Szx-zGP-*Zf$M7bLS5HYuTT3o8Yd_fU2fuQ(5H5TMFeVY;+`+-ofkfXl^bu z3yaTrc`Fb`YioRhf;JWwEV#6l%KfKR)36cMtc5wX6%|TKN|=iNIW@Jhzh6_l zdIg0(8q*G{ZW1}=fW$`cP}TA4Y4gJIVEzsfC}&(08zH>aR1nK|`~K zv|EHpb-l%=XynGLH1m5|4#Q!vb4XlKy>^dx$w$Kd)68 z&k=-bnmwnlB?@Q87|&r9o}o9edN2__8RUp^v9f}g$_~OjNGikMy&j);SZyccx_9rM z=fNbCiLvp~PLt#Wky9SCBJco6)mI`~9&CLtqJJLpuXmIos=RR69Cit43h8+<=e1wW zz{cc|x*6RK#`)4LH6dpyD84-TF)=lDPhCAMD(cx-_4B|$VopxZl}9*;Y9ZQ5w>^L; zo|s3KF8B7@nwn?x3QB}3Nmb6}dtMuPK0DqK&i!M(h^RXj;~t9prU`ccY+<#+V#^A4 zJedn*03S-x=(l(d^{pZmm6bEa?H^>uKN-nDvrbMt{t0b^OIKtxm1u0%%z z@yvuKalgYXHh14F&^&;o*Wy*$zkPch5bzQej6m?RJKRfCx8dY>i&NI!kP>?p7zjmB z+;;4SzP|p$hiorU!Nd`y|GFNUa~sH0DB#Cpr`+g@o?G&tVwxxT`*X~(3a+reIN3G_ z$ltpThc*AP{EcUdG^P(D+w$0b)6y%pAy`EiqWHyHsD$u0mzX*l7*2LNqn>tqg_zq;2QaqIPIBg+kr4@AUS0q)n+TKh zm@Qc1-)G{2LKPJk3kp2a;0T9{Us+i>hgF{7YJ@nBse_c&y8Zm0EO?cR>*vp(P^jSX zb8ry8y@T~p2 z^1ZoJ0fqAO=g$!0Ko-(9IQ*qqB3e?^%-!7&1L>}P`0xRMfLz>-IH+MEyubJ5E>fIE zMwqxq_lky))zF;BhKQ9LU`T9~1rVKRCr03DJXO3RkR_Sy+tG<%F z{OjOgYC<1?9e8q^H$H^s$H0O+c#Zv&i(mvM(lZpAjJ2RoL?=ecW$Lh80y2I8`IyBSm=d+G%JF2 zk`cU-`)Tu}IlTmKpIC3uTuTfvK4!i&A8x0QT}) znk7mx_562;;`KVekJL*|h9zO7GpF=)zczh!2zsPMY&>yW-D`YWpLfpE7}T1^kr9uE z*HqFbK)a2NKis%Ld;WYdAWvmwcB?b}Y4&26e2?caD=MRTUcaZ5hC6t6x$3x5K7~d9 ztsL8*M-F*!DojO!nf=GAjn?#6)qmrb--nz%f1XZ{0ZWeS^ONL8zahA7yd$EwmitD( zmK37Oad{-}-Md29ZRn(Kd# z^oh^yMk{Mxri7X*ARquL2s{C11VuoDbj&@j4RIv*3$pj`{XZ`%y7MAWMp+5%Ft5>G zDU-h7EDoV+=Yg2y(|F>Z{(eYQ=#mnx2M>TD0S@}}D0k?5_o8nY`u&c*>B!minJIgI z14J=XQ_~4^Sy?|Q=q`w!*`e3Z_g)zw69ZI`Yi~y;M+W=*>)UJN(OXd>%@2qM2M05I z%m6*$o}9z9k-9|4EABZK z0ARC`kd-5aXG!J+8M^yo-qF@9(cV+M9gxTU z9u$~$vUtC7v^4IqdlEz?fUJmp;CH(it^+4r2lVUXmDpOL+^c!>R=f3EMV{wE+}P)K z>C5`L%_)3uOhz89HZdK83W)*1ckkYnllz*Xo`%uw0+FZjdYU$%zSP#%rl()Fv9TF? z8y|0NWks?+i>Su=Zm{l`A2(B&>JC@2GMj)stM7p8A&y3s2mVh^*-t7>G}wHn+A`mY27%w$?9m4@99Qi|Gc)$xASM_hj)T)=*^M$e4+-?g75hJ6b!L<3|J)9>ljO3Qsv@Eg=7Kq&yo zQ&Lh`LgmVci3$2Ga3uQ369Z1eCZbKujB&H9$6{>jA27j?o5yXj&Af@MOS%aXG7!QJ z#NU;$_oOxFL)FHYiULji{-~dmJt<|DS*4B!AME0JCVNvzulLP@gewW^LxR||4{8~< zMCMp#%2+6o6_vzFyd5vBxeZ7y{rawplOdAt4+7))Xjvg;@fbTKXU!>sFub!y8dSx( z)u$x#JX`YmQM_ALjyt=bgB=fKJpRX5A=STX6gjMZjt%2CmO#$+pl*BB)=*&>fG^qwm0oPbjegohWrNbEQy}KOeS>+dthMmq%i2sHrm3*KVl;s9KLQ#J7ybsDnuR{$tS^4^GB`t@t0PsfH) z%z0(3<|TB3ey#m?$x0zDdFsAL`y7L~&l`D(RAgn%LsT$%?0{75;e4=TCvoObH;a5n zOZFd<&t%5XouZZjFRgUn-&*`Ry|8dKT)ycV*B0+5|MA&b3~vcs{1zSyP~bUqvP>SL zi3`~AHnkf+eq?ihn0VIg|AfW&Vq80gQawSF2N+4=FHL|+;Sje@qwbn2cLEtBL5T5^ zr8SgrZ+XuJeAw>{BS`A1>gwI^xpX$(JwK{G{=EX+Z{7VLH39zw$W=RE%Xgzg;r

    ^XR5w=Tcs=2` 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 _60`8G=^8#_D9_OpwN>oFb9WBW_5 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°Mwr=HEDb8WRJ>EOFSF7 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 literal 0 HcmV?d00001 From d885345ecf33e4dd5bac47fb4bcc22d1fd976afa Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 12:25:58 +0200 Subject: [PATCH 190/695] fix location of example graph for 'tl_sg' --- plugins/tplink/{ => example-graphs}/tl_sg-day.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/tplink/{ => example-graphs}/tl_sg-day.png (100%) diff --git a/plugins/tplink/tl_sg-day.png b/plugins/tplink/example-graphs/tl_sg-day.png similarity index 100% rename from plugins/tplink/tl_sg-day.png rename to plugins/tplink/example-graphs/tl_sg-day.png From bceabb82fb5cf245ef80c6d7f611b29696e92856 Mon Sep 17 00:00:00 2001 From: Chris Butler Date: Thu, 5 Oct 2017 11:05:49 +0100 Subject: [PATCH 191/695] jenkins: Added support for jobs folders Newer versions of Jenkins have support for grouping jobs into "folders", for example when using a multibranch setup, the main "job" will actually be a folder containing a job for each branch. This changeset adds a "jobDepth" parameter that can be used to control how far down the folder tree to report on. --- plugins/jenkins/jenkins_ | 96 +++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index d3d90c70..2b9c91a0 100755 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -28,6 +28,7 @@ env.port Jenkins Port env.context Jenkins Context path env.user User for the API Tokent env.apiToken Jenkins API Token (see https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients) +env.jobDepth How far into job "folders" should the plugin check for jobs Example: @@ -62,6 +63,7 @@ my $port = ($ENV{'port'} || '4040'); my $user = ($ENV{'user'} || ''); my $apiToken = ($ENV{'apiToken'} || ''); my $context = ($ENV{'context'} || ''); +my $jobDepth = ($ENV{'jobDepth'} || 1); my $wgetBin = "/usr/bin/wget"; my $type = basename($0); @@ -130,8 +132,15 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } else { my $cmd = "$wgetBin $auth -qO- $url:$port$context"; + my $tree = 'jobs[name,color]'; + for (2..$jobDepth) { + $tree = "jobs[name,color,$tree]"; + } + if( $type eq "results" ) { - my $counts = get_results(''); + my $result = `$cmd'/api/json?depth=$jobDepth&tree=$tree'`; + my $parsed = decode_json($result); + my $counts = parse_results($parsed->{'jobs'}); foreach my $status (keys %{$counts}) { print "build_$status.value $counts->{$status}\n"; @@ -140,9 +149,11 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } if( $type eq "running" ) { - my $running_count = get_running(''); - print "build_running.value ", $running_count, "\n"; - exit; + my $result = `$cmd'/api/json?depth=$jobDepth&tree=$tree'`; + my $parsed = decode_json($result); + my $count = parse_running_builds($parsed->{'jobs'}); + print "build_running.value ", $count, "\n"; + exit; } if( $type eq "queue" ) { @@ -153,53 +164,36 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } } -{ - my %counts; - - sub get_results{ - my $query_context = shift; - die "get_results requires an argument" unless defined $query_context; - unless (%counts) { - # initialise - %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); - } - my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; - my $result = `$cmd`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if (exists $cur->{'color'} && defined $states{$cur->{'color'}}) { - $counts{$states{$cur->{'color'}}} += 1; - } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ - my $uri = URI->new($cur->{'url'}); - my $folder = $uri->path; - get_results($folder); - } else { - warn "Ignoring unknown color " . $cur->{'color'} . "\n" - } - } - return \%counts; - } +sub parse_running_builds { + my $builds = shift; + my $count = 0; + foreach my $cur (@{$builds}) { + if( defined($cur->{'jobs'}) ) { + $count += parse_running_builds($cur->{'jobs'}); + } elsif( defined ($cur->{'color'}) and $cur->{'color'} =~ /anime$/ ) { + $count += 1; + } + } + return $count; } -{ - my $running_count; - sub get_running{ - my $query_context = shift; - die "get_results requires an argument" unless defined $query_context; - $running_count //= 0; - my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; - my $result = `$cmd`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if( exists $cur->{'color'} && $cur->{'color'} =~ /anime$/ ) { - $running_count += 1; - } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ - my $uri = URI->new($cur->{'url'}); - my $folder = $uri->path; - get_running($folder); - } - } - return $running_count; - } -} +sub parse_results { + my $builds = shift; + my %counts = ('stable' => 0, 'unstable' => 0, 'failing' => 0, 'disabled' => 0); + foreach my $cur(@{$builds}) { + if( defined($cur->{'jobs'}) ) { + my $new_counts = parse_results($cur->{'jobs'}); + foreach my $new_count_key (keys %{$new_counts}) { + $counts{$new_count_key} += $new_counts->{$new_count_key}; + } + } elsif (defined($cur->{'color'})) { + if (defined($states{$cur->{'color'}})) { + $counts{$states{$cur->{'color'}}} += 1; + } else { + warn "Ignoring unknown color " . $cur->{'color'} . "\n" + } + } + } + return \%counts; +} From dec7c0c87910946d2f2048e2f1d433d163f3686f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:29:21 +0200 Subject: [PATCH 192/695] jenkins_: unify whitespace --- plugins/jenkins/jenkins_ | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index 2b9c91a0..31b759c6 100755 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -23,11 +23,11 @@ You can set the modes with naming the softlink: This plugin is configurable environment variables. -env.url Jenkins Host -env.port Jenkins Port -env.context Jenkins Context path -env.user User for the API Tokent -env.apiToken Jenkins API Token (see https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients) +env.url Jenkins Host +env.port Jenkins Port +env.context Jenkins Context path +env.user User for the API Tokent +env.apiToken Jenkins API Token (see https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients) env.jobDepth How far into job "folders" should the plugin check for jobs Example: @@ -82,51 +82,51 @@ my %states = ( 'aborted'=>'failing', 'aborted_anime'=>'failing' ); -my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); +my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { if( $type eq "results" ) { - print "graph_args --base 1000 -l 0\n"; - print "graph_title Jenkins Build Results\n"; - print "graph_vlabel Build Results\n"; - print "graph_category devel\n"; - print "graph_info The Graph shows the Status of each Build\n"; - print "build_disabled.draw AREA\n"; - print "build_disabled.label disabled\n"; - print "build_disabled.type GAUGE\n"; - print "build_disabled.colour 8A8A8A\n"; - print "build_failing.draw STACK\n"; - print "build_failing.label failing\n"; - print "build_failing.type GAUGE\n"; - print "build_failing.colour E61217\n"; - print "build_unstable.draw STACK\n"; - print "build_unstable.label unstable\n"; - print "build_unstable.type GAUGE\n"; - print "build_unstable.colour F3E438\n"; - print "build_stable.draw STACK\n"; - print "build_stable.label stable\n"; - print "build_stable.type GAUGE\n"; - print "build_stable.colour 294D99\n"; - exit; + print "graph_args --base 1000 -l 0\n"; + print "graph_title Jenkins Build Results\n"; + print "graph_vlabel Build Results\n"; + print "graph_category devel\n"; + print "graph_info The Graph shows the Status of each Build\n"; + print "build_disabled.draw AREA\n"; + print "build_disabled.label disabled\n"; + print "build_disabled.type GAUGE\n"; + print "build_disabled.colour 8A8A8A\n"; + print "build_failing.draw STACK\n"; + print "build_failing.label failing\n"; + print "build_failing.type GAUGE\n"; + print "build_failing.colour E61217\n"; + print "build_unstable.draw STACK\n"; + print "build_unstable.label unstable\n"; + print "build_unstable.type GAUGE\n"; + print "build_unstable.colour F3E438\n"; + print "build_stable.draw STACK\n"; + print "build_stable.label stable\n"; + print "build_stable.type GAUGE\n"; + print "build_stable.colour 294D99\n"; + exit; } if( $type eq "queue" ) { print "graph_args --base 1000 -l 0\n"; - print "graph_title Jenkins Queue Length\n"; - print "graph_vlabel Number of Jobs in Queue\n"; - print "graph_category devel\n"; - print "graph_info The Graph shows the Number of Jobs in the Build Queue\n"; - print "build_count.label Jobs in Queue\n"; - print "build_count.type GAUGE\n"; + print "graph_title Jenkins Queue Length\n"; + print "graph_vlabel Number of Jobs in Queue\n"; + print "graph_category devel\n"; + print "graph_info The Graph shows the Number of Jobs in the Build Queue\n"; + print "build_count.label Jobs in Queue\n"; + print "build_count.type GAUGE\n"; exit; } if( $type eq "running" ) { print "graph_args --base 1000 -l 0\n"; - print "graph_title Jenkins Builds Running\n"; - print "graph_vlabel Builds currently running\n"; - print "graph_category devel\n"; - print "graph_info The Graph shows the Number of Builds, currently running\n"; - print "build_running.label running Builds\n"; - print "build_running.type GAUGE\n"; + print "graph_title Jenkins Builds Running\n"; + print "graph_vlabel Builds currently running\n"; + print "graph_category devel\n"; + print "graph_info The Graph shows the Number of Builds, currently running\n"; + print "build_running.label running Builds\n"; + print "build_running.type GAUGE\n"; exit; } } else { @@ -136,7 +136,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { for (2..$jobDepth) { $tree = "jobs[name,color,$tree]"; } - + if( $type eq "results" ) { my $result = `$cmd'/api/json?depth=$jobDepth&tree=$tree'`; my $parsed = decode_json($result); @@ -153,7 +153,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $parsed = decode_json($result); my $count = parse_running_builds($parsed->{'jobs'}); print "build_running.value ", $count, "\n"; - exit; + exit; } if( $type eq "queue" ) { From c2522f5bd248c3999e819955f20832c8be133f44 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:34:09 +0200 Subject: [PATCH 193/695] jenkins_: emit warning in case of unknown 'mode' --- plugins/jenkins/jenkins_ | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index 31b759c6..5daac77e 100755 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -107,9 +107,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "build_stable.label stable\n"; print "build_stable.type GAUGE\n"; print "build_stable.colour 294D99\n"; - exit; - } - if( $type eq "queue" ) { + } elsif( $type eq "queue" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Jenkins Queue Length\n"; print "graph_vlabel Number of Jobs in Queue\n"; @@ -117,9 +115,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_info The Graph shows the Number of Jobs in the Build Queue\n"; print "build_count.label Jobs in Queue\n"; print "build_count.type GAUGE\n"; - exit; - } - if( $type eq "running" ) { + } elsif( $type eq "running" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Jenkins Builds Running\n"; print "graph_vlabel Builds currently running\n"; @@ -127,7 +123,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_info The Graph shows the Number of Builds, currently running\n"; print "build_running.label running Builds\n"; print "build_running.type GAUGE\n"; - exit; + } else { + warn "Unknown mode requested: $type\n"; } } else { my $cmd = "$wgetBin $auth -qO- $url:$port$context"; @@ -145,22 +142,17 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { foreach my $status (keys %{$counts}) { print "build_$status.value $counts->{$status}\n"; } - exit; - } - - if( $type eq "running" ) { + } elsif( $type eq "running" ) { my $result = `$cmd'/api/json?depth=$jobDepth&tree=$tree'`; my $parsed = decode_json($result); my $count = parse_running_builds($parsed->{'jobs'}); print "build_running.value ", $count, "\n"; - exit; - } - - if( $type eq "queue" ) { + } elsif( $type eq "queue" ) { my $result = `$cmd/queue/api/json`; my $parsed = decode_json($result); print "build_count.value ", scalar( @{$parsed->{'items'}} ), "\n"; - exit; + } else { + warn "Unknown mode requested: $type\n"; } } From 30a78e2e32907592d595c9442d73dff292f4639c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:34:47 +0200 Subject: [PATCH 194/695] jenkins_: improve documentation wording --- plugins/jenkins/jenkins_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index 5daac77e..27fe1958 100755 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -13,7 +13,7 @@ This plugin displays the following charts: 2) Number of Jobs in the Build Queue 3) Number of Builds, currently running -You can set the modes with naming the softlink: +You can set the modes with naming the symlink: 1) jenkins_results 2) jenkins_queue @@ -21,7 +21,7 @@ You can set the modes with naming the softlink: =head1 CONFIGURATION -This plugin is configurable environment variables. +This plugin is configurable via environment variables. env.url Jenkins Host env.port Jenkins Port From 97ab640be8dc9e75b097f3b436cc7c95ceb91f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20de=20Kock?= Date: Tue, 3 Oct 2017 08:27:04 +0200 Subject: [PATCH 195/695] Change commit and rollback types to "COUNTER" By doing this, munin knows to take the difference of each request, thereby showing you a true commit and rollback "per minute" --- plugins/postgresql/postgresql_transactions | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index f13740a5..d5d68a0c 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -33,10 +33,12 @@ if [ "$1" = "config" ]; then echo 'commits.label commits' echo 'commits.min 0' + echo 'commits.type COUNTER' echo 'commits.info Number of transaction commits.' echo 'rollbacks.label rollbacks' echo 'rollbacks.min 0' + echo 'rollbacks.type COUNTER' echo 'rollbacks.info Number of transaction rollbacks.' exit 0 fi From 93a567afbc2bd79ce19863344317d91489161bf5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:54:35 +0200 Subject: [PATCH 196/695] postgresql_transactions: use DERIVE instead of COUNTER --- plugins/postgresql/postgresql_transactions | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index d5d68a0c..c127e633 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -29,17 +29,17 @@ if [ "$1" = "config" ]; then echo 'graph_category db' echo 'graph_info Shows summarized commits and rollbacks in transactions on the PostgreSQL Server.' echo 'graph_title PostgreSQL Transactions' - echo 'graph_vlabel Number of Commits and Rollbacks' + echo 'graph_vlabel Commits and Rollbacks per second' echo 'commits.label commits' echo 'commits.min 0' - echo 'commits.type COUNTER' - echo 'commits.info Number of transaction commits.' + echo 'commits.type DERIVE' + echo 'commits.info Number of transaction commits per second.' echo 'rollbacks.label rollbacks' echo 'rollbacks.min 0' - echo 'rollbacks.type COUNTER' - echo 'rollbacks.info Number of transaction rollbacks.' + echo 'rollbacks.type DERIVE' + echo 'rollbacks.info Number of transaction rollbacks per second.' exit 0 fi psql -h ${dbserver} -U ${dbuser} -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align From fdf6cea2b146233d8b5f4b3fbbb123a12a85d71b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:56:07 +0200 Subject: [PATCH 197/695] postgresql_transactions: change from bash to sh; fix shellcheck issues --- plugins/postgresql/postgresql_transactions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index c127e633..47db7b64 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Plugin to monitor PostgreSQL Commits and Rollbacks in Transactions # @@ -42,4 +42,4 @@ if [ "$1" = "config" ]; then echo 'rollbacks.info Number of transaction rollbacks per second.' exit 0 fi -psql -h ${dbserver} -U ${dbuser} -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align +psql -h "$dbserver" -U "$dbuser" -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align From 154cef14c57f9ae424995b59fda2c08c0fd7de37 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 13:58:31 +0200 Subject: [PATCH 198/695] postgresql_transactions: add support for DIRTYCONFIG --- plugins/postgresql/postgresql_transactions | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index 47db7b64..e21708ab 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -24,7 +24,8 @@ dbserver='localhost' dbuser='postgres' -if [ "$1" = "config" ]; then + +do_config() { echo 'graph_args --base 1000 --lower-limit 0' echo 'graph_category db' echo 'graph_info Shows summarized commits and rollbacks in transactions on the PostgreSQL Server.' @@ -40,6 +41,17 @@ if [ "$1" = "config" ]; then echo 'rollbacks.min 0' echo 'rollbacks.type DERIVE' echo 'rollbacks.info Number of transaction rollbacks per second.' - exit 0 +} + + +do_fetch() { + psql -h "$dbserver" -U "$dbuser" -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align +} + + +if [ "$1" = "config" ]; then + do_config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi +else + do_fetch fi -psql -h "$dbserver" -U "$dbuser" -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align From 3c10c360cd2e4282886e1a2b88b5e58df55a0b18 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:06:30 +0200 Subject: [PATCH 199/695] postgresql_transactions: configurable host and user --- plugins/postgresql/postgresql_transactions | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index e21708ab..82e01619 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -21,8 +21,8 @@ # Log info: # -dbserver='localhost' -dbuser='postgres' +dbhost=${dbhost:-localhost} +dbuser=${dbuser:-postgres} do_config() { @@ -45,7 +45,7 @@ do_config() { do_fetch() { - psql -h "$dbserver" -U "$dbuser" -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align + psql -h "$dbhost" -U "$dbuser" -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align } From e93a46bc76df5a94e66f196212334d7bc4d0b84e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:06:48 +0200 Subject: [PATCH 200/695] postgresql_transactions: use perldoc documentation header --- plugins/postgresql/postgresql_transactions | 56 ++++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index 82e01619..894a9aab 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -1,25 +1,39 @@ #!/bin/sh -# -# Plugin to monitor PostgreSQL Commits and Rollbacks in Transactions -# -# Author: -# Guilherme Augusto da Rocha Silva -# -# Created: -# 9th of november 2007 -# -# Usage: -# Place in /etc/munin/plugins/ (or link it there using ln -s) -# -# Parameters: -# config (required) -# -# General info: -# Require permission for database access and read (no writes are processed). -# Recommended user is PostgreSQL database owner (default: postgres). -# -# Log info: -# + +: <<=cut + +=head1 NAME + +postgresql_transactions - Plugin to monitor PostgreSQL Commits and Rollbacks in Transactions + + +=head1 USAGE + +Usage: + Place in /etc/munin/plugins/ (or link it there using ln -s) + +General info: + Requires permission for database read access (no writes are processed). + Recommended user is PostgreSQL database owner (default: postgres). + + +=head1 CONFIGURATION + +The following configuration directives may be placed below /etc/munin/plugin-conf.d/ (optional): + + [postgresql_transactions] + user postgres + env.dbuser postgres + env.dbhost localhost + + +=head1 AUTHOR + + Guilherme Augusto da Rocha Silva + + Copyright (C) 2007 Guilherme Augusto da Rocha Silva + +=cut dbhost=${dbhost:-localhost} dbuser=${dbuser:-postgres} From 8a5b34c9c8e78388cf31b56cfdab76799feebd72 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:35:12 +0200 Subject: [PATCH 201/695] lxc_ram: simplify quoting --- plugins/lxc/lxc_ram | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 4208f875..0e9f6d4d 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -74,20 +74,20 @@ if [ "$1" = "config" ]; then for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" - echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' - echo 'mem_usage_'$guest'.type GAUGE' - echo 'mem_usage_'$guest'.draw AREASTACK' + echo "mem_usage_$guest.label $guest_name: Mem usage" + echo "mem_usage_$guest.type GAUGE" + echo "mem_usage_$guest.draw AREASTACK" done for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" - echo 'mem_cache_'$guest'.label '$guest_name': Cache' - echo 'mem_cache_'$guest'.type GAUGE' - echo 'mem_active_'$guest'.label '$guest_name': Active' - echo 'mem_active_'$guest'.type GAUGE' - echo 'mem_inactive_'$guest'.label '$guest_name': Inactive' - echo 'mem_inactive_'$guest'.type GAUGE' + echo "mem_cache_$guest.label $guest_name: Cache" + echo "mem_cache_$guest.type GAUGE" + echo "mem_active_$guest.label $guest_name: Active" + echo "mem_active_$guest.type GAUGE" + echo "mem_inactive_$guest.label $guest_name: Inactive" + echo "mem_inactive_$guest.type GAUGE" done else @@ -95,14 +95,14 @@ if [ "$1" = "config" ]; then do guest="$(clean_fieldname $guest_name)" - echo 'mem_usage_'$guest'.label '$guest_name': Mem usage' - echo 'mem_usage_'$guest'.type GAUGE' - echo 'mem_cache_'$guest'.label '$guest_name': Cache' - echo 'mem_cache_'$guest'.type GAUGE' - echo 'mem_active_'$guest'.label '$guest_name': Active' - echo 'mem_active_'$guest'.type GAUGE' - echo 'mem_inactive_'$guest'.label '$guest_name': Inactive' - echo 'mem_inactive_'$guest'.type GAUGE' + echo "mem_usage_$guest.label $guest_name: Mem usage" + echo "mem_usage_$guest.type GAUGE" + echo "mem_cache_$guest.label $guest_name: Cache" + echo "mem_cache_$guest.type GAUGE" + echo "mem_active_$guest.label $guest_name: Active" + echo "mem_active_$guest.type GAUGE" + echo "mem_inactive_$guest.label $guest_name: Inactive" + echo "mem_inactive_$guest.type GAUGE" done fi @@ -116,18 +116,18 @@ fi guest="$(clean_fieldname $guest_name)" tmp_v=`$f_comm -n $guest_name memory.usage_in_bytes` - echo 'mem_usage_'$guest'.value '$tmp_v + echo "mem_usage_$guest.value $tmp_v" tmp_g=`$f_comm -n $guest_name memory.stat | grep total_cache` tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo 'mem_cache_'$guest'.value '$tmp_v + echo "mem_cache_$guest.value $tmp_v" tmp_g=`$f_comm -n $guest_name memory.stat | grep total_active_anon` tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo 'mem_active_'$guest'.value '$tmp_v + echo "mem_active_$guest.value $tmp_v" tmp_g=`$f_comm -n $guest_name memory.stat | grep total_inactive_anon` tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo 'mem_inactive_'$guest'.value '$tmp_v + echo "mem_inactive_$guest.value $tmp_v" done From aaee0699710c23de10db0044ca7e905750e9d27b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:41:53 +0200 Subject: [PATCH 202/695] lxc_ram: fix shellcheck issues --- plugins/lxc/lxc_ram | 49 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 0e9f6d4d..fe023963 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -40,11 +40,18 @@ Unknown license =cut -. $MUNIN_LIBDIR/plugins/plugin.sh -guest_names=`lxc-ls | sort -u` +# configurable: true/false +areastack=${areastack:-false} + + +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" + + +guest_names=$(lxc-ls | sort -u) for guest in $guest_names; do - if lxc-info -n $guest 2>&1 | grep -qs RUNNING ; then + if lxc-info -n "$guest" 2>&1 | grep -qs RUNNING ; then active="$active $guest" fi done @@ -56,11 +63,10 @@ f_comm='lxc-cgroup ' if [ "$1" = "autoconf" ]; then if [ -r /proc/stat ]; then echo yes - exit 0 else echo "no (no /proc/stat)" - exit 0 fi + exit 0 fi if [ "$1" = "config" ]; then @@ -72,7 +78,7 @@ if [ "$1" = "config" ]; then if [ "$areastack" = "true" ]; then for guest_name in $guest_names; do - guest="$(clean_fieldname $guest_name)" + guest="$(clean_fieldname "$guest_name")" echo "mem_usage_$guest.label $guest_name: Mem usage" echo "mem_usage_$guest.type GAUGE" @@ -80,7 +86,7 @@ if [ "$1" = "config" ]; then done for guest_name in $guest_names; do - guest="$(clean_fieldname $guest_name)" + guest="$(clean_fieldname "$guest_name")" echo "mem_cache_$guest.label $guest_name: Cache" echo "mem_cache_$guest.type GAUGE" @@ -93,7 +99,7 @@ if [ "$1" = "config" ]; then else for guest_name in $guest_names; do - guest="$(clean_fieldname $guest_name)" + guest="$(clean_fieldname "$guest_name")" echo "mem_usage_$guest.label $guest_name: Mem usage" echo "mem_usage_$guest.type GAUGE" @@ -111,23 +117,18 @@ if [ "$1" = "config" ]; then fi - for guest_name in $guest_names; - do - guest="$(clean_fieldname $guest_name)" +for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" - tmp_v=`$f_comm -n $guest_name memory.usage_in_bytes` - echo "mem_usage_$guest.value $tmp_v" + value=$($f_comm -n "$guest_name" memory.usage_in_bytes) + echo "mem_usage_$guest.value $value" - tmp_g=`$f_comm -n $guest_name memory.stat | grep total_cache` - tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo "mem_cache_$guest.value $tmp_v" + value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') + echo "mem_cache_$guest.value $value" - tmp_g=`$f_comm -n $guest_name memory.stat | grep total_active_anon` - tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo "mem_active_$guest.value $tmp_v" + value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') + echo "mem_active_$guest.value $value" - tmp_g=`$f_comm -n $guest_name memory.stat | grep total_inactive_anon` - tmp_v=`echo $tmp_g | awk '{print($2)}'` - echo "mem_inactive_$guest.value $tmp_v" - - done + value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') + echo "mem_inactive_$guest.value $value" +done From a49b362e35779e143daf2f6d6f022b847f73f0e1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:42:10 +0200 Subject: [PATCH 203/695] lxc_ram: change from bash to sh --- plugins/lxc/lxc_ram | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index fe023963..f01186ed 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # -*- sh -*- : << =cut From 937ed37daee9275bb115760e5688f9a0cb24f371 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:47:00 +0200 Subject: [PATCH 204/695] lxc_ram: unify whitespace --- plugins/lxc/lxc_ram | 120 +++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index f01186ed..f07a2aac 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -51,84 +51,76 @@ areastack=${areastack:-false} guest_names=$(lxc-ls | sort -u) for guest in $guest_names; do - if lxc-info -n "$guest" 2>&1 | grep -qs RUNNING ; then - active="$active $guest" - fi + if lxc-info -n "$guest" 2>&1 | grep -qs RUNNING ; then + active="$active $guest" + fi done guest_names="$active" - + f_comm='lxc-cgroup ' if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - else - echo "no (no /proc/stat)" - fi - exit 0 + if [ -r /proc/stat ]; then + echo yes + else + echo "no (no /proc/stat)" + fi + exit 0 fi if [ "$1" = "config" ]; then + echo 'graph_title Memory ' + echo 'graph_args -l 0 --base 1024' + echo 'graph_vlabel byte' + echo 'graph_category memory' - echo 'graph_title Memory ' - echo 'graph_args -l 0 --base 1024' - echo 'graph_vlabel byte' - echo 'graph_category memory' + if [ "$areastack" = "true" ]; then + for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" + echo "mem_usage_$guest.label $guest_name: Mem usage" + echo "mem_usage_$guest.type GAUGE" + echo "mem_usage_$guest.draw AREASTACK" + done - if [ "$areastack" = "true" ]; then - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - - echo "mem_usage_$guest.label $guest_name: Mem usage" - echo "mem_usage_$guest.type GAUGE" - echo "mem_usage_$guest.draw AREASTACK" - done - - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - - echo "mem_cache_$guest.label $guest_name: Cache" - echo "mem_cache_$guest.type GAUGE" - echo "mem_active_$guest.label $guest_name: Active" - echo "mem_active_$guest.type GAUGE" - echo "mem_inactive_$guest.label $guest_name: Inactive" - echo "mem_inactive_$guest.type GAUGE" - - done - else - for guest_name in $guest_names; - do - guest="$(clean_fieldname "$guest_name")" - - echo "mem_usage_$guest.label $guest_name: Mem usage" - echo "mem_usage_$guest.type GAUGE" - echo "mem_cache_$guest.label $guest_name: Cache" - echo "mem_cache_$guest.type GAUGE" - echo "mem_active_$guest.label $guest_name: Active" - echo "mem_active_$guest.type GAUGE" - echo "mem_inactive_$guest.label $guest_name: Inactive" - echo "mem_inactive_$guest.type GAUGE" - - done - fi - - exit 0 + for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" + echo "mem_cache_$guest.label $guest_name: Cache" + echo "mem_cache_$guest.type GAUGE" + echo "mem_active_$guest.label $guest_name: Active" + echo "mem_active_$guest.type GAUGE" + echo "mem_inactive_$guest.label $guest_name: Inactive" + echo "mem_inactive_$guest.type GAUGE" + done + else + for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" + echo "mem_usage_$guest.label $guest_name: Mem usage" + echo "mem_usage_$guest.type GAUGE" + echo "mem_cache_$guest.label $guest_name: Cache" + echo "mem_cache_$guest.type GAUGE" + echo "mem_active_$guest.label $guest_name: Active" + echo "mem_active_$guest.type GAUGE" + echo "mem_inactive_$guest.label $guest_name: Inactive" + echo "mem_inactive_$guest.type GAUGE" + done + fi + exit 0 fi for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" + guest="$(clean_fieldname "$guest_name")" - value=$($f_comm -n "$guest_name" memory.usage_in_bytes) - echo "mem_usage_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') - echo "mem_cache_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') - echo "mem_active_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') - echo "mem_inactive_$guest.value $value" + value=$($f_comm -n "$guest_name" memory.usage_in_bytes) + echo "mem_usage_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') + echo "mem_cache_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') + echo "mem_active_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') + echo "mem_inactive_$guest.value $value" done From 2ce1b3211a21cd6cefaefd25c374d30f70257a5e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:49:53 +0200 Subject: [PATCH 205/695] lxc_ram: use functions for actions --- plugins/lxc/lxc_ram | 55 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index f07a2aac..2deb1a1e 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -60,16 +60,17 @@ guest_names="$active" f_comm='lxc-cgroup ' -if [ "$1" = "autoconf" ]; then + +do_autoconf() { if [ -r /proc/stat ]; then echo yes else echo "no (no /proc/stat)" fi - exit 0 -fi +} -if [ "$1" = "config" ]; then + +do_config() { echo 'graph_title Memory ' echo 'graph_args -l 0 --base 1024' echo 'graph_vlabel byte' @@ -105,22 +106,32 @@ if [ "$1" = "config" ]; then echo "mem_inactive_$guest.type GAUGE" done fi - exit 0 +} + + +do_fetch() { + for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" + + value=$($f_comm -n "$guest_name" memory.usage_in_bytes) + echo "mem_usage_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') + echo "mem_cache_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') + echo "mem_active_$guest.value $value" + + value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') + echo "mem_inactive_$guest.value $value" + done +} + + +if [ "$1" = "autoconf" ]; then + do_autoconf +elif [ "$1" = "config" ]; then + do_config +else + do_fetch fi - - -for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - - value=$($f_comm -n "$guest_name" memory.usage_in_bytes) - echo "mem_usage_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') - echo "mem_cache_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') - echo "mem_active_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') - echo "mem_inactive_$guest.value $value" -done From a0cafdcf1f296d8c4c30f6507fcc641aac0c483b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:50:32 +0200 Subject: [PATCH 206/695] lxc_ram: add support for DIRTYCONFIG --- plugins/lxc/lxc_ram | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 2deb1a1e..6ab81b44 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -132,6 +132,7 @@ if [ "$1" = "autoconf" ]; then do_autoconf elif [ "$1" = "config" ]; then do_config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi else do_fetch fi From 196c0572019643272d9a263598e6c4be11602b56 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 14:54:15 +0200 Subject: [PATCH 207/695] lxc_ram: simplify config with/without 'areastack' --- plugins/lxc/lxc_ram | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 6ab81b44..6bb765e6 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -76,36 +76,20 @@ do_config() { echo 'graph_vlabel byte' echo 'graph_category memory' - if [ "$areastack" = "true" ]; then - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - echo "mem_usage_$guest.label $guest_name: Mem usage" - echo "mem_usage_$guest.type GAUGE" + for guest_name in $guest_names; do + guest="$(clean_fieldname "$guest_name")" + echo "mem_usage_$guest.label $guest_name: Mem usage" + echo "mem_usage_$guest.type GAUGE" + if [ "$areastack" = "true" ]; then echo "mem_usage_$guest.draw AREASTACK" - done - - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - echo "mem_cache_$guest.label $guest_name: Cache" - echo "mem_cache_$guest.type GAUGE" - echo "mem_active_$guest.label $guest_name: Active" - echo "mem_active_$guest.type GAUGE" - echo "mem_inactive_$guest.label $guest_name: Inactive" - echo "mem_inactive_$guest.type GAUGE" - done - else - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - echo "mem_usage_$guest.label $guest_name: Mem usage" - echo "mem_usage_$guest.type GAUGE" - echo "mem_cache_$guest.label $guest_name: Cache" - echo "mem_cache_$guest.type GAUGE" - echo "mem_active_$guest.label $guest_name: Active" - echo "mem_active_$guest.type GAUGE" - echo "mem_inactive_$guest.label $guest_name: Inactive" - echo "mem_inactive_$guest.type GAUGE" - done - fi + fi + echo "mem_cache_$guest.label $guest_name: Cache" + echo "mem_cache_$guest.type GAUGE" + echo "mem_active_$guest.label $guest_name: Active" + echo "mem_active_$guest.type GAUGE" + echo "mem_inactive_$guest.label $guest_name: Inactive" + echo "mem_inactive_$guest.type GAUGE" + done } From e87d3a6b3c0fad8f76b9ea1839b04e728ab5235b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:30:27 +0200 Subject: [PATCH 208/695] kvm_net: remove unused function 'find_vms_tap' --- plugins/libvirt/kvm_net | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index bca7b7f3..c9f1d93d 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -103,27 +103,7 @@ def list_pids(): pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() -def find_vms_tap(): - ''' Check if kvm is installed - @return a list of pids from running kvm - ''' - result = [] - tap = "" - mac = "" - kvm = Popen("ip a | grep -A 1 tap | awk '{print $2}' | grep -v '^$'", shell=True, stdout=PIPE) - res = kvm.communicate()[0].split('\n') - for line in res: - try: - if len(line) > 0: - if re.match(r"^tap.*", line): - tap = re.sub(r"(tap[^:]+):", r"\1", line) - else: - result.append(tap) - except Exception as inst: - continue - return result - if __name__ == "__main__": if len(sys.argv) > 1: if sys.argv[1] in ['autoconf', 'detect']: From 7f98e21f2b459c90b8fcdc040c491d599eec5e2c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:33:36 +0200 Subject: [PATCH 209/695] kvm_net: unify quoting --- plugins/libvirt/kvm_net | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index c9f1d93d..6bf14553 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -19,9 +19,10 @@ import re, os, sys from subprocess import Popen, PIPE def config(vm_names): - ''' Print the plugin's config + """ Print the plugin's config + @param vm_names : a list of "cleaned" vms' name - ''' + """ base_config = """graph_title KVM Network I/O graph_vlabel Bytes rx(-)/tx(+) per second graph_category Virtualization @@ -41,21 +42,23 @@ graph_args --base 1024 print "%s_out.draw LINE2" % vm def clean_vm_name(vm_name): - ''' Replace all special chars + """ Replace all special chars + @param vm_name : a vm's name @return cleaned vm's name - ''' + """ # suffix part defined in conf - suffix = os.getenv('vmsuffix') + suffix = os.getenv("vmsuffix") if suffix: - vm_name = re.sub(suffix,'',vm_name) + vm_name = re.sub(suffix, "", vm_name) return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) def fetch(vms): - ''' Fetch values for a list of pids + """ Fetch values for a list of pids + @param dictionnary {kvm_pid: cleaned vm name} - ''' + """ res = {} for pid in vms: tap = get_vm_mac(pid) @@ -71,16 +74,16 @@ def fetch(vms): continue def detect_kvm(): - ''' Check if kvm is installed - ''' + """ Check if kvm is installed """ kvm = Popen("which kvm", shell=True, stdout=PIPE) kvm.communicate() return not bool(kvm.returncode) def find_vm_names(pids): - '''Find and clean vm names from pids + """Find and clean vm names from pids + @return a dictionnary of {pids : cleaned vm name} - ''' + """ result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") @@ -88,25 +91,27 @@ def find_vm_names(pids): return result def get_vm_mac(pid): - '''Find and clean vm names from pids + """Find and clean vm names from pids + @return the mac address for a specified pid - ''' + """ cmdline = open("/proc/%s/cmdline" % pid, "r") line = cmdline.readline() mac = re.sub(r"^.*ifname=(tap[^,]+),.*$",r"\1", line) return mac def list_pids(): - ''' Find the pid of kvm processes + """ Find the pid of kvm processes + @return a list of pids from running kvm - ''' + """ pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() if __name__ == "__main__": if len(sys.argv) > 1: - if sys.argv[1] in ['autoconf', 'detect']: + if sys.argv[1] in ["autoconf", "detect"]: if detect_kvm(): print "yes" else: From f6080f5f8e15fcdc5b015e7a58f210fd97a22fe1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:35:31 +0200 Subject: [PATCH 210/695] kvm_net: unify whitespace --- plugins/libvirt/kvm_net | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 6bf14553..40c78468 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -18,6 +18,7 @@ import re, os, sys from subprocess import Popen, PIPE + def config(vm_names): """ Print the plugin's config @@ -41,6 +42,7 @@ graph_args --base 1024 print "%s_out.min 0" % vm print "%s_out.draw LINE2" % vm + def clean_vm_name(vm_name): """ Replace all special chars @@ -51,9 +53,9 @@ def clean_vm_name(vm_name): suffix = os.getenv("vmsuffix") if suffix: vm_name = re.sub(suffix, "", vm_name) - return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) - + + def fetch(vms): """ Fetch values for a list of pids @@ -73,12 +75,14 @@ def fetch(vms): print inst continue + def detect_kvm(): """ Check if kvm is installed """ kvm = Popen("which kvm", shell=True, stdout=PIPE) kvm.communicate() return not bool(kvm.returncode) + def find_vm_names(pids): """Find and clean vm names from pids @@ -87,9 +91,10 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result - + + def get_vm_mac(pid): """Find and clean vm names from pids @@ -100,6 +105,7 @@ def get_vm_mac(pid): mac = re.sub(r"^.*ifname=(tap[^,]+),.*$",r"\1", line) return mac + def list_pids(): """ Find the pid of kvm processes @@ -122,5 +128,3 @@ if __name__ == "__main__": fetch(find_vm_names(list_pids())) else: fetch(find_vm_names(list_pids())) - - From 67cc769c87f01f273a65ea7613543b3c6ebedb9d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:36:11 +0200 Subject: [PATCH 211/695] kvm_net: use print function for python3-compatibility --- plugins/libvirt/kvm_net | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 40c78468..c416657f 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -30,17 +30,17 @@ graph_category Virtualization graph_info This graph shows the network I/O of the virtual machines graph_args --base 1024 """ - print base_config + print(base_config) for vm in vm_names: - print "%s_in.label %s" % (vm, vm) - print "%s_in.type COUNTER" % vm - print "%s_in.min 0" % vm - print "%s_in.draw LINE2" % vm - print "%s_out.negative %s_in" % (vm, vm) - print "%s_out.label %s" % (vm, vm) - print "%s_out.type COUNTER" % vm - print "%s_out.min 0" % vm - print "%s_out.draw LINE2" % vm + print("%s_in.label %s" % (vm, vm)) + print("%s_in.type COUNTER" % vm) + print("%s_in.min 0" % vm) + print("%s_in.draw LINE2" % vm) + print("%s_out.negative %s_in" % (vm, vm)) + print("%s_out.label %s" % (vm, vm)) + print("%s_out.type COUNTER" % vm) + print("%s_out.min 0" % vm) + print("%s_out.draw LINE2" % vm) def clean_vm_name(vm_name): @@ -68,11 +68,11 @@ def fetch(vms): f = open("/proc/net/dev", "r") for line in f.readlines(): if tap in line: - print "%s_in.value %s" % (vms[pid], line.split()[1]) - print "%s_out.value %s" % (vms[pid], line.split()[9]) + print("%s_in.value %s" % (vms[pid], line.split()[1])) + print("%s_out.value %s" % (vms[pid], line.split()[9])) break except Exception as inst: - print inst + print(inst) continue @@ -119,9 +119,9 @@ if __name__ == "__main__": if len(sys.argv) > 1: if sys.argv[1] in ["autoconf", "detect"]: if detect_kvm(): - print "yes" + print("yes") else: - print "no" + print("no") elif sys.argv[1] == "config": config(find_vm_names(list_pids()).values()) else: From 3267bbd0ef6ce14e44a613e4de08c5b901e0d1cf Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:37:22 +0200 Subject: [PATCH 212/695] kvm_net: switch to python3 --- plugins/libvirt/kvm_net | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index c416657f..ca4ffe07 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -1,5 +1,4 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +#!/usr/bin/python3 # vim: set fileencoding=utf-8 # # Munin plugin to show the network I/O per vm @@ -112,7 +111,7 @@ def list_pids(): @return a list of pids from running kvm """ pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) - return pid.communicate()[0].split() + return pid.communicate()[0].decode().split() if __name__ == "__main__": From a9059703a6ccf1204a0ce6bdf065b2ab8c6d29ba Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:40:40 +0200 Subject: [PATCH 213/695] kvm_net: switch to perldoc documentation header --- plugins/libvirt/kvm_net | 46 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index ca4ffe07..e464b6ed 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -1,18 +1,36 @@ #!/usr/bin/python3 -# vim: set fileencoding=utf-8 -# -# Munin plugin to show the network I/O per vm -# -# Copyright Igor Borodikhin -# -# License : GPLv3 -# -# -# parsed environment variables: -# vmsuffix: part of vm name to be removed -# -#%# capabilities=autoconf -#%# family=contrib +""" + +=head1 NAME + +kvm_net - Munin plugin to show the network I/O per VM + + +=head1 CONFIGURATION + +parsed environment variables: + + * vmsuffix: part of vm name to be removed + + +=head1 AUTHOR + +Copyright (C) 2012 - Igor Borodikhin +Copyright (C) 2018 - Lars Kruse + + +=head1 LICENSE + +GPLv3 + + +=head1 MAGIC MARKERS + + #%# capabilities=autoconf + #%# family=contrib + +=cut +""" import re, os, sys from subprocess import Popen, PIPE From aa8f1a59c4dbbed02f470718aafcae11d0c66f19 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 17:44:22 +0200 Subject: [PATCH 214/695] kvm_net: fix python code style issues --- plugins/libvirt/kvm_net | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index e464b6ed..e03794ba 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -32,8 +32,10 @@ GPLv3 =cut """ -import re, os, sys +import os +import re from subprocess import Popen, PIPE +import sys def config(vm_names): @@ -78,7 +80,6 @@ def fetch(vms): @param dictionnary {kvm_pid: cleaned vm name} """ - res = {} for pid in vms: tap = get_vm_mac(pid) try: @@ -105,10 +106,11 @@ def find_vm_names(pids): @return a dictionnary of {pids : cleaned vm name} """ + vm_name_regex = re.compile(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$") result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(vm_name_regex.sub(r"\1", cmdline.readline())) return result @@ -119,7 +121,7 @@ def get_vm_mac(pid): """ cmdline = open("/proc/%s/cmdline" % pid, "r") line = cmdline.readline() - mac = re.sub(r"^.*ifname=(tap[^,]+),.*$",r"\1", line) + mac = re.sub(r"^.*ifname=(tap[^,]+),.*$", r"\1", line) return mac From 89e4dd6420d156a23e2cab3dacf9e3d157073d15 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 21:40:04 +0200 Subject: [PATCH 215/695] kvm_net: avoid shell when using Popen --- plugins/libvirt/kvm_net | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index e03794ba..1399fdb5 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -96,9 +96,9 @@ def fetch(vms): def detect_kvm(): """ Check if kvm is installed """ - kvm = Popen("which kvm", shell=True, stdout=PIPE) + kvm = Popen(["which", "kvm"], stdout=PIPE) kvm.communicate() - return not bool(kvm.returncode) + return kvm.returncode == 0 def find_vm_names(pids): @@ -130,7 +130,7 @@ def list_pids(): @return a list of pids from running kvm """ - pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) + pid = Popen(["pidof", "qemu-kvm", "qemu-system-x86_64", "kvm"], stdout=PIPE) return pid.communicate()[0].decode().split() From 54330cc3e693865bc569cb6e23a3db41a4ae9bcd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 21:40:20 +0200 Subject: [PATCH 216/695] kvm_net: simplify argument handling --- plugins/libvirt/kvm_net | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 1399fdb5..f7b20918 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -135,15 +135,15 @@ def list_pids(): if __name__ == "__main__": - if len(sys.argv) > 1: - if sys.argv[1] in ["autoconf", "detect"]: - if detect_kvm(): - print("yes") - else: - print("no") - elif sys.argv[1] == "config": - config(find_vm_names(list_pids()).values()) + action = sys.argv[1] if len(sys.argv) > 1 else None + if action == "autoconf": + if detect_kvm(): + print("yes") else: - fetch(find_vm_names(list_pids())) + print("no") + elif action == "config": + vm_data = find_vm_names(list_pids()) + config(vm_data.values()) else: - fetch(find_vm_names(list_pids())) + vm_data = find_vm_names(list_pids()) + fetch(vm_data) From b6c6a02efe0189c41baff4a79329563cc229c57c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 21:48:08 +0200 Subject: [PATCH 217/695] kvm_net: update VM name parsing The old parser seemed to rely on a simple "-name foo" argument format of kvm/qemu. The changed parser also accepts the following formats: * name,foo=bar,baz=bot * guest=name,foo=bar --- plugins/libvirt/kvm_net | 60 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index f7b20918..a852218a 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -38,6 +38,9 @@ from subprocess import Popen, PIPE import sys +VM_NAME_REGEX = re.compile("^.*\x00-{arg_name}\x00(.+)\x00.*$") + + def config(vm_names): """ Print the plugin's config @@ -106,11 +109,33 @@ def find_vm_names(pids): @return a dictionnary of {pids : cleaned vm name} """ - vm_name_regex = re.compile(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$") result = {} for pid in pids: - cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(vm_name_regex.sub(r"\1", cmdline.readline())) + name = None + name_arg_values = _get_kvm_process_arguments(pid, "name") + if name_arg_values: + name_arg_value = name_arg_values[0] + if "," in name_arg_value: + # the modern parameter format may look like this: + # guest=foo,debug-threads=on + for index, token in enumerate(name_arg_value.split(",")): + if (index == 0) and ("=" not in token): + # the first item may the plain name + name = value + elif "=" in token: + key, value = token.split("=", 1) + if key == "guest": + name = value + else: + # unknown format (no "mapping") + pass + else: + name = name_arg_value + if name is None: + print("Failed to parse VM name from commandline of process: {}" + .format(name_arg_values), file=sys.stderr) + else: + result[pid] = clean_vm_name(name) return result @@ -125,6 +150,35 @@ def get_vm_mac(pid): return mac +def _get_kvm_process_arguments(pid, arg_name): + """ parse all value with the given name from the process identified by PID + + The result is a list of tokens, that follow this argument name. The result + is empty in case of problems. + """ + # the "cmdline" (e.g. /proc/self/cmdline) is a null-separated token list + try: + with open("/proc/%s/cmdline" % pid, "r") as cmdline_file: + cmdline = cmdline_file.read() + except IOError: + # the process seems to have died meanwhile + return [] + is_value = False + result = [] + for arg_token in cmdline.split("\0"): + if is_value: + # the previous token was our argument name + result.append(arg_token) + is_value = False + elif arg_token == "-{}".format(arg_name): + # this is our argument name - we want to store the next value + is_value = True + else: + # any other irrelevant value + pass + return result + + def list_pids(): """ Find the pid of kvm processes From e0df6aa788ae74fb4db16c220cd0d872279cdc19 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 21:53:39 +0200 Subject: [PATCH 218/695] kvm_net: improve the network interface name parser and admit its limits The network interface parser of this plugin was overly specific before. It relied on a specific format of the arguments handed over to kvm while starting the VM. For example the following format was usable: ... -netdev tap,ifname=foo,... But kvm/qemu support a variety of ways for configuring network interfaces via the commandline. E.g. libvirt does not use the "ifname" parameter above. Thus VMs running on a host controlled via libvirt cannot be tracked with this plugin. This limititation is now clearly documented in the header of the plugin. --- plugins/libvirt/kvm_net | 71 ++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index a852218a..15cb6ef5 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -6,6 +6,24 @@ kvm_net - Munin plugin to show the network I/O per VM +=head1 APPLICABLE SYSTEMS + +Virtualization server with VMs based on KVM may be able to track the network +traffic of their VMs, if the KVM processes are started in a specific way. + +Probably proxmox-based virtualization hosts fit into this category. + +You can easily check if your KVM processes are started in the expected way, by +running the following command: + + ps -ef | grep "netdev.*ifname=" + +The plugin can be used, if the above command outputs one line for every +currently running VM. + +In all other cases you need to use other munin plugins instead, e.g. "libvirt". + + =head1 CONFIGURATION parsed environment variables: @@ -39,6 +57,7 @@ import sys VM_NAME_REGEX = re.compile("^.*\x00-{arg_name}\x00(.+)\x00.*$") +KVM_INTERFACE_NAME_REGEX = re.compile("(?:^|,)ifname=([^,]+)(?:,|$)") def config(vm_names): @@ -83,18 +102,35 @@ def fetch(vms): @param dictionnary {kvm_pid: cleaned vm name} """ - for pid in vms: - tap = get_vm_mac(pid) - try: - f = open("/proc/net/dev", "r") - for line in f.readlines(): - if tap in line: - print("%s_in.value %s" % (vms[pid], line.split()[1])) - print("%s_out.value %s" % (vms[pid], line.split()[9])) - break - except Exception as inst: - print(inst) - continue + for pid, vm_data in vms.items(): + vm_interface_names = get_vm_network_interface_names(pid) + sum_incoming = 0 + sum_outgoing = 0 + interface_found = False + with open("/proc/net/dev", "r") as net_file: + for line in net_file.readlines(): + tokens = line.split() + current_interface_name = tokens[0].rstrip(":").strip() + if current_interface_name in vm_interface_names: + sum_incoming += int(tokens[1]) + sum_outgoing += int(tokens[9]) + interface_found = True + if not interface_found: + # we want to distinguish "no traffic" from "not found" + sum_incoming = "U" + sum_outgoing = "U" + print("%s_in.value %s" % (vm_data, sum_incoming)) + print("%s_out.value %s" % (vm_data, sum_outgoing)) + + +def get_vm_network_interface_names(pid): + """ return the MAC addresses configured for network interfacs of a PID """ + result = set() + for netdev_description in _get_kvm_process_arguments(pid, "netdev"): + match = KVM_INTERFACE_NAME_REGEX.search(netdev_description) + if match: + result.add(match.groups()[0]) + return result def detect_kvm(): @@ -139,17 +175,6 @@ def find_vm_names(pids): return result -def get_vm_mac(pid): - """Find and clean vm names from pids - - @return the mac address for a specified pid - """ - cmdline = open("/proc/%s/cmdline" % pid, "r") - line = cmdline.readline() - mac = re.sub(r"^.*ifname=(tap[^,]+),.*$", r"\1", line) - return mac - - def _get_kvm_process_arguments(pid, arg_name): """ parse all value with the given name from the process identified by PID From e0b74c69d705136984a6f4fbc18f1b94736fb45b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 10 Jun 2018 22:04:14 +0200 Subject: [PATCH 219/695] kvm_net: add technical details to 'graph_info' --- plugins/libvirt/kvm_net | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 15cb6ef5..6a0a8024 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -65,13 +65,19 @@ def config(vm_names): @param vm_names : a list of "cleaned" vms' name """ - base_config = """graph_title KVM Network I/O -graph_vlabel Bytes rx(-)/tx(+) per second -graph_category Virtualization -graph_info This graph shows the network I/O of the virtual machines -graph_args --base 1024 - """ - print(base_config) + print("graph_title KVM Network I/O") + print("graph_vlabel Bytes rx(-)/tx(+) per second") + print("graph_category virtualization") + print("graph_args --base 1024") + print("graph_info This graph shows the network I/O of the virtual " + "machines. It is only usable for VMs that were started in a very " + "specific way. If you see no values in the diagrams, then you " + "should check, if the command \"ps -ef | grep 'netdev.*ifname='\" " + "returns one line of output for every running VM. If there is no " + "output, then you need to change the setup of your VMs or you need " + "to use a different munin plugin for monitoring the network traffic " + "(e.g. 'libvirt').") + print() for vm in vm_names: print("%s_in.label %s" % (vm, vm)) print("%s_in.type COUNTER" % vm) From f90a77035667a8f52482f1c6449b92f3e2303179 Mon Sep 17 00:00:00 2001 From: webstoney Date: Mon, 11 Jun 2018 08:44:14 +0200 Subject: [PATCH 220/695] Update w1_ Get rid of perl, use grep in suggest to clarify/simplify, put in sensors group --- plugins/raspberry-pi/w1_ | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/raspberry-pi/w1_ b/plugins/raspberry-pi/w1_ index 60637cba..0d745538 100644 --- a/plugins/raspberry-pi/w1_ +++ b/plugins/raspberry-pi/w1_ @@ -52,7 +52,7 @@ esac if [ "$1" = "suggest" ]; then if [ -r /sys/bus/w1/devices ]; then - ls /sys/bus/w1/devices|head -n -1 + ls /sys/bus/w1/devices|grep -v bus_master fi exit 0 fi @@ -71,7 +71,7 @@ if [ "$1" = "config" ]; then echo graph_title Temperature Sensor $sensor_id echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel temperature (°C)' - echo 'graph_category temperature' + echo 'graph_category sensors' echo 'graph_info This graph shows 1-wire sensor temperature.' echo "w1.label $sensor_id" echo "w1.info Temperature at $sensor_id." @@ -81,4 +81,4 @@ if [ "$1" = "config" ]; then fi -sed -n '/t=/ s/.*t=//p' /sys/bus/w1/devices/$sensor_id/w1_slave | perl -e '$l=;print "w1.value ", $l/1000 , "\n";' +sed -n '/t=/ s/.*t=//p' /sys/bus/w1/devices/$sensor_id/w1_slave | awk '{print "w1.value", $1/1000}' From 8ccf6e8ef4c54187af99175b112dd6eec76cd341 Mon Sep 17 00:00:00 2001 From: Wanpeng Qian Date: Wed, 13 Jun 2018 12:01:04 +0900 Subject: [PATCH 221/695] change dependency from gawk to awk --- plugins/zfs/zpool_iostat | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zpool_iostat b/plugins/zfs/zpool_iostat index 0e655f58..b209384d 100755 --- a/plugins/zfs/zpool_iostat +++ b/plugins/zfs/zpool_iostat @@ -60,7 +60,7 @@ fi zlines=$("$ZPOOL_BIN" iostat -v | wc -l | sed 's/ //g') iostats=$("$ZPOOL_BIN" iostat -v 1 1 | tail "-$zlines") zlist=$(echo "$iostats" \ - | gawk '/alloc/ {next}; /avail/ {next}; /raid/ {next}; /mirror/ {next}; + | awk '/alloc/ {next}; /avail/ {next}; /raid/ {next}; /mirror/ {next}; { if ( $4 >=0 ) print $1}' \ | tr ' ' '\n') @@ -71,9 +71,9 @@ get_device_iostat_column() { local stat_column="$2" # convert all numeric values into kB echo "$iostats" \ - | gawk '{ if ($1 == "'"$device_label"'") print $'"$stat_column"'; }' \ - | gawk '/M/ {print strtonum($1)*1000}; - /K/ {print strtonum($1)}; + | awk '{ if ($1 == "'"$device_label"'") print $'"$stat_column"'; }' \ + | awk '/M/ {print int($1)*1000}; + /K/ {print int($1)}; /[0-9]$/ {print int($1)/1000}' } From 791e47a3a4720d6ffce5dfe06f89c19db904b6aa Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 20:58:53 +0200 Subject: [PATCH 222/695] Create rtom_allsessions_097_spdd Add 0.9.7 compatible version --- plugins/rtorrent/rtom_allsessions_097_spdd | 157 +++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 plugins/rtorrent/rtom_allsessions_097_spdd diff --git a/plugins/rtorrent/rtom_allsessions_097_spdd b/plugins/rtorrent/rtom_allsessions_097_spdd new file mode 100644 index 00000000..a33db538 --- /dev/null +++ b/plugins/rtorrent/rtom_allsessions_097_spdd @@ -0,0 +1,157 @@ +#!/usr/bin/perl -w +# +# xmlrpc based munin plugin for monitoring rtorrent's upload/download speed +# prerequisites: +# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# +# written by Gabor Hudiczius +# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter +# email: ghudiczius@gmail.com +# +# 0.0.0 - 071218 +# initial release +# +# 0.0.1 - 071220 +# minor textbugs fixed +# +# 0.1.0d - 080519 +# full rewrite in perl +# support for scgi_port and scgi_local +# configurable via munin env variables +# different ul/dl scale can be set for asymmetric connections +# using get_(up|down)_total, and derive +# +# 0.2.0 - 080619 +# upload and download limit displayed on the graph +# +# +# Parameters: +# +# config required +# +# +# Configurable variables +# +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both +# +# +# Configuration example +# +# [rtom_allsessions_*] +# user username +# env.src socket +# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket +# env.category Category +# +# [rtom_allsessions_*] +# user username +# env.port 5000,5001,5002,5003 +# env.category Category +# +#%# family=auto + + +if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + exit 1; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + my $diff = $ENV{"diff"} || ""; + my $category = $ENV{"category"} || ""; + print "graph_order down up\n"; + print "graph_title rTorrent speeds\n"; + print "graph_args --base 1024\n"; + print "graph_vlabel Bytes per \${graph_period}\n"; + print "graph_category torrent ".${category}."\n"; + print "down.label Download B/s\n"; + print "down.info Download speed in Bytes per seconds\n"; + print "down.type DERIVE\n"; + print "down.min 0\n"; + print "down.draw AREA\n"; + if ( ( defined $diff ) && ( $diff eq "yes" ) ) { + print "up.label Upload b/s\n"; + print "up.info Upload speed in bits per seconds\n"; + print "up.cdef up,8,*\n"; + } else { + print "up.label Upload B/s\n"; + print "up.info Upload speed in Bytes per seconds\n"; + } + print "up.type DERIVE\n"; + print "up.min 0\n"; + print "up.draw LINE2\n"; + exit 0; +} + +use IO::Socket; +my $src = $ENV{"src"} || ""; +my @sockets = split /,/, $ENV{"socket"} || ""; +my $ip = $ENV{"ip"} || "127.0.0.1"; +my @ports = split /,/, $ENV{"port"} || ""; + +my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; +my $llen = length $line; +my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; +my $hlen = length $header; + +my $up = -1; +my $down = -1; + +if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + my $up_tmp = -1; + my $down_tmp = -1; + while (( $up_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $up_tmp = $2; + } + } + while (( $down_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $down_tmp = $2; + } + } + close (SOCK); + $up = $up + $up_tmp; + $down = $down + $down_tmp; + } +} else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + my $up_tmp = -1; + my $down_tmp = -1; + while (( $up_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $up_tmp = $2; + } + } + while (( $down_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $down_tmp = $2; + } + } + close (SOCK); + $up = $up + $up_tmp; + $down = $down + $down_tmp; + } +} + + + +print "up.value ${up}\ndown.value ${down}\n"; + +exit; From ea77d6f358c3f706673c8c68d4c1721abcd8f5b5 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 20:59:40 +0200 Subject: [PATCH 223/695] Create rtom_allsessions_097_mem Add 0.9.7 compatible version --- plugins/rtorrent/rtom_allsessions_097_mem | 111 ++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 plugins/rtorrent/rtom_allsessions_097_mem diff --git a/plugins/rtorrent/rtom_allsessions_097_mem b/plugins/rtorrent/rtom_allsessions_097_mem new file mode 100644 index 00000000..29190896 --- /dev/null +++ b/plugins/rtorrent/rtom_allsessions_097_mem @@ -0,0 +1,111 @@ +#!/usr/bin/perl -w +# +# xmlrpc based munin plugin for monitoring rtorrent's memory usage +# prerequisites: +# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# +# written by Gabor Hudiczius +# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter +# email: ghudiczius@gmail.com +# +# 0.2.0 - 080619 +# support for scgi_port and scgi_local +# configurable via munin env variables +# initial release +# +# +# Parameters: +# +# config required +# +# +# Configurable variables +# +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" +# port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" +# category Change graph category +# +# Configuration example +# +# [rtom_allsessions_*] +# user username +# env.src socket +# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket +# env.category Category +# +# [rtom_allsessions_*] +# user username +# env.port 5000,5001,5002,5003 +# env.category Category +# +#%# family=auto + + +if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + exit 1; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + my $category = $ENV{"category"} || ""; + print "graph_title rTorrent memory usage\n"; + print "graph_args --base 1024 --lower-limit 0\n"; + print "graph_vlabel Bytes\n"; + print "graph_category torrent ".${category}."\n"; + print "mem.label Memory usage\n"; + print "mem.info Memory usage of rTorrent\n"; + print "mem.type GAUGE\n"; + print "mem.draw LINE2\n"; + exit 0; +} + +use IO::Socket; +my $src = $ENV{"src"} || ""; +my @sockets = split /,/, $ENV{"socket"} || ""; +my $ip = $ENV{"ip"} || "127.0.0.1"; +my @ports = split /,/, $ENV{"port"} || ""; + +my $mem = 0; +my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $line = "pieces.memory.current"; +my $llen = length $line; +my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; +my $hlen = length $header; + +if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $mem = $mem + $2; + } + } + close (SOCK); + } +} else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $mem = $mem + $2; + } + } + close (SOCK); + } +} + +print "mem.value ${mem}\n"; + +exit; From 3be6c4f8a50889aab589bd5e1d235766f3912f3b Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 21:00:35 +0200 Subject: [PATCH 224/695] Create rtom_allsessions_097_peers Add 0.9.7 compatible version --- plugins/rtorrent/rtom_allsessions_097_peers | 152 ++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 plugins/rtorrent/rtom_allsessions_097_peers diff --git a/plugins/rtorrent/rtom_allsessions_097_peers b/plugins/rtorrent/rtom_allsessions_097_peers new file mode 100644 index 00000000..9421cb46 --- /dev/null +++ b/plugins/rtorrent/rtom_allsessions_097_peers @@ -0,0 +1,152 @@ +#!/usr/bin/perl -w +# +# xmlrpc based munin plugin for monitoring rtorrent's peer count +# prerequisites: +# - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# +# written by Gabor Hudiczius +# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter +# email: ghudiczius@gmail.com +# +# 0.2.0 - 080619 +# support for scgi_port and scgi_local +# configurable via munin env variables +# initial release +# +# +# Parameters: +# +# config required +# +# +# Configurable variables +# +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# category Change graph category +# +# Configuration example +# +# [rtom_allsessions_*] +# user username +# env.src socket +# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket +# env.category Category +# +# [rtom_allsessions_*] +# user username +# env.port 5000,5001,5002,5003 +# env.category Category +# +#%# family=auto + + +if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + exit 1; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + my $category = $ENV{"category"} || ""; + print "graph_title rTorrent peer statistics\n"; + print "graph_args --base 1000 --lower-limit 0\n"; + print "graph_vlabel peers\n"; + print "graph_category torrent ".${category}."\n"; + print "outgoing.label outgoing\n"; + print "outgoing.draw AREA\n"; + print "outgoing.info number of outgoing connections\n"; + print "incoming.label incoming\n"; + print "incoming.draw STACK\n"; + print "incoming.info number of incoming connections\n"; + print "plain.label plain text\n"; + print "plain.draw LINE2\n"; + print "plain.info number of plain text connections\n"; + print "encrypted.label encrypted\n"; + print "encrypted.draw LINE2\n"; + print "encrypted.info number of encrypted connections\n"; + print "total.label total\n"; + print "total.draw LINE2\n"; + print "total.info total number of connections\n"; + exit 0; +} + +use IO::Socket; + +my $src = $ENV{"src"} || ""; +my @sockets = split /,/, $ENV{"socket"} || ""; +my $ip = $ENV{"ip"} || "127.0.0.1"; +my @ports = split /,/, $ENV{"port"} || ""; + +my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $tpattern = qr/[0-9A-F]{20}/; + +my $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +my $llen = length $line; +my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; +my $hlen = length $header; +my $tor = 0; +my $tot = 0; +my $enc = 0; +my $inc = 0; +my $pline = ""; +my $ppline = ""; +my $out = 0; +my $pla = 0; + + +if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$tpattern/ ) { + $tor += 1; + } elsif ( $line =~ /$pattern/ ) { + $tot += 1; + $enc += $2; + $line = ; + $line =~ /$pattern/; + $inc += $2; + } + $ppline = $pline; + $pline = $line; + } + close (SOCK); + $out = $out + $tot - $inc; + $pla = $pla + $tot - $enc; + } +} else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$tpattern/ ) { + $tor += 1; + } elsif ( $line =~ /$pattern/ ) { + $tot += 1; + $enc += $2; + $line = ; + $line =~ /$pattern/; + $inc += $2; + } + $ppline = $pline; + $pline = $line; + } + close (SOCK); + $out = $out + $tot - $inc; + $pla = $pla + $tot - $enc; + } +} + + +print "torrents.value ${tor}\ntotal.value ${tot}\nencrypted.value ${enc}\nplain.value ${pla}\nincoming.value ${inc}\noutgoing.value ${out}\n"; + +exit; From 74692d0d908e595e3b3e35085fd2f11ba8eec0ee Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 21:01:09 +0200 Subject: [PATCH 225/695] Create rtom_allsessions_097_vol Add 0.9.7 compatible version --- plugins/rtorrent/rtom_allsessions_097_vol | 127 ++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 plugins/rtorrent/rtom_allsessions_097_vol diff --git a/plugins/rtorrent/rtom_allsessions_097_vol b/plugins/rtorrent/rtom_allsessions_097_vol new file mode 100644 index 00000000..efb2f767 --- /dev/null +++ b/plugins/rtorrent/rtom_allsessions_097_vol @@ -0,0 +1,127 @@ +#!/usr/bin/perl -w +# +# xmlrpc based munin plugin for monitoring rtorrent's torrent count +# prerequisites: +# - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# +# written by Gabor Hudiczius +# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter +# email: ghudiczius@gmail.com +# +# 0.2.0 - 080619 +# support for scgi_port and scgi_local +# configurable via munin env variables +# initial release +# +# +# Parameters: +# +# config required +# +# +# Configurable variables +# +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# category Change graph category +# +# Configuration example +# +# [rtom_allsessions_*] +# user username +# env.src socket +# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket +# env.category Category +# +# [rtom_allsessions_*] +# user username +# env.port 5000,5001,5002,5003 +# env.category Category +# +#%# family=auto + +my @views = ( "default", "started", "stopped", "complete", "incomplete" ); + +if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + exit 1; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + my $category = $ENV{"category"} || ""; + print "graph_args --base 1000 -r --lower-limit 0\n"; + print "graph_title rTorrent volume\n"; + print "graph_vlabel active torrents\n"; + print "graph_category torrent ".${category}."\n"; + print "complete.label complete\n"; + print "complete.draw AREA\n"; + print "complete.info complete torrents\n"; + print "incomplete.label incomplete\n"; + print "incomplete.draw STACK\n"; + print "incomplete.info incomplete torrents\n"; + print "stopped.label stopped\n"; + print "stopped.draw LINE2\n"; + print "stopped.info stopped torrents\n"; + print "started.label started\n"; + print "started.draw LINE2\n"; + print "started.info started torrents\n"; + print "default.label total\n"; + print "default.draw LINE2\n"; + print "default.info all torrents\n"; + print "hashing.graph no\n"; + print "seeding.graph no\n"; + print "active.graph no\n"; + exit 0; +} + +use IO::Socket; + +my $src = $ENV{"src"} || ""; +my @sockets = split /,/, $ENV{"socket"} || ""; +my $ip = $ENV{"ip"} || "127.0.0.1"; +my @ports = split /,/, $ENV{"port"} || ""; + +my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; + +foreach ( @views ) { + my $num = 0; + my $line = "d.multicall2${_}d.hash="; + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; + } + } + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; + } + } + close (SOCK); + } + } + print "${_}.value ${num}\n"; +} + +exit; From 592876ea1a75e92ae552d7d45a417573f1e8d4b7 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 21:02:45 +0200 Subject: [PATCH 226/695] Update rtom_allsessions_097_peers fix version --- plugins/rtorrent/rtom_allsessions_097_peers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rtorrent/rtom_allsessions_097_peers b/plugins/rtorrent/rtom_allsessions_097_peers index 9421cb46..f4eb0b44 100644 --- a/plugins/rtorrent/rtom_allsessions_097_peers +++ b/plugins/rtorrent/rtom_allsessions_097_peers @@ -2,7 +2,7 @@ # # xmlrpc based munin plugin for monitoring rtorrent's peer count # prerequisites: -# - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c +# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c # check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations # # written by Gabor Hudiczius From 91fc3e72c2bcff8140b1ce9665dead8c6aa8c8b9 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Mon, 2 Jul 2018 21:03:01 +0200 Subject: [PATCH 227/695] Update rtom_allsessions_097_vol fix version --- plugins/rtorrent/rtom_allsessions_097_vol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rtorrent/rtom_allsessions_097_vol b/plugins/rtorrent/rtom_allsessions_097_vol index efb2f767..6b14e4f7 100644 --- a/plugins/rtorrent/rtom_allsessions_097_vol +++ b/plugins/rtorrent/rtom_allsessions_097_vol @@ -2,7 +2,7 @@ # # xmlrpc based munin plugin for monitoring rtorrent's torrent count # prerequisites: -# - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c +# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c # check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations # # written by Gabor Hudiczius From 8bfb81806d1e1489512d5936b1733c5e769e0d0c Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Fri, 6 Jul 2018 16:41:11 +0200 Subject: [PATCH 228/695] drop seperate files and make api version configureable --- plugins/rtorrent/rtom_allsessions_097_mem | 111 -------------- plugins/rtorrent/rtom_allsessions_097_peers | 152 ------------------- plugins/rtorrent/rtom_allsessions_097_spdd | 157 -------------------- plugins/rtorrent/rtom_allsessions_097_vol | 127 ---------------- plugins/rtorrent/rtom_allsessions_mem | 16 +- plugins/rtorrent/rtom_allsessions_peers | 20 ++- plugins/rtorrent/rtom_allsessions_spdd | 15 +- plugins/rtorrent/rtom_allsessions_vol | 145 +++++++++--------- 8 files changed, 115 insertions(+), 628 deletions(-) delete mode 100644 plugins/rtorrent/rtom_allsessions_097_mem delete mode 100644 plugins/rtorrent/rtom_allsessions_097_peers delete mode 100644 plugins/rtorrent/rtom_allsessions_097_spdd delete mode 100644 plugins/rtorrent/rtom_allsessions_097_vol diff --git a/plugins/rtorrent/rtom_allsessions_097_mem b/plugins/rtorrent/rtom_allsessions_097_mem deleted file mode 100644 index 29190896..00000000 --- a/plugins/rtorrent/rtom_allsessions_097_mem +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/perl -w -# -# xmlrpc based munin plugin for monitoring rtorrent's memory usage -# prerequisites: -# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations -# -# written by Gabor Hudiczius -# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter -# email: ghudiczius@gmail.com -# -# 0.2.0 - 080619 -# support for scgi_port and scgi_local -# configurable via munin env variables -# initial release -# -# -# Parameters: -# -# config required -# -# -# Configurable variables -# -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" -# port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" -# category Change graph category -# -# Configuration example -# -# [rtom_allsessions_*] -# user username -# env.src socket -# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket -# env.category Category -# -# [rtom_allsessions_*] -# user username -# env.port 5000,5001,5002,5003 -# env.category Category -# -#%# family=auto - - -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_title rTorrent memory usage\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel Bytes\n"; - print "graph_category torrent ".${category}."\n"; - print "mem.label Memory usage\n"; - print "mem.info Memory usage of rTorrent\n"; - print "mem.type GAUGE\n"; - print "mem.draw LINE2\n"; - exit 0; -} - -use IO::Socket; -my $src = $ENV{"src"} || ""; -my @sockets = split /,/, $ENV{"socket"} || ""; -my $ip = $ENV{"ip"} || "127.0.0.1"; -my @ports = split /,/, $ENV{"port"} || ""; - -my $mem = 0; -my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = "pieces.memory.current"; -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; - -if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $mem = $mem + $2; - } - } - close (SOCK); - } -} else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $mem = $mem + $2; - } - } - close (SOCK); - } -} - -print "mem.value ${mem}\n"; - -exit; diff --git a/plugins/rtorrent/rtom_allsessions_097_peers b/plugins/rtorrent/rtom_allsessions_097_peers deleted file mode 100644 index f4eb0b44..00000000 --- a/plugins/rtorrent/rtom_allsessions_097_peers +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/perl -w -# -# xmlrpc based munin plugin for monitoring rtorrent's peer count -# prerequisites: -# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations -# -# written by Gabor Hudiczius -# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter -# email: ghudiczius@gmail.com -# -# 0.2.0 - 080619 -# support for scgi_port and scgi_local -# configurable via munin env variables -# initial release -# -# -# Parameters: -# -# config required -# -# -# Configurable variables -# -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# category Change graph category -# -# Configuration example -# -# [rtom_allsessions_*] -# user username -# env.src socket -# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket -# env.category Category -# -# [rtom_allsessions_*] -# user username -# env.port 5000,5001,5002,5003 -# env.category Category -# -#%# family=auto - - -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_title rTorrent peer statistics\n"; - print "graph_args --base 1000 --lower-limit 0\n"; - print "graph_vlabel peers\n"; - print "graph_category torrent ".${category}."\n"; - print "outgoing.label outgoing\n"; - print "outgoing.draw AREA\n"; - print "outgoing.info number of outgoing connections\n"; - print "incoming.label incoming\n"; - print "incoming.draw STACK\n"; - print "incoming.info number of incoming connections\n"; - print "plain.label plain text\n"; - print "plain.draw LINE2\n"; - print "plain.info number of plain text connections\n"; - print "encrypted.label encrypted\n"; - print "encrypted.draw LINE2\n"; - print "encrypted.info number of encrypted connections\n"; - print "total.label total\n"; - print "total.draw LINE2\n"; - print "total.info total number of connections\n"; - exit 0; -} - -use IO::Socket; - -my $src = $ENV{"src"} || ""; -my @sockets = split /,/, $ENV{"socket"} || ""; -my $ip = $ENV{"ip"} || "127.0.0.1"; -my @ports = split /,/, $ENV{"port"} || ""; - -my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $tpattern = qr/[0-9A-F]{20}/; - -my $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; -my $tor = 0; -my $tot = 0; -my $enc = 0; -my $inc = 0; -my $pline = ""; -my $ppline = ""; -my $out = 0; -my $pla = 0; - - -if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$tpattern/ ) { - $tor += 1; - } elsif ( $line =~ /$pattern/ ) { - $tot += 1; - $enc += $2; - $line = ; - $line =~ /$pattern/; - $inc += $2; - } - $ppline = $pline; - $pline = $line; - } - close (SOCK); - $out = $out + $tot - $inc; - $pla = $pla + $tot - $enc; - } -} else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$tpattern/ ) { - $tor += 1; - } elsif ( $line =~ /$pattern/ ) { - $tot += 1; - $enc += $2; - $line = ; - $line =~ /$pattern/; - $inc += $2; - } - $ppline = $pline; - $pline = $line; - } - close (SOCK); - $out = $out + $tot - $inc; - $pla = $pla + $tot - $enc; - } -} - - -print "torrents.value ${tor}\ntotal.value ${tot}\nencrypted.value ${enc}\nplain.value ${pla}\nincoming.value ${inc}\noutgoing.value ${out}\n"; - -exit; diff --git a/plugins/rtorrent/rtom_allsessions_097_spdd b/plugins/rtorrent/rtom_allsessions_097_spdd deleted file mode 100644 index a33db538..00000000 --- a/plugins/rtorrent/rtom_allsessions_097_spdd +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/perl -w -# -# xmlrpc based munin plugin for monitoring rtorrent's upload/download speed -# prerequisites: -# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations -# -# written by Gabor Hudiczius -# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter -# email: ghudiczius@gmail.com -# -# 0.0.0 - 071218 -# initial release -# -# 0.0.1 - 071220 -# minor textbugs fixed -# -# 0.1.0d - 080519 -# full rewrite in perl -# support for scgi_port and scgi_local -# configurable via munin env variables -# different ul/dl scale can be set for asymmetric connections -# using get_(up|down)_total, and derive -# -# 0.2.0 - 080619 -# upload and download limit displayed on the graph -# -# -# Parameters: -# -# config required -# -# -# Configurable variables -# -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# -# -# Configuration example -# -# [rtom_allsessions_*] -# user username -# env.src socket -# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket -# env.category Category -# -# [rtom_allsessions_*] -# user username -# env.port 5000,5001,5002,5003 -# env.category Category -# -#%# family=auto - - -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $diff = $ENV{"diff"} || ""; - my $category = $ENV{"category"} || ""; - print "graph_order down up\n"; - print "graph_title rTorrent speeds\n"; - print "graph_args --base 1024\n"; - print "graph_vlabel Bytes per \${graph_period}\n"; - print "graph_category torrent ".${category}."\n"; - print "down.label Download B/s\n"; - print "down.info Download speed in Bytes per seconds\n"; - print "down.type DERIVE\n"; - print "down.min 0\n"; - print "down.draw AREA\n"; - if ( ( defined $diff ) && ( $diff eq "yes" ) ) { - print "up.label Upload b/s\n"; - print "up.info Upload speed in bits per seconds\n"; - print "up.cdef up,8,*\n"; - } else { - print "up.label Upload B/s\n"; - print "up.info Upload speed in Bytes per seconds\n"; - } - print "up.type DERIVE\n"; - print "up.min 0\n"; - print "up.draw LINE2\n"; - exit 0; -} - -use IO::Socket; -my $src = $ENV{"src"} || ""; -my @sockets = split /,/, $ENV{"socket"} || ""; -my $ip = $ENV{"ip"} || "127.0.0.1"; -my @ports = split /,/, $ENV{"port"} || ""; - -my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; - -my $up = -1; -my $down = -1; - -if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - my $up_tmp = -1; - my $down_tmp = -1; - while (( $up_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $up_tmp = $2; - } - } - while (( $down_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $down_tmp = $2; - } - } - close (SOCK); - $up = $up + $up_tmp; - $down = $down + $down_tmp; - } -} else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - my $up_tmp = -1; - my $down_tmp = -1; - while (( $up_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $up_tmp = $2; - } - } - while (( $down_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $down_tmp = $2; - } - } - close (SOCK); - $up = $up + $up_tmp; - $down = $down + $down_tmp; - } -} - - - -print "up.value ${up}\ndown.value ${down}\n"; - -exit; diff --git a/plugins/rtorrent/rtom_allsessions_097_vol b/plugins/rtorrent/rtom_allsessions_097_vol deleted file mode 100644 index 6b14e4f7..00000000 --- a/plugins/rtorrent/rtom_allsessions_097_vol +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/perl -w -# -# xmlrpc based munin plugin for monitoring rtorrent's torrent count -# prerequisites: -# - rtorrent 0.9.7 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations -# -# written by Gabor Hudiczius -# web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter -# email: ghudiczius@gmail.com -# -# 0.2.0 - 080619 -# support for scgi_port and scgi_local -# configurable via munin env variables -# initial release -# -# -# Parameters: -# -# config required -# -# -# Configurable variables -# -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# category Change graph category -# -# Configuration example -# -# [rtom_allsessions_*] -# user username -# env.src socket -# env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket -# env.category Category -# -# [rtom_allsessions_*] -# user username -# env.port 5000,5001,5002,5003 -# env.category Category -# -#%# family=auto - -my @views = ( "default", "started", "stopped", "complete", "incomplete" ); - -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_args --base 1000 -r --lower-limit 0\n"; - print "graph_title rTorrent volume\n"; - print "graph_vlabel active torrents\n"; - print "graph_category torrent ".${category}."\n"; - print "complete.label complete\n"; - print "complete.draw AREA\n"; - print "complete.info complete torrents\n"; - print "incomplete.label incomplete\n"; - print "incomplete.draw STACK\n"; - print "incomplete.info incomplete torrents\n"; - print "stopped.label stopped\n"; - print "stopped.draw LINE2\n"; - print "stopped.info stopped torrents\n"; - print "started.label started\n"; - print "started.draw LINE2\n"; - print "started.info started torrents\n"; - print "default.label total\n"; - print "default.draw LINE2\n"; - print "default.info all torrents\n"; - print "hashing.graph no\n"; - print "seeding.graph no\n"; - print "active.graph no\n"; - exit 0; -} - -use IO::Socket; - -my $src = $ENV{"src"} || ""; -my @sockets = split /,/, $ENV{"socket"} || ""; -my $ip = $ENV{"ip"} || "127.0.0.1"; -my @ports = split /,/, $ENV{"port"} || ""; - -my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; - -foreach ( @views ) { - my $num = 0; - my $line = "d.multicall2${_}d.hash="; - my $llen = length $line; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; - - if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } - } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } - } - print "${_}.value ${num}\n"; -} - -exit; diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 1b12be09..1c83292b 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -27,6 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category +# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls # # Configuration example # @@ -35,6 +36,7 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category +# env.api current # # [rtom_allsessions_*] # user username @@ -66,10 +68,16 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; +my $api = $ENV{"api"} || "legacy"; -my $mem = 0; +my $mem = 0; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = "get_memory_usage"; +my $line = ""; +if ($api =~ /legacy/) { + $line = "get_memory_usage"; +} else { + $line = "pieces.memory.current"; +} my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; @@ -88,10 +96,10 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { } } close (SOCK); - } + } } else { for $port (@ports) - { + { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); my $line = "${hlen}:${header},${line}"; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index a3cc1705..e0ea4532 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -22,9 +22,10 @@ # # Configurable variables # -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# category Change graph category +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# category Change graph category +# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls # # Configuration example # @@ -33,6 +34,7 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category +# env.api current # # [rtom_allsessions_*] # user username @@ -76,11 +78,17 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; +my $api = $ENV{"api"} || "legacy"; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; -my $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +my $line = ""; +if ($api =~ /legacy/) { + $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +} else { + $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +} my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; @@ -119,10 +127,10 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { close (SOCK); $out = $out + $tot - $inc; $pla = $pla + $tot - $enc; - } + } } else { for $port (@ports) - { + { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); my $line = "${hlen}:${header},${line}"; diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index ed89061a..26e51435 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -36,6 +36,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both +# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls # # # Configuration example @@ -45,6 +46,7 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category +# env.api current # # [rtom_allsessions_*] # user username @@ -90,9 +92,16 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; +my $api = $ENV{"api"} || "legacy"; my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; +my $line = ""; +if ($api =~ /legacy/) { + $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; +} else { + $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; +} + my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; @@ -123,10 +132,10 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { close (SOCK); $up = $up + $up_tmp; $down = $down + $down_tmp; - } + } } else { for $port (@ports) - { + { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); my $line = "${hlen}:${header},${line}"; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 830575f4..fe99af9b 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's torrent count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -17,14 +17,15 @@ # # Parameters: # -# config required +# config required # # # Configurable variables # -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# category Change graph category +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# category Change graph category +# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls # # Configuration example # @@ -33,45 +34,47 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category +# env.api current # # [rtom_allsessions_*] # user username # env.port 5000,5001,5002,5003 # env.category Category +# env.api legacy # #%# family=auto my @views = ( "default", "started", "stopped", "complete", "incomplete" ); if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; + exit 1; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_args --base 1000 -r --lower-limit 0\n"; - print "graph_title rTorrent volume\n"; - print "graph_vlabel active torrents\n"; - print "graph_category filetransfer".${category}."\n"; - print "complete.label complete\n"; - print "complete.draw AREA\n"; - print "complete.info complete torrents\n"; - print "incomplete.label incomplete\n"; - print "incomplete.draw STACK\n"; - print "incomplete.info incomplete torrents\n"; - print "stopped.label stopped\n"; - print "stopped.draw LINE2\n"; - print "stopped.info stopped torrents\n"; - print "started.label started\n"; - print "started.draw LINE2\n"; - print "started.info started torrents\n"; - print "default.label total\n"; - print "default.draw LINE2\n"; - print "default.info all torrents\n"; - print "hashing.graph no\n"; - print "seeding.graph no\n"; - print "active.graph no\n"; - exit 0; + my $category = $ENV{"category"} || ""; + print "graph_args --base 1000 -r --lower-limit 0\n"; + print "graph_title rTorrent volume\n"; + print "graph_vlabel active torrents\n"; + print "graph_category torrent ".${category}."\n"; + print "complete.label complete\n"; + print "complete.draw AREA\n"; + print "complete.info complete torrents\n"; + print "incomplete.label incomplete\n"; + print "incomplete.draw STACK\n"; + print "incomplete.info incomplete torrents\n"; + print "stopped.label stopped\n"; + print "stopped.draw LINE2\n"; + print "stopped.info stopped torrents\n"; + print "started.label started\n"; + print "started.draw LINE2\n"; + print "started.info started torrents\n"; + print "default.label total\n"; + print "default.draw LINE2\n"; + print "default.info all torrents\n"; + print "hashing.graph no\n"; + print "seeding.graph no\n"; + print "active.graph no\n"; + exit 0; } use IO::Socket; @@ -80,48 +83,54 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; +my $api = $ENV{"api"} || "legacy"; -my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; +my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; foreach ( @views ) { - my $num = 0; - my $line = "d.multicall${_}d.get_hash="; - my $llen = length $line; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; + my $num = 0; + my $line = ""; + if ($api =~ /legacy/) { + $line = "d.multicall${_}d.get_hash="; + } else { + $line = "d.multicall2${_}d.hash="; + } + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; - if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } - } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } - } - print "${_}.value ${num}\n"; + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; + } + } + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; + } + } + close (SOCK); + } + } + print "${_}.value ${num}\n"; } exit; From 121448e72d29963ec0964e67f09972edbbf26ac3 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Tue, 10 Jul 2018 16:58:28 +0200 Subject: [PATCH 229/695] fix indentation, rename lagacy to pre09 --- plugins/rtorrent/rtom_allsessions_mem | 84 ++++++------ plugins/rtorrent/rtom_allsessions_peers | 164 ++++++++++++------------ plugins/rtorrent/rtom_allsessions_spdd | 150 +++++++++++----------- plugins/rtorrent/rtom_allsessions_vol | 140 ++++++++++---------- 4 files changed, 269 insertions(+), 269 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 1c83292b..9011b9d3 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -27,7 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls +# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls # # Configuration example # @@ -47,20 +47,20 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; + exit 1; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_title rTorrent memory usage\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel Bytes\n"; - print "graph_category filetransfer".${category}."\n"; - print "mem.label Memory usage\n"; - print "mem.info Memory usage of rTorrent\n"; - print "mem.type GAUGE\n"; - print "mem.draw LINE2\n"; - exit 0; + my $category = $ENV{"category"} || ""; + print "graph_title rTorrent memory usage\n"; + print "graph_args --base 1024 --lower-limit 0\n"; + print "graph_vlabel Bytes\n"; + print "graph_category filetransfer".${category}."\n"; + print "mem.label Memory usage\n"; + print "mem.info Memory usage of rTorrent\n"; + print "mem.type GAUGE\n"; + print "mem.draw LINE2\n"; + exit 0; } use IO::Socket; @@ -68,12 +68,12 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "legacy"; +my $api = $ENV{"api"} || "pre09"; my $mem = 0; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $line = ""; -if ($api =~ /legacy/) { +if ($api =~ /pre09/) { $line = "get_memory_usage"; } else { $line = "pieces.memory.current"; @@ -83,35 +83,35 @@ my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $mem = $mem + $2; - } - } - close (SOCK); - } + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $mem = $mem + $2; + } + } + close (SOCK); + } } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $mem = $mem + $2; - } - } - close (SOCK); - } + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $mem = $mem + $2; + } + } + close (SOCK); + } } print "mem.value ${mem}\n"; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index e0ea4532..3e01b4a8 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -17,15 +17,15 @@ # # Parameters: # -# config required +# config required # # # Configurable variables # -# src "socket" when using scgi_socket, or anything else when using scgi_port -# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" -# category Change graph category -# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls +# src "socket" when using scgi_socket, or anything else when using scgi_port +# socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" +# category Change graph category +# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls # # Configuration example # @@ -45,31 +45,31 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; + exit 1; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_title rTorrent peer statistics\n"; - print "graph_args --base 1000 --lower-limit 0\n"; - print "graph_vlabel peers\n"; - print "graph_category filetransfer".${category}."\n"; - print "outgoing.label outgoing\n"; - print "outgoing.draw AREA\n"; - print "outgoing.info number of outgoing connections\n"; - print "incoming.label incoming\n"; - print "incoming.draw STACK\n"; - print "incoming.info number of incoming connections\n"; - print "plain.label plain text\n"; - print "plain.draw LINE2\n"; - print "plain.info number of plain text connections\n"; - print "encrypted.label encrypted\n"; - print "encrypted.draw LINE2\n"; - print "encrypted.info number of encrypted connections\n"; - print "total.label total\n"; - print "total.draw LINE2\n"; - print "total.info total number of connections\n"; - exit 0; + my $category = $ENV{"category"} || ""; + print "graph_title rTorrent peer statistics\n"; + print "graph_args --base 1000 --lower-limit 0\n"; + print "graph_vlabel peers\n"; + print "graph_category filetransfer".${category}."\n"; + print "outgoing.label outgoing\n"; + print "outgoing.draw AREA\n"; + print "outgoing.info number of outgoing connections\n"; + print "incoming.label incoming\n"; + print "incoming.draw STACK\n"; + print "incoming.info number of incoming connections\n"; + print "plain.label plain text\n"; + print "plain.draw LINE2\n"; + print "plain.info number of plain text connections\n"; + print "encrypted.label encrypted\n"; + print "encrypted.draw LINE2\n"; + print "encrypted.info number of encrypted connections\n"; + print "total.label total\n"; + print "total.draw LINE2\n"; + print "total.info total number of connections\n"; + exit 0; } use IO::Socket; @@ -78,20 +78,20 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "legacy"; +my $api = $ENV{"api"} || "pre09"; -my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $tpattern = qr/[0-9A-F]{20}/; +my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $tpattern = qr/[0-9A-F]{20}/; my $line = ""; -if ($api =~ /legacy/) { +if ($api =~ /pre09/) { $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; } else { $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; } -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; +my $llen = length $line; +my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; +my $hlen = length $header; my $tor = 0; my $tot = 0; @@ -104,55 +104,55 @@ my $pla = 0; if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$tpattern/ ) { - $tor += 1; - } elsif ( $line =~ /$pattern/ ) { - $tot += 1; - $enc += $2; - $line = ; - $line =~ /$pattern/; - $inc += $2; - } - $ppline = $pline; - $pline = $line; - } - close (SOCK); - $out = $out + $tot - $inc; - $pla = $pla + $tot - $enc; - } + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$tpattern/ ) { + $tor += 1; + } elsif ( $line =~ /$pattern/ ) { + $tot += 1; + $enc += $2; + $line = ; + $line =~ /$pattern/; + $inc += $2; + } + $ppline = $pline; + $pline = $line; + } + close (SOCK); + $out = $out + $tot - $inc; + $pla = $pla + $tot - $enc; + } } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$tpattern/ ) { - $tor += 1; - } elsif ( $line =~ /$pattern/ ) { - $tot += 1; - $enc += $2; - $line = ; - $line =~ /$pattern/; - $inc += $2; - } - $ppline = $pline; - $pline = $line; - } - close (SOCK); - $out = $out + $tot - $inc; - $pla = $pla + $tot - $enc; - } + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$tpattern/ ) { + $tor += 1; + } elsif ( $line =~ /$pattern/ ) { + $tot += 1; + $enc += $2; + $line = ; + $line =~ /$pattern/; + $inc += $2; + } + $ppline = $pline; + $pline = $line; + } + close (SOCK); + $out = $out + $tot - $inc; + $pla = $pla + $tot - $enc; + } } diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index 26e51435..d608dd78 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -36,7 +36,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls +# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls # # # Configuration example @@ -46,7 +46,7 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category -# env.api current +# env.api pre09 # # [rtom_allsessions_*] # user username @@ -57,34 +57,34 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; + exit 1; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $diff = $ENV{"diff"} || ""; - my $category = $ENV{"category"} || ""; - print "graph_order down up\n"; - print "graph_title rTorrent speeds\n"; - print "graph_args --base 1024\n"; - print "graph_vlabel Bytes per \${graph_period}\n"; - print "graph_category filetransfer".${category}."\n"; - print "down.label Download B/s\n"; - print "down.info Download speed in Bytes per seconds\n"; - print "down.type DERIVE\n"; - print "down.min 0\n"; - print "down.draw AREA\n"; - if ( ( defined $diff ) && ( $diff eq "yes" ) ) { - print "up.label Upload b/s\n"; - print "up.info Upload speed in bits per seconds\n"; - print "up.cdef up,8,*\n"; - } else { - print "up.label Upload B/s\n"; - print "up.info Upload speed in Bytes per seconds\n"; - } - print "up.type DERIVE\n"; - print "up.min 0\n"; - print "up.draw LINE2\n"; - exit 0; + my $diff = $ENV{"diff"} || ""; + my $category = $ENV{"category"} || ""; + print "graph_order down up\n"; + print "graph_title rTorrent speeds\n"; + print "graph_args --base 1024\n"; + print "graph_vlabel Bytes per \${graph_period}\n"; + print "graph_category filetransfer".${category}."\n"; + print "down.label Download B/s\n"; + print "down.info Download speed in Bytes per seconds\n"; + print "down.type DERIVE\n"; + print "down.min 0\n"; + print "down.draw AREA\n"; + if ( ( defined $diff ) && ( $diff eq "yes" ) ) { + print "up.label Upload b/s\n"; + print "up.info Upload speed in bits per seconds\n"; + print "up.cdef up,8,*\n"; + } else { + print "up.label Upload B/s\n"; + print "up.info Upload speed in Bytes per seconds\n"; + } + print "up.type DERIVE\n"; + print "up.min 0\n"; + print "up.draw LINE2\n"; + exit 0; } use IO::Socket; @@ -92,11 +92,11 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "legacy"; +my $api = $ENV{"api"} || "pre09"; my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $line = ""; -if ($api =~ /legacy/) { +if ($api =~ /pre09/) { $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; } else { $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; @@ -110,53 +110,53 @@ my $up = -1; my $down = -1; if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - my $up_tmp = -1; - my $down_tmp = -1; - while (( $up_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $up_tmp = $2; - } - } - while (( $down_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $down_tmp = $2; - } - } - close (SOCK); - $up = $up + $up_tmp; - $down = $down + $down_tmp; - } + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + my $up_tmp = -1; + my $down_tmp = -1; + while (( $up_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $up_tmp = $2; + } + } + while (( $down_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $down_tmp = $2; + } + } + close (SOCK); + $up = $up + $up_tmp; + $down = $down + $down_tmp; + } } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - my $up_tmp = -1; - my $down_tmp = -1; - while (( $up_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $up_tmp = $2; - } - } - while (( $down_tmp == -1 ) && ( $line = ) ) { - if ( $line =~ /$pattern/ ) { - $down_tmp = $2; - } - } - close (SOCK); - $up = $up + $up_tmp; - $down = $down + $down_tmp; - } + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + my $up_tmp = -1; + my $down_tmp = -1; + while (( $up_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $up_tmp = $2; + } + } + while (( $down_tmp == -1 ) && ( $line = ) ) { + if ( $line =~ /$pattern/ ) { + $down_tmp = $2; + } + } + close (SOCK); + $up = $up + $up_tmp; + $down = $down + $down_tmp; + } } diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index fe99af9b..3dac5aa8 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's torrent count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -25,7 +25,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use 'legacy' (pre 0.9.0) or current (0.9.0+) API calls +# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls # # Configuration example # @@ -40,41 +40,41 @@ # user username # env.port 5000,5001,5002,5003 # env.category Category -# env.api legacy +# env.api pre09 # #%# family=auto my @views = ( "default", "started", "stopped", "complete", "incomplete" ); if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; + exit 1; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { - my $category = $ENV{"category"} || ""; - print "graph_args --base 1000 -r --lower-limit 0\n"; - print "graph_title rTorrent volume\n"; - print "graph_vlabel active torrents\n"; - print "graph_category torrent ".${category}."\n"; - print "complete.label complete\n"; - print "complete.draw AREA\n"; - print "complete.info complete torrents\n"; - print "incomplete.label incomplete\n"; - print "incomplete.draw STACK\n"; - print "incomplete.info incomplete torrents\n"; - print "stopped.label stopped\n"; - print "stopped.draw LINE2\n"; - print "stopped.info stopped torrents\n"; - print "started.label started\n"; - print "started.draw LINE2\n"; - print "started.info started torrents\n"; - print "default.label total\n"; - print "default.draw LINE2\n"; - print "default.info all torrents\n"; - print "hashing.graph no\n"; - print "seeding.graph no\n"; - print "active.graph no\n"; - exit 0; + my $category = $ENV{"category"} || ""; + print "graph_args --base 1000 -r --lower-limit 0\n"; + print "graph_title rTorrent volume\n"; + print "graph_vlabel active torrents\n"; + print "graph_category torrent ".${category}."\n"; + print "complete.label complete\n"; + print "complete.draw AREA\n"; + print "complete.info complete torrents\n"; + print "incomplete.label incomplete\n"; + print "incomplete.draw STACK\n"; + print "incomplete.info incomplete torrents\n"; + print "stopped.label stopped\n"; + print "stopped.draw LINE2\n"; + print "stopped.info stopped torrents\n"; + print "started.label started\n"; + print "started.draw LINE2\n"; + print "started.info started torrents\n"; + print "default.label total\n"; + print "default.draw LINE2\n"; + print "default.info all torrents\n"; + print "hashing.graph no\n"; + print "seeding.graph no\n"; + print "active.graph no\n"; + exit 0; } use IO::Socket; @@ -83,54 +83,54 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "legacy"; +my $api = $ENV{"api"} || "pre09"; my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; foreach ( @views ) { - my $num = 0; - my $line = ""; - if ($api =~ /legacy/) { - $line = "d.multicall${_}d.get_hash="; - } else { - $line = "d.multicall2${_}d.hash="; - } - my $llen = length $line; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; + my $num = 0; + my $line = ""; + if ($api =~ /pre09/) { + $line = "d.multicall${_}d.get_hash="; + } else { + $line = "d.multicall2${_}d.hash="; + } + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; - if ( ( defined $src ) && ( $src eq "socket" ) ) { - for $socket (@sockets) - { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } - } else { - for $port (@ports) - { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; - flush SOCK; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $num++; - } - } - close (SOCK); - } + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; } - print "${_}.value ${num}\n"; + } + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + my $line = "${hlen}:${header},${line}"; + print SOCK $line; + flush SOCK; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $num++; + } + } + close (SOCK); + } + } + print "${_}.value ${num}\n"; } exit; From 51927e79f3b84d4d841d0e2a1a4e8306f492b851 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Tue, 10 Jul 2018 17:04:22 +0200 Subject: [PATCH 230/695] again formating --- plugins/rtorrent/rtom_allsessions_mem | 2 +- plugins/rtorrent/rtom_allsessions_peers | 2 +- plugins/rtorrent/rtom_allsessions_spdd | 2 +- plugins/rtorrent/rtom_allsessions_vol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 9011b9d3..be30bda8 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -27,7 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls # # Configuration example # diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 3e01b4a8..6157eb04 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -25,7 +25,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls # # Configuration example # diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index d608dd78..61827270 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -36,7 +36,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls # # # Configuration example diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 3dac5aa8..f474c3d6 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -25,7 +25,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use 'pre09' (pre 0.9.0) or 'current' (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or 'current' (0.9.0+) API calls # # Configuration example # From 348776e66bd385e3ec11f5851a3a68bcc2bbe27a Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Wed, 11 Jul 2018 17:21:38 +0200 Subject: [PATCH 231/695] fix category to be the same on all --- plugins/rtorrent/rtom_allsessions_vol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index f474c3d6..4977b509 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -55,7 +55,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -r --lower-limit 0\n"; print "graph_title rTorrent volume\n"; print "graph_vlabel active torrents\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "complete.label complete\n"; print "complete.draw AREA\n"; print "complete.info complete torrents\n"; From 7a37bfb1d85822d1a9fd7f4bf0c2533f85a37878 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 18:58:23 +0200 Subject: [PATCH 232/695] Plugins: unify shebang for ruby scripts --- plugins/icecast/icecast2_simple | 2 +- plugins/minecraft/minecraft-users | 2 +- plugins/mssql/microsoft-sql | 2 +- plugins/mssql/microsoft-sql-buffer-cache-hit-ratio | 2 +- plugins/mssql/microsoft-sql-data-file-sizes | 2 +- plugins/mssql/microsoft-sql-log-file-size | 2 +- plugins/oracle/oracle-pga-monitor | 2 +- plugins/oracle/oracle-sga | 2 +- plugins/other/port_ | 2 +- plugins/php/php_eaccelerator | 2 +- plugins/puppet/puppet_runtime | 2 +- plugins/router/ag241-adsl | 2 +- plugins/router/d-link-dir-655-router-statistics-plugin | 2 +- plugins/router/snmp__linksys_poe | 2 +- plugins/snmp/snmp_room_alert_ | 2 +- plugins/ssh/sshd_invalid_countries_ruby | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/icecast/icecast2_simple b/plugins/icecast/icecast2_simple index c69ee2a2..5ebf8acf 100755 --- a/plugins/icecast/icecast2_simple +++ b/plugins/icecast/icecast2_simple @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # # Plugin author: Gunnar Wolf # diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index ecfa641b..8a4c12ee 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby +#!/usr/bin/env ruby # Config: # [minecraft_users] # env.host awesomeserver.com diff --git a/plugins/mssql/microsoft-sql b/plugins/mssql/microsoft-sql index 457298b1..181396ba 100755 --- a/plugins/mssql/microsoft-sql +++ b/plugins/mssql/microsoft-sql @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for MSSQL - transaction monitoring # diff --git a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio index ef48f336..61ca3d7d 100755 --- a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +++ b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for MSSQL - Buffer cache hit ratio monitoring # diff --git a/plugins/mssql/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes index 1e068e5d..f7143658 100755 --- a/plugins/mssql/microsoft-sql-data-file-sizes +++ b/plugins/mssql/microsoft-sql-data-file-sizes @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for MSSQL - Data file size monitoring # diff --git a/plugins/mssql/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size index 45951c6a..3bbf0c0e 100755 --- a/plugins/mssql/microsoft-sql-log-file-size +++ b/plugins/mssql/microsoft-sql-log-file-size @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for MSSQL - log files monitoring # diff --git a/plugins/oracle/oracle-pga-monitor b/plugins/oracle/oracle-pga-monitor index a0eaa1a3..bc723a0f 100755 --- a/plugins/oracle/oracle-pga-monitor +++ b/plugins/oracle/oracle-pga-monitor @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for PGA memory components monitoring # diff --git a/plugins/oracle/oracle-sga b/plugins/oracle/oracle-sga index d167296d..dddbdc04 100755 --- a/plugins/oracle/oracle-sga +++ b/plugins/oracle/oracle-sga @@ -1,4 +1,4 @@ -#! /usr/bin/ruby +#!/usr/bin/env ruby # # Munin Plugin for SGA memory components monitoring # diff --git a/plugins/other/port_ b/plugins/other/port_ index bfd9d832..7249cbac 100755 --- a/plugins/other/port_ +++ b/plugins/other/port_ @@ -1,4 +1,4 @@ -#! /opt/csw/bin/ruby +#!/usr/bin/env ruby # # Wildcard-script to monitor network port usage using netstat. To monitor a # port, link port_ to this file. E.g. This plugin shall run by root user diff --git a/plugins/php/php_eaccelerator b/plugins/php/php_eaccelerator index d8a8a818..34edc549 100755 --- a/plugins/php/php_eaccelerator +++ b/plugins/php/php_eaccelerator @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # Monitor your EAccelerator usage. # Requires: ruby diff --git a/plugins/puppet/puppet_runtime b/plugins/puppet/puppet_runtime index 8962f4df..b2cdebeb 100755 --- a/plugins/puppet/puppet_runtime +++ b/plugins/puppet/puppet_runtime @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # This plugin reports the duration of the most recent puppet agent run. # It requires read access to the puppet logfile (defaults to /var/log/messages). diff --git a/plugins/router/ag241-adsl b/plugins/router/ag241-adsl index de0d11ee..9a3d7e63 100755 --- a/plugins/router/ag241-adsl +++ b/plugins/router/ag241-adsl @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # munin plugin to retrieve connection statistics from the web admin interface # on a Linksys AG241v2 ADSL modem # Makes use of the http://modemaddress/ADSLCStatus.htm page diff --git a/plugins/router/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin index 8c7923de..202e0538 100755 --- a/plugins/router/d-link-dir-655-router-statistics-plugin +++ b/plugins/router/d-link-dir-655-router-statistics-plugin @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # # Munin plugin for the D-link DIR-655 router # diff --git a/plugins/router/snmp__linksys_poe b/plugins/router/snmp__linksys_poe index c3fa6b88..886326db 100755 --- a/plugins/router/snmp__linksys_poe +++ b/plugins/router/snmp__linksys_poe @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby " =head1 NAME diff --git a/plugins/snmp/snmp_room_alert_ b/plugins/snmp/snmp_room_alert_ index 249c6c12..4114aaf9 100755 --- a/plugins/snmp/snmp_room_alert_ +++ b/plugins/snmp/snmp_room_alert_ @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # encoding: utf-8 # Plugin to monitor Room Alert 11E environmental units. diff --git a/plugins/ssh/sshd_invalid_countries_ruby b/plugins/ssh/sshd_invalid_countries_ruby index 9a6c11f4..45cc9148 100755 --- a/plugins/ssh/sshd_invalid_countries_ruby +++ b/plugins/ssh/sshd_invalid_countries_ruby @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby +#!/usr/bin/env ruby # Plugin to monitor the number of invalid access to sshd per country # # Require read permitions for SYSLOG From 353c0fc063af6c8f56445d6c8fcb2143a51d279c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Sp=C3=A4th?= Date: Mon, 27 Jun 2016 02:41:34 +0200 Subject: [PATCH 233/695] Added rough CPU usage reading Also translated strings to english --- plugins/minecraft/minecraft-users-ram_ | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 9db52074..d2cd2406 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -1,6 +1,7 @@ #!/bin/bash # I'm not the author, just wanted to add this plugin to github # Author & instructions: http://wiki.natenom.name/minecraft/munin-plugin +# CPU usage update and translation by Leandro Späth (https://github.com/LeoDJ) LC_LANG=C MC_PORT=${0##*_} @@ -9,9 +10,11 @@ if [ "$1" = "config" ] then printf 'graph_title Minecraft-Server (Port %s)\n' ${MC_PORT} printf 'graph_category games' - printf 'graph_vlabel Anzahl\n' - printf 'users.label Benutzer\n' - printf 'ramusage.label Verwendeter RAM in GiB\n' + printf 'graph_scale no\n' + printf 'graph_vlabel Players / RAM / CPU\n' + printf 'users.label Players\n' + printf 'ramusage.label RAM usage in GiB\n' + printf 'cpuusage.label CPU usage\n' exit 0 fi @@ -22,9 +25,13 @@ then #is running MEMORYRSS=$(ps -p ${MC_PID} -o rss | cut -d' ' -f2) MEMGiB=$(echo "scale=2;${MEMORYRSS}/1024/1024" | bc -l) + CPUPERCENT=$(ps -p ${MC_PID} -o %cpu | sed -n 2p) + CPU=$(echo "scale=2;${CPUPERCENT}/100" | bc -l) else MEMGiB=0 + CPU=0 fi printf "users.value %i\n" "${PLAYERCOUNT}" printf "ramusage.value %3.2f\n" "${MEMGiB}" +printf "cpuusage.value %3.2f\n" "${CPU}" From 99a5c384b050fb36606c55348a963d0f6c2c6f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Sp=C3=A4th?= Date: Wed, 29 Jun 2016 17:38:42 +0200 Subject: [PATCH 234/695] fixed CPU percent calculation --- plugins/minecraft/minecraft-users-ram_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index d2cd2406..84b94e29 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -25,7 +25,7 @@ then #is running MEMORYRSS=$(ps -p ${MC_PID} -o rss | cut -d' ' -f2) MEMGiB=$(echo "scale=2;${MEMORYRSS}/1024/1024" | bc -l) - CPUPERCENT=$(ps -p ${MC_PID} -o %cpu | sed -n 2p) + CPUPERCENT=$(top -bp ${MC_PID} -n 1 | sed -n '$p' | tr -s ' ' | cut -d ' ' -f10) CPU=$(echo "scale=2;${CPUPERCENT}/100" | bc -l) else MEMGiB=0 From fed6ff107911041db68f28fb1cf516d9db0a2168 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:14:18 +0200 Subject: [PATCH 235/695] Plugin minecraft-users-ram_: add documentation header --- plugins/minecraft/minecraft-users-ram_ | 35 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 84b94e29..2330df3d 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -1,7 +1,36 @@ #!/bin/bash -# I'm not the author, just wanted to add this plugin to github -# Author & instructions: http://wiki.natenom.name/minecraft/munin-plugin -# CPU usage update and translation by Leandro Späth (https://github.com/LeoDJ) + +: <<=cut + +=head1 NAME + +minecraft-users-ram_ - monitor ressource usage of a local minecraft server + +=head1 APPLICABLE SYSTEMS + +Every system with a running minecraft server. + + +=head1 USAGE + +Symlink this plugin to /etc/munin/plugins/ by adding the TCP port number used +by the local mincecraft server. Afterwards restart the munin-node. + +Some more instructions: http://wiki.natenom.name/minecraft/munin-plugin + + +=head1 AUTHOR + + 2011 Natenom + 2017 Leandro Späth + + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities= + +=cut LC_LANG=C MC_PORT=${0##*_} From 51e91e93549c99d19631944c926106d28726988f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:18:38 +0200 Subject: [PATCH 236/695] Plugin minecraft-users-ram_: more quoting, simpler output --- plugins/minecraft/minecraft-users-ram_ | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 2330df3d..4b2b23b2 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -37,24 +37,23 @@ MC_PORT=${0##*_} if [ "$1" = "config" ] then - printf 'graph_title Minecraft-Server (Port %s)\n' ${MC_PORT} - printf 'graph_category games' - printf 'graph_scale no\n' - printf 'graph_vlabel Players / RAM / CPU\n' - printf 'users.label Players\n' - printf 'ramusage.label RAM usage in GiB\n' - printf 'cpuusage.label CPU usage\n' + echo "graph_title Minecraft-Server (Port $MC_PORT)" + echo 'graph_category games' + echo 'graph_scale no' + echo 'graph_vlabel Players / RAM / CPU' + echo 'users.label Players' + echo 'ramusage.label RAM usage in GiB' + echo 'cpuusage.label CPU usage' exit 0 fi -PLAYERCOUNT=$(netstat -tn | grep -i ${MC_PORT} | grep ESTABLISHED | wc -l) -MC_PID=$(netstat -tlpn | grep ${MC_PORT} | sed -n -e '1p' | awk '{ print $7 }' | cut -d'/' -f1) +PLAYERCOUNT=$(netstat -tn | grep -i "$MC_PORT" | grep ESTABLISHED | wc -l) +MC_PID=$(netstat -tlpn | grep "$MC_PORT" | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) if [ ! -z "${MC_PID}" ] then #is running - MEMORYRSS=$(ps -p ${MC_PID} -o rss | cut -d' -' -f2) + MEMORYRSS=$(ps -p "$MC_PID" -o rss | cut -d '\n' -f2) MEMGiB=$(echo "scale=2;${MEMORYRSS}/1024/1024" | bc -l) - CPUPERCENT=$(top -bp ${MC_PID} -n 1 | sed -n '$p' | tr -s ' ' | cut -d ' ' -f10) + CPUPERCENT=$(top -bp "$MC_PID" -n 1 | sed -n '$p' | tr -s ' ' | cut -d ' ' -f10) CPU=$(echo "scale=2;${CPUPERCENT}/100" | bc -l) else MEMGiB=0 From 94f46d392ba972fe720e5b83be02ef9e2d630d8a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:25:57 +0200 Subject: [PATCH 237/695] Plugin minecraft-users-ram_: simplify calculation; do not reduce accuracy --- plugins/minecraft/minecraft-users-ram_ | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 4b2b23b2..e3314151 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -51,15 +51,13 @@ PLAYERCOUNT=$(netstat -tn | grep -i "$MC_PORT" | grep ESTABLISHED | wc -l) MC_PID=$(netstat -tlpn | grep "$MC_PORT" | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) if [ ! -z "${MC_PID}" ] then #is running - MEMORYRSS=$(ps -p "$MC_PID" -o rss | cut -d '\n' -f2) - MEMGiB=$(echo "scale=2;${MEMORYRSS}/1024/1024" | bc -l) - CPUPERCENT=$(top -bp "$MC_PID" -n 1 | sed -n '$p' | tr -s ' ' | cut -d ' ' -f10) - CPU=$(echo "scale=2;${CPUPERCENT}/100" | bc -l) + MEMGiB=$(ps -p "$MC_PID" -o rss | sed -n '2p' | awk '{ print $1 / 1024 / 1024 }') + CPU=$(top -bp "$MC_PID" -n 1 | sed -n '$p' | awk '{ print $10 / 100; }') else MEMGiB=0 CPU=0 fi -printf "users.value %i\n" "${PLAYERCOUNT}" -printf "ramusage.value %3.2f\n" "${MEMGiB}" -printf "cpuusage.value %3.2f\n" "${CPU}" +echo "users.value $PLAYERCOUNT" +echo "ramusage.value $MEMGiB" +echo "cpuusage.value $CPU" From 00149864b2885b71524ca8a2de06fa28f59bf32a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:29:00 +0200 Subject: [PATCH 238/695] Plugin minecraft-users-ram_: more precise netstat output filtering --- plugins/minecraft/minecraft-users-ram_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index e3314151..7e1d18d6 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -47,8 +47,8 @@ then exit 0 fi -PLAYERCOUNT=$(netstat -tn | grep -i "$MC_PORT" | grep ESTABLISHED | wc -l) -MC_PID=$(netstat -tlpn | grep "$MC_PORT" | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) +PLAYERCOUNT=$(netstat -tn | grep ":$MC_PORT " | grep ESTABLISHED | wc -l) +MC_PID=$(netstat -tlpn | grep ":$MC_PORT " | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) if [ ! -z "${MC_PID}" ] then #is running MEMGiB=$(ps -p "$MC_PID" -o rss | sed -n '2p' | awk '{ print $1 / 1024 / 1024 }') From f6a8f2beb1f39e3c4165429dec42175b178433b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:29:49 +0200 Subject: [PATCH 239/695] Plugin minecraft-users-ram_: reduce scope of LC_LANG override --- plugins/minecraft/minecraft-users-ram_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 7e1d18d6..49c4e59c 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -32,7 +32,7 @@ Some more instructions: http://wiki.natenom.name/minecraft/munin-plugin =cut -LC_LANG=C + MC_PORT=${0##*_} if [ "$1" = "config" ] @@ -47,7 +47,7 @@ then exit 0 fi -PLAYERCOUNT=$(netstat -tn | grep ":$MC_PORT " | grep ESTABLISHED | wc -l) +PLAYERCOUNT=$(LC_LANG=C netstat -tn | grep ":$MC_PORT " | grep ESTABLISHED | wc -l) MC_PID=$(netstat -tlpn | grep ":$MC_PORT " | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) if [ ! -z "${MC_PID}" ] then #is running From 651f6ac0566bce99b01917984a8d07691374a584 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 19:30:24 +0200 Subject: [PATCH 240/695] Plugin minecraft-users-ram_: return "U" if no value can be retrieved --- plugins/minecraft/minecraft-users-ram_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 49c4e59c..5e97c060 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -54,8 +54,8 @@ then #is running MEMGiB=$(ps -p "$MC_PID" -o rss | sed -n '2p' | awk '{ print $1 / 1024 / 1024 }') CPU=$(top -bp "$MC_PID" -n 1 | sed -n '$p' | awk '{ print $10 / 100; }') else - MEMGiB=0 - CPU=0 + MEMGiB="U" + CPU="U" fi echo "users.value $PLAYERCOUNT" From a1cc26f2d3c0baeb52d13e14823951f14ca63afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Manuel=20Febrer=20Cort=C3=A9s?= Date: Fri, 31 Mar 2017 14:19:14 +0200 Subject: [PATCH 241/695] Update multibandwidth bing have some random spikes. Added a variable that is used to indicate the maximum value of mbps that can be recorded (in bps). --- plugins/network/multibandwidth | 86 +++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index a7edf4c8..fdb1b994 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -1,37 +1,38 @@ -#!/bin/sh - -. "$MUNIN_LIBDIR/plugins/plugin.sh" - -: <<=cut - -=head1 NAME - -multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. - -=head1 APPLICABLE SYSTEMS - -All systems with “bash”, and “munin” - -=head1 REQUIREMENTS - -bing installed. - -You can install bing by using (Ubuntu/Debian): apt-get install bing - -=head1 CONFIGURATION - -The following is the default configuration - -[multibandwidth] -user root -env.hosts example.org example2.org example3.org -env.samples 10 -env.small_packet_size 44 -env.big_packet_size 108 - -- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. - -- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. +#!/bin/sh + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +: <<=cut + +=head1 NAME + +multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. + +=head1 APPLICABLE SYSTEMS + +All systems with “bash”, and “munin” + +=head1 REQUIREMENTS + +bing installed. + +You can install bing by using (Ubuntu/Debian): apt-get install bing + +=head1 CONFIGURATION + +The following is the default configuration + +[multibandwidth] +user root +env.hosts example.org example2.org example3.org +env.samples 15 +env.small_packet_size 44 +env.big_packet_size 108 +env.max_mbps 15728640 + +- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. + +- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. - env.small_packet_size explanation: Specifies the number of data bytes to be sent in the small packets. The default and minimum value is 44. @@ -40,6 +41,8 @@ env.big_packet_size 108 packets. The default is 108. The size should be chosen so that big packet roundtrip times are long enough to be accurately measured. +- env.max_mbps explanation: bing have some random spikes. This variable is used to indicate + the maximum value of mbps that can be recorded (in bps). =head1 MAGIC MARKERS @@ -101,11 +104,20 @@ for host in $hosts; do | cut -d "b" -f1) if (echo "$SPEED" | grep -q "M"); then - echo "$SPEED" | awk '{a+=$1} END{print a*1000000}' + VALUE=`echo "$SPEED" | sed 's/.$//'` + RATE=`echo "$VALUE * 1048576" | bc -l` + + if [ $(echo "$RATE" >= "$max_mbps" | bc >/dev/null && echo "no" || echo "yes") = "yes" ]; then + echo "$max_mbps" + else + echo "$RATE" + fi elif (echo "$SPEED" | grep -q "K"); then - echo "$SPEED" | awk '{a+=$1} END{print a*1000}' + VALUE=`echo "$SPEED" | sed 's/.$//'` + echo "$VALUE * 1024" | bc -l elif (echo "$SPEED" | grep -q "G"); then - echo "$SPEED" | awk '{a+=$1} END{print a*1000000000}' + VALUE=`echo "$SPEED" | sed 's/.$//'` + echo "$VALUE * 1073742000" | bc -l else echo "Error: no data (timeout)" >&2 fi From 1ae4ac09dae4e2b8fcbec46bca0b867be82875df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Manuel=20Febrer=20Cort=C3=A9s?= Date: Tue, 4 Apr 2017 15:48:24 +0200 Subject: [PATCH 242/695] Update multibandwidth I will update more changes in the next days --- plugins/network/multibandwidth | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index fdb1b994..d0dfe8a2 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -1,38 +1,38 @@ -#!/bin/sh - -. "$MUNIN_LIBDIR/plugins/plugin.sh" - -: <<=cut - -=head1 NAME - -multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. - -=head1 APPLICABLE SYSTEMS - -All systems with “bash”, and “munin” - -=head1 REQUIREMENTS - -bing installed. - -You can install bing by using (Ubuntu/Debian): apt-get install bing - -=head1 CONFIGURATION - -The following is the default configuration - -[multibandwidth] -user root -env.hosts example.org example2.org example3.org -env.samples 15 -env.small_packet_size 44 -env.big_packet_size 108 -env.max_mbps 15728640 - -- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. - -- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. +#!/bin/sh + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +: <<=cut + +=head1 NAME + +multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. + +=head1 APPLICABLE SYSTEMS + +All systems with “bash”, and “munin” + +=head1 REQUIREMENTS + +bing installed. + +You can install bing by using (Ubuntu/Debian): apt-get install bing + +=head1 CONFIGURATION + +The following is the default configuration + +[multibandwidth] +user root +env.hosts example.org example2.org example3.org +env.samples 15 +env.small_packet_size 44 +env.big_packet_size 108 +env.max_mbps 15728640 + +- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. + +- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. - env.small_packet_size explanation: Specifies the number of data bytes to be sent in the small packets. The default and minimum value is 44. @@ -107,7 +107,7 @@ for host in $hosts; do VALUE=`echo "$SPEED" | sed 's/.$//'` RATE=`echo "$VALUE * 1048576" | bc -l` - if [ $(echo "$RATE" >= "$max_mbps" | bc >/dev/null && echo "no" || echo "yes") = "yes" ]; then + if [ `echo "$RATE > $max_mbps" | bc` -eq "1" ]; then echo "$max_mbps" else echo "$RATE" From 49aecf4e0e40eb9820d75a4b2f6f7100bd171c12 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:25:57 +0200 Subject: [PATCH 243/695] Plugin multibandwidth: simplify speed calculation --- plugins/network/multibandwidth | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index d0dfe8a2..280c1406 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -95,7 +95,6 @@ esac #Calculating the bandwidth for host in $hosts; do fieldname="host_$(clean_fieldname "$host")" - printf "$fieldname.value "; SPEED=$(timeout 6 bing localhost "$host" -n -c 1 -e "$samples" -s "$small_packet_size" -S "$big_packet_size" 2>/dev/null \ |grep "estimated link" -A 2 \ @@ -103,22 +102,17 @@ for host in $hosts; do | awk '{print $2}' \ | cut -d "b" -f1) - if (echo "$SPEED" | grep -q "M"); then - VALUE=`echo "$SPEED" | sed 's/.$//'` - RATE=`echo "$VALUE * 1048576" | bc -l` - - if [ `echo "$RATE > $max_mbps" | bc` -eq "1" ]; then - echo "$max_mbps" - else - echo "$RATE" - fi - elif (echo "$SPEED" | grep -q "K"); then - VALUE=`echo "$SPEED" | sed 's/.$//'` - echo "$VALUE * 1024" | bc -l - elif (echo "$SPEED" | grep -q "G"); then - VALUE=`echo "$SPEED" | sed 's/.$//'` - echo "$VALUE * 1073742000" | bc -l + if echo "$SPEED" | grep -q "M"; then + RATE=$(echo "$SPEED" | awk '{ print int($1 * 1024 * 1024); }') + elif echo "$SPEED" | grep -q "K"; then + RATE=$(echo "$SPEED" | awk '{ print int($1 * 1024); }') + elif echo "$SPEED" | grep -q "G"; then + RATE=$(echo "$SPEED" | awk '{ print int($1 * 1024 * 1024 * 1024); }') else echo "Error: no data (timeout)" >&2 fi + if [ "$RATE" -gt "$max_mbps" ]; then + RATE="$max_mbps" + fi + echo "${fieldname}.value $RATE" done From ade6f5f228193bf0e75d4d54fcac0eab1b4d4da5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:26:20 +0200 Subject: [PATCH 244/695] Plugin multibandwidth: proper handling of invalid values --- plugins/network/multibandwidth | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index 280c1406..4cbfefe0 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -109,6 +109,7 @@ for host in $hosts; do elif echo "$SPEED" | grep -q "G"; then RATE=$(echo "$SPEED" | awk '{ print int($1 * 1024 * 1024 * 1024); }') else + RATE="U" echo "Error: no data (timeout)" >&2 fi if [ "$RATE" -gt "$max_mbps" ]; then From 580a39ed41377e6e704a377807c53b0de929d45e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:29:31 +0200 Subject: [PATCH 245/695] Plugin multibandwidth: rename environment setting for limiting the valid range --- plugins/network/multibandwidth | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index 4cbfefe0..810a5725 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -28,7 +28,7 @@ env.hosts example.org example2.org example3.org env.samples 15 env.small_packet_size 44 env.big_packet_size 108 -env.max_mbps 15728640 +env.max_valid_bps 15728640 - env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. @@ -41,7 +41,7 @@ env.max_mbps 15728640 packets. The default is 108. The size should be chosen so that big packet roundtrip times are long enough to be accurately measured. -- env.max_mbps explanation: bing have some random spikes. This variable is used to indicate +- env.max_valid_bps explanation: bing have some random spikes. This variable is used to indicate the maximum value of mbps that can be recorded (in bps). =head1 MAGIC MARKERS @@ -112,8 +112,9 @@ for host in $hosts; do RATE="U" echo "Error: no data (timeout)" >&2 fi - if [ "$RATE" -gt "$max_mbps" ]; then - RATE="$max_mbps" + if [ -n "$max_valid_bps" ] && [ "$RATE" -gt "$max_valid_bps" ]; then + # the value is outside of the allowed range; discard it + RATE="U" fi echo "${fieldname}.value $RATE" done From 5fb2e02eab9b490148491fb1f8d49acb3308231c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:34:38 +0200 Subject: [PATCH 246/695] Plugin multibandwidth: explicit handling of configuration variables --- plugins/network/multibandwidth | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index 810a5725..699c2248 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -20,7 +20,8 @@ You can install bing by using (Ubuntu/Debian): apt-get install bing =head1 CONFIGURATION -The following is the default configuration +The following example configuration shows all settings. Only "hosts" is required for +minimal configuration. [multibandwidth] user root @@ -30,19 +31,22 @@ env.small_packet_size 44 env.big_packet_size 108 env.max_valid_bps 15728640 -- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. +- env.hosts: space separated list of hostnames or IPs of the hosts to calculate the bandwidth. + This setting is required. -- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. +- env.samples: Reset stats after sending this number of ECHO_REQUEST packets. + Defaults to 15 samples. -- env.small_packet_size explanation: Specifies the number of data bytes to be sent in the small +- env.small_packet_size: Specifies the number of data bytes to be sent in the small packets. The default and minimum value is 44. -- env.big_packet_size explanation: Specifies the number of data bytes to be sent in the big +- env.big_packet_size: Specifies the number of data bytes to be sent in the big packets. The default is 108. The size should be chosen so that big packet roundtrip times are long enough to be accurately measured. -- env.max_valid_bps explanation: bing have some random spikes. This variable is used to indicate +- env.max_valid_bps: bing have some random spikes. This variable is used to indicate the maximum value of mbps that can be recorded (in bps). + Defaults to the empty string (no validity check). =head1 MAGIC MARKERS @@ -64,6 +68,12 @@ GPLv2 =cut +hosts=${hosts:-} +samples=${samples:-15} +small_packet_size=${small_packet_size:-44} +big_packet_size=${big_packet_size:-108} +max_valid_bps=${max_valid_bps:-15728640} + case $1 in config) From ecc6e30d55178a134d813c7271976dc2ea1e4498 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:36:49 +0200 Subject: [PATCH 247/695] Plugin multibandwidth: unify whitespace --- plugins/network/multibandwidth | 46 +++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index 699c2248..bfd46ca2 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -77,37 +77,37 @@ max_valid_bps=${max_valid_bps:-15728640} case $1 in config) - echo graph_title MultiBandwidth - echo 'graph_vlabel bps' - echo 'graph_args --base 1024 -l 0' - echo 'graph_scale yes' - echo 'graph_category network' - echo 'graph_info This graph shows the bandwidth between localhost and serveral hosts' - for host in $hosts; do - fieldname="host_$(clean_fieldname "$host")" - echo "$fieldname.label $host" - echo "$fieldname.draw LINE2" - echo "$fieldname.info Bandwidth statistics for $host" - done - exit 0;; + echo graph_title MultiBandwidth + echo 'graph_vlabel bps' + echo 'graph_args --base 1024 -l 0' + echo 'graph_scale yes' + echo 'graph_category network' + echo 'graph_info This graph shows the bandwidth between localhost and serveral hosts' + for host in $hosts; do + fieldname="host_$(clean_fieldname "$host")" + echo "$fieldname.label $host" + echo "$fieldname.draw LINE2" + echo "$fieldname.info Bandwidth statistics for $host" + done + exit 0 + ;; autoconf) - if command -v bing >/dev/null 2>&1; then - echo 'yes' - exit 0; - else - echo 'no (bing not installed)' - exit 0; - fi - + if command -v bing 2>/dev/null; then + echo 'yes' + else + echo 'no (bing not installed)' + fi + exit 0 + ;; esac -#Calculating the bandwidth +# Calculating the bandwidth for host in $hosts; do fieldname="host_$(clean_fieldname "$host")" SPEED=$(timeout 6 bing localhost "$host" -n -c 1 -e "$samples" -s "$small_packet_size" -S "$big_packet_size" 2>/dev/null \ - |grep "estimated link" -A 2 \ + | grep "estimated link" -A 2 \ | grep bps \ | awk '{print $2}' \ | cut -d "b" -f1) From 14436e808ec6fb8528a5d05ad5d20d09ff4e64f2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 11 Jul 2018 20:38:38 +0200 Subject: [PATCH 248/695] Plugin multibandwidth: fix 'applicable systems' documentation hint --- plugins/network/multibandwidth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index bfd46ca2..b7ebfdc6 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -10,7 +10,7 @@ multibandwidth - Plugin to monitor the bandwidth between localhost and serveral =head1 APPLICABLE SYSTEMS -All systems with “bash”, and “munin” +All systems with "bing" installed. =head1 REQUIREMENTS From 143a9543c43ad30fdf3d11cf2c60797801d2edf9 Mon Sep 17 00:00:00 2001 From: Sledge Sulaweyo Date: Sun, 15 Jul 2018 15:14:39 +0200 Subject: [PATCH 249/695] switch default API to current --- plugins/rtorrent/rtom_allsessions_mem | 2 +- plugins/rtorrent/rtom_allsessions_peers | 2 +- plugins/rtorrent/rtom_allsessions_spdd | 2 +- plugins/rtorrent/rtom_allsessions_vol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index be30bda8..a29fa2ef 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -68,7 +68,7 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "pre09"; +my $api = $ENV{"api"} || "current"; my $mem = 0; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 6157eb04..7b6187c7 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -78,7 +78,7 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "pre09"; +my $api = $ENV{"api"} || "current"; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index 61827270..5a366105 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -92,7 +92,7 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "pre09"; +my $api = $ENV{"api"} || "current"; my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $line = ""; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 4977b509..a5db49d2 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -83,7 +83,7 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "pre09"; +my $api = $ENV{"api"} || "current"; my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; From b35840c5d0ec17740e5f42db5dc47f36fff192b9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 15 Jul 2018 17:23:44 +0200 Subject: [PATCH 250/695] Plugin rtom_allsessions_*: mention the default API version --- plugins/rtorrent/rtom_allsessions_mem | 2 +- plugins/rtorrent/rtom_allsessions_peers | 2 +- plugins/rtorrent/rtom_allsessions_spdd | 2 +- plugins/rtorrent/rtom_allsessions_vol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index a29fa2ef..565eea90 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -27,7 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 7b6187c7..f452677c 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -25,7 +25,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index 5a366105..a9ce779b 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -36,7 +36,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # # Configuration example diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index a5db49d2..97818291 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -25,7 +25,7 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or 'current' (0.9.0+) API calls +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # From 37b533a5c3a51db7716d06cf87bd35af11d909d9 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Wed, 18 Jul 2018 13:22:27 -0500 Subject: [PATCH 251/695] Fix Odd number of elements in hash at linux_diskstat_ line 321 Reading from /sys/block/*/stat does not provide the disk major and minor numbers (versus reading from /proc/diskstats). When saving the disk data back to a temp state file, these empty values for the disk major and minor numbers get turned into undef values on state restore. On the line: `my ( $prev_time, %prev_diskstat ) = restore_state();` `%prev_diskstat` will 'slurp' up all returned values from the function call (converting the list into key value pairs) but if one of last values is from the major or minor number (which will be undef), the key (major or minor) will be sent but the undef value will not be sent. Hence this results in an attempted hash assignment where one of the key values pairs is missing a value and thus the 'Odd number of elements in hash assignment ' warning is thrown. --- plugins/disk/linux_diskstat_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/linux_diskstat_ b/plugins/disk/linux_diskstat_ index de522d38..6628307b 100755 --- a/plugins/disk/linux_diskstat_ +++ b/plugins/disk/linux_diskstat_ @@ -498,7 +498,7 @@ sub read_sysfs { $cur_device =~ tr#!#/#; # Faking missing diskstats values - unshift @elems, ( '', '', $cur_device ); + unshift @elems, ( -1, -1, $cur_device ); push @lines, \@elems; From a26b9e8db6b07ed7749b67dd3c5dcd006db6c10e Mon Sep 17 00:00:00 2001 From: Neraud Date: Tue, 17 Jul 2018 15:17:35 +0200 Subject: [PATCH 252/695] Added git_commit_behind --- plugins/git/git_commit_behind | 260 ++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100755 plugins/git/git_commit_behind diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind new file mode 100755 index 00000000..5732b575 --- /dev/null +++ b/plugins/git/git_commit_behind @@ -0,0 +1,260 @@ +#! /usr/bin/env python3 + +"""=cut +=head1 NAME + +git_commit_behind - Munin plugin to monitor local git repositories and report +how many commits behind their remote they are + +=head1 NOTES + +This plugin is similar to how apt_all works for apt packages. + +To be able to check how behind a git repository is, we need to run git fetch. +To avoid fetching all repos every 5 minutes and slowing down the munin-node, +the fetch operation is triggered via a cron job. + +=head1 REQUIREMENTS + + - Python3 + - Git + +=head1 INSTALLATION + +Link this plugin, as usual. +For example : + ln -s /path/to/git_commit_behind /etc/munin/plugins/git_commit_behind + +You also need to setup a cron job to trigger the git fetches. + +The plugin can be called with an "update" mode to handle the fetches : +munin-run git_commit_behind update +It will run the fetches randomly (1 in chances), +and ensure that it is run at least every seconds. + +For example, you can use the following cron : + +# If the git_commit_behind plugin is enabled, fetch git repositories approx. +# once an hour (12 invocations an hour, 1 in 12 chance that the update will +# happen), but ensure that there will never be more than two hours +# (7200 seconds) interval between updates. +*/5 * * * * root if [ -x /etc/munin/plugins/git_commit_behind ]; then /usr/sbin/munin-run git_commit_behind update 7200 12 >/dev/null; fi + +=head1 CONFIGURATION + +Use your "/etc/munin/plugin-conf.d/munin-node" to configure this plugin. + [git_commit_behind] + user root + env.git_path /path/to/git + +Then, for each repository you want to check, you need the following +configuration block under the git_commit_behind section + env.repo.[repoCode].path /path/to/local/repo + env.repo.[repoCode].name Repo Name + env.repo.[repoCode].user user + env.repo.[repoCode].warning 10 + env.repo.[repoCode].critical 100 + +[repoCode] can only contain letters, numbers and underscores. + +path : mandatory, the local path to your git repository +name : optional (default : [repoCode]), a cleaner name that will be displayed +user : optional (default : empty), the owner of the repository + if set and different from the user running the plugin, the git commands + will be executed as this user +warning : optional (default 10), the warning threshold +critical : optional (default 100), the critical threshold + +For example : + + [git_commit_behind] + user root + + env.repo.munin_contrib.path /opt/munin-contrib + env.repo.munin_contrib.name Munin Contrib + + env.repo.other_repo.path /path/to/other-repo + env.repo.other_repo.name Other Repo + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +1.0.0 + +=head1 AUTHOR + +Neraud (https://github.com/Neraud) + +=head1 LICENSE + +GPLv2 + +=cut""" + + +from __future__ import print_function + +import logging +import os +from random import randint +import re +from subprocess import check_output, call, DEVNULL, CalledProcessError +import sys +import time + + +plugin_version = "1.0.0" + +debug = int(os.getenv('MUNIN_DEBUG', os.getenv('DEBUG', 0))) > 0 +if debug: + logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s %(levelname)-7s %(message)s') + +conf = { + 'git_path': os.getenv('git_path', '/usr/bin/git'), + 'state_file': os.getenv('MUNIN_STATEFILE', + '/var/lib/munin-node/plugin-state/nobody/' + + 'git_commit_behind.state') +} + +repo_codes = set([re.search('repo\.([^.]+)\..*', elem).group(1) + for elem in os.environ.keys() if 'repo.' in elem]) + +repos_conf = {} +for code in repo_codes: + repos_conf[code] = { + 'name': os.getenv('repo.%s.name' % code, code), + 'path': os.getenv('repo.%s.path' % code, None), + 'user': os.getenv('repo.%s.user' % code, None), + 'warning': os.getenv('repo.%s.warning' % code, '10'), + 'critical': os.getenv('repo.%s.critical' % code, '100') + } + + +def print_config(): + print('graph_title Git repositories - Commits behind') + + print('graph_args --base 1000 -r --lower-limit 0') + print('graph_vlabel number of commits behind') + print('graph_scale yes') + print('graph_info This graph shows the number of commits behind' + + ' for each configured git repository') + print('graph_category system') + + print('graph_order %s' % ' '.join(repo_codes)) + + for repo_code in repos_conf.keys(): + print('%s.label %s' % (repo_code, repos_conf[repo_code]['name'])) + print('%s.warning %s' % (repo_code, repos_conf[repo_code]['warning'])) + print('%s.critical %s' % + (repo_code, repos_conf[repo_code]['critical'])) + + +def generate_git_command(repo_conf, git_command): + if not repo_conf['user'] or repo_conf['user'] == os.environ['USER']: + cmd = [conf['git_path']] + git_command + else: + shell_cmd = 'cd %s ; %s %s' % ( + repo_conf['path'], conf['git_path'], ' '.join(git_command)) + cmd = ['su', '-', repo_conf['user'], '-c', shell_cmd] + return cmd + + +def execute_git_command(repo_conf, git_command): + cmd = generate_git_command(repo_conf, git_command) + return check_output(cmd, cwd=repo_conf['path']).decode('utf-8').rstrip() + + +def get_info(): + if not os.access(conf['git_path'], os.X_OK): + print('Git (%s) is missing, or not executable !' % + conf['git_path'], file=sys.stderr) + sys.exit(1) + + for repo_code in repos_conf.keys(): + logging.debug(' - %s' % repo_code) + try: + remote_branch = execute_git_command( + repos_conf[repo_code], + ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']) + logging.debug('remote_branch = %s' % remote_branch) + + commits_behind = execute_git_command( + repos_conf[repo_code], + ['rev-list', 'HEAD..%s' % remote_branch, '--count']) + + print('%s.value %d' % (repo_code, int(commits_behind))) + except CalledProcessError as e: + logging.error('Error executing git command : %s' % str(e)) + except FileNotFoundError as e: + logging.error('Repo not found at path %s' % + repos_conf[repo_code]['path']) + + +def check_update_repos(): + if len(sys.argv) > 2: + max_interval = int(sys.argv[2]) + else: + max_interval = 7200 + + if len(sys.argv) > 3: + probability = int(sys.argv[3]) + else: + probability = 12 + + if not os.path.isfile(conf['state_file']): + logging.debug('No state file -> updating') + do_update_repos() + elif os.path.getmtime(conf['state_file']) + max_interval < time.time(): + logging.debug('State file last modified too long ago -> updating') + do_update_repos() + elif randint(1, probability) == 1: + logging.debug('Recent state, but random matched -> updating') + do_update_repos() + else: + logging.debug('Recent state and random missed -> skipping') + + +def do_update_repos(): + for repo_code in repos_conf.keys(): + try: + logging.info('Fetching repo %s' % repo_code) + execute_git_command(repos_conf[repo_code], ['fetch']) + except CalledProcessError as e: + logging.error('Error executing git command : %s' % str(e)) + except FileNotFoundError as e: + logging.error('Repo not found at path %s' % + repos_conf[repo_code]['path']) + logging.debug('Updating the state file') + open(conf['state_file'], 'w') + + +if len(sys.argv) > 1: + action = sys.argv[1] + if action == 'config': + print_config() + elif action == 'autoconf': + if os.access(conf['git_path'], os.X_OK): + test_git = call([conf['git_path'], '--version'], stdout=DEVNULL) + if test_git == 0: + print('yes') + else: + print('no (git seems to be broken ?!)') + else: + print('no (git is missing or not executable)') + elif action == 'version': + print('Git commit behind Munin plugin, version {0}'.format( + plugin_version)) + elif action == 'update': + check_update_repos() + elif action: + logging.warn('Unknown argument \'%s\'' % action) + sys.exit(1) + else: + get_info() +else: + get_info() From 15d05f30c1b150721abb05965bd8c5fbf558f808 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 23 Jul 2018 02:35:54 +0200 Subject: [PATCH 253/695] Revert "nginx_byprojects: use more compatible perl expression" This reverts commit a836f07d56e35d230cdd9586bd35aae6343e9771. Closes: #922 --- plugins/nginx/nginx_byprojects/byprojects_access | 2 +- plugins/nginx/nginx_byprojects/byprojects_bandwidth | 2 +- plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/nginx/nginx_byprojects/byprojects_access b/plugins/nginx/nginx_byprojects/byprojects_access index 22572bf3..bc400504 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_access +++ b/plugins/nginx/nginx_byprojects/byprojects_access @@ -38,7 +38,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = $ENV{@loglist}; +my %envLogs = %ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } diff --git a/plugins/nginx/nginx_byprojects/byprojects_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_bandwidth index 42a036ec..af245e96 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_bandwidth @@ -44,7 +44,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = $ENV{@loglist}; +my %envLogs = %ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } diff --git a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth index 0feb15fd..21b36902 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth @@ -44,7 +44,7 @@ my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; my @loglist = grep {$_ =~ /site\./} keys(%ENV); -my %envLogs = $ENV{@loglist}; +my %envLogs = %ENV{@loglist}; my %logs; while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } From 4ac9c7ba2ea7630e1114085feb339d5499fec84f Mon Sep 17 00:00:00 2001 From: Xavier Serrano Date: Wed, 25 Jul 2018 10:49:58 +0200 Subject: [PATCH 254/695] Use JSON format to obtain data from 'ceph -s' 'ceph -s' text output format strongly depends upon ceph version. Using JSON format to obtain data works across different ceph versions. --- plugins/ceph/ceph_osd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/ceph/ceph_osd b/plugins/ceph/ceph_osd index 9d208b53..f8feaf62 100755 --- a/plugins/ceph/ceph_osd +++ b/plugins/ceph/ceph_osd @@ -44,6 +44,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo "osds.value $(ceph -s | grep osdmap | awk '{ print $3 }')" -echo "up.value $(ceph -s | grep osdmap | awk '{ print $5 }')" -echo "in.value $(ceph -s | grep osdmap | awk '{ print $7 }')" +echo "osds.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_osds')" +echo "up.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_up_osds')" +echo "in.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_in_osds')" From 273f69f553d82542dc1eaa347632f7a557234450 Mon Sep 17 00:00:00 2001 From: Xavier Serrano Date: Wed, 25 Jul 2018 11:32:41 +0200 Subject: [PATCH 255/695] Execute 'ceph -s' only once to get data --- plugins/ceph/ceph_osd | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/ceph/ceph_osd b/plugins/ceph/ceph_osd index f8feaf62..c8f782d2 100755 --- a/plugins/ceph/ceph_osd +++ b/plugins/ceph/ceph_osd @@ -44,6 +44,9 @@ if [ "$1" = "config" ]; then exit 0 fi -echo "osds.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_osds')" -echo "up.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_up_osds')" -echo "in.value $(ceph -s --format=json | jq '.osdmap.osdmap.num_in_osds')" +CEPH_STATUS=$(ceph -s --format=json) + +echo "osds.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_osds')" +echo "up.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_up_osds')" +echo "in.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_in_osds')" + From fdb2bb0d55710a2dbc7ad115ed58681247de7fb1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 22:19:04 +0200 Subject: [PATCH 256/695] fix wrong example-graph filenames --- .../{ilias-day.png => ilias_session-day.png} | Bin ...k_quality_day.png => olsrd-link_quality_day.png} | Bin ...t_day.png => olsrd-neighbour_link_count_day.png} | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename plugins/ilias/example-graphs/{ilias-day.png => ilias_session-day.png} (100%) rename plugins/network/example-graphs/{olsr-link_quality_day.png => olsrd-link_quality_day.png} (100%) rename plugins/network/example-graphs/{olsr-neighbour_link_count_day.png => olsrd-neighbour_link_count_day.png} (100%) diff --git a/plugins/ilias/example-graphs/ilias-day.png b/plugins/ilias/example-graphs/ilias_session-day.png similarity index 100% rename from plugins/ilias/example-graphs/ilias-day.png rename to plugins/ilias/example-graphs/ilias_session-day.png diff --git a/plugins/network/example-graphs/olsr-link_quality_day.png b/plugins/network/example-graphs/olsrd-link_quality_day.png similarity index 100% rename from plugins/network/example-graphs/olsr-link_quality_day.png rename to plugins/network/example-graphs/olsrd-link_quality_day.png diff --git a/plugins/network/example-graphs/olsr-neighbour_link_count_day.png b/plugins/network/example-graphs/olsrd-neighbour_link_count_day.png similarity index 100% rename from plugins/network/example-graphs/olsr-neighbour_link_count_day.png rename to plugins/network/example-graphs/olsrd-neighbour_link_count_day.png From c71c2ebb88744ed08a18dfaed1abf5154c4dae11 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 22:22:20 +0200 Subject: [PATCH 257/695] Add plugin 'wireless_channel_active_' --- plugins/wifi/wireless_channel_active_ | 139 ++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 plugins/wifi/wireless_channel_active_ diff --git a/plugins/wifi/wireless_channel_active_ b/plugins/wifi/wireless_channel_active_ new file mode 100755 index 00000000..8f0477e1 --- /dev/null +++ b/plugins/wifi/wireless_channel_active_ @@ -0,0 +1,139 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +wireless_channel_active - Show currently used channel of wifi enabled devices + +=head1 APPLICABLE SYSTEMS + +Information is parsed from the output of the tool "iwinfo" (OpenWrt) or "iw" (most systems). + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi device added (e.g. "phy0"). + +Root permissions are probably required for accessing "iw". + + [wireless_channel_active*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + + +set -eu + +if which iwinfo >/dev/null; then + # "iwinfo" has a stable output format but is only available on OpenWrt + get_physical_interfaces() { iwinfo | sed -n 's/^.*PHY name: \+\(.*\)$/\1/p'; } + get_physical_interface_current_channel() { iwinfo "$1" info \ + | grep Channel | sed 's/^.*Channel: \+\([0-9]\+\) .*$/\1/'; } + get_physical_interfaces_channel_descriptions() { + # return: CHANNEL CHANNEL_DESCRIPTION + # e.g.: "104 5.520 GHz (Channel 104)" + iwinfo "$1" freqlist | sed 's/^[^0-9]*//' \ + | sed 's/^.*Channel \+\([0-9]\+\).*$/\1 \0/'; } +else + # "iw" is available everywhere - but its output format is not recommended for non-humans + get_physical_interfaces() { iw list | awk '/^\w/ {print $2}'; } + get_physical_interface_current_channel() { iw dev | awk ' + /^\w+#/ { phy_name=gensub("#", "", 1, $1); } + /channel/ { if (phy_name == "phy0") print($2); }'; } + get_physical_interfaces_channel_descriptions() { + # example input: "* 5680 MHz [136]" + # return: CHANNEL CHANNEL_DESCRIPTION + # e.g.: "136 5680 MHz [136]" + iw phy phy0 channels | awk ' + /\*/ { chan_num=gensub("^.*\\[", "", 1, gensub("\\].*$", "", 1, $0)); + print(chan_num, $2, $3, $4); }'; } +fi + + +get_selected_physical_interface() { + # The physical interface name should be safe (phyX) and needs no cleanup. + # pick the last segment after the final "_" + echo "$0" | sed 's/.*_//' +} + + +do_config() { + local phy + phy=$(get_selected_physical_interface) + [ -z "$phy" ] && echo >&2 "Missing wireless PHY" && return 1 + echo "graph_title Wireless channel usage - $phy" + echo "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" + echo "graph_vlabel Channel usage by time in percent" + echo "graph_category network" + echo "graph_info This graph shows the currently used channel of your WiFi device(s)" + get_physical_interfaces_channel_descriptions "$phy" | while read -r channel description; do + fieldname="channel_${channel}" + echo "${fieldname}.label $description" + echo "${fieldname}.draw AREASTACK" + # percent scaling + echo "${fieldname}.cdef 100,${fieldname},*" + done +} + + +do_fetch() { + local phy + local current_channel + phy=$(get_selected_physical_interface) + [ -z "$phy" ] && echo >&2 "Missing wireless PHY" && return 1 + current_channel=$(get_physical_interface_current_channel "$phy") + get_physical_interfaces_channel_descriptions "$phy" | while read -r channel description; do + [ "$current_channel" = "$channel" ] && value=1 || value=0 + echo "channel_${channel}.value $value" + done +} + + +ACTION="${1:-}" + +case "$ACTION" in + config) + do_config || exit 1 + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ]; then do_fetch; fi + ;; + autoconf) + if [ -z "$(get_physical_interfaces)" ]; then + echo "no (no wifi interfaces found)" + else + echo "yes" + fi + ;; + suggest) + get_physical_interfaces + ;; + "") + do_fetch + ;; + *) + echo >&2 "Invalid action (valid: config / autoconf / suggest / )" + echo >&2 + exit 2 + ;; +esac From 01c2f1ca2dced33d782a4eac9b2190fdec0aae61 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 22:29:44 +0200 Subject: [PATCH 258/695] Change category for plugins from 'network' to 'wireless' --- plugins/wifi/wifi_signal | 2 +- plugins/wifi/wireless_channel_active_ | 2 +- plugins/wifi/wireless_signal_noise_ | 2 +- plugins/wifi/wireless_signal_ranges_ | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/wifi/wifi_signal b/plugins/wifi/wifi_signal index 4456532b..88ed22d1 100755 --- a/plugins/wifi/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -22,7 +22,7 @@ do_config () { echo "graph_title WiFi signal and noise" echo "graph_args --base 1000 -u 0" echo "graph_vlabel dB" - echo "graph_category network" + echo "graph_category wireless" echo "graph_info This graph shows the noise and singal levels of your WiFi devices" awk -F'[ :]*' '/:/ { diff --git a/plugins/wifi/wireless_channel_active_ b/plugins/wifi/wireless_channel_active_ index 8f0477e1..82ae5551 100755 --- a/plugins/wifi/wireless_channel_active_ +++ b/plugins/wifi/wireless_channel_active_ @@ -86,7 +86,7 @@ do_config() { echo "graph_title Wireless channel usage - $phy" echo "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" echo "graph_vlabel Channel usage by time in percent" - echo "graph_category network" + echo "graph_category wireless" echo "graph_info This graph shows the currently used channel of your WiFi device(s)" get_physical_interfaces_channel_descriptions "$phy" | while read -r channel description; do fieldname="channel_${channel}" diff --git a/plugins/wifi/wireless_signal_noise_ b/plugins/wifi/wireless_signal_noise_ index c8761519..170b20ce 100755 --- a/plugins/wifi/wireless_signal_noise_ +++ b/plugins/wifi/wireless_signal_noise_ @@ -110,7 +110,7 @@ do_config() { 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_category wireless" echo "graph_info This graph shows the signal and noise for all wifi peers" echo "noise.label Noise floor" echo "noise.draw LINE" diff --git a/plugins/wifi/wireless_signal_ranges_ b/plugins/wifi/wireless_signal_ranges_ index 2f893970..0678fd05 100755 --- a/plugins/wifi/wireless_signal_ranges_ +++ b/plugins/wifi/wireless_signal_ranges_ @@ -99,7 +99,7 @@ do_config() { echo "graph_title Wireless signal quality ranges - $wifi" echo "graph_args --upper-limit 0" echo "graph_vlabel Signal ranges" - echo "graph_category network" + echo "graph_category wireless" echo "graph_info This graph shows numbers of peers with defined wifi signal ranges" lower="noise" for level in $SIGNAL_THRESHOLDS; do From 7c3074b5f05424f971bb18632211b59849064627 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 22:32:40 +0200 Subject: [PATCH 259/695] Adjust categories for some mail-related plugins --- plugins/mail/imap_bandwidth | 2 +- plugins/mail/mail_connections | 2 +- plugins/mail/postfwd-rbl | 2 +- plugins/mail/procmail_ | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/mail/imap_bandwidth b/plugins/mail/imap_bandwidth index be8084b1..18060355 100755 --- a/plugins/mail/imap_bandwidth +++ b/plugins/mail/imap_bandwidth @@ -116,7 +116,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title IMAP bandwidth' echo 'graph_vlabel to (+) / from (-) server [bit/s]' - echo 'graph_category network' + echo 'graph_category mail' for item in $SERVERS; do key="$(echo "$item" | cut -f 1 -d =)" clean_name="$(clean_fieldname "$key")" diff --git a/plugins/mail/mail_connections b/plugins/mail/mail_connections index 0a59cbef..ade4f790 100755 --- a/plugins/mail/mail_connections +++ b/plugins/mail/mail_connections @@ -31,7 +31,7 @@ if [ "$1" = "config" ]; then graph_title Open mail server connections graph_args --base 1000 -l 0 graph_vlabel connections per second -graph_category Mail +graph_category mail EOT echo "$PORTS" | while read port field label; do echo "${field}.label $label" diff --git a/plugins/mail/postfwd-rbl b/plugins/mail/postfwd-rbl index c2278f6c..e875e160 100755 --- a/plugins/mail/postfwd-rbl +++ b/plugins/mail/postfwd-rbl @@ -28,7 +28,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Postfwd' - echo 'graph_category fw' + echo 'graph_category spamfilter' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Postfwd statistics' echo 'spamhaus.label Listed on Spamhaus.org' diff --git a/plugins/mail/procmail_ b/plugins/mail/procmail_ index 1838c0b3..8c259caa 100755 --- a/plugins/mail/procmail_ +++ b/plugins/mail/procmail_ @@ -35,7 +35,7 @@ sub config { print "graph_title Destination folders for Postfix\n", "graph_vlabel Received mails\n", - "graph_category Mail\n", + "graph_category mail\n", "graph_info Gives you the total mails stored by Postfix by folder\n"; print "$_.label $_\n" foreach sort keys %{$state->{folders}}; } From eb9019d6ed958a405aea4f5f716cf1859a7dd69a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 22:46:05 +0200 Subject: [PATCH 260/695] Move munin-related plugins to 'munin' directory --- plugins/{healthcheck => munin}/healthcheck_log | 0 plugins/{healthcheck => munin}/healthcheck_process | 0 plugins/{healthcheck => munin}/healthcheck_url | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{healthcheck => munin}/healthcheck_log (100%) rename plugins/{healthcheck => munin}/healthcheck_process (100%) rename plugins/{healthcheck => munin}/healthcheck_url (100%) diff --git a/plugins/healthcheck/healthcheck_log b/plugins/munin/healthcheck_log similarity index 100% rename from plugins/healthcheck/healthcheck_log rename to plugins/munin/healthcheck_log diff --git a/plugins/healthcheck/healthcheck_process b/plugins/munin/healthcheck_process similarity index 100% rename from plugins/healthcheck/healthcheck_process rename to plugins/munin/healthcheck_process diff --git a/plugins/healthcheck/healthcheck_url b/plugins/munin/healthcheck_url similarity index 100% rename from plugins/healthcheck/healthcheck_url rename to plugins/munin/healthcheck_url From 57367ce98c2cfcc67444ea38e0e182f2a5a9ab45 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:04:43 +0200 Subject: [PATCH 261/695] Plugin qstatcod4and5_: fix shellcheck issues --- plugins/other/qstatcod4and5_ | 125 +++++++++++++++++------------------ 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/plugins/other/qstatcod4and5_ b/plugins/other/qstatcod4and5_ index e60d9156..cf027e34 100755 --- a/plugins/other/qstatcod4and5_ +++ b/plugins/other/qstatcod4and5_ @@ -1,45 +1,43 @@ #!/bin/sh ################################################################# # Title : Qstat plugin for Munin # -# Author : Benjamin DUPUIS - Poil # -# Email : poil@quake.fr # -# First release : 18/10/2007 # +# Author : Benjamin DUPUIS - Poil # +# Email : poil@quake.fr # +# First release : 18/10/2007 # #---------------------------------------------------------------# -# Edited: Rouven David Nal - peperoni # -# Edit : 09/01/2009 # -# Plugin edited for COD4+COD5 # -# Email: peperoni@sac-esports.de # +# Edited: Rouven David Nal - peperoni # +# Edit : 09/01/2009 # +# Plugin edited for COD4+COD5 # +# Email: peperoni@sac-esports.de # #---------------------------------------------------------------# ################################################################# -# Variable : # +# Variable : # #---------------------------------------------------------------# -# Set path to QSTAT # -qstat_exe='/usr/local/bin/qstat' # -# Set the Group for munin to be displayed e.x. games or COD4 # -munin_group='games' # +# Set path to QSTAT # +qstat_exe='/usr/local/bin/qstat' # #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# -# Help # +# Help # #---------------------------------------------------------------# usage() { - echo 'For testing the script, run qstatcod4and5_ cods IP PORT' - echo ' - GameType : cods ... run qstat for seeing available gametype' - echo 'For munin you must ln -s /usr/share/munin/plugins/qstatcod4and5_ /etc/munin/plugins/cod4_cods_IP_PORT' - echo 'Example you will test this COD4 Server: 123.456.789.123:28960' - echo 'your symlink looks like this: ln -s /usr/share/munin/plugins/cod4server /etc/munin/plugins/cod4_cods_123.456.789.123_28960' - echo 'Perhaps you must have to set qstat_exe path, actually on'${qstat_exe}; - echo 'Have Fun' + echo 'For testing the script, run qstatcod4and5_ cods IP PORT' + echo ' - GameType : cods ... run qstat for seeing available gametype' + echo 'For munin you must ln -s /usr/share/munin/plugins/qstatcod4and5_ /etc/munin/plugins/cod4_cods_IP_PORT' + echo 'Example you will test this COD4 Server: 123.456.789.123:28960' + echo 'your symlink looks like this: ln -s /usr/share/munin/plugins/cod4server /etc/munin/plugins/cod4_cods_123.456.789.123_28960' + echo 'Perhaps you must have to set qstat_exe path, actually on'${qstat_exe}; + echo 'Have Fun' } config() { - if [ "${script_name}" != "qstatcod4and5_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + if [ "${script_name}" != "qstatcod4and5_" ]; then + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -48,56 +46,55 @@ config() { echo "graph_title Number of players on ${gametype} - ${ip}:${port} graph_vlabel players -graph_category ${munin_group} +graph_category games player.label players" } ################################################################# -# Quake Stat, call qstat # +# Quake Stat, call qstat # #---------------------------------------------------------------# quake_stat() { - if [ "${script_name}" != "qstatcod4and5_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) - else - gametype=$1 - ip=$2 - port=$3 - fi + if [ "${script_name}" != "qstatcod4and5_" ]; then + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) + else + gametype=$1 + ip=$2 + port=$3 + fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep frags | wc -l) - playervalue=$dummy - - if [ -z "${playervalue}" ]; then - playervalue=0 - fi + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + dummy=$("$qstat_exe" -P -pa -sort P "-$gametype" "${ip}:${port}" | grep frags | wc -l) + playervalue=$dummy - echo "player.value "${playervalue}; - else - echo "player.value U" - fi + if [ -z "$playervalue" ]; then + playervalue=0 + fi + + echo "player.value $playervalue" + else + echo "player.value U" + fi } ################################################################# -# Main # +# Main # #---------------------------------------------------------------# case $1 in - config) - config - exit 0 - ;; - help | ?) - usage - exit 0 - ;; - autoconf) - echo "no (edit the script for set qstat path)" - ;; - *) - quake_stat $1 $2 $3 - exit 0 - ;; + config) + config "$1" "$2" "$3" + exit 0 + ;; + help | ?) + usage + exit 0 + ;; + autoconf) + echo "no (edit the script for set qstat path)" + ;; + *) + quake_stat "$1" "$2" "$3" + exit 0 + ;; esac - From ade3b205bce009e35ca18bb15b07bff54d3edd8e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:05:10 +0200 Subject: [PATCH 262/695] Move 'qstatcod4and5_' to quake directory --- plugins/{other => quake}/qstatcod4and5_ | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{other => quake}/qstatcod4and5_ (100%) diff --git a/plugins/other/qstatcod4and5_ b/plugins/quake/qstatcod4and5_ similarity index 100% rename from plugins/other/qstatcod4and5_ rename to plugins/quake/qstatcod4and5_ From c6c9e5b41e43a4ee8a1bb715320671264b6f876d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:05:51 +0200 Subject: [PATCH 263/695] Move 'update' plugin to munin directory --- plugins/{other => munin}/update | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/{other => munin}/update (100%) diff --git a/plugins/other/update b/plugins/munin/update similarity index 100% rename from plugins/other/update rename to plugins/munin/update From 2f5a2a79130e0c1a34bb53eee3adf448da370b11 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:09:37 +0200 Subject: [PATCH 264/695] Plugin radwho_cnt: fix shellcheck issues --- plugins/other/radwho_cnt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/other/radwho_cnt b/plugins/other/radwho_cnt index c7664ab3..0254f4fa 100755 --- a/plugins/other/radwho_cnt +++ b/plugins/other/radwho_cnt @@ -18,5 +18,4 @@ if [ "$1" = "config" ]; then exit 0 fi -echo "usercnt.value $($radwho -rci 2>/dev/null | wc -l)" - +echo "usercnt.value $("$radwho" -rci 2>/dev/null | wc -l)" From 1ed78789c13c7a850d3e0509dc1911e34b6d6efe Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:25:07 +0200 Subject: [PATCH 265/695] Plugin zapchans: fix shellcheck issue --- plugins/voip/zapchans | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/voip/zapchans b/plugins/voip/zapchans index c0304f72..bf2a41cb 100755 --- a/plugins/voip/zapchans +++ b/plugins/voip/zapchans @@ -47,6 +47,6 @@ fi # cron, and dumps its output to a separate file for this plugin to read later on. Letting # Munin plugins run as root is a security hole. -zap=`asterisk -rx 'zap show channels' |grep -v "[[:digit:]]*[[:space:]] local" |grep -vc "Chan Extension"` +zap=$(asterisk -rx 'zap show channels' |grep -v "[[:digit:]]*[[:space:]] local" |grep -vc "Chan Extension") echo "calls.value $zap" From 0987cf33a2286d148d4b0e52af0af7edc9583482 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:27:30 +0200 Subject: [PATCH 266/695] Plugin virtualbox_*: fix shellcheck issues --- plugins/virtualbox/virtualbox_cpu_kernel | 11 +++++------ plugins/virtualbox/virtualbox_cpu_user | 11 +++++------ plugins/virtualbox/virtualbox_ram_usage | 16 ++++++++-------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/plugins/virtualbox/virtualbox_cpu_kernel b/plugins/virtualbox/virtualbox_cpu_kernel index 5f4db938..d39db3fd 100755 --- a/plugins/virtualbox/virtualbox_cpu_kernel +++ b/plugins/virtualbox/virtualbox_cpu_kernel @@ -37,8 +37,8 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the percentage of processor time spent in kernel mode by the every single VM process.' echo 'graph_category Virtualization' echo 'graph_period second' - vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` + vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do + VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') echo "${VM_NAME_PRINT}_kernel.label ${VM_NAME}" done exit 0 @@ -47,8 +47,7 @@ fi vboxmanage metrics setup --period 5 --samples 3 sleep 5 -vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` - vboxmanage metrics query "${VM_NAME}" CPU/Load/Kernel | grep -E "^${VM_NAME}" | sed -r 's/^.*([0-9]+\.[0-9]+)%/'''${VM_NAME_PRINT}'''_kernel.value \1/' +vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do +VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') + vboxmanage metrics query "${VM_NAME}" CPU/Load/Kernel | grep -E "^${VM_NAME}" | sed -r 's/^.*([0-9]+\.[0-9]+)%/'"${VM_NAME_PRINT}"'_kernel.value \1/' done - diff --git a/plugins/virtualbox/virtualbox_cpu_user b/plugins/virtualbox/virtualbox_cpu_user index 8bd77674..33c1b469 100755 --- a/plugins/virtualbox/virtualbox_cpu_user +++ b/plugins/virtualbox/virtualbox_cpu_user @@ -37,8 +37,8 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the percentage of processor time spent in user mode by the every single VM process.' echo 'graph_category Virtualization' echo 'graph_period second' - vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` + vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do + VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') echo "${VM_NAME_PRINT}_user.label ${VM_NAME}" done exit 0 @@ -47,8 +47,7 @@ fi vboxmanage metrics setup --period 5 --samples 3 sleep 5 -vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` - vboxmanage metrics query "${VM_NAME}" CPU/Load/User | grep -E "^${VM_NAME}" | sed -r 's/^.*([0-9]+\.[0-9]+)%/'''${VM_NAME_PRINT}'''_user.value \1/' +vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do + VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') + vboxmanage metrics query "${VM_NAME}" CPU/Load/User | grep -E "^${VM_NAME}" | sed -r 's/^.*([0-9]+\.[0-9]+)%/'"${VM_NAME_PRINT}"'_user.value \1/' done - diff --git a/plugins/virtualbox/virtualbox_ram_usage b/plugins/virtualbox/virtualbox_ram_usage index e81db92c..6205c638 100755 --- a/plugins/virtualbox/virtualbox_ram_usage +++ b/plugins/virtualbox/virtualbox_ram_usage @@ -34,7 +34,7 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then -RAMTOT=`free -b | grep Mem | awk '{print $2}'` + RAMTOT=$(free -b | grep Mem | awk '{print $2}') echo 'graph_title Memory usage of virtual machines' echo "graph_args --base 1024 -r --lower-limit 0 --upper-limit $RAMTOT --units-exponent 9" echo 'graph_vlabel GB' @@ -42,11 +42,11 @@ RAMTOT=`free -b | grep Mem | awk '{print $2}'` echo 'graph_category Virtualization' echo 'graph_period second' I=0 - vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` + vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do + VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') echo "${VM_NAME_PRINT}.label ${VM_NAME}" - echo "${VM_NAME_PRINT}.cdef ${VM_NAME_PRINT},1024,*" - if [ ${I} -eq 0 ]; then + echo "${VM_NAME_PRINT}.cdef ${VM_NAME_PRINT},1024,*" + if [ "$I" -eq 0 ]; then echo "${VM_NAME_PRINT}.draw AREA" else echo "${VM_NAME_PRINT}.draw STACK" @@ -59,8 +59,8 @@ fi vboxmanage metrics setup --period 5 --samples 3 sleep 5 -vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read VM_NAME; do - VM_NAME_PRINT=`echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g'` # s/[.]/_/g;s/[ ]/_/g;s/[-]/_/g - vboxmanage metrics query "${VM_NAME}" RAM/Usage/Used | grep -E "^${VM_NAME}" | sed -r 's/^.* ([0-9]+) kB$/'''${VM_NAME_PRINT}'''.value \1/' +vboxmanage list vms | sed -r 's/^\"(.*)\" \{.*\}$/\1/' | while read -r VM_NAME; do + VM_NAME_PRINT=$(echo -e "${VM_NAME}" | sed 's/[^A-Za-z0-9_]/_/g') # s/[.]/_/g;s/[ ]/_/g;s/[-]/_/g + vboxmanage metrics query "${VM_NAME}" RAM/Usage/Used | grep -E "^${VM_NAME}" | sed -r 's/^.* ([0-9]+) kB$/'"${VM_NAME_PRINT}"'.value \1/' done From 4b4a6a01e1e085516868134be88d8200db49c50b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:51:11 +0200 Subject: [PATCH 267/695] Plugins xen_*: fix shellcheck issues --- plugins/xen/xen | 50 +++++++++++++++++-------------------- plugins/xen/xen_memory | 20 ++++++--------- plugins/xen/xen_traffic_ | 16 ++++++------ plugins/xen/xen_traffic_all | 33 ++++++++++++------------ 4 files changed, 55 insertions(+), 64 deletions(-) diff --git a/plugins/xen/xen b/plugins/xen/xen index b44f2692..fe906f2c 100755 --- a/plugins/xen/xen +++ b/plugins/xen/xen @@ -28,9 +28,9 @@ fi if [ "$1" = "config" ]; then - if [ ! -e $statefile ]; then - touch $statefile - fi + if [ ! -e "$statefile" ]; then + touch "$statefile" + fi echo 'graph_title Xen Domain Utilisation' echo 'graph_args --base 1000 -l 0 --upper-limit 100 --rigid' @@ -39,26 +39,24 @@ if [ "$1" = "config" ]; then echo 'graph_category Virtualization' echo 'graph_info This graph shows how many percent of the CPU time was used by each domain' - xm list | grep -v "^Name .* Time(s)$" | \ - while read name domid mem cpu state time console; do - name=`echo $name | sed -e"s/[-.]/_/g"` - TEST=`less $statefile | grep "^${name}$" | wc -l` - if [ $TEST -ne 1 ]; then - echo "$name" >> $statefile - fi - done + xm list | grep -v "^Name .* Time(s)$" | while read -r name domid mem cpu state time console; do + name=$(echo "$name" | sed -e"s/[-.]/_/g") + TEST=$(grep "^${name}$" "$statefile" | wc -l) + if [ "$TEST" -ne 1 ]; then + echo "$name" >> "$statefile" + fi + done - FIRST=1 - cat $statefile | sort | \ - while read name; do + FIRST=1 + sort < "$statefile" | while read -r name; do echo "$name.label $name" echo "$name.type COUNTER" - if [ $FIRST -eq 1 ]; then - echo "$name.draw AREA" - FIRST=0 - else - echo "$name.draw STACK" - fi + if [ "$FIRST" -eq 1 ]; then + echo "$name.draw AREA" + FIRST=0 + else + echo "$name.draw STACK" + fi echo "$name.min 0" echo "$name.max 100" echo "$name.info % of the CPU time spent for $name" @@ -66,12 +64,10 @@ if [ "$1" = "config" ]; then exit 0 fi -xm list | grep -v "^Name .* Time(s)$" | \ -while read name domid mem cpu state time console; do - name=`echo $name | sed -e "s/[-.]/_/g"` - # only seconds - time=`echo $time | sed -e "s/\..//"` - # scale 60s/60s => 100%/60s - time=`echo "$time*100/60" | bc` +# shellcheck disable=SC2034 +xm list | grep -v "^Name .* Time(s)$" | while read -r name domid mem cpu state time console; do + name=$(echo "$name" | sed -e "s/[-.]/_/g") + # scale 60s/60s => 100%/60s + time=$(echo "$time" | awk '{print ($1 * 100 / 60) }') echo "$name.value $time" done diff --git a/plugins/xen/xen_memory b/plugins/xen/xen_memory index ef2532e4..f42f0919 100755 --- a/plugins/xen/xen_memory +++ b/plugins/xen/xen_memory @@ -31,11 +31,10 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel MB' echo 'graph_category Virtualization' echo 'graph_info This graph shows of many mS wall time where used by a domain' - # xm info | while read name bla value; do echo "$name $value"; done - $XM info | while read name bla value; do - #total_memory 2047 - #free_memory 1476 - name=`echo $name | sed -e"s/-/_/"` + "$XM" info | while read -r name bla value; do + # total_memory 2047 + # free_memory 1476 + name=$(echo "$name" | sed -e"s/-/_/") if [ "$name" = "total_memory" ]; then echo "$name.label $name" @@ -55,13 +54,10 @@ if [ "$1" = "config" ]; then exit 0 fi -$XM info | while read name bla value; do - name=`echo $name | sed -e"s/-/_/"` - if [ "$name" = "total_memory" ]; then - echo "$name.value $value" - fi - if [ "$name" = "free_memory" ]; then +# shellcheck disable=SC2034 +"$XM" info | while read -r name bla value; do + name=$(echo "$name" | sed -e"s/-/_/") + if [ "$name" = "total_memory" ] || [ "$name" = "free_memory" ]; then echo "$name.value $value" fi done - diff --git a/plugins/xen/xen_traffic_ b/plugins/xen/xen_traffic_ index f0b9eb85..454b2209 100755 --- a/plugins/xen/xen_traffic_ +++ b/plugins/xen/xen_traffic_ @@ -7,8 +7,8 @@ #%# capabilities=autoconf suggest -DOMAIN=$( basename $0 | sed 's/^xen_traffic_//g' ) -NAME=$( echo $DOMAIN | sed -e's/-/_/g' ) +DOMAIN=$( basename "$0" | sed 's/^xen_traffic_//g' ) +NAME=$( echo "$DOMAIN" | sed -e's/-/_/g' ) if [ "$1" = "autoconf" ]; then if which xm > /dev/null ; then @@ -27,12 +27,13 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "suggest" ]; then - xm list | awk '{print $1}' | egrep -v "^(Name|Domain-0)" + xm list | awk '{print $1}' | grep -v -E "^(Name|Domain-0)" exit 0 fi if [ "$1" = "config" ]; then echo "graph_title Xen Traffic for $NAME" + # shellcheck disable=SC2016 echo 'graph_vlabel bits in (-) / out (+) per ${graph_period}' echo 'graph_args --base 1024 -l 0' echo 'graph_category Virtualization' @@ -47,13 +48,12 @@ if [ "$1" = "config" ]; then exit 0 fi -dev=$( xm network-list $DOMAIN | egrep "^[0-9]+" | sed 's@^.*vif/\([0-9]*\)/\([0-9]*\).*$@vif\1.\2@') +dev=$( xm network-list "$DOMAIN" | grep '^[0-9]\+' | sed 's@^.*vif/\([0-9]*\)/\([0-9]*\).*$@vif\1.\2@') awk -v interface="$dev" \ - 'BEGIN { gsub(/\./, "\\.", interface) } \ + 'BEGIN { gsub(/\./, "\\.", interface) } $1 ~ "^" interface ":" { - split($0, a, /: */); $0 = a[2]; \ - print "in.value " $1 "\nout.value " $9 \ + split($0, a, /: */); $0 = a[2]; + print "in.value " $1 "\nout.value " $9; }' \ /proc/net/dev - diff --git a/plugins/xen/xen_traffic_all b/plugins/xen/xen_traffic_all index b1368739..dc9ac47f 100755 --- a/plugins/xen/xen_traffic_all +++ b/plugins/xen/xen_traffic_all @@ -27,44 +27,43 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Xen Traffic' + # shellcheck disable=SC2016 echo 'graph_vlabel bits received (-) / sent (+) per ${graph_period}' echo 'graph_args --base 1024 -l 0' echo 'graph_category Virtualization' - DOMAINS=$(xm list | awk '{print $1}' | egrep -v "^(Name|Domain-0)") + DOMAINS=$(xm list | awk '{print $1}' | grep -v -E "^(Name|Domain-0)") for dom in $DOMAINS; do - name=$( echo $dom | tr .- __ ) + name=$( echo "$dom" | tr .- __ ) #echo $name"UP.label $name" #echo $name"DOWN.label $name" - echo $name'Down.label received' - echo $name'Down.type COUNTER' - echo $name'Down.graph no' + echo "${name}Down.label received" + echo "${name}Down.type COUNTER" + echo "${name}Down.graph no" echo "${name}Down.cdef ${name}Down,8,*" echo "${name}Up.label ${name}" - echo $name'Up.type COUNTER' + echo "${name}Up.type COUNTER" echo "${name}Up.negative ${name}Down" echo "${name}Up.cdef ${name}Up,8,*" done exit 0 fi -DOMAINS=$(xm list | awk '{print $1}' | egrep -v "^(Name|Domain-0)") +DOMAINS=$(xm list | awk '{print $1}' | grep -v -E "^(Name|Domain-0)") for dom in $DOMAINS; do - dev=$( xm list $dom --long | awk '/vifname / { print $2 }' | sed 's/)//' ) - if [ "$dev" = "" ]; then - dev=$( xm network-list $dom |\ - egrep "^[0-9]+" | sed 's@^.*vif/\([0-9]*\)/\([0-9]*\).*$@vif\1.\2@') - fi - name=$( echo $dom | tr .- __ ) + dev=$( xm list "$dom" --long | awk '/vifname / { print $2 }' | sed 's/)//' ) + if [ -z "$dev" ]; then + dev=$( xm network-list "$dom" | grep '^[0-9]\+' | sed 's@^.*vif/\([0-9]*\)/\([0-9]*\).*$@vif\1.\2@') + fi + name=$( echo "$dom" | tr .- __ ) #awk -v name="$name" -v interface="$dev" -F'[: \t]+' \ #'{ sub(/^ */,""); if ($1 == interface) \ #print name"DOWN.value "$2"\n"name"UP.value "$10; }' /proc/net/dev awk -v name="$name" -v interface="$dev" \ - 'BEGIN { gsub(/\./, "\\.", interface) } \ + 'BEGIN { gsub(/\./, "\\.", interface) } $1 ~ "^" interface ":" { - split($0, a, /: */); $0 = a[2]; \ - print name"Down.value " $1 "\n"name"Up.value " $9 \ + split($0, a, /: */); $0 = a[2]; + print name"Down.value " $1 "\n"name"Up.value " $9; }' \ /proc/net/dev done - From 4d2d1fb94b52f9a64e0a35ae23caef6769ac57b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:51:50 +0200 Subject: [PATCH 268/695] Plugins xen_*: fix 'autoconf' logic and exitcode --- plugins/xen/xen_memory | 8 ++++---- plugins/xen/xen_traffic_ | 13 +++++-------- plugins/xen/xen_traffic_all | 13 +++++-------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/plugins/xen/xen_memory b/plugins/xen/xen_memory index f42f0919..c9e217e5 100755 --- a/plugins/xen/xen_memory +++ b/plugins/xen/xen_memory @@ -15,12 +15,12 @@ XM='/usr/sbin/xm' if [ "$1" = "autoconf" ]; then - if [ -e $XM ]; then + if [ -x "$XM" ]; then echo yes - exit 0 + else + echo "no (xm not found)" fi - echo "no (xm not found)" - exit 1 + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/xen/xen_traffic_ b/plugins/xen/xen_traffic_ index 454b2209..57e96a52 100755 --- a/plugins/xen/xen_traffic_ +++ b/plugins/xen/xen_traffic_ @@ -12,16 +12,13 @@ NAME=$( echo "$DOMAIN" | sed -e's/-/_/g' ) if [ "$1" = "autoconf" ]; then if which xm > /dev/null ; then - echo yes + if [ -r /proc/net/dev ]; then + echo yes + else + echo "no (/proc/net/dev not found)" + fi else echo "no (xm not found)" - exit 1 - fi - if [ -r /proc/net/dev ]; then - echo yes - else - echo "no (/proc/net/dev not found)" - exit 1 fi exit 0 fi diff --git a/plugins/xen/xen_traffic_all b/plugins/xen/xen_traffic_all index dc9ac47f..7c5fa3d2 100755 --- a/plugins/xen/xen_traffic_all +++ b/plugins/xen/xen_traffic_all @@ -11,16 +11,13 @@ if [ "$1" = "autoconf" ]; then if which xm > /dev/null ; then - echo yes + if [ -r /proc/net/dev ]; then + echo yes + else + echo "no (/proc/net/dev not found)" + fi else echo "no (xm not found)" - exit 1 - fi - if [ -r /proc/net/dev ]; then - echo yes - else - echo "no (/proc/net/dev not found)" - exit 1 fi exit 0 fi From c8df9ab4e9a512d8a693d5700738deff478fa61d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:53:04 +0200 Subject: [PATCH 269/695] Plguin xen_memory: change 'draw' style from lines to AREASTACK --- plugins/xen/xen_memory | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/plugins/xen/xen_memory b/plugins/xen/xen_memory index c9e217e5..9fdc4d7b 100755 --- a/plugins/xen/xen_memory +++ b/plugins/xen/xen_memory @@ -36,20 +36,17 @@ if [ "$1" = "config" ]; then # free_memory 1476 name=$(echo "$name" | sed -e"s/-/_/") - if [ "$name" = "total_memory" ]; then + if [ "$name" = "total_memory" ] || [ "$name" = "free_memory" ]; then echo "$name.label $name" echo "$name.type GAUGE" + echo "$name.draw AREASTACK" echo "$name.min 0" - echo "$name.info total memory" - fi - if [ "$name" = "free_memory" ]; then - echo "$name.label $name" - echo "$name.type GAUGE" - echo "$name.draw AREA" -# echo "$name.draw STACK" - echo "$name.min 0" - echo "$name.info free memory" - fi + if [ "$name" = "total_memory" ]; then + echo "$name.info total memory" + elif [ "$name" = "free_memory" ]; then + echo "$name.info free memory" + fi + fi done exit 0 fi From 33e95e6ff9d9e634dfba2fd200b5777bab1ff9da Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 1 Aug 2018 23:56:56 +0200 Subject: [PATCH 270/695] Review and unify category assignments of plugins --- plugins/chat/tinychat_users_ | 2 +- plugins/courier/courier_log | 2 +- .../currency/zcash/zcash_flypool_hashrate_ | 2 +- plugins/gpu/amd_gpu_ | 4 +-- plugins/harddisks/powered_up_ | 2 +- plugins/http/mongrel_memory | 2 +- plugins/http/mongrel_process_memory | 2 +- plugins/jmx/examples/java/java_cpu.conf | 2 +- .../examples/java/java_process_memory.conf | 2 +- plugins/jmx/examples/java/java_threads.conf | 2 +- .../examples/tomcat/catalina_requests.conf | 2 +- .../jmx/examples/tomcat/catalina_threads.conf | 2 +- .../jmx/examples/tomcat/catalina_times.conf | 3 +- .../jmx/examples/tomcat/catalina_traffic.conf | 2 +- plugins/jmx/readme.txt | 2 +- plugins/libvirt/kvm_cpu | 2 +- plugins/libvirt/kvm_io | 2 +- plugins/libvirt/kvm_mem | 2 +- plugins/libvirt/libvirt | 32 +++++++++---------- plugins/libvirt/munin-libvirtpy | 2 +- plugins/moinmoin/moinoin_pages | 2 +- plugins/network/quagga_routes | 2 +- plugins/network/zenus_ | 2 +- plugins/omreport/omreport_fan_speed | 2 +- plugins/omreport/omreport_pwrmon_current | 2 +- plugins/omreport/omreport_pwrmon_power | 2 +- plugins/omreport/omreport_storage_temp | 2 +- plugins/omreport/omreport_temp | 2 +- plugins/other/earthquakes | 2 +- plugins/other/foldingathome_activecpu | 2 +- plugins/other/radwho_cnt | 2 +- plugins/punbb/punbb_users | 2 +- plugins/qmail/qmailsend | 2 +- plugins/qmail/qmailsend_plesk | 2 +- plugins/qmail/qremote | 2 +- plugins/reddit_karma/reddit_karma_ | 2 +- plugins/router/tg585v7__ | 20 ++++++------ plugins/sickbeard/sickbeard_episodes | 2 +- plugins/sickbeard/sickbeard_shows | 2 +- plugins/snmp/snmp__brocade_ifs | 4 +-- plugins/spotweb/spotweb_average | 2 +- plugins/spotweb/spotweb_cat_average | 2 +- plugins/spotweb/spotweb_cat_total | 2 +- plugins/spotweb/spotweb_catsize_average | 2 +- plugins/spotweb/spotweb_total | 2 +- plugins/squid/squid | 22 ++++++------- plugins/squid/squid_efficiency | 2 +- plugins/squid/squid_times | 2 +- plugins/virtualbox/virtualbox_cpu_kernel | 2 +- plugins/virtualbox/virtualbox_cpu_user | 2 +- plugins/virtualbox/virtualbox_ram_usage | 2 +- plugins/voip/zapchans | 2 +- plugins/xen/xen | 2 +- plugins/xen/xen-cpu | 2 +- plugins/xen/xen-multi | 10 +++--- plugins/xen/xen_cpu_v2 | 2 +- plugins/xen/xen_memory | 2 +- plugins/xen/xen_traffic_ | 2 +- plugins/xen/xen_vbd | 2 +- 59 files changed, 99 insertions(+), 100 deletions(-) diff --git a/plugins/chat/tinychat_users_ b/plugins/chat/tinychat_users_ index d366b372..b9c8d258 100755 --- a/plugins/chat/tinychat_users_ +++ b/plugins/chat/tinychat_users_ @@ -36,7 +36,7 @@ echo "graph_title Tinychat Users for $room" echo 'graph_args --base 1000' echo 'graph_scale no' echo 'graph_vlabel Room Users' - echo 'graph_category Chat' + echo 'graph_category chat' echo 'total_count.label Room Users' echo 'total_count.draw AREA' echo 'broadcaster_count.draw STACK' diff --git a/plugins/courier/courier_log b/plugins/courier/courier_log index f1796d4e..62231f58 100755 --- a/plugins/courier/courier_log +++ b/plugins/courier/courier_log @@ -49,7 +49,7 @@ if [ "$1" = "config" ]; then graph_title Courier log ($type) graph_args --base 1000 -l 0 graph_vlabel events per second -graph_category Mail +graph_category mail EOT echo "$CATEGORIES" | while read field label match; do echo "${field}.label $(echo "$label" | tr "_" " ")" diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index 8db6effc..a93c0acc 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -63,7 +63,7 @@ if command == 'config': print("graph_title Flypool {}".format(miner)) print("graph_info zcash.flypool.org Mining Pool Hashrate for {}_{}".format(zcash_address, miner)) print("graph_vlabel Flypool Hashrate") - print("graph_category other") + print("graph_category htc") print("flypool_hs_{}_{}.warning 200:".format(zcash_address, miner)) print("flypool_hs_{}_{}.critical 100:".format(zcash_address, miner)) print("flypool_hs_{}_{}.label H/s:".format(zcash_address, miner)) diff --git a/plugins/gpu/amd_gpu_ b/plugins/gpu/amd_gpu_ index 85491c8d..3bbc2ae8 100755 --- a/plugins/gpu/amd_gpu_ +++ b/plugins/gpu/amd_gpu_ @@ -138,7 +138,7 @@ if [ "$1" = "config" ]; then echo 'graph_title GPU clock' echo "graph_args -l 0 -u $maxclock" echo 'graph_vlabel MHz' - echo 'graph_category sensors' + echo 'graph_category htc' echo "graph_info Core and memory clock info for AMD GPUs" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] @@ -170,7 +170,7 @@ if [ "$1" = "config" ]; then echo 'graph_title GPU load' echo 'graph_args -l 0 -u 100' echo 'graph_vlabel Percentage' - echo 'graph_category sensors' + echo 'graph_category htc' echo "graph_info GPU load" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] diff --git a/plugins/harddisks/powered_up_ b/plugins/harddisks/powered_up_ index 3eddfd25..24eff467 100755 --- a/plugins/harddisks/powered_up_ +++ b/plugins/harddisks/powered_up_ @@ -12,7 +12,7 @@ case $1 in cat <{name} upload.label Uploaded diff --git a/plugins/omreport/omreport_fan_speed b/plugins/omreport/omreport_fan_speed index c8694f11..2401897d 100755 --- a/plugins/omreport/omreport_fan_speed +++ b/plugins/omreport/omreport_fan_speed @@ -92,7 +92,7 @@ else { print "graph_title OpenManage - Fan Probes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Speed in RPMs\n"; - print "graph_category Sensors\n"; + print "graph_category sensors\n"; foreach my $j (sort keys %val) { print "fan_$j.label $val{$j}{\"Probe Name\"}\n"; if ($val{$j}{"Warning Threshold"} !~ m/\[N\/A\]/i) { diff --git a/plugins/omreport/omreport_pwrmon_current b/plugins/omreport/omreport_pwrmon_current index 0734764c..b8e8a0cc 100755 --- a/plugins/omreport/omreport_pwrmon_current +++ b/plugins/omreport/omreport_pwrmon_current @@ -91,7 +91,7 @@ else { print "graph_title OpenManage - Power Monitoring - Current\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Amps\n"; - print "graph_category Sensors\n"; + print "graph_category sensors\n"; foreach my $j (sort keys %val) { print "$j.label $val{$j}{label}\n"; } diff --git a/plugins/omreport/omreport_pwrmon_power b/plugins/omreport/omreport_pwrmon_power index 0d223ca2..c5c2ddbe 100755 --- a/plugins/omreport/omreport_pwrmon_power +++ b/plugins/omreport/omreport_pwrmon_power @@ -101,7 +101,7 @@ else { print "graph_title OpenManage - Power Monitoring - Power\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Watts\n"; - print "graph_category Sensors\n"; + print "graph_category sensors\n"; foreach my $j (sort keys %val) { print "$j.label $val{$j}{label}\n"; if ($val{$j}{warning}) { diff --git a/plugins/omreport/omreport_storage_temp b/plugins/omreport/omreport_storage_temp index b821289d..0c8be449 100755 --- a/plugins/omreport/omreport_storage_temp +++ b/plugins/omreport/omreport_storage_temp @@ -162,7 +162,7 @@ else { print "graph_title OpenManage - Storage - Temperatures\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Temp in Degrees Celsius\n"; - print "graph_category Sensors\n"; + print "graph_category sensors\n"; print "graph_info Temperature sensors within storage enclosures on the system, typically external ones managed via PERC 6/E adapters.\n"; } foreach my $c (sort keys %ctrlrs) { diff --git a/plugins/omreport/omreport_temp b/plugins/omreport/omreport_temp index ddf2c346..f7bcb3f9 100755 --- a/plugins/omreport/omreport_temp +++ b/plugins/omreport/omreport_temp @@ -92,7 +92,7 @@ else { print "graph_title OpenManage - Temperature Probes\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Temperature in Celsius\n"; - print "graph_category Sensors\n"; + print "graph_category sensors\n"; foreach my $j (sort keys %val) { print "probe_$j.label $val{$j}{\"Probe Name\"}\n"; print "probe_$j.warning $val{$j}{\"Warning Threshold\"}\n"; diff --git a/plugins/other/earthquakes b/plugins/other/earthquakes index 38f9ae98..08666c57 100755 --- a/plugins/other/earthquakes +++ b/plugins/other/earthquakes @@ -57,7 +57,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Earthquakes by region\n"; print "graph_args --base 1000\n"; - print "graph_category Other\n"; + print "graph_category other\n"; print "graph_info This graph shows earthquakes by region of magnitude 1.0 or greater.\n"; print "graph_vlabel Number of Earthquakes\n"; diff --git a/plugins/other/foldingathome_activecpu b/plugins/other/foldingathome_activecpu index c14cf82d..3162476e 100755 --- a/plugins/other/foldingathome_activecpu +++ b/plugins/other/foldingathome_activecpu @@ -21,7 +21,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "autoconf": elif len(sys.argv) == 2 and sys.argv[1] == "config": print 'graph_title Active CPU in FoldingAtHome project' print 'graph_vlabel Systems' - print 'graph_category other' + print 'graph_category htc' print 'windows.label Windows' print 'ppc.label MacOS/PowerPC' diff --git a/plugins/other/radwho_cnt b/plugins/other/radwho_cnt index 0254f4fa..722398db 100755 --- a/plugins/other/radwho_cnt +++ b/plugins/other/radwho_cnt @@ -11,7 +11,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel users' echo 'graph_noscale true' - echo 'graph_category other' + echo 'graph_category auth' echo 'usercnt.label users' echo 'graph_info FreeRadius active users count.' echo 'usercnt.info FreeRadius active users count' diff --git a/plugins/punbb/punbb_users b/plugins/punbb/punbb_users index e826400a..453fc923 100755 --- a/plugins/punbb/punbb_users +++ b/plugins/punbb/punbb_users @@ -55,7 +55,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_title Users\n"; print "graph_args --base 1000\n"; print "graph_vlabel current users\n"; - print "graph_category Forum\n"; + print "graph_category forum\n"; print "graph_total Total\n"; print "members.label Members\n"; diff --git a/plugins/qmail/qmailsend b/plugins/qmail/qmailsend index 12a44ec2..b0b750f3 100755 --- a/plugins/qmail/qmailsend +++ b/plugins/qmail/qmailsend @@ -68,7 +68,7 @@ if (exists $ARGV[0]) { print "graph_title Qmail outgoing SMTP replies\n"; print "graph_args --base 1000 -l 0 \n"; print "graph_vlabel replies/hour\n"; - print "graph_category Mail\n"; + print "graph_category mail\n"; print "graph_total Total\n" if (keys (%descriptions) > 1); print "graph_info This graph shows qmail-send transaction response codes.\n"; print "graph_order res" . join(" res", sort by_code keys %descriptions) . "\n"; diff --git a/plugins/qmail/qmailsend_plesk b/plugins/qmail/qmailsend_plesk index 22f2f7f7..2e9e767f 100755 --- a/plugins/qmail/qmailsend_plesk +++ b/plugins/qmail/qmailsend_plesk @@ -68,7 +68,7 @@ if (exists $ARGV[0]) { print "graph_title Qmail outgoing SMTP replies\n"; print "graph_args --base 1000 -l 0 \n"; print "graph_vlabel replies/hour\n"; - print "graph_category Mail\n"; + print "graph_category mail\n"; print "graph_total Total\n" if (keys (%descriptions) > 1); print "graph_info This graph shows qmail-send transaction response codes.\n"; print "graph_order res" . join(" res", sort by_code keys %descriptions) . "\n"; diff --git a/plugins/qmail/qremote b/plugins/qmail/qremote index 52746152..2abe07ce 100755 --- a/plugins/qmail/qremote +++ b/plugins/qmail/qremote @@ -19,7 +19,7 @@ if [ "$1" = "config" ]; then graph_title Qmail-remote processes graph_args --base 1000 -l 0 graph_vlabel running processes -graph_category Mail +graph_category mail remote.label remote remote.type GAUGE EOT diff --git a/plugins/reddit_karma/reddit_karma_ b/plugins/reddit_karma/reddit_karma_ index 689a283c..9ff633c7 100755 --- a/plugins/reddit_karma/reddit_karma_ +++ b/plugins/reddit_karma/reddit_karma_ @@ -49,7 +49,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000' echo 'graph_scale no' echo 'graph_vlabel Link Karma' - echo 'graph_category other' + echo 'graph_category forum' echo 'comment_karma.label Comment Karma' echo 'comment_karma.draw LINE' echo 'link_karma.label Link Karma' diff --git a/plugins/router/tg585v7__ b/plugins/router/tg585v7__ index 1f5b9e10..d410e893 100755 --- a/plugins/router/tg585v7__ +++ b/plugins/router/tg585v7__ @@ -149,7 +149,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { if ( $mode eq 'bandwidth' ) { print < 'Xen Domain CPU Usage', 'graph_args' => '--base 1000 -l 0 --upper-limit 100 --rigid', 'graph_vlabel' => 'Percent (%)', - 'graph_category' => 'Virtualization', + 'graph_category' => 'virtualization', 'graph_info' => 'Display the % of CPU Usage for each domain', ); diff --git a/plugins/xen/xen-multi b/plugins/xen/xen-multi index 233dfb5c..dafaa57a 100755 --- a/plugins/xen/xen-multi +++ b/plugins/xen/xen-multi @@ -169,7 +169,7 @@ if ($ARGV[0] eq "config") { print "graph_args --base 1000 -l 0\n"; print "graph_vlabel cpu seconds\n"; print "graph_scale no\n"; - print "graph_category Virtualization\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows CPU time for each Xen domain.\n"; for $domain (@domainlist) { print "$domains{$domain}{'munindomain'}_cpu_time.label ".trim_label('pos',$domain)."\n"; @@ -186,7 +186,7 @@ if ($ARGV[0] eq "config") { print "graph_args --base 1000 -l 0 --upper-limit 100\n"; print "graph_vlabel %\n"; print "graph_scale no\n"; - print "graph_category Virtualization\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows CPU utilization for each Xen domain.\n"; for $domain (@domainlist) { print "$domains{$domain}{'munindomain'}_cpu.label ".trim_label('pos',$domain)."\n"; @@ -199,7 +199,7 @@ if ($ARGV[0] eq "config") { print "graph_title Xen domains memory usage\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; - print "graph_category Virtualization\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows memory usage for each Xen domain.\n"; for $domain (@domainlist) { print "$domains{$domain}{'munindomain'}_mem.label ".trim_label('pos',$domain)."\n"; @@ -213,7 +213,7 @@ if ($ARGV[0] eq "config") { print "graph_title Xen domains network traffic\n"; print "graph_args --base 1000\n"; print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n"; - print "graph_category Virtualization\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows network traffic for each Xen domain.\n"; for $domain (@domainlist) { print "$domains{$domain}{'munindomain'}_netrx.label none\n"; @@ -233,7 +233,7 @@ if ($ARGV[0] eq "config") { print "graph_title Xen domains disk IOs\n"; print "graph_args --base 1000\n"; print "graph_vlabel IOs per \${graph_period} read (-) / write (+)\n"; - print "graph_category Virtualization\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows disk IOs for each Xen domain.\n"; for $domain (@domainlist) { print "$domains{$domain}{'munindomain'}_vbdrd.label none\n"; diff --git a/plugins/xen/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 index aa58184c..63e41248 100755 --- a/plugins/xen/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -72,7 +72,7 @@ if ( defined($ARGV[0]) ) 'graph_title' => 'Xen Domain CPU Usage v2', 'graph_args' => '--base 1000 -l 0 --upper-limit 100 --rigid', 'graph_vlabel' => 'Percent (%)', - 'graph_category' => 'Virtualization', + 'graph_category' => 'virtualization', 'graph_info' => 'Display the % of CPU Usage for each domain', ); diff --git a/plugins/xen/xen_memory b/plugins/xen/xen_memory index 9fdc4d7b..90fbb56b 100755 --- a/plugins/xen/xen_memory +++ b/plugins/xen/xen_memory @@ -29,7 +29,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_scale no' echo 'graph_vlabel MB' - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo 'graph_info This graph shows of many mS wall time where used by a domain' "$XM" info | while read -r name bla value; do # total_memory 2047 diff --git a/plugins/xen/xen_traffic_ b/plugins/xen/xen_traffic_ index 57e96a52..386320e9 100755 --- a/plugins/xen/xen_traffic_ +++ b/plugins/xen/xen_traffic_ @@ -33,7 +33,7 @@ if [ "$1" = "config" ]; then # shellcheck disable=SC2016 echo 'graph_vlabel bits in (-) / out (+) per ${graph_period}' echo 'graph_args --base 1024 -l 0' - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo 'out.label sent' echo 'out.type DERIVE' echo 'out.min 0' diff --git a/plugins/xen/xen_vbd b/plugins/xen/xen_vbd index da34765a..1f74c24b 100755 --- a/plugins/xen/xen_vbd +++ b/plugins/xen/xen_vbd @@ -42,7 +42,7 @@ if ( defined($ARGV[0])) 'graph_title' => 'Xen Domain I/O usage', 'graph_args' => '--base 1024 -l 0', 'graph_vlabel' => 'read (-), write (+)', - 'graph_category' => 'Virtualization', + 'graph_category' => 'virtualization', 'graph_info' => 'Display the I/O operations for each domain', ); From 68eddb586565c54345ac778b8b1930cb7df750ae Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:06:14 +0200 Subject: [PATCH 271/695] Plugin proftpd_bytes: fix autoconf handling --- plugins/ftp/proftpd_bytes | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/ftp/proftpd_bytes b/plugins/ftp/proftpd_bytes index b34f38f7..730e4d44 100755 --- a/plugins/ftp/proftpd_bytes +++ b/plugins/ftp/proftpd_bytes @@ -27,11 +27,10 @@ STATEFILE=$MUNIN_PLUGSTATE/xferlog-bytes.offset if [ "$1" = "autoconf" ]; then if [ -f "${LOGFILE}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else - echo no - exit 1 + echo "no (missing logfile or 'logtail' executable)" fi + exit 0 fi if [ "$1" = "config" ]; then From e62a8e31425892485e6b4c6e8d26d07c2e2609ee Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:14:41 +0200 Subject: [PATCH 272/695] Plugin proftpd_count: fix autoconf handling --- plugins/ftp/proftpd_count | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/ftp/proftpd_count b/plugins/ftp/proftpd_count index 13f50eec..d147c49a 100755 --- a/plugins/ftp/proftpd_count +++ b/plugins/ftp/proftpd_count @@ -25,13 +25,12 @@ LOGTAIL=${logtail:-`which logtail`} STATEFILE=$MUNIN_PLUGSTATE/xferlog-count.offset if [ "$1" = "autoconf" ]; then - if [ -f "${LOGFILE}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then - echo yes - exit 0 - else - echo no - exit 1 - fi + if [ -f "$LOGFILE" ] && [ -n "$LOGTAIL" ] && [ -x "$LOGTAIL" ] ; then + echo yes + else + echo "no (missing logfile or 'logtail' executable)" + fi + exit 0 fi if [ "$1" = "config" ]; then From b925a10155b05e93eaea9c8cbcc3d0cfa2fb6b0d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:09:36 +0200 Subject: [PATCH 273/695] Plugin proftpd_bytes: fix shellcheck issues --- plugins/ftp/proftpd_bytes | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/plugins/ftp/proftpd_bytes b/plugins/ftp/proftpd_bytes index 730e4d44..a4086347 100755 --- a/plugins/ftp/proftpd_bytes +++ b/plugins/ftp/proftpd_bytes @@ -14,50 +14,48 @@ #%# family=auto #%# capabilities=autoconf -MAXLABEL=20 mktempfile () { -mktemp -t $1 -} + mktemp -t "$1" +} LOGFILE=${logfile:-/var/log/proftpd/xferlog} -LOGTAIL=${logtail:-`which logtail`} +LOGTAIL=${logtail:-$(which logtail)} STATEFILE=$MUNIN_PLUGSTATE/xferlog-bytes.offset if [ "$1" = "autoconf" ]; then - if [ -f "${LOGFILE}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then - echo yes - else - echo "no (missing logfile or 'logtail' executable)" - fi - exit 0 + if [ -f "$LOGFILE" ] && [ -n "$LOGTAIL" ] && [ -x "$LOGTAIL" ] ; then + echo yes + else + echo "no (missing logfile or 'logtail' executable)" + fi + exit 0 fi if [ "$1" = "config" ]; then - echo 'graph_title FTP Server Bytes' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel FTP Server Bytes' - echo 'graph_category network' - echo 'ftp_get.label Bytes GET' - echo 'ftp_put.label Bytes PUT' - exit 0 + echo 'graph_title FTP Server Bytes' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel FTP Server Bytes' + echo 'graph_category network' + echo 'ftp_get.label Bytes GET' + echo 'ftp_put.label Bytes PUT' + exit 0 fi ftp_get=U ftp_put=U -TEMP_FILE=`mktempfile munin-xferlog-bytes.XXXXXX` +TEMP_FILE=$(mktempfile munin-xferlog-bytes.XXXXXX) -if [ -n "$TEMP_FILE" -a -f "$TEMP_FILE" ] +if [ -n "$TEMP_FILE" ] && [ -f "$TEMP_FILE" ] then - $LOGTAIL ${LOGFILE} $STATEFILE | grep "[[:space:]][oi][[:space:]]" > ${TEMP_FILE} - ftp_get=`grep "[[:space:]]o[[:space:]]" ${TEMP_FILE} | awk '{s += $8} END { if ( s ) print s ; else print "0" }'` - ftp_put=`grep "[[:space:]]i[[:space:]]" ${TEMP_FILE} | awk '{s += $8} END { if ( s ) print s ; else print "0" }'` + "$LOGTAIL" "$LOGFILE" "$STATEFILE" | grep "[[:space:]][oi][[:space:]]" >"$TEMP_FILE" + ftp_get=$(grep "[[:space:]]o[[:space:]]" "$TEMP_FILE" | awk '{s += $8} END { if ( s ) print s ; else print "0" }') + ftp_put=$(grep "[[:space:]]i[[:space:]]" "$TEMP_FILE" | awk '{s += $8} END { if ( s ) print s ; else print "0" }') - /bin/rm -f $TEMP_FILE + /bin/rm -f "$TEMP_FILE" fi -echo "ftp_get.value ${ftp_get}" -echo "ftp_put.value ${ftp_put}" - +echo "ftp_get.value $ftp_get" +echo "ftp_put.value $ftp_put" From 9c1689fbefff27141643bd231f43a9f3732d86b5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:15:53 +0200 Subject: [PATCH 274/695] Plugin proftpd_count: fix shellcheck issues --- plugins/ftp/proftpd_count | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/plugins/ftp/proftpd_count b/plugins/ftp/proftpd_count index d147c49a..b0f10b50 100755 --- a/plugins/ftp/proftpd_count +++ b/plugins/ftp/proftpd_count @@ -14,14 +14,13 @@ #%# family=auto #%# capabilities=autoconf -MAXLABEL=20 mktempfile () { -mktemp -t $1 + mktemp -t "$1" } LOGFILE=${logfile:-/var/log/proftpd/xferlog} -LOGTAIL=${logtail:-`which logtail`} +LOGTAIL=${logtail:-$(which logtail)} STATEFILE=$MUNIN_PLUGSTATE/xferlog-count.offset if [ "$1" = "autoconf" ]; then @@ -34,30 +33,28 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - echo 'graph_title FTP Server Transfers' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel FTP Server Transfers' - echo 'graph_category network' - echo 'ftp_get.label Files GET' - echo 'ftp_put.label Files PUT' - exit 0 + echo 'graph_title FTP Server Transfers' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel FTP Server Transfers' + echo 'graph_category network' + echo 'ftp_get.label Files GET' + echo 'ftp_put.label Files PUT' + exit 0 fi ftp_get=U ftp_put=U -TEMP_FILE=`mktempfile munin-xferlog-count.XXXXXX` +TEMP_FILE=$(mktempfile munin-xferlog-count.XXXXXX) -if [ -n "$TEMP_FILE" -a -f "$TEMP_FILE" ] -then - $LOGTAIL ${LOGFILE} $STATEFILE | grep "[[:space:]][oi][[:space:]]" > ${TEMP_FILE} - ftp_get=`grep "[[:space:]]o[[:space:]]" ${TEMP_FILE} | wc -l` - ftp_put=`grep "[[:space:]]i[[:space:]]" ${TEMP_FILE} | wc -l` +if [ -n "$TEMP_FILE" ] && [ -f "$TEMP_FILE" ]; then + "$LOGTAIL" "$LOGFILE" "$STATEFILE" | grep "[[:space:]][oi][[:space:]]" >"$TEMP_FILE" + ftp_get=$(grep "[[:space:]]o[[:space:]]" "$TEMP_FILE" | wc -l) + ftp_put=$(grep "[[:space:]]i[[:space:]]" "$TEMP_FILE" | wc -l) - /bin/rm -f $TEMP_FILE + /bin/rm -f "$TEMP_FILE" fi -echo "ftp_get.value ${ftp_get}" -echo "ftp_put.value ${ftp_put}" - +echo "ftp_get.value $ftp_get" +echo "ftp_put.value $ftp_put" From 45290417abf9e3558c64f9a42f3afa3ee68d6d43 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:20:35 +0200 Subject: [PATCH 275/695] Plugins eoc_subscribers_count: fix shellcheck issues --- plugins/mail/eoc_subscribers_count | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/mail/eoc_subscribers_count b/plugins/mail/eoc_subscribers_count index 116d0183..e5c2097b 100755 --- a/plugins/mail/eoc_subscribers_count +++ b/plugins/mail/eoc_subscribers_count @@ -17,22 +17,21 @@ #%# capabilities=autoconf if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi -LISTNAME=`basename $0 | sed 's/^eoc_subscribers_count_//g' | tr '_' '@'` +LISTNAME=$(basename "$0" | sed 's/^eoc_subscribers_count_//g' | tr '_' '@') if [ "$1" = "config" ]; then - echo 'graph_title Number of subscribers to '${LISTNAME} + echo "graph_title Number of subscribers to ${LISTNAME}" echo 'graph_vlabel subscribers' echo 'graph_category mailinglist' echo 'subscribers.label subscribers' echo 'subscribers.draw AREA' - echo 'subscribers.info Number of subscribers to the list' + echo 'subscribers.info Number of subscribers to the list' exit 0 fi -echo -n subscribers.value\ -enemies-of-carlotta --name ${LISTNAME} --list | wc -l +printf "subscribers.value %s" "$(enemies-of-carlotta --name "${LISTNAME}" --list | wc -l)" From b59ec071a5c62ac94946f5ef9c80fef3ed9bd9b6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:29:46 +0200 Subject: [PATCH 276/695] Plugin cpu-usage-by-process: fix autoconf output --- plugins/cpu/cpu-usage-by-process | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cpu/cpu-usage-by-process b/plugins/cpu/cpu-usage-by-process index ae6469a6..74a0d6ef 100755 --- a/plugins/cpu/cpu-usage-by-process +++ b/plugins/cpu/cpu-usage-by-process @@ -29,7 +29,7 @@ if [ "$1" = "autoconf" ] ; then if [ -n "$procs" ] ; then echo "yes" else - echo "\$procs not defined." + echo "no (environment variable 'procs' is not defined)" fi exit fi From ef851f0c34b0e22897b22296895215947608760f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 00:30:17 +0200 Subject: [PATCH 277/695] Plugin cpu-usage-by-process: fix shellcheck issues --- plugins/cpu/cpu-usage-by-process | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/cpu/cpu-usage-by-process b/plugins/cpu/cpu-usage-by-process index 74a0d6ef..50131c98 100755 --- a/plugins/cpu/cpu-usage-by-process +++ b/plugins/cpu/cpu-usage-by-process @@ -23,9 +23,10 @@ #%# family=auto #%# capabilities=autoconf +procs=${procs:-} -if [ "$1" = "autoconf" ] ; then +if [ "$1" = "autoconf" ] ; then if [ -n "$procs" ] ; then echo "yes" else @@ -35,29 +36,29 @@ if [ "$1" = "autoconf" ] ; then fi if [ "$1" = "config" ] ; then - echo "graph_args --base 1000 -r --lower-limit 0"; - echo "graph_title CPU usage, by process"; - echo "graph_category processes"; - echo "graph_info This graph shows CPU usage, for monitored processes."; + echo "graph_args --base 1000 -r --lower-limit 0" + echo "graph_title CPU usage, by process" + echo "graph_category processes" + echo "graph_info This graph shows CPU usage, for monitored processes." echo 'graph_vlabel %' echo 'graph_scale no' echo 'graph_period second' echo "graph_order $procs" - - FIRSTPROC=1; - for proc in $procs; do + + FIRSTPROC=1 + for proc in $procs; do echo "${proc}.label $proc" echo "${proc}.info CPU used by process $proc" echo "${proc}.type GAUGE" if [ $FIRSTPROC -eq 1 ] ; then echo "${proc}.draw AREA" - export FIRSTPROC=0; - else + export FIRSTPROC=0 + else echo "${proc}.draw STACK" fi - done ; - + done + exit fi @@ -65,8 +66,7 @@ fi for proc in $procs ; do { - ps axo 'pcpu,comm' | grep "$proc" | - awk ' + ps axo 'pcpu,comm' | grep "$proc" | awk ' BEGIN { FS=" " CPU_PROC=0 @@ -76,9 +76,9 @@ for proc in $procs ; do { CPU_PROC+=$0 } - END { - print "'$proc'.value "CPU_PROC - }' + END { + print "'"$proc"'.value "CPU_PROC + }' } -done; +done From 17f784270ae966ee9a13e9f5104a5b8f925b639e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 2 Aug 2018 02:03:42 +0200 Subject: [PATCH 278/695] Whitespace cleanup * remove trailing whitespace * remove empty lines at the end of files --- plugins/amavis/amavis-debian | 6 +- plugins/amavis/amavis_awk | 14 +- plugins/amule/amule_queue | 4 +- plugins/amule/amule_shares | 4 +- plugins/amule/amule_transfers | 4 +- plugins/amule/amule_uptime | 4 +- plugins/apache/apache_activity | 6 +- .../apache_average_time_last_n_requests | 26 +-- plugins/apache/apache_blackbox | 18 +-- plugins/apache/apache_byprojects/README.md | 6 +- .../apache_byprojects/byprojects_access | 8 +- .../apache_byprojects/byprojects_bandwidth | 8 +- .../byprojects_inout_bandwidth | 8 +- plugins/apache/apache_memmory | 4 +- plugins/apache/apache_servers | 2 +- plugins/apache/apache_smaps | 2 +- plugins/apache/apache_status | 2 +- plugins/apache/apache_threads | 7 +- plugins/apache/apache_tmemmory | 4 +- plugins/apache/apache_users | 4 +- plugins/apache/apache_vhosts/apache_logparser | 14 +- .../apache/apache_vhosts/apache_pipelogger | 10 +- plugins/apache/apache_vhosts/apache_vhosts | 24 +-- plugins/apache/apache_watch_ | 6 +- plugins/apache/page_load | 14 +- plugins/apt/deb_packages/README.md | 6 +- plugins/apt/deb_packages/deb_packages.py | 130 +++++++-------- plugins/arangodb/arangodb_ | 32 ++-- plugins/aris/aris_players | 2 +- .../assp/assp-envelope-recipient-statistics | 4 +- plugins/assp/assp-general-runtime-information | 6 +- plugins/assp/assp-message-statistics | 32 ++-- plugins/assp/assp-smtp-connection-statistics | 4 +- plugins/assp/assp-smtp-handler-statistics | 6 +- plugins/asterisk/asterisk | 2 +- .../asterisk_fax_cancelled | 2 +- .../asterisk_fax_current_sessions | 2 +- .../asterisk_fax_failed_completed | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_iofail | 2 +- .../asterisk_fax_iopartial | 2 +- .../asterisk_fax_licensed_channels | 2 +- .../asterisk_fax_max_concurrent | 2 +- .../asterisk_fax_negotiations_failed | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_nofax | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_partial | 2 +- .../asterisk_fax_protocol_error | 2 +- .../asterisk_14_fax_ffa/asterisk_fax_success | 2 +- .../asterisk_fax_switched2t38 | 2 +- .../asterisk_fax_train_failure | 2 +- .../asterisk_fax_txrx_attempts | 2 +- .../asterisk_14_fax_ffa/fax_show_stats.txt | 2 +- plugins/asterisk/asterisk_channels | 2 +- plugins/asterisk/asterisk_inuse | 2 +- plugins/audit/portaudit | 2 +- plugins/battery/acpi_batt_ | 8 +- plugins/battery/acpi_sys_batt_ | 6 +- plugins/battery/batteries | 12 +- plugins/bigbrother/b3error_ | 4 +- plugins/bind/bind9_rr | 2 +- plugins/boinc/boinc_estwk | 88 +++++------ plugins/boinc/boinc_processes | 38 ++--- plugins/boinc/boinc_projs | 58 +++---- plugins/boinc/boinc_wus | 68 ++++---- plugins/bsd/df_abs_bsd | 2 +- plugins/bsd/df_bsd | 2 +- plugins/bsd/spamd-blacklist-bsd | 2 +- plugins/bsd/spamd-tarpit-bsd | 2 +- plugins/celery/celery_tasks | 14 +- plugins/celery/celery_tasks_states | 20 +-- plugins/ceph/ceph-osd-info | 4 +- plugins/ceph/ceph_capacity | 2 +- plugins/ceph/ceph_osd | 2 +- plugins/change.org/changeorg_signature_count | 2 +- plugins/chat/tinychat_users_ | 4 +- plugins/cherokee/munin-plugin-for-cherokee | 4 +- plugins/chilli/chilli_sessions_ | 2 +- plugins/clamav/clamav | 4 +- plugins/condor/condor_activity_ | 20 +-- plugins/condor/condor_ops_ | 16 +- plugins/condor/condor_queue_ | 8 +- plugins/condor/condor_states_ | 6 +- plugins/courier/courier_log | 2 +- plugins/cpu/cpu_by_process | 2 +- plugins/cpu/multicpu1sec | 8 +- plugins/cpu/process_count | 14 +- plugins/cpu/process_cpushare | 20 +-- plugins/currency/nanopool/nanopool_ | 8 +- plugins/cyrus/cyrus-imapd | 6 +- plugins/darwin/dar_cpuusage | 2 +- plugins/darwin/dar_uptime | 6 +- plugins/darwin/dar_vpnd | 6 +- plugins/dhcp/dhcp-pool | 18 +-- plugins/disk/btrfs_subvol_usage | 4 +- plugins/disk/df_with_nfs | 6 +- plugins/disk/du | 8 +- plugins/disk/e2 | 8 +- plugins/disk/file_age | 16 +- plugins/disk/hdsentinel | 2 +- plugins/disk/hp_temp | 4 +- plugins/disk/lvm_snap_used | 8 +- plugins/disk/megaraid-controller-information | 62 ++++---- .../megaraid-hdd-temperature-using-megacli | 4 +- plugins/disk/quota2percent_ | 48 +++--- plugins/disk/raid | 12 +- plugins/disk/raid-mismatch-count | 8 +- plugins/disk/scsi_queue | 16 +- plugins/disk/snmp__areca_ | 2 +- plugins/disk/snmp__hp_temp | 2 +- plugins/disk/xfs | 2 +- plugins/disk/xfs_frag | 4 +- plugins/dkim/dkimproxy_mails | 8 +- plugins/docker/docker_cpu | 6 +- plugins/docker/docker_memory | 2 +- plugins/dovecot/dovecot | 6 +- plugins/drbd/drbd | 2 +- plugins/drbd/drbd-stat | 2 +- plugins/dspam/dspam_ | 2 +- plugins/dvb/2wcomdsr_ | 4 +- plugins/dvb/femon | 28 ++-- plugins/ejabberd/ejabberd_stats_ | 2 +- plugins/emc/emc_vnx_block_lun_perfdata | 114 +++++++------- plugins/emc/emc_vnx_file_ | 142 ++++++++--------- plugins/fan/ibmfan | 6 +- plugins/firebird/firebird | 14 +- plugins/freeradius/freeradius | 2 +- plugins/ftp/proftpd_count | 2 +- plugins/ftp/pure-ftpd | 2 +- plugins/ftp/pure-ftpd-bw | 2 +- plugins/ftp/pureftpd_traffic | 14 +- plugins/funkytown/denon_x311_volume | 6 +- plugins/games/game | 8 +- plugins/geowebcache/geowebcache-bandwidth | 2 +- plugins/geowebcache/geowebcache-blankitems | 2 +- .../geowebcache/geowebcache-cache-hits-ratio | 2 +- plugins/glance/glance_status | 10 +- plugins/google/google-rank | 22 +-- plugins/google/googlecode | 4 +- plugins/gpu/amd_gpu_ | 22 +-- plugins/gpu/nvidia_gpu_ | 8 +- plugins/gpu/nvidia_smi_ | 8 +- plugins/groupwise/snmp__gwia_bytes_ | 32 ++-- plugins/groupwise/snmp__gwia_msgs_ | 36 ++--- plugins/groupwise/snmp__gwmta_msgs_ | 42 ++--- plugins/groupwise/snmp__gwpoa_ | 28 ++-- plugins/gunicorn/gunicorn_memory_status | 8 +- plugins/gunicorn/gunicorn_status | 6 +- plugins/haproxy/haproxy-bytes | 2 +- plugins/haproxy/haproxy-sessions-by-servers | 2 +- plugins/haproxy/haproxy_abort_backend | 8 +- plugins/haproxy/haproxy_active_backend | 4 +- plugins/haproxy/haproxy_bytes_backend | 8 +- .../haproxy/haproxy_bytes_compressor_backend | 8 +- .../haproxy/haproxy_bytes_compressor_frontend | 8 +- plugins/haproxy/haproxy_bytes_frontend | 8 +- plugins/haproxy/haproxy_denied_backend | 8 +- plugins/haproxy/haproxy_denied_frontend | 8 +- plugins/haproxy/haproxy_errors_backend | 8 +- plugins/haproxy/haproxy_errors_frontend | 8 +- plugins/haproxy/haproxy_queue_backend | 10 +- plugins/haproxy/haproxy_rate_backend | 8 +- plugins/haproxy/haproxy_rate_frontend | 8 +- plugins/haproxy/haproxy_reqrate_frontend | 8 +- .../haproxy_response_compressor_backend | 8 +- .../haproxy_response_compressor_frontend | 8 +- plugins/haproxy/haproxy_responses_backend | 4 +- plugins/haproxy/haproxy_responses_frontend | 4 +- plugins/haproxy/haproxy_sessions_backend | 8 +- plugins/haproxy/haproxy_sessions_frontend | 8 +- .../haproxy/haproxy_sessions_total_backend | 8 +- .../haproxy/haproxy_sessions_total_frontend | 8 +- plugins/haproxy/haproxy_warnings_backend | 8 +- plugins/heimdal/heimdal_kdc_requests | 2 +- plugins/http/http-response-times | 14 +- plugins/http/http_load_ | 28 ++-- plugins/http/http_request_time | 6 +- plugins/http/mongrel_memory | 18 +-- plugins/http/mongrel_process_memory | 22 +-- plugins/http/multi_http_responsetime | 6 +- plugins/icecast/icecast2 | 12 +- plugins/icecast/icecast2_ | 2 +- plugins/icecast/icecast2_all | 2 +- plugins/icecast/icecast2_simple | 10 +- plugins/icecast/icecast_ | 4 +- plugins/imapproxy/imapproxy_multi | 6 +- plugins/ip6/ip6_ | 4 +- plugins/ipvs/ipvs_active | 8 +- plugins/ipvs/ipvs_bps | 2 +- plugins/ipvs/ipvs_conn | 10 +- plugins/ipvs/ipvs_cps | 2 +- plugins/ircd/irc2 | 16 +- plugins/ircd/ircd | 4 +- plugins/jchkmail/jchkmail_counters_ | 12 +- plugins/jenkins/jenkins_nodes_ | 20 +-- .../examples/java/java_process_memory.conf | 2 - plugins/jmx/examples/java/java_threads.conf | 2 - .../jmx/examples/tomcat/catalina_times.conf | 4 +- plugins/jmx/readme.txt | 2 +- plugins/joomla/joomla-sessions | 2 +- plugins/jvm/jstat__gctime | 4 +- plugins/jvm/jstat__heap | 4 +- plugins/jvm/jvm_sun_memory | 8 +- plugins/jvm/jvm_sun_minorgcs | 6 +- plugins/jvm/jvm_sun_tenuredgcs | 4 +- plugins/kamailio/kamailio_memory | 4 +- plugins/kamailio/kamailio_mysql_shared_memory | 4 +- plugins/kamailio/kamailio_transactions_users | 4 +- plugins/keystone/keystone_stats | 4 +- plugins/libvirt/kvm_cpu | 4 +- plugins/libvirt/kvm_io | 14 +- plugins/libvirt/kvm_mem | 10 +- plugins/libvirt/libvirt | 148 +++++++++--------- plugins/libvirt/munin-libvirtpy | 6 +- plugins/licensing/flexlm_ | 4 +- plugins/load/load_extended | 8 +- plugins/logins/logins | 6 +- plugins/lustre/lustre_df | 2 +- plugins/lustre/lustre_df_abs | 10 +- plugins/lustre/lustre_df_absfree | 10 +- plugins/lustre/lustre_df_inodes | 2 +- plugins/lxc/lxc_cpu | 14 +- plugins/lxc/lxc_cpu_time | 4 +- plugins/lxc/lxc_net | 14 +- plugins/lxc/lxc_proc | 20 ++- plugins/lxd/lxd_mem | 2 +- plugins/mail/mail_connections | 2 +- plugins/mail/procmail_ | 6 +- plugins/mailman/mailman-queue-check | 2 +- plugins/mailman/mailman_subscribers | 2 +- plugins/memcached/memcached_bytes_all | 6 +- plugins/memcached/memcached_ext_bytes_ | 2 +- plugins/memcached/memcached_ext_connections_ | 2 +- plugins/memcached/memcached_ext_hits_ | 2 +- plugins/memcached/memcached_ext_items_ | 2 +- plugins/memcached/memcached_ext_requests_ | 2 +- plugins/memcached/memcached_ext_traffic_ | 2 +- plugins/memcached/memcached_multi_ | 12 +- plugins/memcached/memcached_servers_ | 2 +- plugins/memory/kmemsum | 8 +- plugins/memory/proc_memory_status | 14 +- plugins/minecraft/bukkit-jsonapi-players | 2 +- plugins/minecraft/bukkit-jsonapi-ramusage | 2 +- plugins/minecraft/bukkit-jsonapi-tps | 2 +- .../bukkit-statistician-killshostile | 2 +- .../bukkit-statistician-killsneutral | 2 +- .../bukkit-statistician-killspassive | 2 +- plugins/minecraft/minecraft-users-ram_ | 2 +- plugins/moblock/moblock_connections | 6 +- plugins/mod_jk/mod_jk | 4 +- plugins/mogilefs/mogilefsd_activity | 8 +- plugins/mogilefs/mogilefsd_queries | 8 +- plugins/moinmoin/moinoin_pages | 10 +- plugins/mongodb/mongo_lag | 10 +- plugins/mssql/microsoft-sql | 6 +- .../microsoft-sql-buffer-cache-hit-ratio | 2 +- plugins/mssql/microsoft-sql-data-file-sizes | 4 +- plugins/mssql/microsoft-sql-log-file-size | 4 +- plugins/mumble/murmur-stats | 2 +- plugins/munin/healthcheck_url | 4 +- plugins/mysql/mysql-schema-size | 2 +- plugins/mysql/mysql-table-size | 4 +- plugins/mysql/mysql_connections | 10 +- plugins/mysql/mysql_connections_per_user | 8 +- plugins/mysql/mysql_qcache | 2 +- plugins/mysql/mysql_report | 2 +- plugins/mysql/mysql_size_ | 2 +- plugins/mysql/mysql_size_all | 4 +- plugins/mysql/mysql_slave_threads | 2 +- plugins/mythtv/mythtv_programs | 30 ++-- plugins/mythtv/mythtv_status_ | 34 ++-- plugins/nagios/nagios_multi_ | 16 +- plugins/nagios/nagiosstatus | 4 +- plugins/netapp/snmp__netapp_cifs | 16 +- plugins/netapp/snmp__netapp_cpu | 10 +- plugins/netapp/snmp__netapp_diskbusy | 2 +- plugins/netapp/snmp__netapp_nfs3calls | 4 +- plugins/netscaler/snmp__netscaler_connections | 12 +- plugins/netscaler/snmp__netscaler_cpu | 14 +- plugins/network/bandwidth_ | 10 +- plugins/network/bgpd | 2 +- plugins/network/brc_rssi | 4 +- plugins/network/ddclient | 10 +- plugins/network/denyhosts | 2 +- plugins/network/fms | 4 +- plugins/network/fms_apps | 6 +- plugins/network/fms_apps_rate | 6 +- plugins/network/fwbuilder_ | 8 +- plugins/network/hfsc | 2 +- plugins/network/hfsc_sep | 2 +- plugins/network/hostsdeny | 4 +- plugins/network/if | 74 ++++----- plugins/network/if1sec-c.c | 14 +- plugins/network/if_uptime | 6 +- plugins/network/ifem_ | 2 +- plugins/network/interfaces_linux_multi | 14 +- plugins/network/ip_forward_ | 2 +- plugins/network/ipfwnat_ | 4 +- plugins/network/ipt_basic_ | 4 +- plugins/network/ldap_connections | 6 +- plugins/network/netatalk | 2 +- plugins/network/nsd3 | 22 +-- plugins/network/qos_ | 4 +- plugins/network/radio | 2 +- plugins/network/shorewall-accounting_ | 2 +- plugins/network/shorewall_ | 10 +- plugins/network/shorewall_acc | 6 +- plugins/network/tc_drops_ | 4 +- plugins/network/tc_packets_ | 4 +- plugins/network/transmission | 24 +-- plugins/network/ubiquiti_airfiber_ | 26 +-- plugins/network/ubiquiti_airos_ | 28 ++-- plugins/network/umts_sig | 2 +- plugins/network/upnpc_ | 4 +- plugins/network/vnstat | 2 +- plugins/network/vnstat_ | 2 +- plugins/newznab/nn_ | 34 ++-- plugins/nginx/nginx-combined | 22 +-- plugins/nginx/nginx_byprojects/README.md | 10 +- .../nginx/nginx_byprojects/byprojects_access | 8 +- .../nginx_byprojects/byprojects_bandwidth | 8 +- .../byprojects_inout_bandwidth | 10 +- plugins/nginx/nginx_upstream | 2 +- plugins/nginx/nginx_vhost_traffic | 30 ++-- plugins/nova/nova_floating_ips | 2 +- plugins/nova/nova_instance_ | 2 +- plugins/nova/nova_instance_launched | 4 +- plugins/nova/nova_instance_timing | 2 +- plugins/nova/nova_services | 2 +- plugins/nscd/nscd_ | 2 +- plugins/ntp/ntp_kernel_pll_prec | 2 +- plugins/ntp/ntp_kernel_pll_tol | 4 +- plugins/ntp/ntp_peers | 16 +- plugins/ntp/ntp_peers_ipv6 | 8 +- plugins/ntp/ntp_pool_score_ | 4 +- plugins/ntp/ntp_queries | 13 +- plugins/omreport/omreport_pwrmon_current | 2 +- plugins/omreport/omreport_pwrmon_power | 2 +- plugins/omreport/omreport_storage_temp | 2 +- plugins/omreport/omreport_temp | 4 +- plugins/openvz/openvz-load-avg | 10 +- plugins/openvz/vpsmem | 2 +- plugins/openwrt/snmp__memory_openwrt | 2 +- plugins/oracle/oracle-pga-monitor | 14 +- plugins/oracle/oracle-sga | 18 +-- plugins/oracle/oracle__connections | 6 +- plugins/oracle/oracle_connections | 12 +- plugins/oracle/oracle_sysstat | 58 +++---- plugins/other/beanstalkd | 2 +- plugins/other/cm2 | 10 +- plugins/other/delayed_jobs_queue_size | 2 +- plugins/other/earthquakes | 8 +- plugins/other/foldingathome_activecpu | 2 +- plugins/other/globesurfer | 8 +- plugins/other/opentracker_ | 8 +- plugins/other/pid | 2 +- plugins/other/port_ | 4 +- plugins/other/proc_ | 24 +-- plugins/other/s9y | 10 +- plugins/other/xcache_hits | 2 +- plugins/other/xcache_items | 2 +- plugins/passenger/passenger_status | 12 +- plugins/pdns/pdns_rec_qtypes | 6 +- plugins/pf/pf_bytes | 2 +- plugins/pf/pf_openbsd | 2 +- plugins/pf/pf_packets | 2 +- plugins/pf/pf_states | 2 +- plugins/php/eaccelerator | 14 +- plugins/php/eaccelerator-python | 24 +-- plugins/php/eaccelerator-usage | 2 +- plugins/php/php5-fpm_status | 2 +- plugins/php/php_apc_ | 8 +- plugins/php/php_eaccelerator | 12 +- plugins/php/php_errors_ | 6 +- plugins/php/php_opcache.php | 2 +- plugins/php/php_time_execution | 18 +-- plugins/php/php_xcache | 2 +- plugins/ping/fping_ | 4 +- plugins/ping/multi_tcp_ping | 2 +- plugins/ping/multiping | 4 +- plugins/ping/ping-with-ceil | 4 +- plugins/poseidon/snmp__poseidon-sensors | 54 +++---- plugins/postfix/greyfix | 12 +- plugins/postfix/policyd-spf-python | 6 +- plugins/postfix/postfix-queue-size | 2 +- plugins/postfix/postfix_filtered | 16 +- plugins/postfix/postfix_filtered_awk | 14 +- plugins/postfix/postfix_mailfiltered | 4 +- plugins/postfix/postfix_mailqueuelog_ | 6 +- plugins/postfix/postfix_mailstats_ | 14 +- plugins/postfix/postfix_mailvolume_multi | 4 +- plugins/postfix/postfix_stats | 6 +- plugins/postfix/postgrey | 6 +- plugins/postfix/postgrey-new | 6 +- plugins/postgresql/pgbouncer_ | 8 +- plugins/postgresql/postgres_block_read_ | 10 +- plugins/postgresql/postgres_queries2_ | 6 +- plugins/postgresql/postgres_queries3_ | 14 +- plugins/postgresql/postgres_size_detail_ | 2 +- plugins/postgresql/postgres_space_ | 14 +- plugins/postgresql/slony_lag_events_ | 6 +- plugins/postgresql/slony_lag_time | 8 +- plugins/postgresql/slony_lag_time_ | 8 +- plugins/power/apcupsd_pct | 4 +- plugins/power/apcupsd_ww | 8 +- plugins/power/currentcost | 118 +++++++------- plugins/power/nut | 6 +- plugins/power/snmp__ipoman_ | 54 +++---- plugins/power/snmp__sentry | 12 +- plugins/power5/consumed_cpu_cycles | 4 +- plugins/power5/cpu_in_lpar | 4 +- plugins/power5/weight_of_a_lpar | 4 +- plugins/powermta/pmta_ | 82 +++++----- plugins/powermta/powermta_vmta_recpients | 2 +- plugins/printer/hp2600_count_ | 12 +- plugins/printer/hp2600_status_ | 14 +- plugins/printer/snmp__hpclj | 12 +- plugins/printer/xerox-wc3220 | 6 +- plugins/printer/xerox-wc7232-consumables | 8 +- plugins/qmail/qremote | 2 +- plugins/qmail/queuestats | 2 +- plugins/qmail/spamdyke | 10 +- plugins/qpsmtpd/qpsmtpd_mailstats | 12 +- plugins/rabbitmq/rabbitmq_connections | 2 +- plugins/rabbitmq/rabbitmq_consumers | 6 +- plugins/rabbitmq/rabbitmq_messages | 6 +- .../rabbitmq/rabbitmq_messages_unacknowledged | 6 +- .../rabbitmq/rabbitmq_messages_uncommitted | 6 +- plugins/rabbitmq/rabbitmq_queue_memory | 6 +- plugins/rackspace/README | 2 +- plugins/rackspace/rackspace_cdn_count.php | 2 +- plugins/raspberry-pi/cpu_freq_1sec | 6 +- plugins/reddit_karma/reddit_karma_ | 4 +- plugins/redis/redis_ | 16 +- plugins/redis/resque | 4 +- plugins/router/ag241-adsl | 4 +- plugins/router/avm-fritzbox-wan-traffic | 2 +- plugins/router/cisco-epc3010_ | 8 +- plugins/router/conexant_adsl | 4 +- .../d-link-dir-655-router-statistics-plugin | 12 +- plugins/router/dsl-connection-speed | 2 +- plugins/router/dsl-stats | 2 +- plugins/router/freeboxuptime | 6 +- plugins/router/snmp__juniper | 4 +- plugins/router/snmp__juniper_spu | 2 +- plugins/router/snmp__linksys_poe | 2 +- plugins/router/speedport_300 | 4 +- plugins/router/tg585v7__ | 2 +- plugins/rsync/rsyncd_bytes | 2 +- plugins/rsync/rsyncd_count | 2 +- plugins/rtorrent/rtom_mem | 2 +- plugins/s3/s3_items | 48 +++--- plugins/s3/s3_storage | 18 +-- plugins/sabnzbd/sabnzbd_dataleft | 6 +- plugins/sabnzbd/sabnzbd_speed | 6 +- plugins/samba/samba_locked | 8 +- plugins/samba/samba_users | 10 +- .../san/emc_comprehensive/emc_comprehensive | 34 ++-- plugins/scalix/scalix_clients | 4 +- plugins/scalix/scalix_indexwork | 4 +- plugins/scalix/scalix_processes | 2 +- plugins/scalix/scalix_queues | 4 +- plugins/security/forefront_ | 24 +-- plugins/senderscore/senderscore | 4 +- plugins/sendmail/sendmail_mailq | 2 +- plugins/sensors/alertme_keyfobsathome | 4 +- plugins/sensors/alertme_power | 2 +- plugins/sensors/allnet__ | 2 +- plugins/sensors/ip_thermo_125 | 10 +- plugins/sensors/mbmon | 20 +-- plugins/sensors/nvclock | 2 +- plugins/sensors/voltcraft_tcm220_ | 16 +- plugins/sfsnmp/snmp__sfsnmp_fan | 8 +- plugins/sfsnmp/snmp__sfsnmp_temp | 8 +- plugins/sfsnmp/snmp__sfsnmp_volt | 8 +- plugins/sge/sge_queue_ | 8 +- plugins/sge/sge_queue_xml_ | 18 +-- plugins/shoutcast/shoutcast | 2 +- plugins/shoutcast/shoutcast2_multi | 12 +- plugins/sickbeard/sickbeard_episodes | 4 +- plugins/sickbeard/sickbeard_shows | 4 +- plugins/smf/smf_errors | 2 +- plugins/smf/smf_mail_queue | 2 +- plugins/smf/smf_online | 4 +- plugins/smf/smf_stats | 4 +- plugins/smstools/smstools_ | 6 +- plugins/snmp/multi_snmp_querier | 16 +- plugins/snmp/snmp__brocade_ifs | 14 +- plugins/snmp/snmp__cpu_usage | 6 +- plugins/snmp/snmp__cyberpower | 10 +- plugins/snmp/snmp__fn | 22 +-- plugins/snmp/snmp__webthermometer | 2 +- plugins/snmp/snmp__wmsconnectedplayers | 6 +- .../snmp/snmp__wmsplayerallocatedbandwidth | 6 +- plugins/snmp/snmp__wmsstreaminghttpplayers | 6 +- plugins/snmp/snmp__wmsstreamingmmsplayers | 6 +- plugins/snmp/snmp__wmsstreamingplayers | 6 +- plugins/snmp/snmp__wmsstreamingrtspplayers | 6 +- plugins/solaris/forks | 2 +- plugins/solaris/fsstat_act_ | 10 +- plugins/solaris/fsstat_bytes | 6 +- plugins/solaris/interrupts | 2 +- plugins/solaris/io_disk | 14 +- plugins/solaris/solaris-memstat | 6 +- plugins/solr/solr | 6 +- plugins/solr/solr-stats | 2 +- plugins/solr/solr4_ | 18 +-- plugins/solr/solrmulticore | 26 +-- plugins/solr/wfsolr_ | 2 +- plugins/sphinx/sphinx_documents | 2 +- plugins/spotweb/spotweb_average | 4 +- plugins/spotweb/spotweb_cat_average | 12 +- plugins/spotweb/spotweb_cat_total | 10 +- plugins/spotweb/spotweb_catsize_average | 10 +- plugins/spotweb/spotweb_total | 2 +- plugins/squid/squid | 20 +-- plugins/squid/squid_efficiency | 2 +- plugins/squid/squid_times | 10 +- plugins/ssh/hostdenied | 12 +- plugins/ssh/openssh-denyhosts | 6 +- plugins/swap/swapspace-info | 12 +- plugins/syncthing/strelaysrv_ | 10 +- plugins/syncthing/syncthing_ | 8 +- plugins/synology/snmp__synology_hddtemp | 4 +- plugins/synology/snmp__synology_temperature | 2 +- plugins/synology/snmp__synology_ups | 2 +- plugins/system/auth | 2 +- plugins/system/blockhosts | 10 +- plugins/system/cpu_linux_multi | 12 +- plugins/system/fresh-backups | 2 +- plugins/system/hugepages | 2 +- plugins/system/irq | 14 +- plugins/system/pagefaults_by_process | 2 +- plugins/system/raminfo | 16 +- plugins/system/read_serial_temperature | 6 +- plugins/system/total_by_process_ | 2 +- plugins/tarsnap/README.md | 6 +- plugins/tarsnap/tarsnap | 6 +- plugins/teamspeak/teamspeak_user | 12 +- plugins/teamspeak/ts3v2_ | 32 ++-- plugins/teamspeak/tsuser | 6 +- plugins/thin/thin_memory | 22 +-- plugins/thin/thin_threads | 28 ++-- plugins/thin/thins_peak_memory | 26 +-- plugins/tinydns/tinydns_err | 2 +- plugins/tomcat/tomcat_access | 2 +- plugins/tor/tor_ | 12 +- plugins/tplink/tl_sg | 6 +- plugins/user/membyuser | 4 +- plugins/user/multipsu | 6 +- plugins/user/system_users | 2 +- plugins/uwsgi/uwsgi_ | 2 +- plugins/varnish/README-varnish4.md | 2 +- plugins/varnish/varnish2_ | 2 +- plugins/varnish/varnish4_ | 6 +- plugins/vdr/vdr_ | 2 +- plugins/vmware/esxcli_env_ | 2 +- plugins/vmware/esxi | 8 +- plugins/vmware/esxi__sensors | 2 +- plugins/vmware/fusion_ | 110 ++++++------- plugins/vmware/vm_cpu_load | 4 +- plugins/voip/murmur_ice_users | 76 ++++----- plugins/voip/murmur_users | 18 +-- .../voip/murmurice_host_port_id_description | 20 +-- .../murmurice_host_port_id_description_avg | 20 +-- plugins/vpn/openvpn_as_mtime | 6 +- plugins/vpn/openvpn_as_traffic | 2 +- plugins/vpn/openvpn_as_ttime | 4 +- plugins/vpn/openvpn_as_users | 10 +- plugins/vserver/vserver_jiffies | 4 +- plugins/vserver/vserver_limit_hits | 8 +- plugins/vserver/vserver_limits | 4 +- plugins/weather/buienradar_ | 14 +- plugins/weather/novra_s300 | 10 +- plugins/weather/temperatures | 2 +- plugins/weather/wfrog | 6 +- plugins/websphere/webspherelogin | 10 +- plugins/wifi/wifi_signal | 2 +- plugins/wordpress/wordpress | 4 +- plugins/wordpress/wordpress-mu-or-network | 6 +- plugins/wordpress/wordpress2 | 4 +- plugins/wowza/wowza-media-server | 14 +- plugins/wuala/wuala_stats | 6 +- plugins/xastir/xastir | 4 +- plugins/xen/xen-cpu | 16 +- plugins/xen/xen_cpu_v2 | 20 +-- plugins/xen/xen_traffic_all | 2 +- plugins/xen/xen_vbd | 28 ++-- plugins/yacy/yacy | 10 +- plugins/yum/yum_activity | 6 +- plugins/zfs/zfs-filesystem-graph | 6 +- .../zfs/zfs-stats-for-freebsd-arc-efficiency | 4 +- .../zfs/zfs-stats-for-freebsd-arc-utilization | 4 +- ...stats-for-freebsd-cache-hits-by-cache-list | 2 +- ...-stats-for-freebsd-cache-hits-by-data-type | 6 +- .../zfs/zfs-stats-for-freebsd-dmu-prefetch | 6 +- plugins/zfs/zfs_arcstats | 22 +-- plugins/zfs/zfs_cache_efficiency | 2 +- plugins/zfs/zlist | 2 +- plugins/zfs/zpool_capacity | 4 +- plugins/znc/README.md | 2 +- plugins/zope/README-zodb | 4 +- plugins/zope/zeo_monitor_ | 12 +- plugins/zope/zope_cache_parameters | 2 +- plugins/zope/zope_conflict_errors | 8 +- plugins/zope/zope_db_activity | 2 +- 604 files changed, 2927 insertions(+), 2945 deletions(-) diff --git a/plugins/amavis/amavis-debian b/plugins/amavis/amavis-debian index ef73e689..b80e527c 100755 --- a/plugins/amavis/amavis-debian +++ b/plugins/amavis/amavis-debian @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the amavis mail filter for Debian # (based upon a plugin authored by Geoffroy Desvernay) # @@ -32,7 +32,7 @@ BC=${bc:-`which bc`} mktempfile () { mktemp -} +} if [ "$1" = "autoconf" ]; then if [ -f "${AMAVIS_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" -a -n "${BC}" -a -x "${BC}" ] ; then @@ -96,7 +96,7 @@ then virus=`grep 'INFECTED' ${TEMP_FILE} | wc -l` spam=`grep 'Blocked SPAM' ${TEMP_FILE} | wc -l` other=`echo ${total}-${clean}-${virus}-${other}-${spam} | ${BC}` - + /bin/rm -f $TEMP_FILE fi diff --git a/plugins/amavis/amavis_awk b/plugins/amavis/amavis_awk index 154a8d7c..332a2b60 100755 --- a/plugins/amavis/amavis_awk +++ b/plugins/amavis/amavis_awk @@ -2,7 +2,7 @@ # # Plugin to monitor Amavis virus and spam statistics. # -# +# # Based on a routine by William Towle # Uncomment the cdef lines to convert the graph to mails/minute # Comment out the line "total.graph no" to show the total on the graph. This may not be aesthetically pleasing. @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_order clean p_spam b_spam virus total' - + echo 'clean.min 0' echo 'clean.type ABSOLUTE' #echo 'clean.cdef clean,60,*' @@ -70,12 +70,12 @@ fi $LOGTAIL ${MAIL_LOG} $STATEFILE | \ -awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; total= 0 } +awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; total= 0 } - { - - if (index($0, "Passed CLEAN")) { na++ ; total++ } - else if (index($0, "Passed SPAMMY")) { nb++ ; total++ } + { + + if (index($0, "Passed CLEAN")) { na++ ; total++ } + else if (index($0, "Passed SPAMMY")) { nb++ ; total++ } else if (index($0, "Blocked SPAMMY")) { nc++ ; total++ } else if (index($0, "INFECTED")) { nd++ ; total++ } } diff --git a/plugins/amule/amule_queue b/plugins/amule/amule_queue index 649280bb..53f5afd0 100755 --- a/plugins/amule/amule_queue +++ b/plugins/amule/amule_queue @@ -1,13 +1,13 @@ #!/bin/sh # # Plugin to monitor the number of clients in the the aMule queue. -# +# # In order to use this plugin, you need to enable the "Online Signature" feature # available in aMule's configuration options. You will also need to tell aMule to save # the signature file in '/tmp' . # For detailed instructions with screenshots, see http://linux.andreagozzi.com/content/munin_stuff.php # -# +# # Parameters understood: # # config (required) diff --git a/plugins/amule/amule_shares b/plugins/amule/amule_shares index 0b569cf0..33789a36 100755 --- a/plugins/amule/amule_shares +++ b/plugins/amule/amule_shares @@ -1,13 +1,13 @@ #!/bin/sh # # Plugin to monitor the number of shared files with the aMule ed2k/KAD client. -# +# # In order to use this plugin, you need to enable the "Online Signature" feature # available in aMule's configuration options. You will also need to tell aMule to save # the signature file in '/tmp' . # For detailed instructions with screenshots, see http://linux.andreagozzi.com/content/munin_stuff.php # -# +# # Parameters understood: # # config (required) diff --git a/plugins/amule/amule_transfers b/plugins/amule/amule_transfers index cae83ab0..2591c344 100755 --- a/plugins/amule/amule_transfers +++ b/plugins/amule/amule_transfers @@ -1,13 +1,13 @@ #!/bin/sh # # Plugin to monitor the UL/DL speed of the aMule ed2k/KAD client. -# +# # In order to use this plugin, you need to enable the "Online Signature" feature # available in aMule's configuration options. You will also need to tell aMule to save # the signature file in '/tmp' . # For detailed instructions with screenshots, see http://linux.andreagozzi.com/content/munin_stuff.php # -# +# # Parameters understood: # # config (required) diff --git a/plugins/amule/amule_uptime b/plugins/amule/amule_uptime index 9931a1ea..fbf34e39 100755 --- a/plugins/amule/amule_uptime +++ b/plugins/amule/amule_uptime @@ -1,13 +1,13 @@ #!/bin/sh # # Plugin to monitor the current aMule ed2k/KAD client uptime. -# +# # In order to use this plugin, you need to enable the "Online Signature" feature # available in aMule's configuration options. You will also need to tell aMule to save # the signature file in '/tmp' . # For detailed instructions with screenshots, see http://linux.andreagozzi.com/content/munin_stuff.php # -# +# # Parameters understood: # # config (required) diff --git a/plugins/apache/apache_activity b/plugins/apache/apache_activity index 99e0e966..777125fb 100755 --- a/plugins/apache/apache_activity +++ b/plugins/apache/apache_activity @@ -46,13 +46,13 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { } my $ua = LWP::UserAgent->new(timeout => 30); my @badports; - + foreach my $port (@PORTS) { my $url = sprintf $URL, $port; my $response = $ua->request(HTTP::Request->new('GET',$url)); push @badports, $port unless $response->is_success and $response->content =~ /Scoreboard/im; } - + if (@badports) { print "no (no apache server-status on ports @badports)\n"; exit 1; @@ -75,7 +75,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "config") { print $val, "\n"; print "activity_${port}_${char}.type GAUGE\n"; } - } + } exit 0; } diff --git a/plugins/apache/apache_average_time_last_n_requests b/plugins/apache/apache_average_time_last_n_requests index 1a32e602..3524ab02 100755 --- a/plugins/apache/apache_average_time_last_n_requests +++ b/plugins/apache/apache_average_time_last_n_requests @@ -3,7 +3,7 @@ # Raphaël Droz - 2016-01-08 # # Monitors the average time requests matching a custom regexp takes -# For instance monitor time execution of files in http://example.com/foo/bar, +# For instance monitor time execution of files in http://example.com/foo/bar, # requests from google, images etc. # # Simply add an entry in the 'type' hashref and modify the description fields @@ -13,7 +13,7 @@ # # NOTE: You need to add a field in your Apache logs showing time executed. # This is normally done using the %T (seconds) or %D (microseconds) -# For instance: +# For instance: # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %T %v" # Check http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#formats for more info # @@ -62,14 +62,14 @@ my $types = { # any kind of request total => { munin_fields => { - label => 'All requests', + label => 'All requests', draw => 'LINE2', info => 'Average seconds per any request', }, sum => 0, lines => 0, - matches => sub { - return 1; + matches => sub { + return 1; }, }, @@ -82,11 +82,11 @@ my $types = { }, sum => 0, lines => 0, - matches => sub { - my ($fields) = @_; - my $script; - ($script = $fields->[6]) =~ s/\?.*\z //mx; - return $script =~ m{ \.(png|jpe?g|gif|tiff|ilbm|tga) \z }mx; + matches => sub { + my ($fields) = @_; + my $script; + ($script = $fields->[6]) =~ s/\?.*\z //mx; + return $script =~ m{ \.(png|jpe?g|gif|tiff|ilbm|tga) \z }mx; }, }, }; @@ -101,7 +101,7 @@ if (@ARGV && $ARGV[0] eq 'config') { } } exit(0); -} +} my $config_file = `ls -1 $ACCESS_LOG_PATTERN | tail -n 1`; @@ -121,13 +121,13 @@ foreach my $line (@lines) { $types->{$type}->{'lines'}++; } } -} +} } foreach my $type (keys %{$types}) { my $value = $types->{$type}->{'lines'} ? $types->{$type}->{'sum'} / $types->{$type}->{'lines'} : 'U'; printf "%s.value %s\n", ($type, $value); -} +} diff --git a/plugins/apache/apache_blackbox b/plugins/apache/apache_blackbox index 1346aa4e..e5d3c901 100755 --- a/plugins/apache/apache_blackbox +++ b/plugins/apache/apache_blackbox @@ -5,10 +5,10 @@ # luis peralta - luis@11870.com # http://www.ziritione.org # -# Installing: configure apache blackbox and set the logfile to /var/log/blackbox.log +# Installing: configure apache blackbox and set the logfile to /var/log/blackbox.log # or change the BLACKBOXLOG setting below. # -# Dependencies: apache mod_logio, apache blackbox +# Dependencies: apache mod_logio, apache blackbox # http://www.devco.net/archives/2008/03/05/detailed_apache_stats.php # # Last version available at: http://www.ziritione.org/http_status @@ -23,13 +23,13 @@ use strict; -my $BLACKBOXLOG = "/var/log/blackbox.log"; +my $BLACKBOXLOG = "/var/log/blackbox.log"; -my %WANTED = ( "apache.status.200" => "_200", - "apache.status.301" => "_301", - "apache.status.302" => "_302", - "apache.status.404" => "_404", - "apache.status.5xx" => "_5xx", +my %WANTED = ( "apache.status.200" => "_200", + "apache.status.301" => "_301", + "apache.status.302" => "_302", + "apache.status.404" => "_404", + "apache.status.5xx" => "_5xx", ); my $arg = shift(); @@ -78,7 +78,7 @@ graph_total total\n"); ); $num++; } - + } diff --git a/plugins/apache/apache_byprojects/README.md b/plugins/apache/apache_byprojects/README.md index 40ff2f29..736ecae1 100644 --- a/plugins/apache/apache_byprojects/README.md +++ b/plugins/apache/apache_byprojects/README.md @@ -2,15 +2,15 @@ Those plugins are used to monitor different projects or vhost (i.e. either different log files or using regular expression as filters) on the same web server. ## munin_byprojects_access -Count the number of hits per projects/vhost. +Count the number of hits per projects/vhost. ![byproject_access](https://www.mantor.org/~northox/misc/munin-plugins/nginx_byprojects_access1-month.png "byproject_access") ## munin_byprojects_bandwidth -Count the total bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. +Count the total bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_bandwidth-month.png "byproject_bandwidth") ## munin_byprojects_inout_bandwidth -Counts the in/out bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. +Counts the in/out bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_inout_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_inout_bandwidth-month.png "byproject_inout_bandwidth") ## Installation diff --git a/plugins/apache/apache_byprojects/byprojects_access b/plugins/apache/apache_byprojects/byprojects_access index a6dc444b..26b47745 100755 --- a/plugins/apache/apache_byprojects/byprojects_access +++ b/plugins/apache/apache_byprojects/byprojects_access @@ -3,16 +3,16 @@ use strict; # # byprojects_access # -# Perl script to monitor access *byprojects* (e.g. vhost) from multiple files +# Perl script to monitor access *byprojects* (e.g. vhost) from multiple files # and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # # You need logtail (https://www.fourmilab.ch/webtools/logtail/) # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -74,7 +74,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_access.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail : $!"; while () { my $buf = $_; diff --git a/plugins/apache/apache_byprojects/byprojects_bandwidth b/plugins/apache/apache_byprojects/byprojects_bandwidth index 5f6c5372..06546922 100755 --- a/plugins/apache/apache_byprojects/byprojects_bandwidth +++ b/plugins/apache/apache_byprojects/byprojects_bandwidth @@ -3,10 +3,10 @@ use strict; # # byprojects_bandwidth # -# Perl script to monitor total bandwidth *byprojects* (e.g. vhost) from multiple +# Perl script to monitor total bandwidth *byprojects* (e.g. vhost) from multiple # files and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # @@ -17,7 +17,7 @@ use strict; # "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" # where %I is input and %O is output. # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -81,7 +81,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_totalbandwidth.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail : $!"; while () { my $buf = $_; diff --git a/plugins/apache/apache_byprojects/byprojects_inout_bandwidth b/plugins/apache/apache_byprojects/byprojects_inout_bandwidth index 5722ce52..e08bb006 100755 --- a/plugins/apache/apache_byprojects/byprojects_inout_bandwidth +++ b/plugins/apache/apache_byprojects/byprojects_inout_bandwidth @@ -3,10 +3,10 @@ use strict; # # byprojects_inout_bandwidth # -# Perl script to monitor in/out bandwidth *byprojects* (e.g. vhost) from +# Perl script to monitor in/out bandwidth *byprojects* (e.g. vhost) from # multiple files and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # @@ -17,7 +17,7 @@ use strict; # "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" # where %I is input and %O is output. # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -84,7 +84,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_inoutbandwidth.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail : $!"; while () { my $buf = $_; diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 10d89b3b..4e829191 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -36,7 +36,7 @@ PROCS=$binname if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -65,5 +65,5 @@ VAL2=`ps auxf | grep ${PROCS} | grep ^${USR} | grep -v grep | awk '{s+=$6} END { VAL3=`expr $VAL2 / $VAL1` echo "servers.value $VAL3" - + diff --git a/plugins/apache/apache_servers b/plugins/apache/apache_servers index 04238fdb..9d20febe 100755 --- a/plugins/apache/apache_servers +++ b/plugins/apache/apache_servers @@ -34,7 +34,7 @@ USR=$apuser PROCS=$binname if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi diff --git a/plugins/apache/apache_smaps b/plugins/apache/apache_smaps index 3f063c12..abc0974e 100755 --- a/plugins/apache/apache_smaps +++ b/plugins/apache/apache_smaps @@ -8,7 +8,7 @@ # to Apache2::SizeLimit. # Author: Kjetil Kjernsmo , based on work by William Viker -# Copyright (C) 2007 Opera Software ASA +# Copyright (C) 2007 Opera Software ASA # # Contibutors: Earle Nietzel # diff --git a/plugins/apache/apache_status b/plugins/apache/apache_status index 9ca3f043..a17c4043 100755 --- a/plugins/apache/apache_status +++ b/plugins/apache/apache_status @@ -144,7 +144,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ($response->content =~ /^Total Accesses:/im ) { next; } else { - print "no (ExtendedStatus option for apache" + print "no (ExtendedStatus option for apache" . " mod_status is missing on port $port)\n"; exit 0; } diff --git a/plugins/apache/apache_threads b/plugins/apache/apache_threads index a231b392..f7c8fb6a 100755 --- a/plugins/apache/apache_threads +++ b/plugins/apache/apache_threads @@ -32,7 +32,7 @@ GPLv2 if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -81,8 +81,3 @@ done echo "threads.value `echo $((SUM / $COUNT))`" - - - - - diff --git a/plugins/apache/apache_tmemmory b/plugins/apache/apache_tmemmory index 3617c753..9a0c5d55 100755 --- a/plugins/apache/apache_tmemmory +++ b/plugins/apache/apache_tmemmory @@ -35,7 +35,7 @@ PROCS=$binname if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -60,5 +60,5 @@ VAL1=`ps auxf | grep ${PROCS} | grep -v grep | awk '{s+=$6} END {print s}'` VAL2=`expr $VAL1 / 1024` echo "servers.value $VAL2" - + diff --git a/plugins/apache/apache_users b/plugins/apache/apache_users index 4193a1af..bc178f41 100755 --- a/plugins/apache/apache_users +++ b/plugins/apache/apache_users @@ -100,7 +100,7 @@ do else USERNAME[$I]="anonymous" fi - NAME[$I]="_${USERNAME[I]}" # Output sort order + NAME[$I]="_${USERNAME[I]}" # Output sort order else NAME[$I]="${USERNAME[I]}" fi @@ -140,7 +140,7 @@ then FILENAMES=$( find $DIRECTORY -type f -not -wholename $TIMESTAMP | sort) awk '{ printf "%s.label %s\n%s.draw AREA\n", $1, $3, $1 }' $( echo "$FILENAMES" | head -n1 ) - + for FILENAME in $( echo "$FILENAMES" | tail -n+2) do awk '{ printf "%s.label %s\n%s.draw STACK\n", $1, $3, $1 }' $FILENAME diff --git a/plugins/apache/apache_vhosts/apache_logparser b/plugins/apache/apache_vhosts/apache_logparser index 1f427b5e..1da00230 100755 --- a/plugins/apache/apache_vhosts/apache_logparser +++ b/plugins/apache/apache_vhosts/apache_logparser @@ -95,7 +95,7 @@ sub count { #find sitename $file=~s/$site/$1/; $file=$vhost if $vhost; - + # skip broken lines next unless $file; @@ -103,7 +103,7 @@ sub count { my $vpm=clean_fieldname("$file"); $temp{$vpm}{'label'}="$file"; $temp{$vpm}{'label'}=~s/www\.//; - + # count all requests $temp{$vpm}{'requests'}++; @@ -118,13 +118,13 @@ sub count { # average bytes $temp{$vpm}{'avg_bytes'}=$temp{$vpm}{'bytes'}/$temp{$vpm}{'requests'} || 0; } - + # count by status / error code $temp{$vpm}{"status"}{$status}++ if $status; if ($time) { # microsec to millisec - $time=sprintf("%d",$time/1000); + $time=sprintf("%d",$time/1000); # min/max execution time $temp{$vpm}{'max_time'}=max($temp{$vpm}{'max_time'},$time) || 0; @@ -144,9 +144,9 @@ while (1) { # tail files, calls &count with linearray $tail->read; - # begin transaction + # begin transaction $share->lock(LOCK_EX); - + # get data (may be updated by other loggers too) my %old=eval{%{thaw($share->fetch)}}; # using eval to suppress thaw error on empty string at the first run @@ -182,7 +182,7 @@ while (1) { $share->store( freeze \%old ); # end transaction $share->unlock; - + # parse/write every n seconds (plus processing time) sleep $nsec; } diff --git a/plugins/apache/apache_vhosts/apache_pipelogger b/plugins/apache/apache_vhosts/apache_pipelogger index 73c73eee..5bdf9188 100755 --- a/plugins/apache/apache_vhosts/apache_pipelogger +++ b/plugins/apache/apache_vhosts/apache_pipelogger @@ -40,12 +40,12 @@ while () { # sanity check next unless m/^([\d\w\.\-_]+\s){5}([\d\w\.\-_]+$)/; # escaped "." and "-" - + # sitename to munin fieldname my $vpm=clean_fieldname($vhost); $temp{$vpm}{'label'}=$vhost; $temp{$vpm}{'label'}=~s/www\.//; - + # count all requests $temp{$vpm}{'requests'}++; @@ -60,7 +60,7 @@ while () { # average bytes $temp{$vpm}{'avg_bytes'}=$temp{$vpm}{'bytes'}/$temp{$vpm}{'requests'} || 0 if ($bytes); } - + # count by status / error code $temp{$vpm}{"status"}{$status}++ if $status; @@ -77,9 +77,9 @@ while () { }; sub periodic_write { - # begin transaction + # begin transaction $share->lock(LOCK_EX); - + # get data (may be updated by other loggers too) my %old=eval{%{thaw($share->fetch)}}; # using eval to suppress thaw error on empty string at the first run diff --git a/plugins/apache/apache_vhosts/apache_vhosts b/plugins/apache/apache_vhosts/apache_vhosts index 74150dce..a13141ca 100755 --- a/plugins/apache/apache_vhosts/apache_vhosts +++ b/plugins/apache/apache_vhosts/apache_vhosts @@ -18,13 +18,13 @@ This plugin requires data from apache. You can get at the data in two ways: - slightly less performant, but easier to apply to existing installations - If you want response time stats, you have to log them in apache: - LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined-time + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined-time - Configure the log parser to match your installation regarding naming and log folders - -You can use both solutions simultaneously, the data will be merged. + +You can use both solutions simultaneously, the data will be merged. Be aware that a apache log CustomLog directive in the master config will only log those vhosts that have no directive of their own. - + Install plugin conf (after [apache_*]) [apache_vhosts] @@ -36,14 +36,14 @@ env.checks requests bytes time # subgraphs - create multigraph subgraphs (watch your graphing performance...), default 0 # checks - enable stats on bytes and response times per request, you have to log these in apache -A word on performance: +A word on performance: Requests/sec should not be much of a problem. Pipelogger and Logparser should not have man performance problems, as the apply one regex per line and add some stats. -Stats are saved every n seconds (default: 7) to shared mem in serialized format. That should be ok on the most loaded servers (unless you watch cache logs). -I would estimate that > 10k log lines/sec could start becoming a problem, you might have to start tuning there or use a dedicated system. +Stats are saved every n seconds (default: 7) to shared mem in serialized format. That should be ok on the most loaded servers (unless you watch cache logs). +I would estimate that > 10k log lines/sec could start becoming a problem, you might have to start tuning there or use a dedicated system. You might think about splitting the logs over multiple Logparser scripts to parallelize and merge in larger intervals. -Graphing is another matter, the more vhosts you have. -With subgraphs off, you do 3 main graphs * 4 timescales (day, week, month, year). +Graphing is another matter, the more vhosts you have. +With subgraphs off, you do 3 main graphs * 4 timescales (day, week, month, year). With subgraphs on, you get 2 checks * (1 + 6 * #vhosts) + 1 check * (1 + #vhosts * #statuscodes * 4) With hundreds of vhosts that becomes a problem, as munin-update and munin-html do not scale well. @@ -152,7 +152,7 @@ ${site}_${graph}_$check.type GAUGE END } # end graph } # end sites - } # end subgraph + } # end subgraph } # end checks @@ -173,7 +173,7 @@ graph_period minute graph_order $order END foreach my $site (keys %data) { - + print <new (timeout => 30); my @badports; - + foreach my $port (@PORTS) { my $url = sprintf $URL, $port; my $response = $ua->request (HTTP::Request->new('GET', $url)); push @badports, $port unless $response->is_success; } - + if (@badports) { print "no (no mod_watch exists on ports @badports)\n"; exit 1; @@ -76,7 +76,7 @@ foreach my $port (@PORTS) { my ($server, undef, $ifInOctets, $ifOutOctets, $ifRequests, $ifDocuments) = split (/\s/, $string, 6); push @servers, $server unless $server eq "SERVER"; - push @data, "$server $ifInOctets $ifOutOctets $ifRequests $ifDocuments" + push @data, "$server $ifInOctets $ifOutOctets $ifRequests $ifDocuments" unless $server eq "SERVER"; } } diff --git a/plugins/apache/page_load b/plugins/apache/page_load index cc09a885..8c952c68 100755 --- a/plugins/apache/page_load +++ b/plugins/apache/page_load @@ -22,38 +22,38 @@ # # apache2.conf # LogFormat "%h %l %u %t \"%r\" %>s %O %b %D \"%{Referer}i\" \"%{User-Agent}i\"" custom -# +# # According to : http://httpd.apache.org/docs/2.2/mod/mod_log_config.html # %D The time taken to serve the request, in microseconds. # In our case %D -> 9 -###################################################################################################### +###################################################################################################### # GLOBALS LOGFILE="/var/log/apache2/access.log" BUFFER_SIZE=500 ###################################################################################################### - + do_ () { command="tail -n $BUFFER_SIZE $LOGFILE | awk '{sum=sum+\$9} END {print \"exec_time.value \"(sum/$BUFFER_SIZE)/1000000}'" eval $command exit 0 } - + do_config () { echo "graph_title Average page execution time" echo "graph_vlabel Seconds" echo "graph_category webserver" echo "graph_args --base 1000 -l 0" echo "graph_info Average page execution time" - + echo "exec_time.label Execution time" echo "exec_time.type GAUGE" } - + case $1 in config|'') eval do_$1 esac - + exit $? diff --git a/plugins/apt/deb_packages/README.md b/plugins/apt/deb_packages/README.md index 7dbfc489..781df2c5 100644 --- a/plugins/apt/deb_packages/README.md +++ b/plugins/apt/deb_packages/README.md @@ -24,8 +24,8 @@ This plugin has checked on Debian - Wheezy and squeeze. If you want to use it on older installations, tell me whether it works or which errors you had. It should run past python-apt 0.7 and python 2.5. -check out this git repository from - +check out this git repository from + aptitude install python-apt git clone git://github.com/munin-monitoring/contrib.git cd contrib/plugins/apt/deb_packages @@ -51,7 +51,7 @@ A typical configuration looks like this # Age in seconds an $CACHE_FILE can be. If it is older, the script updates # default if not set is 3540 (one hour) # at the moment this is not used, the plugin always runs (if munin calls it) - # + # env.CACHE_FILE_MAX_AGE 3540 # All these numbers are only for sorting, so you can use env.graph01_sort_by_0 diff --git a/plugins/apt/deb_packages/deb_packages.py b/plugins/apt/deb_packages/deb_packages.py index 538a8efc..931d1d8f 100755 --- a/plugins/apt/deb_packages/deb_packages.py +++ b/plugins/apt/deb_packages/deb_packages.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -""" +""" A munin plugin that prints archive and their upgradable packets TODO: make it usable and readable as commandline tool @@ -13,18 +13,18 @@ TODO: separate into 2 graphs sorting a packet to the newest archive (WONTFIX unless someone asks for) -TODO: +TODO: • addinge alternative names for archives "stable -> squeeze" -TODO: add gray as +TODO: add gray as foo.colour 000000 to 'now', '', '', '', '', 'Debian dpkg status file' TODO: update only if system was updated (aptitutde update has been run) • check modification date of /var/cache/apt/pkgcache.bin • cache file must not be older than mod_date of pkgcache.bin + X -TODO: shorten ext_info with getShortestConfigOfOptions -TODO: check whether cachefile matches the config +TODO: shorten ext_info with getShortestConfigOfOptions +TODO: check whether cachefile matches the config • i have no clever idea to do this without 100 lines of code -BUG: If a package will be upgraded, and brings in new dependencies, +BUG: If a package will be upgraded, and brings in new dependencies, these new deps will not be counted. WONTFIX """ import sys @@ -32,7 +32,7 @@ import argparse import apt_pkg from apt.progress.base import OpProgress from time import time, strftime -import os +import os import StringIO import string import re @@ -41,7 +41,7 @@ from types import StringTypes, TupleType, DictType, ListType, BooleanType class EnvironmentConfigBroken(Exception): pass -# print environmental things +# print environmental things # for k,v in os.environ.iteritems(): print >> sys.stderr, "%r : %r" % (k,v) def getEnv(name, default=None, cast=None): @@ -68,14 +68,14 @@ MAX_LIST_SIZE_EXT_INFO = getEnv('MAX_LIST_SIZE_EXT_INFO', default=50, cast=int) STATE_DIR = getEnv('MUNIN_PLUGSTATE', default='.') CACHE_FILE = os.path.join(STATE_DIR, "deb_packages.state") -""" +""" There is no need to execute this script every 5 minutes. The Results are put to this file, next munin-run can read from it CACHE_FILE is usually /var/lib/munin/plugin-state/debian_packages.state """ CACHE_FILE_MAX_AGE = getEnv('CACHE_FILE_MAX_AGE', default=3540, cast=int) -""" +""" Age in seconds an $CACHE_FILE can be. If it is older, the script updates """ @@ -103,9 +103,9 @@ class Apt(object): doc = "apt_pkg.Cache instance, lazy instantiated" def fget(self): class NullProgress(OpProgress): - """ used for do not giving any progress info, - while doing apt things used, cause documented - use of None as OpProgress did not worked in + """ used for do not giving any progress info, + while doing apt things used, cause documented + use of None as OpProgress did not worked in python-apt 0.7 """ def __init__(self): @@ -119,8 +119,8 @@ class Apt(object): def update(*args,**kwords): pass - if self._cache is None: - self._cache = apt_pkg.Cache(NullProgress()) + if self._cache is None: + self._cache = apt_pkg.Cache(NullProgress()) return self._cache return locals() @@ -129,7 +129,7 @@ class Apt(object): doc = "apt_pkg.DepCache object" def fget(self): - if self._depcache is None: + if self._depcache is None: self._depcache = apt_pkg.DepCache(self.cache) return self._depcache @@ -180,7 +180,7 @@ apt = Apt() apt.installedPackages apt.upgradablePackages - initialisation is lazy + initialisation is lazy """ def weightOfPackageFile(detail_tuple, option_tuple): @@ -214,7 +214,7 @@ def Tree(): class TreeTwig(defaultdict): def __init__(self, defaultFactory): - super(TreeTwig, self).__init__(defaultFactory) + super(TreeTwig, self).__init__(defaultFactory) def printAsTree(self, indent=0): for k, tree in self.iteritems(): @@ -245,16 +245,16 @@ class TreeTwig(defaultdict): def getShortestConfigOfOptions(optionList = ['label', 'archive', 'site']): - """ + """ tries to find the order to print a tree of the optionList - with the local repositories with the shortest line + with the local repositories with the shortest line possible options are: 'component' 'label' 'site' 'archive' - 'origin' - 'architecture' + 'origin' + 'architecture' Architecture values are usually the same and can be ignored. tells you which representation of a tree as line is shortest. @@ -262,19 +262,19 @@ def getShortestConfigOfOptions(optionList = ['label', 'archive', 'site']): to write the shortest readable output. """ l = optionList # just because l is much shorter - + # creating possible iterations fieldCount = len(optionList) if fieldCount == 1: selection = l elif fieldCount == 2: - selection = [(x,y) - for x in l + selection = [(x,y) + for x in l for y in l if x!=y ] elif fieldCount == 3: - selection = [(x,y,z) - for x in l - for y in l if x!=y + selection = [(x,y,z) + for x in l + for y in l if x!=y for z in l if z!=y and z!=x] else: raise Exception("NotImplemented for size %s" % fieldCount) @@ -289,7 +289,7 @@ def getShortestConfigOfOptions(optionList = ['label', 'archive', 'site']): r = min( d.items(), key=lambda x: x[1] ) return list(r[0]), r[1] - + def getOptionsTree(cache, keys=None): """ t = getOptionsTree(cache, ['archive', 'site', 'label']) @@ -322,16 +322,16 @@ def createKey(key, file): """ if type(key) in StringTypes: return file.__getattribute__(key) - elif type(key) in (TupleType, ListType): + elif type(key) in (TupleType, ListType): nKey = tuple() for pKey in key: nKey = nKey.__add__((file.__getattribute__(pKey),)) return nKey else: - raise Exception("Not implemented for keytype %s" % type(key)) + raise Exception("Not implemented for keytype %s" % type(key)) def getOptionsTree2(cache, primary=None, secondary=None): - """ + """ primary muss ein iterable oder StringType sein secondary muss iterable oder StringType sein t1 = getOptionsTree2(apt.cache, 'origin', ['site', 'archive']) @@ -369,24 +369,24 @@ def getOptionsTree2(cache, primary=None, secondary=None): dKey = file.__getattribute__(sKey) d = d[dKey] return t - + #def getAttributeSet(iterable, attribute): # return set(f.__getattribute__(attribute) for f in iterable) # #def getOrigins(cache): -# return getAttributeSet(cache.file_list, 'origin') +# return getAttributeSet(cache.file_list, 'origin') # #def getArchives(cache): -# return getAttributeSet(cache.file_list, 'archive') +# return getAttributeSet(cache.file_list, 'archive') # #def getComponents(cache): -# return getAttributeSet(cache.file_list, 'component') +# return getAttributeSet(cache.file_list, 'component') # #def getLabels(cache): -# return getAttributeSet(cache.file_list, 'label') +# return getAttributeSet(cache.file_list, 'label') # #def getSites(cache): -# return getAttributeSet(cache.file_list, 'site') +# return getAttributeSet(cache.file_list, 'site') # class PackageStat(defaultdict): @@ -397,16 +397,16 @@ class PackageStat(defaultdict): with some abilities to print output munin likes """ - sortDict = { 'label': defaultdict( lambda : 20, - {'Debian': 90, + sortDict = { 'label': defaultdict( lambda : 20, + {'Debian': 90, '' : 1, 'Debian Security' : 90, 'Debian Backports': 90}), 'archive': defaultdict( lambda : 5, - { 'now': 0, + { 'now': 0, 'experimental': 10, - 'unstable': 50, - 'sid': 50, + 'unstable': 50, + 'sid': 50, 'testing': 70, 'wheezy': 70, 'squeeze-backports': 80, @@ -426,9 +426,9 @@ class PackageStat(defaultdict): } """ Values to sort options (label, archive, origin ...) - (0..99) is allowed. + (0..99) is allowed. (this is needed for other graphs to calc aggregated weights) - higher is more older and more official or better + higher is more older and more official or better """ dpkgStatusValue = { 'site': '', 'origin': '', 'label': '', 'component': '', 'archive': 'now' } @@ -443,7 +443,7 @@ class PackageStat(defaultdict): 'component' : 10**2, } """ - Dict that stores multipliers + Dict that stores multipliers to compile a sorting value for each archivefile """ @@ -483,7 +483,7 @@ class PackageStat(defaultdict): def addPackage(self, sourceFile, package): if self.packetHandler.decider(package): self.packetHandler.adder(package, self) - + @classmethod def configD(cls, key, value): i = { 'rrdName': cls.generate_rrd_name_from(key), @@ -514,8 +514,8 @@ class PackageStat(defaultdict): print "{rrdName}.draw AREASTACK".format(**i) def optionIsDpkgStatus(self, details, options=None): - """ - give it details and options and it tells you whether the datails looks like they come from + """ + give it details and options and it tells you whether the datails looks like they come from a 'Debian dpkg status file'. """ # setting defaults @@ -530,7 +530,7 @@ class PackageStat(defaultdict): return isNow def printValues(self): - print "\nmultigraph packages_{option}_{type}".format(option=self.generate_rrd_name_from(self.option), + print "\nmultigraph packages_{option}_{type}".format(option=self.generate_rrd_name_from(self.option), type=self.packetHandler.type) for options, item in self.options_sorted: if not self.packetHandler.includeNow and self.optionIsDpkgStatus(details=options): @@ -555,7 +555,7 @@ packetHandlerD = {} class PacketHandler(object): """ - Baseclass, that represents the Interface which is used + Baseclass, that represents the Interface which is used """ type = None @@ -591,7 +591,7 @@ class PacketHandler(object): return weightOfPackageFile(details, options) class PacketHandlerUpgradable(PacketHandler): - + type='upgradable' includeNow = False extInfoItemString = " {i[0].name} <{i[1]} -> {i[2]}>" @@ -628,7 +628,7 @@ class PacketHandlerInstalled(PacketHandler): # this item (as i) is used for input in extInfoItemString item = package packageStat[keys].append(item) - + # registering PackageHandler for Usage packetHandlerD[PacketHandlerInstalled.type] = PacketHandlerInstalled @@ -637,7 +637,7 @@ class Munin(object): def __init__(self, commandLineArgs=None): self.commandLineArgs = commandLineArgs self.argParser = self._argParser() - self.executionMatrix = { + self.executionMatrix = { 'config': self.config, 'run' : self.run, 'autoconf' : self.autoconf, @@ -685,7 +685,7 @@ class Munin(object): else: raise Exception('DPKG-statusfile %r not found, really strange!!!'%dpkgStatusFile) newestFileTimestamp = max(timeL) - age = newestFileTimestamp - cacheMTime + age = newestFileTimestamp - cacheMTime if age > 0: return True else: @@ -709,7 +709,7 @@ class Munin(object): # cacheNeedUpdate = True if self._cacheIsOutdated() or self.args.nocache: - # save stdout + # save stdout stdoutDef = sys.stdout try: out = StringIO.StringIO() @@ -765,7 +765,7 @@ class Munin(object): def _argParser(self): parser = argparse.ArgumentParser(description="Show some statistics "\ "about debian packages installed on system by archive", - ) + ) parser.set_defaults(command='run', debug=True, nocache=True) parser.add_argument('--nocache', '-n', default=False, action='store_true', @@ -775,7 +775,7 @@ class Munin(object): run ........ munin run (writes values) autoconf ... writes 'yes' """ - parser.add_argument('command', nargs='?', + parser.add_argument('command', nargs='?', choices=['config', 'run', 'autoconf', 'drun'], help='mode munin wants to use. "run" is default' + helpCommand) return parser @@ -783,10 +783,10 @@ class Munin(object): def _envParser(self): """ reads environVars from [deb_packages] and generate - a list of dicts, each dict holds a set of settings made in + a list of dicts, each dict holds a set of settings made in munin config. - [ - { 'type' = 'installed', + [ + { 'type' = 'installed', 'sort_by' = ['label', 'archive'], 'show_ext' = ['origin', 'site'], }, @@ -816,7 +816,7 @@ class Munin(object): configPart['show_ext'][m.group('optNumber')] = os.getenv(var) else: print >> sys.stderr, "configuration option %r was ignored" % (var) - # we have now dicts for 'sort_by' and 'show_ext' keys + # we have now dicts for 'sort_by' and 'show_ext' keys # changing them to lists for graphConfig in config.itervalues(): graphConfig['sort_by'] = [val for key, val in sorted(graphConfig['sort_by'].items())] @@ -839,13 +839,13 @@ class Munin(object): "Graph must be sorted by anything" raise EnvironmentConfigBroken("Environment Config broken") # check for valid options for sort_by - unusableOptions = set(graph['sort_by']) - PackageStat.viewSet - if unusableOptions: + unusableOptions = set(graph['sort_by']) - PackageStat.viewSet + if unusableOptions: print >> sys.stderr, \ "%r are not valid options for 'sort_by'" % (unusableOptions) raise EnvironmentConfigBroken("Environment Config broken") # check for valid options for sort_by - unusableOptions = set(graph['show_ext']) - PackageStat.viewSet + unusableOptions = set(graph['show_ext']) - PackageStat.viewSet if unusableOptions: print >> sys.stderr, \ "%r are not valid options for 'show_ext'" % (x) diff --git a/plugins/arangodb/arangodb_ b/plugins/arangodb/arangodb_ index a90863af..4ee21e92 100755 --- a/plugins/arangodb/arangodb_ +++ b/plugins/arangodb/arangodb_ @@ -1,7 +1,7 @@ #!/usr/bin/python """ -Plugin to monitor ArangoDB servers. It works with the new server statistics +Plugin to monitor ArangoDB servers. It works with the new server statistics interface of ArangoDB 1.3. Not every value seems senseful, but there are nice graphs generated... @@ -30,19 +30,19 @@ Usage: Links possible: arangodb_conn HTTP client connections arangodb_time_total Total request/queue/connection time - arangodb_bytes_total Total sent/received bytes + arangodb_bytes_total Total sent/received bytes Configuration: - No configuration required. Just enable the admin interface of ArangoDB. -Thanks to the authors of other Python munin plugins. I've used some of -them as inspiring example. +Thanks to the authors of other Python munin plugins. I've used some of +them as inspiring example. Possible todos: - support of munin-like configuration parameters - add more statistics - + """ from os.path import basename @@ -57,7 +57,7 @@ except ImportError: def getServerStatus(group): raw = urllib2.urlopen( "http://127.0.0.1:8529/_admin/statistics" ).read() - + return json.loads( raw )[group] def doData(plugin_name): @@ -66,26 +66,26 @@ def doData(plugin_name): elif plugin_name== 'arangodb_time_total': data = getServerStatus('client') - timeTotal = data['totalTime']['sum'] - timeConnection = data['connectionTime']['sum'] - timeRequest = data['requestTime']['sum'] - timeQueue = data['queueTime']['sum'] - + timeTotal = data['totalTime']['sum'] + timeConnection = data['connectionTime']['sum'] + timeRequest = data['requestTime']['sum'] + timeQueue = data['queueTime']['sum'] + print "total.value " + str(int(round(timeTotal))) print "connection.value " + str(int(round(timeConnection))) print "request.value " + str(int(round(timeRequest))) print "queue.value " + str(int(round(timeQueue))) - + elif plugin_name== 'arangodb_bytes_total': data = getServerStatus('client') bytesReceived = data['bytesReceived']['sum'] bytesSent = data['bytesSent']['sum'] print "received.value " + str(int(round(bytesReceived))) print "sent.value " + str(int(round(bytesSent))) - + else: pass - + def doConfig(plugin_name): if plugin_name == 'arangodb_conn': print "graph_title ArangoDB current connections" @@ -103,7 +103,7 @@ def doConfig(plugin_name): print "connection.label connection" print "request.label request" print "queue.label queue" - + elif plugin_name == 'arangodb_bytes_total': print "graph_title ArangoDB total bytes" print "graph_args --base 1024" @@ -125,7 +125,7 @@ def doConfig(plugin_name): else: pass - + plugin_name = basename(sys.argv[0]) diff --git a/plugins/aris/aris_players b/plugins/aris/aris_players index b783f92c..95e95cc2 100755 --- a/plugins/aris/aris_players +++ b/plugins/aris/aris_players @@ -18,7 +18,7 @@ # Parameters: # # config (required) -# +# # #%# family=manual diff --git a/plugins/assp/assp-envelope-recipient-statistics b/plugins/assp/assp-envelope-recipient-statistics index 98366914..e21af08b 100755 --- a/plugins/assp/assp-envelope-recipient-statistics +++ b/plugins/assp/assp-envelope-recipient-statistics @@ -73,13 +73,13 @@ unless( defined(@ARGV) ){ # =============== THE GET ==================== if( $ARGV[0] eq "" ){ my $agent = LWP::UserAgent->new(); - $agent->agent("$agentname"); + $agent->agent("$agentname"); $response = $agent->get( $url ); &response_error() unless $response->is_success; @content = split( /\n/, $response->content ); my $line = ""; - my $count = $index{from}; + my $count = $index{from}; my $label; my( $key, $value, $last ); while( 1 ){ diff --git a/plugins/assp/assp-general-runtime-information b/plugins/assp/assp-general-runtime-information index 931b0ed9..32aa0349 100755 --- a/plugins/assp/assp-general-runtime-information +++ b/plugins/assp/assp-general-runtime-information @@ -12,7 +12,7 @@ use strict; use warnings; -use File::Basename; +use File::Basename; use LWP; use Mail::Sendmail; @@ -53,11 +53,11 @@ unless( defined(@ARGV) ){ # =============== THE GET ==================== if( $ARGV[0] eq "" ){ my $agent = LWP::UserAgent->new(); - $agent->agent("$agentname"); + $agent->agent("$agentname"); $response = $agent->get( $url ); &response_error() unless $response->is_success; @content = split( /\n/, $response->content ); - + my $line = ""; my $count = $index{from}; my $label; diff --git a/plugins/assp/assp-message-statistics b/plugins/assp/assp-message-statistics index 0657dbd0..78032338 100755 --- a/plugins/assp/assp-message-statistics +++ b/plugins/assp/assp-message-statistics @@ -17,23 +17,23 @@ use LWP; use Mail::Sendmail; # -------------------------- DEBUG VARS --------------------------------- -my $DEBUG = 0; # for debugging purpose -my $EMAILDEBUG = 0; # for email debugging -my $pluginname = &basename( "$0" ); # get the basename of the plugin -my @to = qw( webmaster@bguel.info ); # the list of admins receivced messages on an -my $from = "$pluginname-at-host\@guel.info"; # the host from where it comes -my $muninnodename = "mail.guel.info"; # the Node from where it comes -my $smtp = "mail.guel.info"; # the smtp relay to send the mail +my $DEBUG = 0; # for debugging purpose +my $EMAILDEBUG = 0; # for email debugging +my $pluginname = &basename( "$0" ); # get the basename of the plugin +my @to = qw( webmaster@bguel.info ); # the list of admins receivced messages on an +my $from = "$pluginname-at-host\@guel.info"; # the host from where it comes +my $muninnodename = "mail.guel.info"; # the Node from where it comes +my $smtp = "mail.guel.info"; # the smtp relay to send the mail # ------------------------- GLOBAL VARS --------------------------------- -my $version = "1.0"; # UA Version -my $agentname = "$pluginname Munin Plugin V$version"; # UA String -my $url = "http://localhost:55553/"; # (defaults to localhost) -my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my $version = "1.0"; # UA Version +my $agentname = "$pluginname Munin Plugin V$version"; # UA String +my $url = "http://localhost:55553/"; # (defaults to localhost) +my $response = 0; # the server output +my @content = (); # the content we're retrive from $response my %index = ( # for Version 2 - 'from' => 66, # <-- index frame from ( a tweak for other ASSP Versions ) - 'to' => 100 # <-- index frame to ( "" ) + 'from' => 66, # <-- index frame from ( a tweak for other ASSP Versions ) + 'to' => 100 # <-- index frame to ( "" ) ); @@ -85,13 +85,13 @@ unless( defined(@ARGV) ){ # =============== THE GET ==================== if( $ARGV[0] eq "" ){ my $agent = LWP::UserAgent->new(); - $agent->agent("$agentname"); + $agent->agent("$agentname"); $response = $agent->get( $url ); &response_error() unless $response->is_success; @content = split( /\n/, $response->content ); my $line = ""; - my $count = $index{from}; + my $count = $index{from}; my $label; my( $key, $value, $last ); while( 1 ){ diff --git a/plugins/assp/assp-smtp-connection-statistics b/plugins/assp/assp-smtp-connection-statistics index 4caabb9f..ddddd1e0 100755 --- a/plugins/assp/assp-smtp-connection-statistics +++ b/plugins/assp/assp-smtp-connection-statistics @@ -63,13 +63,13 @@ unless( defined(@ARGV) ){ # =============== THE GET ==================== if( $ARGV[0] eq "" ){ my $agent = LWP::UserAgent->new(); - $agent->agent("$agentname"); + $agent->agent("$agentname"); $response = $agent->get( $url ); &response_error() unless $response->is_success; @content = split( /\n/, $response->content ); my $line = ""; - my $count = $index{from}; + my $count = $index{from}; my $label; my( $key, $value, $last ); while( 1 ){ diff --git a/plugins/assp/assp-smtp-handler-statistics b/plugins/assp/assp-smtp-handler-statistics index c43570f6..c80e5083 100755 --- a/plugins/assp/assp-smtp-handler-statistics +++ b/plugins/assp/assp-smtp-handler-statistics @@ -12,7 +12,7 @@ use strict; use warnings; -use File::Basename; +use File::Basename; use LWP; use Mail::Sendmail; @@ -53,11 +53,11 @@ unless( defined(@ARGV) ){ # =============== THE GET ==================== if( $ARGV[0] eq "" ){ my $agent = LWP::UserAgent->new(); - $agent->agent("$agentname"); + $agent->agent("$agentname"); $response = $agent->get( $url ); &response_error() unless $response->is_success; @content = split( /\n/, $response->content ); - + my $line = ""; my $count = $index{from}; my $label; diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index abd87f2c..1810fbb9 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -193,7 +193,7 @@ multigraph asterisk_codecs graphs_title Asterisk channels per codec graph_args --base 1000 -l 0 graph_vlabel channels -graph_category voip +graph_category voip END foreach my $codec (@CODECS) { diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled index 9351eb4a..7139f770 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_cancelled @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions index f3155b21..421c7e5f 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_current_sessions @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed index 1b115136..9fd45f1f 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_failed_completed @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail index ef31b90d..b3c84623 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iofail @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial index d94a0d85..67f2604d 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_iopartial @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels index 5b779350..863e9c2d 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_licensed_channels @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent index 998bd139..7fa2e46c 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_max_concurrent @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed index fd402f63..102c44e2 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_negotiations_failed @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax index b284676b..658d126f 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_nofax @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial index 81ede198..4f09016d 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_partial @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error index 5ee5c896..11d13afc 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_protocol_error @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success index 15ce0d71..6060d919 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_success @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 index 7980aa00..1be0f3a8 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_switched2t38 @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure index 9252ff75..c41ab216 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_train_failure @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts index cd495ea9..55767225 100755 --- a/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts +++ b/plugins/asterisk/asterisk_14_fax_ffa/asterisk_fax_txrx_attempts @@ -77,7 +77,7 @@ address with the copyright notice upgrade with your name. use strict; use Asterisk::AMI; - eval "use Asterisk::AMI"; + eval "use Asterisk::AMI"; print "Asterisk::AMI not found. Exiting...\n" if $@; exit( 0 ) if $@; diff --git a/plugins/asterisk/asterisk_14_fax_ffa/fax_show_stats.txt b/plugins/asterisk/asterisk_14_fax_ffa/fax_show_stats.txt index a85a9ffe..300f8926 100644 --- a/plugins/asterisk/asterisk_14_fax_ffa/fax_show_stats.txt +++ b/plugins/asterisk/asterisk_14_fax_ffa/fax_show_stats.txt @@ -31,7 +31,7 @@ Receive Attempts : 0 Completed FAXes : 0 Failed FAXes : 0 -Digium G.711 +Digium G.711 Licensed Channels : 1 Max Concurrent : 0 Success : 0 diff --git a/plugins/asterisk/asterisk_channels b/plugins/asterisk/asterisk_channels index a69c4af4..fde261ff 100755 --- a/plugins/asterisk/asterisk_channels +++ b/plugins/asterisk/asterisk_channels @@ -5,7 +5,7 @@ # Converted to /bin/sh by Steve Schnepp # June 2012 # Version 1.0 -# +# # Run as root or make sure munin user can run 'asterisk -r' command # # [asterisk_channels] diff --git a/plugins/asterisk/asterisk_inuse b/plugins/asterisk/asterisk_inuse index f782bf24..837890e1 100755 --- a/plugins/asterisk/asterisk_inuse +++ b/plugins/asterisk/asterisk_inuse @@ -1,7 +1,7 @@ #!/bin/sh # # add this into munin/plugin-conf.d/plugins.conf: -# +# # [asterisk_inuse] # user root # diff --git a/plugins/audit/portaudit b/plugins/audit/portaudit index f067e1eb..f7cced66 100755 --- a/plugins/audit/portaudit +++ b/plugins/audit/portaudit @@ -16,7 +16,7 @@ PORTAUDIT=/usr/local/sbin/portaudit if [ "$1" = "autoconf" ]; then if [ -x $PORTAUDIT ]; then - echo yes + echo yes exit 0 else echo no '(portaudit not found)' diff --git a/plugins/battery/acpi_batt_ b/plugins/battery/acpi_batt_ index a03e4b1c..3e625809 100755 --- a/plugins/battery/acpi_batt_ +++ b/plugins/battery/acpi_batt_ @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# -*- perl -*- +# -*- perl -*- =head1 NAME @@ -41,8 +41,8 @@ The plugin shows: #%# family=power -=head1 VERSION -=head1 BUGS +=head1 VERSION +=head1 BUGS None known. @@ -99,7 +99,7 @@ sub read_data $val = "unknown"; } elsif ( $var ne "batery type" or $var ne "serial number" or $var ne "OEM info" ) - { + { @tmp = split(" " ,$val); $val = trim($tmp[0]); } diff --git a/plugins/battery/acpi_sys_batt_ b/plugins/battery/acpi_sys_batt_ index e8937610..40c8d127 100755 --- a/plugins/battery/acpi_sys_batt_ +++ b/plugins/battery/acpi_sys_batt_ @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# -*- perl -*- +# -*- perl -*- =head1 NAME @@ -36,8 +36,8 @@ The plugin shows: #%# family=power -=head1 VERSION -=head1 BUGS +=head1 VERSION +=head1 BUGS None known. diff --git a/plugins/battery/batteries b/plugins/battery/batteries index 197e9b7c..05aa480c 100755 --- a/plugins/battery/batteries +++ b/plugins/battery/batteries @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# -*- perl -*- +# -*- perl -*- =head1 NAME @@ -35,11 +35,11 @@ The plugin shows: #%# family=power #%# capabilities=autoconf -=head1 VERSION +=head1 VERSION 1.0 -=head1 BUGS +=head1 BUGS None known. @@ -219,7 +219,7 @@ sub read_data } -my $graphs = +my $graphs = { 'batteryes_capacity' => { 'vlabel' => 'Capacity, Ah', 'title' => '%s capacity', 'args' => '--base 1000', 'fields' => [qw/design_capacity last_full_capacity design_capacity_low design_capacity_warning capacity_granularity_1 capacity_granularity_2 remaining_capacity/] }, @@ -231,7 +231,7 @@ my $graphs = 'fields' => [qw/present_rate/] } }; -my $fields = +my $fields = { 'design_capacity' => { 'source' => 'both', 'draw' => 'AREA' , 'label' => 'Design capacity' , 'info' => 'Battery design capacity' }, 'last_full_capacity' => { 'source' => 'both', 'draw' => 'AREA' , 'label' => 'Last full capacity' , 'info' => 'Battery full charge capacity' }, @@ -250,7 +250,7 @@ my $fields = # ------------------------------------ start here ----------------------------------- -if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) +if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) { printf("%s\n", (-e $proc_path or -e $sys_path) ? "yes" : "no ($proc_path and $sys_path not exists)"); exit (0); diff --git a/plugins/bigbrother/b3error_ b/plugins/bigbrother/b3error_ index 23741fe2..853bb544 100755 --- a/plugins/bigbrother/b3error_ +++ b/plugins/bigbrother/b3error_ @@ -23,7 +23,7 @@ STATEFILE=$MUNIN_PLUGSTATE/${B3NAME}.offset mktempfile () { mktemp -t $1 -} +} if [ "$1" = "autoconf" ]; then @@ -49,7 +49,7 @@ fi if [ "$1" = "config" ]; then echo "graph_title BigBrotherBot errors - ${B3NAME}" - cat <<'EOM' + cat <<'EOM' graph_args --base 1000 -l 0 graph_vlabel Errors graph_category Games diff --git a/plugins/bind/bind9_rr b/plugins/bind/bind9_rr index 1e29be03..e510f556 100755 --- a/plugins/bind/bind9_rr +++ b/plugins/bind/bind9_rr @@ -84,7 +84,7 @@ sub do_stats { if ($3 eq 'IN' and $4 !~ /^TYPE/) { my $crr = lc $2; $IN{$crr}++; - } + } } } close(Q); diff --git a/plugins/boinc/boinc_estwk b/plugins/boinc/boinc_estwk index 0042f19e..47d4cb00 100755 --- a/plugins/boinc/boinc_estwk +++ b/plugins/boinc/boinc_estwk @@ -288,7 +288,7 @@ Linux machines running BOINC and munin-node - or - -Linux servers (running munin-node) used to collect data from other systems +Linux servers (running munin-node) used to collect data from other systems which are running BOINC, but not running munin-node (e.g. non-Linux systems) =head1 CONFIGURATION @@ -319,48 +319,48 @@ Warning level - minimum estimated work (default: 24.00 hours) =item B -Password for BOINC (default: none) +Password for BOINC (default: none) =back =head2 B -Using of variable B poses a security risk. Even if the Munin -configuration file for this plugin containing BOINC-password is properly -protected, the password is exposed as environment variable and finally passed -to boinccmd as a parameter. It is therefore possible for local users of the -machine running this plugin to eavesdrop the BOINC password. +Using of variable B poses a security risk. Even if the Munin +configuration file for this plugin containing BOINC-password is properly +protected, the password is exposed as environment variable and finally passed +to boinccmd as a parameter. It is therefore possible for local users of the +machine running this plugin to eavesdrop the BOINC password. -Using of variable password is therefore strongly discouraged and is left here +Using of variable password is therefore strongly discouraged and is left here as a legacy option and for testing purposes. -It should be always possible to use B variable instead - in such case -the file gui_rpc_auth.cfg is read by boinccmd binary directly. -If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg -can be copied to special directory in a secure way (e.g. via scp) and properly +It should be always possible to use B variable instead - in such case +the file gui_rpc_auth.cfg is read by boinccmd binary directly. +If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg +can be copied to special directory in a secure way (e.g. via scp) and properly protected by file permissions. =head1 INTERPRETATION -This plugin shows the estimated remaining computation time for all CPUs of +This plugin shows the estimated remaining computation time for all CPUs of the machine and the estimated remaining computation time of longest workunit. -The estimation is based on assumption that the workunits of different lengths +The estimation is based on assumption that the workunits of different lengths will be distributed to the CPUs evenly (which is not always the case). -The warning level can be used to warn in forward about the risk of workunits +The warning level can be used to warn in forward about the risk of workunits local cache depletion and BOINC client running out of the work. -Although such warning can be achieved by configuring Munin master, there is +Although such warning can be achieved by configuring Munin master, there is also this option to configure it on munin-node side. =head1 EXAMPLES =head2 Local BOINC Example -BOINC is running on local machine. The BOINC binaries are installed in +BOINC is running on local machine. The BOINC binaries are installed in F, the BOINC is running in directory -F under username boinc, group boinc and the password is used +F under username boinc, group boinc and the password is used to protect access to BOINC. -Warning will be set when estimated work for any of CPUs will decrease under +Warning will be set when estimated work for any of CPUs will decrease under 48 hours: [boinc_*] @@ -371,17 +371,17 @@ Warning will be set when estimated work for any of CPUs will decrease under =head2 Remote BOINC Example -BOINC is running on 2 remote machines C and C. -On the local machine the binary of command-line interface is installed in +BOINC is running on 2 remote machines C and C. +On the local machine the binary of command-line interface is installed in directory F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -These files are owned and readable by root, readable by group munin and not -readable by others. -There are 2 symbolic links to this plugin created in the munin plugins -directory (usually F): F and +These files are owned and readable by root, readable by group munin and not +readable by others. +There are 2 symbolic links to this plugin created in the munin plugins +directory (usually F): F and F [snmp_foo_boinc*] @@ -396,33 +396,33 @@ F env.host bar env.boincdir /etc/munin/boinc/bar -This way the plugin can be used by Munin the same way as the Munin plugins +This way the plugin can be used by Munin the same way as the Munin plugins utilizng SNMP (although this plugin itself does not use SNMP). =head1 BUGS -The estimation is based on simple assumption, that longest workunits will be -processed first. This is the case when work is distributed evenly among CPUs. -But this is not always the case, because various deadlines for various -workunits may fire the "panic mode" of BOINC and scheduling could be much -different. -For example, there can be 4 CPUs, and BOINC having downloaded 4 workunits -with estimated run-time 1 hour each and 3 workunits with estimated run-time -4 hours each. +The estimation is based on simple assumption, that longest workunits will be +processed first. This is the case when work is distributed evenly among CPUs. +But this is not always the case, because various deadlines for various +workunits may fire the "panic mode" of BOINC and scheduling could be much +different. +For example, there can be 4 CPUs, and BOINC having downloaded 4 workunits +with estimated run-time 1 hour each and 3 workunits with estimated run-time +4 hours each. This Munin plugin will report estimated work 4 hours for each CPU. -But if deadline of those 1-hour workunits will be much shorter than deadline -of those 4-hours workunits, BOINC will schedule short workunits first (for all +But if deadline of those 1-hour workunits will be much shorter than deadline +of those 4-hours workunits, BOINC will schedule short workunits first (for all 4 CPUs) and after finishing them it will schedule those long workunits. This will result in real computation for 5 hours on 3 CPUs but only 1 hour on -remaining 4th CPU. So after 1 hour of computation 1 of CPUs will run out of +remaining 4th CPU. So after 1 hour of computation 1 of CPUs will run out of work. -There is no C capability at the moment. This is due to the fact, that -BOINC installations may vary over different systems, sometimes using default -directory from distribution (e.g. F in Debian or Ubuntu), but +There is no C capability at the moment. This is due to the fact, that +BOINC installations may vary over different systems, sometimes using default +directory from distribution (e.g. F in Debian or Ubuntu), but often running in user directories or in other separate directories. -Also the user-ID under which BOINC runs often differs. -Under these circumstances the C would be either lame or too +Also the user-ID under which BOINC runs often differs. +Under these circumstances the C would be either lame or too complicated. =head1 AUTHOR diff --git a/plugins/boinc/boinc_processes b/plugins/boinc/boinc_processes index adf6f584..d0e05f74 100755 --- a/plugins/boinc/boinc_processes +++ b/plugins/boinc/boinc_processes @@ -5,12 +5,12 @@ # Parameters: # # password -- The password for RPC authentication -# (default: boinc_cmd will look for a file +# (default: boinc_cmd will look for a file # 'gui_rpc_auth.cfg' and use the password in it) # host -- the host to connect to (default: localhost) # port -- optional (default: 31416) # -# This plugin can monitor boinc processes running on local/remote machines. +# This plugin can monitor boinc processes running on local/remote machines. # You can see the progress on various projects. # # Author: Petr Ruzicka @@ -49,14 +49,14 @@ sub autoconf { $reply =~ /(.*)<\/nonce>/; $hash = md5_hex($1, $password); - + print $client "$hash\003"; - + { local $/ = "\003"; $reply = <$client>; } - + if ($reply =~ //) { print "yes\n"; exit 0; @@ -70,29 +70,29 @@ sub config { my $client = IO::Socket::INET->new ( PeerAddr => $host, PeerPort => $port, Proto => 'tcp' ) - or die "Can't bind : $@\n"; + or die "Can't bind : $@\n"; print $client "\003"; - { + { local $/ = "\003"; $reply = <$client>; } $reply =~ /(.*)<\/nonce>/; my $hash = md5_hex($1, $password); - + print $client "$hash\003"; - + { local $/ = "\003"; $reply = <$client>; } - + if ($reply !~ //) { die "Wrong password: $_"; } - + print $client ""; while (chomp($reply = <$client>) && ($reply ne "")) { @@ -101,7 +101,7 @@ sub config { print "graph_category htc\n"; print "graph_args -l 0\n"; print "graph_vlabel %\n"; - } + } if ($reply =~ /(.*)<\/project_name>/) { my $boinc_munin_name=$1; $boinc_munin_name =~ /(\w+).*/; @@ -115,31 +115,31 @@ sub report { my $client = IO::Socket::INET->new ( PeerAddr => $host, PeerPort => $port, Proto => 'tcp' ) - or die "Can't bind : $@\n"; + or die "Can't bind : $@\n"; print $client "\003"; - { + { local $/ = "\003"; $reply = <$client>; } $reply =~ /(.*)<\/nonce>/; my $hash = md5_hex($1, $password); - + print $client "$hash\003"; - + { local $/ = "\003"; $reply = <$client>; } - + if ($reply !~ //) { die "Wrong password: $_"; } - + print $client ""; - + while (chomp($reply = <$client>) && ($reply ne "")) { if ($reply =~ /(\w+).*<\/project_name>/) { $project = $1; diff --git a/plugins/boinc/boinc_projs b/plugins/boinc/boinc_projs index 25ee64b6..352397c1 100755 --- a/plugins/boinc/boinc_projs +++ b/plugins/boinc/boinc_projs @@ -279,7 +279,7 @@ Linux machines running BOINC and munin-node - or - -Linux servers (running munin-node) used to collect data from other systems +Linux servers (running munin-node) used to collect data from other systems which are running BOINC, but not running munin-node (e.g. non-Linux systems) =head1 CONFIGURATION @@ -306,40 +306,40 @@ Directory containing appropriate file gui_rpc_auth.cfg (default: none) =item B -Password for BOINC (default: none) +Password for BOINC (default: none) =back =head2 B -Using of variable B poses a security risk. Even if the Munin -configuration file for this plugin containing BOINC-password is properly -protected, the password is exposed as environment variable and finally passed -to boinccmd as a parameter. It is therefore possible for local users of the -machine running this plugin to eavesdrop the BOINC password. +Using of variable B poses a security risk. Even if the Munin +configuration file for this plugin containing BOINC-password is properly +protected, the password is exposed as environment variable and finally passed +to boinccmd as a parameter. It is therefore possible for local users of the +machine running this plugin to eavesdrop the BOINC password. -Using of variable password is therefore strongly discouraged and is left here +Using of variable password is therefore strongly discouraged and is left here as a legacy option and for testing purposes. -It should be always possible to use B variable instead - in such case -the file gui_rpc_auth.cfg is read by boinccmd binary directly. -If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg -can be copied to special directory in a secure way (e.g. via scp) and properly +It should be always possible to use B variable instead - in such case +the file gui_rpc_auth.cfg is read by boinccmd binary directly. +If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg +can be copied to special directory in a secure way (e.g. via scp) and properly protected by file permissions. =head1 INTERPRETATION -This plugin shows the number of currently running BOINC tasks on the machine. -If machine is attached to several BOINC projects, data for all these projects +This plugin shows the number of currently running BOINC tasks on the machine. +If machine is attached to several BOINC projects, data for all these projects are displayed. =head1 EXAMPLES =head2 Local BOINC Example -BOINC is running on local machine. The BOINC binaries are installed in +BOINC is running on local machine. The BOINC binaries are installed in F, the BOINC is running in directory -F under username boinc, group boinc and the password is used +F under username boinc, group boinc and the password is used to protect access to BOINC: [boinc_*] @@ -349,17 +349,17 @@ to protect access to BOINC: =head2 Remote BOINC Example -BOINC is running on 2 remote machines C and C. -On the local machine the binary of command-line interface is installed in +BOINC is running on 2 remote machines C and C. +On the local machine the binary of command-line interface is installed in directory F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -These files are owned and readable by root, readable by group munin and not -readable by others. -There are 2 symbolic links to this plugin created in the munin plugins -directory (usually F): F and +These files are owned and readable by root, readable by group munin and not +readable by others. +There are 2 symbolic links to this plugin created in the munin plugins +directory (usually F): F and F [boincprojs_foo] @@ -379,12 +379,12 @@ SNMP and IPMI plugins. =head1 BUGS -There is no C capability at the moment. This is due to the fact, that -BOINC installations may vary over different systems, sometimes using default -directory from distribution (e.g. F in Debian or Ubuntu), but +There is no C capability at the moment. This is due to the fact, that +BOINC installations may vary over different systems, sometimes using default +directory from distribution (e.g. F in Debian or Ubuntu), but often running in user directories or in other separate directories. -Also the user-ID under which BOINC runs often differs. -Under these circumstances the C would be either lame or too +Also the user-ID under which BOINC runs often differs. +Under these circumstances the C would be either lame or too complicated. =head1 AUTHOR diff --git a/plugins/boinc/boinc_wus b/plugins/boinc/boinc_wus index 3c9515cc..d5a4b5dd 100755 --- a/plugins/boinc/boinc_wus +++ b/plugins/boinc/boinc_wus @@ -317,7 +317,7 @@ Linux machines running BOINC and munin-node - or - -Linux servers (running munin-node) used to collect data from other systems +Linux servers (running munin-node) used to collect data from other systems which are running BOINC, but not running munin-node (e.g. non-Linux systems) =head1 CONFIGURATION @@ -348,45 +348,45 @@ Display unusual states details (default: 0 = Summarize unusual states as C -Password for BOINC (default: none) +Password for BOINC (default: none) =back =head2 B -Using of variable B poses a security risk. Even if the Munin -configuration file for this plugin containing BOINC-password is properly -protected, the password is exposed as environment variable and finally passed -to boinccmd as a parameter. It is therefore possible for local users of the -machine running this plugin to eavesdrop the BOINC password. +Using of variable B poses a security risk. Even if the Munin +configuration file for this plugin containing BOINC-password is properly +protected, the password is exposed as environment variable and finally passed +to boinccmd as a parameter. It is therefore possible for local users of the +machine running this plugin to eavesdrop the BOINC password. -Using of variable password is therefore strongly discouraged and is left here +Using of variable password is therefore strongly discouraged and is left here as a legacy option and for testing purposes. -It should be always possible to use B variable instead - in such case -the file gui_rpc_auth.cfg is read by boinccmd binary directly. -If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg -can be copied to special directory in a secure way (e.g. via scp) and properly +It should be always possible to use B variable instead - in such case +the file gui_rpc_auth.cfg is read by boinccmd binary directly. +If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg +can be copied to special directory in a secure way (e.g. via scp) and properly protected by file permissions. =head1 INTERPRETATION -This plugin shows how many BOINC workunits are in all the various states. -The most important states C, C, C, -C, C, C and C are always +This plugin shows how many BOINC workunits are in all the various states. +The most important states C, C, C, +C, C, C and C are always displayed. All other states are shown as C. -If the variable B is used, additionally also states -C and C are shown separately (they are included in -C otherwise). +If the variable B is used, additionally also states +C and C are shown separately (they are included in +C otherwise). =head1 EXAMPLES =head2 Local BOINC Example -BOINC is running on local machine. The BOINC binaries are installed in +BOINC is running on local machine. The BOINC binaries are installed in F, the BOINC is running in directory -F under username boinc, group boinc and the password is used +F under username boinc, group boinc and the password is used to protect access to BOINC: [boinc_*] @@ -397,17 +397,17 @@ to protect access to BOINC: =head2 Remote BOINC Example -BOINC is running on 2 remote machines C and C. -On the local machine the binary of command-line interface is installed in +BOINC is running on 2 remote machines C and C. +On the local machine the binary of command-line interface is installed in directory F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -The BOINC password used on the remote machine C is stored in file +The BOINC password used on the remote machine C is stored in file F. -These files are owned and readable by root, readable by group munin and not -readable by others. -There are 2 symbolic links to this plugin created in the munin plugins -directory (usually F): F and +These files are owned and readable by root, readable by group munin and not +readable by others. +There are 2 symbolic links to this plugin created in the munin plugins +directory (usually F): F and F [snmp_foo_boinc*] @@ -422,17 +422,17 @@ F env.host bar env.boincdir /etc/munin/boinc/bar -This way the plugin can be used by Munin the same way as the Munin plugins +This way the plugin can be used by Munin the same way as the Munin plugins utilizng SNMP (although this plugin itself does not use SNMP). =head1 BUGS -There is no C capability at the moment. This is due to the fact, that -BOINC installations may vary over different systems, sometimes using default -directory from distribution (e.g. F in Debian or Ubuntu), but +There is no C capability at the moment. This is due to the fact, that +BOINC installations may vary over different systems, sometimes using default +directory from distribution (e.g. F in Debian or Ubuntu), but often running in user directories or in other separate directories. -Also the user-ID under which BOINC runs often differs. -Under these circumstances the C would be either lame or too +Also the user-ID under which BOINC runs often differs. +Under these circumstances the C would be either lame or too complicated. =head1 AUTHOR diff --git a/plugins/bsd/df_abs_bsd b/plugins/bsd/df_abs_bsd index 1c40bd57..ecbe233a 100755 --- a/plugins/bsd/df_abs_bsd +++ b/plugins/bsd/df_abs_bsd @@ -57,7 +57,7 @@ graph_info Filesystem usage for i in range(1,len(rawdata)): dataline=rawdata[i].split() self.parsed_data[re.sub('/', '_', dataline[0])] = (int(dataline[1]), int(dataline[2]), dataline[5]) - + if __name__ == "__main__": processor = checker() processor.get_data() diff --git a/plugins/bsd/df_bsd b/plugins/bsd/df_bsd index 49390ab5..a2119c37 100755 --- a/plugins/bsd/df_bsd +++ b/plugins/bsd/df_bsd @@ -55,7 +55,7 @@ graph_info Filesystem usage for i in range(1,len(rawdata)): dataline=rawdata[i].split() self.parsed_data[re.sub('/', '_', dataline[0])] = (re.sub('%', '', dataline[4]), dataline[5]) - + if __name__ == "__main__": processor = checker() processor.get_data() diff --git a/plugins/bsd/spamd-blacklist-bsd b/plugins/bsd/spamd-blacklist-bsd index 257f980c..f95f344b 100755 --- a/plugins/bsd/spamd-blacklist-bsd +++ b/plugins/bsd/spamd-blacklist-bsd @@ -77,4 +77,4 @@ black.label Blacklisted""" processor = checker(blacklists) processor.process_lines(logfile) print processor - + diff --git a/plugins/bsd/spamd-tarpit-bsd b/plugins/bsd/spamd-tarpit-bsd index 6989aea5..d547b03c 100755 --- a/plugins/bsd/spamd-tarpit-bsd +++ b/plugins/bsd/spamd-tarpit-bsd @@ -68,4 +68,4 @@ tarpit.label Average tarpit delay""" processor = checker() processor.process_lines(logfile) print processor - + diff --git a/plugins/celery/celery_tasks b/plugins/celery/celery_tasks index ee65d17f..668e2524 100755 --- a/plugins/celery/celery_tasks +++ b/plugins/celery/celery_tasks @@ -79,7 +79,7 @@ def get_data(what, api_url, *args): except IOError: print 'Could not connect to the celerymon webserver' sys.exit(-1) - + def check_web_server_status(api_url): try: request = urllib.urlopen(api_url) @@ -104,25 +104,25 @@ def print_config(task_names): print '%s.type DERIVE' % (clean_task_name(name)) print '%s.min 0' % (clean_task_name(name)) print '%s.info number of %s tasks' % (clean_task_name(name), name) - + # Values def print_values(task_names = None, api_url = None): for task_name in task_names: count = len(get_data('task_details', api_url, task_name)) print '%s.value %d' % (clean_task_name(task_name), count) - + if __name__ == '__main__': task_names = os.environ.get('tasks', None) api_url = os.environ.get('api_url', API_URL) - + check_web_server_status(api_url) - + if not task_names: print 'You need to define at least one task name' sys.exit(-1) - + task_names = task_names.split(',') - + if len(sys.argv) > 1: if sys.argv[1] == 'config': print_config(task_names) diff --git a/plugins/celery/celery_tasks_states b/plugins/celery/celery_tasks_states index e62ab1c4..cf978076 100755 --- a/plugins/celery/celery_tasks_states +++ b/plugins/celery/celery_tasks_states @@ -82,7 +82,7 @@ def get_data(what, api_url, *args): except IOError: print 'Could not connect to the celerymon webserver' sys.exit(-1) - + def check_web_server_status(api_url): try: request = urllib.urlopen(api_url) @@ -115,33 +115,33 @@ def print_config(workers = None): # Values def print_values(workers = None, api_url = None): data = get_data('tasks', api_url) - + counters = dict([(key, 0) for key in TASK_STATES]) for task_name, task_data in data: state = task_data['state'] hostname = task_data['worker']['hostname'] - + if workers and hostname not in workers: continue - + counters[state] += 1 - + for name in TASK_STATES: name_cleaned = clean_state_name(name) value = counters[name] print '%s.value %d' % (name_cleaned, value) - + if __name__ == '__main__': workers = os.environ.get('workers', 'all') api_url = os.environ.get('api_url', API_URL) - + check_web_server_status(api_url) - + if workers in [None, '', 'all']: workers = None else: workers = workers.split(',') - + if len(sys.argv) > 1: if sys.argv[1] == 'config': print_config(workers) @@ -149,4 +149,4 @@ if __name__ == '__main__': print 'yes' else: print_values(workers, api_url) - + diff --git a/plugins/ceph/ceph-osd-info b/plugins/ceph/ceph-osd-info index 96933ce2..d4dd4e8c 100755 --- a/plugins/ceph/ceph-osd-info +++ b/plugins/ceph/ceph-osd-info @@ -31,7 +31,7 @@ Jort Bloem This is one of the first programs I wrote in Python. I got carried away by Python's powerful one-line commands. Just because you can, doesn't -mean you should. +mean you should. This program needs a rewrite, and if there were any problems with it, I probably would. @@ -102,7 +102,7 @@ def read_osd(filename): return json.loads(result) except: pass - + return None def osd_list(): diff --git a/plugins/ceph/ceph_capacity b/plugins/ceph/ceph_capacity index 42a9685c..05e45b38 100755 --- a/plugins/ceph/ceph_capacity +++ b/plugins/ceph/ceph_capacity @@ -36,7 +36,7 @@ WARNING_LEVEL=${warning_level:-"80"} CRITICAL_LEVEL=${critical_level:-"90"} if [ "$1" = "config" ]; then - + echo 'graph_title CEPH capacity' echo 'graph_category fs' echo 'graph_vlabel GB' diff --git a/plugins/ceph/ceph_osd b/plugins/ceph/ceph_osd index 9d208b53..de8a0fd2 100755 --- a/plugins/ceph/ceph_osd +++ b/plugins/ceph/ceph_osd @@ -28,7 +28,7 @@ fi if [ "$1" = "config" ]; then - + echo 'graph_title CEPH OSDs' echo 'graph_category fs' echo 'graph_vlabel nr' diff --git a/plugins/change.org/changeorg_signature_count b/plugins/change.org/changeorg_signature_count index b9e7ab8a..775c8af7 100755 --- a/plugins/change.org/changeorg_signature_count +++ b/plugins/change.org/changeorg_signature_count @@ -65,7 +65,7 @@ for i in environ.get('petitions').split(): continue # NB: user-agent's tweak is needed to avoid a 403 req = urllib.request.Request("https://api.change.org/v1/petitions/{}?api_key={}&fields=title".format(i, environ.get('APIkey')), - data=None, + data=None, headers={ 'User-Agent': 'curl/7.38.0' }) response = urllib.request.urlopen(req).read().decode('utf-8') petition_titles[i] = json.loads(response) diff --git a/plugins/chat/tinychat_users_ b/plugins/chat/tinychat_users_ index b9c8d258..4e0a1fbb 100755 --- a/plugins/chat/tinychat_users_ +++ b/plugins/chat/tinychat_users_ @@ -4,7 +4,7 @@ ######################### # Munin Plugin to track monitor the number of users a tinychat room gets. # Author Phil Wray ( http://www.infjs.com ) -# +# # # # Usage: @@ -40,7 +40,7 @@ echo "graph_title Tinychat Users for $room" echo 'total_count.label Room Users' echo 'total_count.draw AREA' echo 'broadcaster_count.draw STACK' - echo 'broadcaster_count.label Broadcasting' + echo 'broadcaster_count.label Broadcasting' exit 0 fi diff --git a/plugins/cherokee/munin-plugin-for-cherokee b/plugins/cherokee/munin-plugin-for-cherokee index 17e878bf..157ff744 100755 --- a/plugins/cherokee/munin-plugin-for-cherokee +++ b/plugins/cherokee/munin-plugin-for-cherokee @@ -82,7 +82,7 @@ def get_data(): base64string = base64.standard_b64encode('%s:%s' % (http_user, http_pass)) if len(http_user) > 0 and len(http_pass) > 0: #print "DEBUG: AUTH: " + base64string - request.add_header("Authorization", "Basic %s" % base64string) + request.add_header("Authorization", "Basic %s" % base64string) #print "DEBUG: GET: " + url raw_data = urllib2.urlopen(request).read() #print "DEBUG: " + raw_data @@ -129,7 +129,7 @@ def munin_config(response): for key in section: print key + '.label ' + key.capitalize() print key + '.draw AREASTACK' - + if len(sys.argv) > 1 and sys.argv[1] == "config": munin_config(get_data()) else: diff --git a/plugins/chilli/chilli_sessions_ b/plugins/chilli/chilli_sessions_ index 1e73f349..6c3dd9b0 100755 --- a/plugins/chilli/chilli_sessions_ +++ b/plugins/chilli/chilli_sessions_ @@ -80,7 +80,7 @@ case "$1" in done echo "total" - + exit 0 ;; config) diff --git a/plugins/clamav/clamav b/plugins/clamav/clamav index 042a3d4e..16f9979c 100755 --- a/plugins/clamav/clamav +++ b/plugins/clamav/clamav @@ -4,7 +4,7 @@ log=/var/log/clamav/clamd.log if [ "$1" = "autoconf" ]; then - if [ -r $log ]; then + if [ -r $log ]; then echo "yes" else echo "no (cannot read $log)" @@ -16,7 +16,7 @@ if [ "$1" = "config" ]; then cat < /dev/null || rm -f $pidfile } @@ -33,7 +33,7 @@ run_daemon() { run_watchdog if [ -f $pidfile ]; then touch $pidfile - else + else $pluginfull acquire & fi } @@ -54,9 +54,9 @@ graph_title $graph graph_category 1sec graph_vlabel average cpu use % graph_scale no -graph_total All CPUs +graph_total All CPUs update_rate 1 -graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y +graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y EOF cpun=0 for ((i=0; i<$cpus; i++)); do diff --git a/plugins/cpu/process_count b/plugins/cpu/process_count index 0802c8cd..166a02dd 100755 --- a/plugins/cpu/process_count +++ b/plugins/cpu/process_count @@ -26,7 +26,7 @@ -if [ "$1" = "autoconf" ] ; then +if [ "$1" = "autoconf" ] ; then if [ -n "$procs" ] ; then echo "yes" else @@ -49,23 +49,23 @@ if [ "$1" = "config" ] ; then echo 'graph_scale no' echo 'graph_period second' echo "graph_order $cprocs" - - for proc in $procs; do + + for proc in $procs; do cproc=${proc//[^A-Za-z0-9_]/_} echo "${cproc}.label $proc" echo "${cproc}.info CPU used by process $proc" done ; - + exit fi for proc in $procs ; do { cproc=${proc//[^A-Za-z0-9_]/_} ps axo '%mem,comm,command' | grep -v grep | grep "$proc" | LC_ALL=us_US awk ' - BEGIN { - SUM=0 + BEGIN { + SUM=0 } - { + { SUM+=1 COMM=$2 } diff --git a/plugins/cpu/process_cpushare b/plugins/cpu/process_cpushare index d6ee3dc2..029ac9d0 100755 --- a/plugins/cpu/process_cpushare +++ b/plugins/cpu/process_cpushare @@ -26,7 +26,7 @@ -if [ "$1" = "autoconf" ] ; then +if [ "$1" = "autoconf" ] ; then if [ -n "$procs" ] ; then echo "yes" else @@ -49,13 +49,13 @@ if [ "$1" = "config" ] ; then echo 'graph_scale no' echo 'graph_period second' echo "graph_order $cprocs" - - for proc in $procs; do + + for proc in $procs; do cproc=${proc//[^A-Za-z0-9_]/_} echo "${cproc}.label $proc" echo "${cproc}.info CPU used by process $proc" done ; - + exit fi @@ -64,14 +64,14 @@ fi for proc in $procs ; do { cproc=${proc//[^A-Za-z0-9_]/_} ps axo 'pcpu,comm,command' | grep -v grep | grep "$proc" | LC_ALL=us_US awk ' - BEGIN { - SUM=0 + BEGIN { + SUM=0 } - { - SUM+=$1 + { + SUM+=$1 } - END { - print "'${cproc}'.value "SUM + END { + print "'${cproc}'.value "SUM } ' } diff --git a/plugins/currency/nanopool/nanopool_ b/plugins/currency/nanopool/nanopool_ index 3bc8bed0..7338734a 100755 --- a/plugins/currency/nanopool/nanopool_ +++ b/plugins/currency/nanopool/nanopool_ @@ -75,7 +75,7 @@ except: from urllib2 import Request from urllib2 import urlopen from urllib2 import URLError - + def define_graph_types(): @@ -131,7 +131,7 @@ def request_data(): print("API request error: {0}". format(err), file=sys.stderr) exit(1) except: - print("Unhandled error:", sys.exc_info()[0]) + print("Unhandled error:", sys.exc_info()[0]) exit(1) try: result = json.loads(txt.decode("utf-8")) @@ -158,7 +158,7 @@ def write_config_worker(): print("worker_{0}_hashrate.info Hashrate of worker '{1}'".format(worker_name, val["id"])) print("worker_{0}_hashrate.min 0".format(worker_name)) print("worker_{0}_hashrate.draw LINE1".format(worker_name)) - + for val in worker_data: print("") worker_name = "_".join(val["id"].split()) @@ -234,7 +234,7 @@ def write_data_worker(data): print("") print("multigraph worker_shares_{0}.worker_{1}".format(account_address, worker_name)) print("wshares.value {0} ".format(val["rating"])) - + def write_config(): diff --git a/plugins/cyrus/cyrus-imapd b/plugins/cyrus/cyrus-imapd index 4492b7a1..dbb28a50 100755 --- a/plugins/cyrus/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -25,7 +25,7 @@ cyrus-imapd - Munin plugin to monitor the load on a cyrus imapd server =head1 CONFIGURATION The user running this plugin needs read and write access to the -cyrus-imapd proc directory. You will need to add the following to the +cyrus-imapd proc directory. You will need to add the following to the munin-node/plugin configuration: [cyrus-imapd] @@ -87,8 +87,8 @@ if [ "x${PROCDIR}x" = "xx" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. - +# graphs should look. + if [ "$1" = "config" ]; then echo 'graph_title Cyrus IMAPd Load' echo 'graph_args --base 1000 -l 0' diff --git a/plugins/darwin/dar_cpuusage b/plugins/darwin/dar_cpuusage index 788cbfeb..056dba02 100755 --- a/plugins/darwin/dar_cpuusage +++ b/plugins/darwin/dar_cpuusage @@ -91,7 +91,7 @@ for ( $i = 1; ($i < $#top and $quit == 0); $i++ ) { print "sys.value " . $sys . "\n"; print "user.value " . $usr . "\n"; print "idle.value " . $idl . "\n"; - $quit = 1; + $quit = 1; } } diff --git a/plugins/darwin/dar_uptime b/plugins/darwin/dar_uptime index 5271d453..2a49173d 100755 --- a/plugins/darwin/dar_uptime +++ b/plugins/darwin/dar_uptime @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/perl # -*- perl -*- =head1 NAME @@ -69,10 +69,10 @@ if ( defined($ARGV[0])) { } } -@uptime = `/usr/bin/uptime`; +@uptime = `/usr/bin/uptime`; foreach(@uptime) { $_ =~ m/^.+up (.+?), \d+ us.+$/; - $timestr = $1; + $timestr = $1; if ( $timestr =~ m/^(\d+) day.+?$/ ) { $days = $1; } else { $days = 0; } diff --git a/plugins/darwin/dar_vpnd b/plugins/darwin/dar_vpnd index f454429b..ab1e9f73 100755 --- a/plugins/darwin/dar_vpnd +++ b/plugins/darwin/dar_vpnd @@ -32,12 +32,12 @@ my $cmd = "ps -ef | awk '/[p]ppd/ {print substr(\$NF,2);}' | wc -l"; if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_category network\n"; - print "graph_args --base 1024 -r --lower-limit 0\n"; + print "graph_args --base 1024 -r --lower-limit 0\n"; print "graph_title Number of VPN Connections\n"; print "graph_vlabel VPN Connections\n"; - print "graph_info The Graph shows the Number of VPN Connections\n"; + print "graph_info The Graph shows the Number of VPN Connections\n"; print "connections.label Number of VPN Connections\n"; - print "connections.type GAUGE\n"; + print "connections.type GAUGE\n"; } else { my $output = `$cmd`; print "connections.value $output"; diff --git a/plugins/dhcp/dhcp-pool b/plugins/dhcp/dhcp-pool index 14874d99..09f39666 100755 --- a/plugins/dhcp/dhcp-pool +++ b/plugins/dhcp/dhcp-pool @@ -51,7 +51,7 @@ elsif ( defined $ARGV[0] and $ARGV[0] eq "config" ) { # Determine the available IP pools %pools = determine_pools(); - + # Print a label for each pool foreach $start (keys %pools) { $label = ip2string($start); @@ -63,10 +63,10 @@ elsif ( defined $ARGV[0] and $ARGV[0] eq "config" ) { } else { my (@activeleases, %pools, $start, $end, $size, $free, $label, $lease); - - # Determine all leased IP addresses + + # Determine all leased IP addresses @activeleases = determine_active_leases(); - + # Determine the available IP pools %pools = determine_pools(); @@ -109,14 +109,14 @@ sub determine_pools { # The range statement gives the lowest and highest IP addresses in a range. $size = $end - $start + 1; - + $pools{$start} = $size; } } return %pools; } -# Very simple parser for dhcpd.leases. This will break very easily if dhcpd decides to +# Very simple parser for dhcpd.leases. This will break very easily if dhcpd decides to # format the file differently. Ideally a simple recursive-descent parser should be used. # # Returns an array with currently leased IP's @@ -155,7 +155,7 @@ sub determine_active_leases { } } - + # Set TZ back to its original setting if (defined($mytz)) { $ENV{'TZ'} = $mytz; @@ -186,9 +186,9 @@ sub string2ip { } return undef; } - + # -# Returns a dotted quad notation of an +# Returns a dotted quad notation of an # sub ip2string { my $ip = shift; diff --git a/plugins/disk/btrfs_subvol_usage b/plugins/disk/btrfs_subvol_usage index 887eacc6..cba66d98 100755 --- a/plugins/disk/btrfs_subvol_usage +++ b/plugins/disk/btrfs_subvol_usage @@ -19,8 +19,8 @@ env.fsroot /path/to/btrfs/filesystem =head1 USAGE -Link/Copy this plugin to /etc/munin/plugins/ and restart the munin-node. - +Link/Copy this plugin to /etc/munin/plugins/ and restart the munin-node. + =head1 AUTHOR Alexander Knöbel diff --git a/plugins/disk/df_with_nfs b/plugins/disk/df_with_nfs index 08f25b3f..d1a19697 100755 --- a/plugins/disk/df_with_nfs +++ b/plugins/disk/df_with_nfs @@ -60,7 +60,7 @@ if [ "$1" = "autoconf" ]; then fi clean_name() { - echo $1 $7 $2 | sed 's/[\/.-]/_/g'| awk "{ + echo $1 $7 $2 | sed 's/[\/.-]/_/g'| awk "{ if (\$3 == \"tmpfs\") n=\$1\$2 else @@ -80,12 +80,12 @@ if [ "$1" = "config" ]; then df -T -P -l -x none -x unknown -x udf -x iso9660 -x romfs -x ramfs -x tmpfs | sed 1d | grep -v "//" | sort | while read i; do name=`clean_name $i` echo -n "$name.label " - echo $i | awk "{ + echo $i | awk "{ dir=\$7 if (length(dir) <= $MAXLABEL) print dir else - printf (\"...%s\n\", substr (dir, length(dir)-$MAXLABEL+4, $MAXLABEL-3)) + printf (\"...%s\n\", substr (dir, length(dir)-$MAXLABEL+4, $MAXLABEL-3)) print \"$name.info \" \$7 \" (\" \$2 \") -> \" \$1; }" echo "$name.warning 92" diff --git a/plugins/disk/du b/plugins/disk/du index f0133d49..4f125077 100755 --- a/plugins/disk/du +++ b/plugins/disk/du @@ -16,10 +16,10 @@ # Requirements # - bash (or change first line to sh instead of bash or any other shell) # - existing and readable directory to scan -# - du command, it exists on most of the *nix operating systems +# - du command, it exists on most of the *nix operating systems # ################################################################# -# +# # Configuration # # directory to check @@ -44,8 +44,8 @@ ID=1; # Changelog # # Revision 0.1 Tue 03 Feb 2009 02:16:02 PM CET _KaszpiR_ -# - initial release, -# +# - initial release, +# ################################################################# # Magick markers (optional - used by munin-config and som installation # scripts): diff --git a/plugins/disk/e2 b/plugins/disk/e2 index 4eaed72e..ea6a0d3c 100755 --- a/plugins/disk/e2 +++ b/plugins/disk/e2 @@ -33,7 +33,7 @@ print_details() { val=`echo ${line#*:}` val1=${val%% *} [ -z "$line" ] && break - + case "$key" in "Mount count") myname=$1; mc=$val ;; "Maximum mount count") mmc=$val ;; @@ -90,7 +90,7 @@ if [ "$1" = "config" ]; then echo "graph_category disk" args="-l 0" case "$plugin" in - mounts) + mounts) echo "graph_vlabel times" echo "graph_title Ext2 Filesystem mount details" ;; @@ -98,14 +98,14 @@ if [ "$1" = "config" ]; then echo "graph_vlabel days" echo "graph_title Ext2 Filesystem fsck interval" ;; - writes) + writes) args="$args --base 1024" echo "graph_vlabel bytes" echo "graph_title Ext2 Filesystem lifetime writes$blkdevAppend" ;; esac echo "graph_args $args" - + if [ -z "$blkdev" ]; then lse2 else diff --git a/plugins/disk/file_age b/plugins/disk/file_age index 29636d14..52743de3 100755 --- a/plugins/disk/file_age +++ b/plugins/disk/file_age @@ -8,23 +8,23 @@ case $1 in COUNTER=1 while [ $COUNTER -gt 0 ]; do FILE_PATH="file${COUNTER}_path" - + # Is the path for this file specified? eval FILE=\$$FILE_PATH if [ "$FILE" == "" ]; then break; fi - + # It is! Add it to the graphs. GRAPH_ORDER="$GRAPH_ORDER file_$COUNTER" - + # Does this file have a specified label? LABEL_COUNTER="file${COUNTER}_label" eval LABEL=\$$LABEL_COUNTER if [ "$LABEL" == "" ]; then LABEL=`basename $FILE` fi - + # Associated warning level? WARNING="file${COUNTER}_warning" eval WARNING=\$$WARNING @@ -44,7 +44,7 @@ case $1 in echo "file_$COUNTER.min 0" let COUNTER=COUNTER+1 done; - + echo "graph_order $GRAPH_ORDER" echo "graph_title File age" echo 'graph_args --base 1000 -l 0' @@ -63,14 +63,14 @@ while [ $COUNTER -gt 0 ]; do if [ "$FILE" == "" ]; then break; fi - - # If the file isn't readable, say it's zero. + + # If the file isn't readable, say it's zero. if [ ! -r "$FILE" ]; then VALUE=0 else VALUE=$(($(date +%s) - $(stat -c '%Y' "$FILE"))) fi - + echo "file_$COUNTER.value $VALUE" let COUNTER=COUNTER+1 done; diff --git a/plugins/disk/hdsentinel b/plugins/disk/hdsentinel index dfb1dc36..f4bd3735 100755 --- a/plugins/disk/hdsentinel +++ b/plugins/disk/hdsentinel @@ -1,7 +1,7 @@ #!/bin/bash # -*- sh -*- # -# Multigraph plugin to monitor harddrive temperature, condition, +# Multigraph plugin to monitor harddrive temperature, condition, # performance and estimated remaining lifetime through HDSentinel. # # To use, download the latest HDSentinel for Linux x86 or x64 from diff --git a/plugins/disk/hp_temp b/plugins/disk/hp_temp index 3c524f81..373231b9 100755 --- a/plugins/disk/hp_temp +++ b/plugins/disk/hp_temp @@ -77,7 +77,7 @@ while () $name =~ s/\ /_/g; # add the ID to be 100% unique $name .= '_'.$id; - + if ($mode eq 'config') { # only needed here in config @@ -95,7 +95,7 @@ while () } # calc warning from threshold, 5% less my $warning = sprintf("%.0f", $threshold * 95 / 100); - + print $name.".label ".$location."\n"; print $name.".warning ".$warning."\n"; print $name.".critical ".$threshold."\n"; diff --git a/plugins/disk/lvm_snap_used b/plugins/disk/lvm_snap_used index 91a34012..aabbe460 100755 --- a/plugins/disk/lvm_snap_used +++ b/plugins/disk/lvm_snap_used @@ -3,7 +3,7 @@ # Plugin to monitor the % of allocated area of a LVM snapshot # # Parameters: -# +# # config # autoconf # @@ -12,7 +12,7 @@ # #%# family=auto #%# capabilities=autoconf -# +# # 2011/05/20 - pmoranga - initial version # # 2012/01/27 - Sébastien Gross @@ -35,7 +35,7 @@ fi if [ "$1" = "config" ]; then - echo 'graph_title Allocated space for snapshot' + echo 'graph_title Allocated space for snapshot' echo 'graph_vlabel %' echo 'graph_category disk' echo 'graph_args -l 0 -u 100 -r' @@ -48,7 +48,7 @@ ${lvdisplay} -C | awk '$3 ~ /^s/{print}' | while read line; do 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 diff --git a/plugins/disk/megaraid-controller-information b/plugins/disk/megaraid-controller-information index fdb19eec..6b18b4d5 100755 --- a/plugins/disk/megaraid-controller-information +++ b/plugins/disk/megaraid-controller-information @@ -1,72 +1,72 @@ #!/usr/bin/perl -w # -# Munin plugin for MegaRAID +# Munin plugin for MegaRAID # This plugin can graph:- Currently Drive Temperature and Error Count -# +# #--------------------- # Examples # Create a symbolic link to MegaRaid__ # ln -s /usr/share/munin/plugins/MegaRaid_ /etc/munin/plugins/MegaRaid_0_temp # graph temperature on adapter 0 -# +# # ln -s /usr/share/munin/plugins/MegaRaid_ /etc/munin/plugins/MegaRaid_0_error # graph media errors on adapter 0 -# +# # ln -s /usr/share/munin/plugins/MegaRaid_ /etc/munin/plugins/MegaRaid_1_temp # graph temperature on adapter 1 -# +# #--------------------- # Log # Revision 0.1 2011/04/16 idobson # -First version only basic support of the MegaRaid controller -# +# # Revision 0.2 2011/04/17 fkatzenb # -Added bash statement to remove the log file created each time MegaCli64 is ran # -Added a few comments and visual changes -# +# # Revision 1.0 2011/04/17 fkatzenb # -Revamped Code to symbolic link for sensor type and future growth -# +# # Revision 1.1 2011/04/17 fkatzenb # -Revised scalling -# +# # Revision 1.2 2011/04/28 fkatzenb # -Added support for graph_info support # -Added warning & critical alerts support # -Added data info -# +# # Revision 2.0 2011/04/29 fkatzenb # -Added remaining support for SMART Errors -# +# # Revision 2.1 2011/04/29 fkatzenb # -Added version information for in the graph description -# -# +# +# #--------------------- -# +# # Add the following to your /etc/munin/plugin-conf.d/munin-node: -# +# # [MegaRaid_*] # user root -# +# #--------------------- -# -# +# +# # Magic markers (optional - used by munin-config and installation scripts): -# +# #%# family=auto #%# capabilities=autoconf -# +# my $DisplayVer=2.1; use strict; use warnings; my $DevID=0; #Device Number found -my $DevData=0; #Device Data found +my $DevData=0; #Device Data found # Parse out Adapter number and parameter desired from file name and remove whitespace -my $Parameters=`basename $0 | sed 's/^MegaRaid_//g' | tr '_' '-'` ; +my $Parameters=`basename $0 | sed 's/^MegaRaid_//g' | tr '_' '-'` ; chomp $Parameters; my ($Adapter,$Type)=split(/-/,$Parameters); @@ -128,13 +128,13 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf" ) { print "yes\n"; exit 0; } else { - print "no\n"; + print "no\n"; exit 1 } } #Read Output of MegaRaid command -$Command.=" -PDList -a".$Adapter; +$Command.=" -PDList -a".$Adapter; my @Output=qx($Command); #Munin Config Options @@ -145,14 +145,14 @@ if ($ARGV[0] and $ARGV[0] eq "config"){ print "graph_scale yes\n"; print "graph_category disk\n"; print "graph_info $config{$Type}->{description}
    Generated by MegaRaid_, Version $DisplayVer
    \n"; - + foreach my $Line (@Output) { $Line=~ s/\r//g; $Line=~ s/\n//g; - + #Find the device ID - if ( $Line=~ m/Slot Number: /i ) { - $DevID=$Line; + if ( $Line=~ m/Slot Number: /i ) { + $DevID=$Line; $DevID=~ s/Slot Number: //; print "A".$Adapter."_D".$DevID."_$Type.label A$Adapter:D$DevID $config{$Type}->{label}\n"; print "A".$Adapter."_D".$DevID."_$Type.info Adapter: $Adapter / Drive: $DevID - $config{$Type}->{info_tag}\n"; @@ -161,9 +161,9 @@ if ($ARGV[0] and $ARGV[0] eq "config"){ } if ($config{$Type}->{critical} ne '') { print "A".$Adapter."_D".$DevID."_$Type.critical $config{$Type}->{critical}\n"; - } + } } - } + } exit 0; } @@ -176,7 +176,7 @@ foreach my $Line (@Output) { if ( $Line=~ m/Slot Number: /i ) { $DevID=$Line; $DevID=~ s/Slot Number: //; chomp $DevID; } #Find the data and print it out - if ( $Line=~ m/$config{$Type}->{lookfor}/i ) { + if ( $Line=~ m/$config{$Type}->{lookfor}/i ) { $DevData=$Line; $DevData=~s/$config{$Type}->{lookfor}//; $DevData=~s/C.*//; diff --git a/plugins/disk/megaraid-hdd-temperature-using-megacli b/plugins/disk/megaraid-hdd-temperature-using-megacli index bb45ee9c..261ba7c4 100755 --- a/plugins/disk/megaraid-hdd-temperature-using-megacli +++ b/plugins/disk/megaraid-hdd-temperature-using-megacli @@ -1,8 +1,8 @@ #!/bin/bash # Plugin to monitor harddrive temperatures connected to a MegaRAID controller -# -# Plugin must be ran as root so add these configuration in +# +# Plugin must be ran as root so add these configuration in # /etc/munin/plugin-conf.d/munin-node. # # [megacli*] diff --git a/plugins/disk/quota2percent_ b/plugins/disk/quota2percent_ index 6b3e6c62..91e16315 100755 --- a/plugins/disk/quota2percent_ +++ b/plugins/disk/quota2percent_ @@ -28,18 +28,18 @@ You could define two alert levels, the graph language, min. human UID and dealin env.humanuid [value] (default: 1000, only need if there is an other value define for UID_MIN in /etc/login.defs) env.low_uid [never|no|yes] (default: never) set to no for producing rrd files for system user, but don't show those graphs (e.g. for later analyses) - if set to yes system user graphs are drawn + if set to yes system user graphs are drawn =head1 DESCRIPTION -Wild card Plugin for monitoring the utilization of devices with quota rules. -A graph is drawn for each user, which shows the usage as a percentage of his hard limit. System accounts (UID <1000) are suppressed. -In addition, a graph is displayed which indicates the ratio device size to device coverage. -The script repqutoa, usually part of the package quota, is needed. +Wild card Plugin for monitoring the utilization of devices with quota rules. +A graph is drawn for each user, which shows the usage as a percentage of his hard limit. System accounts (UID <1000) are suppressed. +In addition, a graph is displayed which indicates the ratio device size to device coverage. +The script repqutoa, usually part of the package quota, is needed. The plugin itself can be stored in any directory. For example, the device sdb1 shell be monitored, a symbolic link must be created -in the /etc/munin/plugins/ directory as follows: +in the /etc/munin/plugins/ directory as follows: -=over +=over I<<< ln -s //quota2percent_ quota2percent_sdb1 >>> @@ -72,7 +72,7 @@ V17.0214 add example graph for Munin Plugin Gallery remove setting a PATH - remove German comments + remove German comments V17.0124 @@ -83,7 +83,7 @@ V17.0124 Jo Hartmann =head1 LICENSE - + GPLv2 (L) =cut @@ -142,7 +142,7 @@ GPLv2 (L) ################################################### # Reading the quotes for the selected device, using repquota - if repquota "/dev/$Id" &> /dev/null; then + if repquota "/dev/$Id" &> /dev/null; then readarray Quotas < <( repquota "/dev/$Id" | grep " -- " ) else echo "No limitations administered via 'quota' for $Id" >&2 @@ -151,13 +151,13 @@ GPLv2 (L) # the avoidance of a divide-by-zero error Quotas[0]="root -- 1 1 1 1 1 1" - # no rrd file need + # no rrd file need Low_UID="never" fi readarray Totals < <( df "/dev/$Id" ) -# Get the count of Users +# Get the count of Users Users=${#Quotas[@]} @@ -167,7 +167,7 @@ GPLv2 (L) if [ "$1" = "config" ]; then - # Localisation of the graphic texts + # Localisation of the graphic texts case $Language in de) echo "graph_title Quota-Hard-Limit von $Id" @@ -176,9 +176,9 @@ GPLv2 (L) Total_txt="Su. aller Nutzer" Total_info="Inklusive Systemnutzer (UID < $Min_UID)" ;; - es) - echo "graph_title Cuota de límite absoluto de $Id" - echo "graph_vlabel el % de uso del límite duro" + es) + echo "graph_title Cuota de límite absoluto de $Id" + echo "graph_vlabel el % de uso del límite duro" echo "graph_info El gráfico muestra la disponibilidad de espacio regulado por cuotas para todos los usuarios regulares (UID >= $Min_UID) como porcentaje de límites duros." Total_txt="Suma de todos los usuarios " Total_info="La inclusión de usuario del sistema (UID < $Min_UID) " @@ -188,11 +188,11 @@ GPLv2 (L) echo "graph_vlabel Usage in %" echo "graph_info The graphic shows the allocation of the quota-regulated storage space for all regular users (UID >= $Min_UID) as a percentage of the hard limit ." Total_txt="all users" - Total_info="system users (UID < $Min_UID) included" + Total_info="system users (UID < $Min_UID) included" ;; esac - # Defaults configuration + # Defaults configuration echo "graph_category disk" echo "graph_args --lower-limit 0 --upper-limit 100" echo "graph_printf %5.2lf %%" @@ -208,9 +208,9 @@ GPLv2 (L) # Determine the currently processing UID Cur_UID="$(id -u "$User")" - # skip if actual user a system user und low_uid ist set to never + # skip if actual user a system user und low_uid ist set to never [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue - + # No graph for none human uid if low_uid ist set to no [ "$Cur_UID" -lt "$Min_UID" ] && echo "$Fieldname.graph $Low_UID" @@ -218,7 +218,7 @@ GPLv2 (L) echo "$Fieldname.label $User" echo "$Fieldname.warning $Warning" echo "$Fieldname.critical $Critical" - + done # configure the total line and send exit code NO ERROR happens @@ -238,15 +238,15 @@ GPLv2 (L) Quota=( ${Quotas[$i]} ) Fieldname="$(clean_fieldname "${Quota[0]}")" - # skip if actual user a system user und low_uid ist set to never + # skip if actual user a system user und low_uid ist set to never [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue - # write the result zu munin + # write the result zu munin echo "${Quota[2]} ${Quota[4]} $Fieldname.value" | awk '{printf "%s %f\n",$3,$1*100/$2}' done -# the value for the total line +# the value for the total line Total=( ${Totals[1]} ) echo "${Total[2]} ${Total[1]} total.value" | awk '{printf "%s %f\n",$3,$1*100/$2}' diff --git a/plugins/disk/raid b/plugins/disk/raid index c835757a..bbfc0e8a 100755 --- a/plugins/disk/raid +++ b/plugins/disk/raid @@ -1,11 +1,11 @@ #!/usr/bin/perl -w -# +# # (c) 2007 Nathan Rutman nathan@clusterfs.com -# -# Plugin to monitor RAID status +# +# Plugin to monitor RAID status # # Results are % of healthy drives in a raid device -# and % rebuilt of devices that are resyncing. +# and % rebuilt of devices that are resyncing. # #%# family=contrib #%# capabilities=autoconf @@ -54,7 +54,7 @@ while (@text) { $failed = $members; $failed =~ s/[^F]+//g; $failed = length($failed); - + $line = shift @text; if ($line =~ /$devstat_re/) { # second line should like "123456 blocks super 1.2 [2/2] [UU]" @@ -66,7 +66,7 @@ while (@text) { # second line did not exist on /proc/mdstat next; } - + $line = shift @text; if ($line =~ /$action_re/) { # third line should like " [==>..................] check = 10.0% (12345/123456) finish=123min speed=12345/sec" diff --git a/plugins/disk/raid-mismatch-count b/plugins/disk/raid-mismatch-count index e84c1dd1..5979b53f 100755 --- a/plugins/disk/raid-mismatch-count +++ b/plugins/disk/raid-mismatch-count @@ -3,22 +3,22 @@ # Copyright (C) 2011 Rory Jaffe # derived from md_sync_speed by Kristian Lyngstøl # Copyright (C) 2010 Kristian Lyngstøl -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ##### -# +# # Magic Markers: # #%# family=auto # #%# capabilities=autoconf diff --git a/plugins/disk/scsi_queue b/plugins/disk/scsi_queue index e1c14cf2..0cbf165b 100755 --- a/plugins/disk/scsi_queue +++ b/plugins/disk/scsi_queue @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Munin plugin which reports queue busy-values per online SCSI +Munin plugin which reports queue busy-values per online SCSI device on Linux, as seen in /proc/scsi/sg/devices If the busy-values often reach the queue depth of the device, @@ -11,7 +11,7 @@ plugin. Wildcard use: If your system has many SCSI-like devices, filtering may be needed to make the resulting graphs readable. -If you symlink the plugin, so that it's executed as +If you symlink the plugin, so that it's executed as scsi_queue_X_through_Y then the plugin will only look at devices /dev/sdX .. /dev/sdY @@ -27,7 +27,7 @@ X and Y are translated into a regular expression like: # Released according to the "New BSD License" AKA the 3-clause # BSD License: -# ==================================================================== +# ==================================================================== # Copyright (c) 2010, Danish National Board of Health. # All rights reserved. # @@ -52,7 +52,7 @@ X and Y are translated into a regular expression like: # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ==================================================================== +# ==================================================================== # $Id: scsi_queue 13630 2010-08-31 15:29:14Z tra $ @@ -144,10 +144,10 @@ def map_procentries_to_devices(list_of_dicts,devfilter_regex): # 2:0:0:0 # 3:0:0:0 # - # The colon-separated values map to the first four parts + # The colon-separated values map to the first four parts # of /proc/scsi/sg/devices - # And the directory entries are symlinks which point to directories - # in /sys/devices. By following a symlink, we may end up in + # And the directory entries are symlinks which point to directories + # in /sys/devices. By following a symlink, we may end up in # a directory which contains directory entries like: # - block:sdb # ... @@ -162,7 +162,7 @@ def map_procentries_to_devices(list_of_dicts,devfilter_regex): # Search for dirent called block:SOMETHING # Put SOMETHING into blockdev_name - # Couldn't make glob.glob() work: The length of the result + # Couldn't make glob.glob() work: The length of the result # of glob() returned TypeError: len() of unsized object on # RHEL 5's python... dirents = os.listdir(sys_pathname) diff --git a/plugins/disk/snmp__areca_ b/plugins/disk/snmp__areca_ index dbb41454..e694fd79 100755 --- a/plugins/disk/snmp__areca_ +++ b/plugins/disk/snmp__areca_ @@ -189,7 +189,7 @@ def snmpwalk(root): # Decode SNMP response rsp.decode(answer) - + # Make sure response matches request (request IDs, communities, etc) if req != rsp: raise 'Unmatched response: %s vs %s' % (str(req), str(rsp)) diff --git a/plugins/disk/snmp__hp_temp b/plugins/disk/snmp__hp_temp index c943ef58..8716370e 100755 --- a/plugins/disk/snmp__hp_temp +++ b/plugins/disk/snmp__hp_temp @@ -4,7 +4,7 @@ # # Munin-plugin to monitor temperature on HP-servers. # Uses SNMP, and needs hpasmd. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/disk/xfs b/plugins/disk/xfs index 7f32746c..8a06a555 100755 --- a/plugins/disk/xfs +++ b/plugins/disk/xfs @@ -94,7 +94,7 @@ while () { close(IN); foreach my $func ( keys %config ) { - print "multigraph xfs_$func\n"; + print "multigraph xfs_$func\n"; if (defined $ARGV[0] and $ARGV[0] eq 'config') { print "graph_title $config{$func}->{title}\n"; diff --git a/plugins/disk/xfs_frag b/plugins/disk/xfs_frag index 7218934a..d41d4fcc 100755 --- a/plugins/disk/xfs_frag +++ b/plugins/disk/xfs_frag @@ -37,7 +37,7 @@ shopt -s nocasematch case $1 in config) - cat <<'EOF' + cat <<'EOF' graph_title XFS fragmentation graph_vlabel Percent graph_category disk @@ -64,4 +64,4 @@ do echo $FIELDNAME.value $FRAG fi done - + diff --git a/plugins/dkim/dkimproxy_mails b/plugins/dkim/dkimproxy_mails index 8fe7cfe6..b68e4890 100755 --- a/plugins/dkim/dkimproxy_mails +++ b/plugins/dkim/dkimproxy_mails @@ -10,7 +10,7 @@ # autoconf (optional) # # Gives the result of the verification. The following values are possible: -# +# # pass # Returned if a valid DKIM-Signature header was found, and the signature contains a correct value for the message. # @@ -22,9 +22,9 @@ # # none # Returned if no DKIM-Signature headers (valid or invalid) were found. -# +# # In case of multiple signatures, the "best" result will be returned. Best is defined as "pass", followed by "fail", "invalid", and "none". -# +# mktempfile () { mktemp -t @@ -59,7 +59,7 @@ if [ "$1" = "config" ]; then echo 'dkiminvalid.label Invalid DKIM-Sigs' echo 'dkiminvalid.min 0' echo 'dkimfail.label Failed DKIM-Sigs' - echo 'dkimfail.min 0' + echo 'dkimfail.min 0' exit 0 fi diff --git a/plugins/docker/docker_cpu b/plugins/docker/docker_cpu index e25b7e95..cb56fa90 100755 --- a/plugins/docker/docker_cpu +++ b/plugins/docker/docker_cpu @@ -79,7 +79,7 @@ for my $i (1 .. $#containers) } } } - + if (defined $ARGV[0] and $ARGV[0] eq "config") { my $nanoSecondsInSecond=1000000000; @@ -88,7 +88,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") if ($$_{'ncpu'} > $graphlimit){ $graphlimit = $$_{'ncpu'}; } - } + } $graphlimit = $graphlimit * 100; print "graph_title Docker container CPU usage\n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit $graphlimit\n"; @@ -97,7 +97,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_period second\n"; print "graph_category virtualization\n"; print "graph_info This graph shows docker container CPU usage.\n"; - + foreach(@result) { print "$$_{'name'}.label $$_{'name'}\n"; diff --git a/plugins/docker/docker_memory b/plugins/docker/docker_memory index 6e43c0b7..6fba71b4 100755 --- a/plugins/docker/docker_memory +++ b/plugins/docker/docker_memory @@ -81,7 +81,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_vlabel Bytes\n"; print "graph_category virtualization\n"; print "graph_info This graph shows docker container memory usage.\n"; - + foreach(@result) { print "$$_{'name'}.label $$_{'name'}\n"; diff --git a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot index 303b3ab1..a9e3de10 100755 --- a/plugins/dovecot/dovecot +++ b/plugins/dovecot/dovecot @@ -2,18 +2,18 @@ # # Munin Plugin # to count logins to your dovecot mailserver -# +# # Created by Dominik Schulz # http://developer.gauner.org/munin/ # Contributions by: # - Stephane Enten # - Steve Schnepp -# +# # Parameters understood: # # config (required) # autoconf (optional - used by munin-config) -# +# # Config variables: # # logfile - Where to find the syslog file diff --git a/plugins/drbd/drbd b/plugins/drbd/drbd index 4216902e..49644f9d 100755 --- a/plugins/drbd/drbd +++ b/plugins/drbd/drbd @@ -96,7 +96,7 @@ open (IN, $file ) || die "Could not open $file for reading: $!"; my ($changed) = $_ =~ /changed:(\d*)/; $store->{ $temp }->{'act_log'}->{changed} = $changed if $changed ne undef; } - + my ($ns) = $_ =~ /ns:(\d*)/; $store->{ $temp }->{'ns'} = $ns if $ns ne undef; diff --git a/plugins/drbd/drbd-stat b/plugins/drbd/drbd-stat index 2a6e7259..16e0cae4 100755 --- a/plugins/drbd/drbd-stat +++ b/plugins/drbd/drbd-stat @@ -85,7 +85,7 @@ open (IN, $file ) || die "Could not open $file for reading: $!"; my ($changed) = $_ =~ /changed:(\d*)/; $store->{ $temp }->{'act_log'}->{changed} = $changed if $changed ne undef; } - + my ($ns) = $_ =~ /ns:(\d*)/; $store->{ $temp }->{'ns'} = $ns if $ns ne undef; diff --git a/plugins/dspam/dspam_ b/plugins/dspam/dspam_ index 78e0f390..b301373e 100755 --- a/plugins/dspam/dspam_ +++ b/plugins/dspam/dspam_ @@ -71,7 +71,7 @@ should be in the format: dspam__, where: a total of all uids, or for a list of all uids (depending on the graph type). NB For advanced uid selection such as 'all users of domain - example.org', please see the environment variable 'pattern' + example.org', please see the environment variable 'pattern' under CONFIGURATION. =head1 INTERPRETATION diff --git a/plugins/dvb/2wcomdsr_ b/plugins/dvb/2wcomdsr_ index 86c43265..273d1cc5 100755 --- a/plugins/dvb/2wcomdsr_ +++ b/plugins/dvb/2wcomdsr_ @@ -2,7 +2,7 @@ ################################################################################################### # -# Multigraph munin plugin to monitor 2wcom DSR01/DSR02 Professional Audio Satellite-Receivers +# Multigraph munin plugin to monitor 2wcom DSR01/DSR02 Professional Audio Satellite-Receivers # through their web interface status page. # # To use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) @@ -140,7 +140,7 @@ echo "graph_info Detected bit error rate (BER) before the Reed Solomon error cor echo "ber_rs.label RS/BCH BER" echo "ber_rs.critical 0.0002" echo "ber_rs.info BER before Reed Solomon error correction" -echo +echo exit 0;; esac diff --git a/plugins/dvb/femon b/plugins/dvb/femon index b51a17fb..7d64e106 100755 --- a/plugins/dvb/femon +++ b/plugins/dvb/femon @@ -2,8 +2,8 @@ # -*- encoding: iso-8859-1 -*- # # Wildcard-plugin to monitor DVB signal information via femon command line utility, -# -# To monitor a dvb device, link femon_ to this file. +# +# To monitor a dvb device, link femon_ to this file. # E.g. # ln -s /usr/share/munin/plugins/femon_ /etc/munin/plugins/femon_adapter0 # ...will monitor /dev/dvb/adapter0. @@ -15,13 +15,13 @@ # Parameters # femonpath - Specify path to femon program (Default: /usr/bin/femon) # graph_args - Specify graph args (Default: --lower-limit 0 --upper-limit 100 --rigid) -# +# # Author: Nicolas Knotzer -# +# # v1.0 02/10/2011 # v1.1 20/10/2011 - Prints OSError.strerror in verbose mode, uses rsplit instead of split for parsing femon output # v1.2 21/10/2011 - Uses subprocess.Popen instead of subprocess.check_output for better compatibility with older python versions (i.e. works with python version >= 2.4) -# v1.3 25/10/2011 - Configure upper and lower graph limits with graph_args environment variable. +# v1.3 25/10/2011 - Configure upper and lower graph limits with graph_args environment variable. # # Copyright (c) 2011 Nicolas Knotzer. # @@ -70,7 +70,7 @@ def find_dvb_adapters() : adapters.append(adapter) except : continue - except : + except : verboselog('Failed to list adapters in /dev/dvb') return(adapters) @@ -78,7 +78,7 @@ def get_dvb_adapter_name() : global plugin_name try : name=[plugin_name[string.rindex(plugin_name,'_')+1:]] - + # Check that the adapter exists in /dev/dvb if not os.path.exists('/dev/dvb/'+name[0]): verboselog('/dev/dvb/'+name[0]+' not found!') @@ -117,7 +117,7 @@ def print_dvb_adapter_values(dvb_adapter) : try : verboselog('Reading values from '+dvb_adapter[0]) mypipe = subprocess.Popen([os.getenv('femonpath','/usr/bin/femon'), '-H', '-c 1', '-a '+dvb_adapter[0].replace('adapter','')], stdout=subprocess.PIPE) - femon_output = mypipe.communicate()[0] + femon_output = mypipe.communicate()[0] verboselog(femon_output) except OSError, e: verboselog('Cannot access femon values! Check user rights or proper femon installation.') @@ -145,13 +145,13 @@ if len(sys.argv)>1 : print_adapter_config (dvb_adapter) sys.exit(0) elif sys.argv[1]=="autoconf" : - if os.path.exists(os.getenv('femonpath','/usr/bin/femon')) : + if os.path.exists(os.getenv('femonpath','/usr/bin/femon')) : if not find_dvb_adapters(): print('no (no dvb adapters accessible)') else : print('yes') sys.exit(0) - else : + else : print('no (femon not found)') sys.exit(0) elif sys.argv[1]=="suggest" : @@ -179,16 +179,16 @@ femon_ - Munin wildcard-plugin to monitor dvb signal information attribute value =head1 APPLICABLE SYSTEMS -Node with B interpreter and B +Node with B interpreter and B installed and in function. =head1 CONFIGURATION - + =head2 Create link in service directory To monitor a dvb device, create a link in the service directory of the munin-node named femon_, which is pointing to this file. - + E.g. ln -s /usr/share/munin/plugins/femon_ /etc/munin/plugins/femon_adapter0 @@ -211,7 +211,7 @@ So following minimal configuration in plugin-conf.d/munin-node is needed. =head2 Set Parameter if needed femonpath - Specify path to femon program (Default: /usr/bin/femon) - + =head1 INTERPRETATION diff --git a/plugins/ejabberd/ejabberd_stats_ b/plugins/ejabberd/ejabberd_stats_ index dac2b196..9eb7c184 100755 --- a/plugins/ejabberd/ejabberd_stats_ +++ b/plugins/ejabberd/ejabberd_stats_ @@ -33,7 +33,7 @@ graph_title ejabberd stats - connected users graph_args --base 1000 --lower-limit 0 graph_vlabel users graph_info The number of currently connected users, as of ejabberd report -graph_category chat +graph_category chat graph_order active connected.label Connected users connected.draw AREA diff --git a/plugins/emc/emc_vnx_block_lun_perfdata b/plugins/emc/emc_vnx_block_lun_perfdata index 99a5a440..5520c6e3 100755 --- a/plugins/emc/emc_vnx_block_lun_perfdata +++ b/plugins/emc/emc_vnx_block_lun_perfdata @@ -2,7 +2,7 @@ : <<=cut -=head1 NAME +=head1 NAME emc_vnx_block_lun_perfdata - Plugin to monitor Block statistics of EMC VNX 5300 Unified Storage Processors @@ -23,16 +23,16 @@ =head1 DESCRIPTION The plugin monitors LUN of EMC Unified Storage FLARE SP's. Probably it can also - be compatible with other Clariion systems. It uses SSH to connect to Control - Stations, then remotely executes /nas/sbin/navicli and fetches and parses data + be compatible with other Clariion systems. It uses SSH to connect to Control + Stations, then remotely executes /nas/sbin/navicli and fetches and parses data from it. Obviously, it's easy to reconfigure plugin not to use Control Stations' - navicli in favor of using locally installed /opt/Navisphere's cli. There is no - difference which Storage Processor to use to gather data, so this plugin tries - both of them and uses the first active one. This plugin also automatically - chooses Primary Control Station from the list by calling /nasmcd/sbin/getreason + navicli in favor of using locally installed /opt/Navisphere's cli. There is no + difference which Storage Processor to use to gather data, so this plugin tries + both of them and uses the first active one. This plugin also automatically + chooses Primary Control Station from the list by calling /nasmcd/sbin/getreason and /nasmcd/sbin/t2slot. - - I left some parts of this plugin as rudimental to make easy to reconfigure it + + I left some parts of this plugin as rudimental to make easy to reconfigure it to draw more (or less) data. The plugin has been tested in the following Operating Environment (OE): @@ -41,15 +41,15 @@ =head1 COMPATIBILITY - The plugin has been written for being compatible with EMC VNX5300 Storage - system, as this is the only EMC storage which i have. By the way, i am pretty - sure it can also work with other VNX1 storages, like VNX5100 and VNX5500, and + The plugin has been written for being compatible with EMC VNX5300 Storage + system, as this is the only EMC storage which i have. By the way, i am pretty + sure it can also work with other VNX1 storages, like VNX5100 and VNX5500, and old-style Clariion systems. - About VNX2 series, i don't know whether the plugin will be able to work with - them. Maybe it would need some corrections in command-line backend. The same - situation is with other EMC systems, so i encourage you to try and fix the - plugin. - + About VNX2 series, i don't know whether the plugin will be able to work with + them. Maybe it would need some corrections in command-line backend. The same + situation is with other EMC systems, so i encourage you to try and fix the + plugin. + =head1 LIST OF GRAPHS Graph category Disk: @@ -70,68 +70,68 @@ First of all, be sure that statistics collection is turned on. You can do this by typing: navicli -h spa setstats -on - on your Control Station or locally through /opt/Navisphere + on your Control Station or locally through /opt/Navisphere - Also, the plugin actively uses buggy "cdef" feature of Munin 2.0, and here we + Also, the plugin actively uses buggy "cdef" feature of Munin 2.0, and here we can be hit by the following bugs: - http://munin-monitoring.org/ticket/1017 - Here I have some workarounds in the + http://munin-monitoring.org/ticket/1017 - Here I have some workarounds in the plugin, be sure that they are working. - http://munin-monitoring.org/ticket/1352 - Metrics in my plugin can be much + http://munin-monitoring.org/ticket/1352 - Metrics in my plugin can be much longer than 15 characters. Without these workarounds "Load" and "Queue Length" would not work. =head2 Installation - The plugin uses SSH to connect to Control Stations. It's possible to use + The plugin uses SSH to connect to Control Stations. It's possible to use 'nasadmin' user, but it would be better if you create read-only global user by - Unisphere Client. The user should have only Operator role. - I created "operator" user but due to the fact that Control Stations already - had one internal "operator" user, the new one was called "operator1". So be + Unisphere Client. The user should have only Operator role. + I created "operator" user but due to the fact that Control Stations already + had one internal "operator" user, the new one was called "operator1". So be careful. After that, copy .bash_profile from /home/nasadmin to a newly created /home/operator1. - - On munin-node side choose a user which will be used to connect through SSH. - Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", - "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created + + On munin-node side choose a user which will be used to connect through SSH. + Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", + "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created user. - - Make a link from /usr/share/munin/plugins/emc_vnx_dm_basic_stats to - /etc/munin/plugins/emc_vnx_dm_basic_stats_, where is any - arbitrary name of your storage system. The plugin will return in its + + Make a link from /usr/share/munin/plugins/emc_vnx_dm_basic_stats to + /etc/munin/plugins/emc_vnx_dm_basic_stats_, where is any + arbitrary name of your storage system. The plugin will return in its answer as "host_name" field. - + For example, assume your storage system is called "VNX5300". - Make a configuration file at + Make a configuration file at /etc/munin/plugin-conf.d/emc_vnx_block_lun_perfdata_VNX5300. For example: - + [emc_vnx_block_lun_perfdata_VNX5300] - user munin + user munin env.username operator1 - env.cs_addr 192.168.1.1 192.168.1.2 + env.cs_addr 192.168.1.1 192.168.1.2 or: [emc_vnx_block_lun_perfdata_VNX5300] - user munin + user munin env.username operator1 env.localcli /opt/Navisphere/bin/naviseccli env.sp_addr 192.168.0.3 192.168.0.4 env.blockpw foobar - Where: + Where: user - SSH Client local user env.username - Remote user with Operator role for Block or File part env.cs_addr - Control Stations addresses for remote (indirect) access. - env.localcli - Optional. Path of localhost 'Naviseccli' binary. If this + env.localcli - Optional. Path of localhost 'Naviseccli' binary. If this variable is set, env.cs_addr is ignored, and local 'navicli' is used. Requires env.blockpw variable. - env.sp_addr - Default is "SPA SPB". In case of "direct" connection to + env.sp_addr - Default is "SPA SPB". In case of "direct" connection to Storage Processors, their addresses/hostnames are written here. env.blockpw - Password for connecting to Storage Processors =head1 ERRATA - It counts Queue Length in not fully correct way. We take parameters totally + It counts Queue Length in not fully correct way. We take parameters totally from both SP's, but after we divide them independently by load of SPA and SPB. Anyway, in most AAA / ALUA cases the formula is correct. @@ -165,7 +165,7 @@ else NAVICLI="/nas/sbin/navicli" fi -# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. +# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. ssh_check_cmd() { ssh -q "$username@$1" "/nasmcd/sbin/getreason | grep -w \"slot_\$(/nasmcd/sbin/t2slot)\" | cut -d- -f1 | awk '{print \$1}' " } @@ -253,7 +253,7 @@ echo "host_name ${TARGET}" echo if [ "$1" = "config" ] ; then - cat <<-EOF + cat <<-EOF multigraph emc_vnx_block_blocks graph_category disk graph_title EMC VNX 5300 LUN Blocks @@ -263,7 +263,7 @@ if [ "$1" = "config" ] ; then while read -r LUN ; do LUN="$(clean_fieldname "$LUN")" - cat <<-EOF + cat <<-EOF ${LUN}_read.label none ${LUN}_read.graph no ${LUN}_read.min 0 @@ -304,8 +304,8 @@ if [ "$1" = "config" ] ; then multigraph emc_vnx_block_ticks graph_category disk graph_title EMC VNX 5300 Counted Load per LUN - graph_vlabel Load, % * Number of LUNs - graph_args --base 1000 -l 0 -r + graph_vlabel Load, % * Number of LUNs + graph_args --base 1000 -l 0 -r EOF echo -n "graph_order " while read -r LUN ; do @@ -332,7 +332,7 @@ if [ "$1" = "config" ] ; then ${LUN}_idleticks_spb.label $LUN Idle Ticks SPB ${LUN}_idleticks_spb.type COUNTER ${LUN}_idleticks_spb.graph no - ${LUN}_load_spa.label $LUN load SPA + ${LUN}_load_spa.label $LUN load SPA ${LUN}_load_spa.draw AREASTACK ${LUN}_load_spb.label $LUN load SPB ${LUN}_load_spb.draw AREASTACK @@ -342,7 +342,7 @@ if [ "$1" = "config" ] ; then done <<< "$LUNLIST" cat <<-EOF - + multigraph emc_vnx_block_outstanding graph_category disk graph_title EMC VNX 5300 Sum of Outstanding Requests @@ -351,14 +351,14 @@ if [ "$1" = "config" ] ; then EOF while read -r LUN ; do LUN="$(clean_fieldname "$LUN")" - cat <<-EOF + cat <<-EOF ${LUN}_outstandsum.label $LUN ${LUN}_outstandsum.type COUNTER EOF done <<< "$LUNLIST" cat <<-EOF - + multigraph emc_vnx_block_nonzeroreq graph_category disk graph_title EMC VNX 5300 Non-Zero Request Count Arrivals @@ -392,7 +392,7 @@ if [ "$1" = "config" ] ; then multigraph emc_vnx_block_queue graph_category disk - graph_title EMC VNX 5300 Counted Block Queue Length + graph_title EMC VNX 5300 Counted Block Queue Length graph_vlabel Length EOF while read -r LUN ; do @@ -451,10 +451,10 @@ if [ "$1" = "config" ] ; then cat <<-EOF ${SPclean}_total_busyticks.label ${SP} ${SPclean}_total_busyticks.graph no - ${SPclean}_total_busyticks.type COUNTER + ${SPclean}_total_busyticks.type COUNTER ${SPclean}_total_bt.label ${SP} ${SPclean}_total_bt.graph no - ${SPclean}_total_bt.type COUNTER + ${SPclean}_total_bt.type COUNTER ${SPclean}_total_idleticks.label ${SP} ${SPclean}_total_idleticks.graph no ${SPclean}_total_idleticks.type COUNTER @@ -469,8 +469,8 @@ fi #BIGCMD="$SSH" while read -r LUN ; do FILTERLUN="$(clean_fieldname "$LUN")" - BIGCMD+="$NAVICLI lun -list -name $LUN -perfData | - sed -ne 's/^Blocks Read\:\ */${FILTERLUN}_read.value /p; + BIGCMD+="$NAVICLI lun -list -name $LUN -perfData | + sed -ne 's/^Blocks Read\:\ */${FILTERLUN}_read.value /p; s/^Blocks Written\:\ */${FILTERLUN}_write.value /p; s/Read Requests\:\ */${FILTERLUN}_readreq.value /p; s/Write Requests\:\ */${FILTERLUN}_writereq.value /p; diff --git a/plugins/emc/emc_vnx_file_ b/plugins/emc/emc_vnx_file_ index 45bda578..32a1c26a 100755 --- a/plugins/emc/emc_vnx_file_ +++ b/plugins/emc/emc_vnx_file_ @@ -2,9 +2,9 @@ : <<=cut -=head1 NAME +=head1 NAME - emc_vnx_file_stats - Plugin to monitor Basic, NFSv3 and NFSv4 statistics of + emc_vnx_file_stats - Plugin to monitor Basic, NFSv3 and NFSv4 statistics of EMC VNX 5300 Unified Storage system's Datamovers =head1 AUTHOR @@ -22,24 +22,24 @@ =head1 DESCRIPTION - The plugin monitors basic statistics of EMC Unified Storage system Datamovers - and NFS statistics of EMC VNX5300 Unified Storage system. Probably it can + The plugin monitors basic statistics of EMC Unified Storage system Datamovers + and NFS statistics of EMC VNX5300 Unified Storage system. Probably it can also be compatible with other Isilon or Celerra systems. It uses SSH to connect - to Control Stations, then remotely executes '/nas/sbin/server_stats' and - fetches and parses data from it. It supports gathering data both from - active/active and active/passive Datamover configurations, ignoring offline or - standby Datamovers. + to Control Stations, then remotely executes '/nas/sbin/server_stats' and + fetches and parses data from it. It supports gathering data both from + active/active and active/passive Datamover configurations, ignoring offline or + standby Datamovers. If all Datamovers are offline or absent, the plugin returns error. This plugin also automatically chooses Primary Control Station from the list by calling '/nasmcd/sbin/getreason' and '/nasmcd/sbin/t2slot'. - + At the moment data is gathered from the following statistics sources: * nfs.v3.op - Tons of timings about NFSv3 RPC calls * nfs.v4.op - Tons of timings about NFSv4 RPC calls * nfs.client - Here new Client addresses are rescanned and added automatically. * basic-std Statistics Group - Basic Statistics of Datamovers (eg. CPU, Memory etc.) - + It's quite easy to comment out unneeded data to make graphs less overloaded or to add new statistics sources. @@ -78,48 +78,48 @@ =head1 COMPATIBILITY - The plugin has been written for being compatible with EMC VNX5300 Storage + The plugin has been written for being compatible with EMC VNX5300 Storage system, as this is the only EMC storage which i have. By the way, i am pretty sure it can also work with other VNX1 storages, like VNX5100 and VNX5500. - About VNX2 series, i don't know whether the plugin will be able to work with + About VNX2 series, i don't know whether the plugin will be able to work with them. Maybe it would need some corrections in command-line backend. The same - situation is with other EMC systems, so i encourage you to try and fix the - plugin. + situation is with other EMC systems, so i encourage you to try and fix the + plugin. =head1 CONFIGURATION - The plugin uses SSH to connect to Control Stations. It's possible to use + The plugin uses SSH to connect to Control Stations. It's possible to use 'nasadmin' user, but it would be better if you create read-only global user by Unisphere Client. The user should have only Operator role. I created "operator" user but due to the fact that Control Stations already - had one internal "operator" user, the new one was called "operator1". So be + had one internal "operator" user, the new one was called "operator1". So be careful. After that, copy .bash_profile from /home/nasadmin to a newly created /home/operator1 - + On munin-node side choose a user which will be used to connect through SSH. - Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", - "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created + Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", + "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created user. - - Make a link from /usr/share/munin/plugins/emc_vnx_file_stats to - /etc/munin/plugins/. If you want to get NFS statistics, name the link as + + Make a link from /usr/share/munin/plugins/emc_vnx_file_stats to + /etc/munin/plugins/. If you want to get NFS statistics, name the link as "emc_vnx_file_nfs_stats_", otherwise to get Basic Datamover statistics you have to name it "emc_vnx_file_basicdm_stats_", where is any - arbitrary name of your storage system. The plugin will return in its + arbitrary name of your storage system. The plugin will return in its answer as "host_name" field. For example, assume your storage system is called "VNX5300". - Make a configuration file at + Make a configuration file at /etc/munin/plugin-conf.d/emc_vnx_file_stats_VNX5300 - - [emc_vnx_file_*] - user munin - env.username operator1 - env.cs_addr 192.168.1.1 192.168.1.2 - env.nas_servers server_2 server_3 - Where: + [emc_vnx_file_*] + user munin + env.username operator1 + env.cs_addr 192.168.1.1 192.168.1.2 + env.nas_servers server_2 server_3 + + Where: user - SSH Client local user env.username - Remote user with Operator role env.cs_addr - Control Stations addresses @@ -143,7 +143,7 @@ cs_addr=${cs_addr:=""} username=${username:=""} nas_servers=${nas_servers:="server_2 server_3"} -# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. +# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. ssh_check_cmd() { ssh -q "$username@$1" "/nasmcd/sbin/getreason | grep -w \"slot_\$(/nasmcd/sbin/t2slot)\" | cut -d- -f1 | awk '{print \$1}' " @@ -192,7 +192,7 @@ if [ "$1" = "suggest" ]; then fi STATSTYPE=$(echo "${0##*/}" | cut -d _ -f 1-5) -if [ "$STATSTYPE" = "emc_vnx_file_nfs_stats" ]; then STATSTYPE=NFS; +if [ "$STATSTYPE" = "emc_vnx_file_nfs_stats" ]; then STATSTYPE=NFS; elif [ "$STATSTYPE" = "emc_vnx_file_basicdm_stats" ]; then STATSTYPE=BASICDM; else echo "Do not know what to do. Name the plugin as 'emc_vnx_file_nfs_stats_' or 'emc_vnx_file_basicdm_stats_'" >&2; exit 1; fi @@ -213,9 +213,9 @@ if [ "$1" = "config" ] ; then run_remote nas_server -i "$server" | grep -q 'type *= nas' || continue nas_server_ok=TRUE filtered_server="$(clean_fieldname "$server")" - + if [ "$STATSTYPE" = "BASICDM" ] ; then - cat <<-EOF + cat <<-EOF multigraph emc_vnx_cpu_percent graph_title EMC VNX 5300 Datamover CPU Util % graph_vlabel % @@ -259,7 +259,7 @@ if [ "$1" = "config" ] ; then ${server}_total.label ${server} Total ${server}_freebuffer.label ${server} Free Buffer ${server}_encumbered.label ${server} Encumbered - + multigraph emc_vnx_filecache graph_title EMC VNX 5300 File Buffer Cache graph_vlabel per second @@ -272,7 +272,7 @@ if [ "$1" = "config" ] ; then ${server}_w_hits.label Watermark Hits ${server}_hits.label Hits ${server}_lookups.label Lookups - + multigraph emc_vnx_fileresolve graph_title EMC VNX 5300 FileResolve graph_vlabel Entries @@ -286,8 +286,8 @@ if [ "$1" = "config" ] ; then if [ "$STATSTYPE" = "NFS" ] ; then #nfs.v3.op data # [nasadmin@mnemonic0 ~]$ server_stats server_2 -info nfs.v3.op -# server_2 : -# +# server_2 : +# # name = nfs.v3.op # description = NFS V3 per operation statistics # type = Set @@ -296,7 +296,7 @@ if [ "$1" = "config" ] ; then # member_of = nfs.v3 member_elements_by_line=$(run_remote server_stats "$server" -info nfs.v3.op | grep member_elements | sed -ne 's/^.*= //p') IFS=',' read -ra graphs <<< "$member_elements_by_line" - cat <<-EOF + cat <<-EOF multigraph vnx_emc_v3_calls_s graph_title EMC VNX 5300 NFSv3 Calls per second graph_vlabel Calls @@ -309,7 +309,7 @@ if [ "$1" = "config" ] ; then done cat <<-EOF - + multigraph vnx_emc_v3_usec_call graph_title EMC VNX 5300 NFSv3 uSeconds per call graph_vlabel uSec / call @@ -362,7 +362,7 @@ if [ "$1" = "config" ] ; then echo "${server}_$field.label $server $field" done cat <<-EOF - + multigraph vnx_emc_v4_op_percent graph_title EMC VNX 5300 NFSv4 Op % graph_vlabel % @@ -376,7 +376,7 @@ if [ "$1" = "config" ] ; then done #nfs.client data -# Total Read Write Suspicious Total Read Write Avg +# Total Read Write Suspicious Total Read Write Avg # Ops/s Ops/s Ops/s Ops diff KiB/s KiB/s KiB/s uSec/call member_elements_by_line=$(run_remote server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no -titles never | sed -ne 's/^.*id=//p' | cut -d' ' -f1) #Somewhy readarray adds extra \n in the end of each variable. So, we use read() with a workaround @@ -437,8 +437,8 @@ if [ "$1" = "config" ] ; then done #nfs-std -# Timestamp NFS Read Read Read Size Write Write Write Size Active -# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads +# Timestamp NFS Read Read Read Size Write Write Write Size Active +# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads cat <<-EOF multigraph vnx_emc_nfs_std_nfs_ops @@ -451,7 +451,7 @@ if [ "$1" = "config" ] ; then echo "${filtered_server}_wops.label $server Write Ops/s" echo "${filtered_server}_wops.draw STACK" echo "${filtered_server}_tops.label $server Total Ops/s" - + cat <<-EOF multigraph vnx_emc_nfs_std_nfs_b_s @@ -465,7 +465,7 @@ if [ "$1" = "config" ] ; then echo "${filtered_server}_wbs.draw STACK" echo "${filtered_server}_tbs.label $server Total B/s" echo "${filtered_server}_tbs.cdef ${filtered_server}_rbs,${filtered_server}_wbs,+" - + cat <<-EOF multigraph vnx_emc_nfs_std_nfs_avg @@ -499,10 +499,10 @@ for server in $nas_servers; do if [ "$STATSTYPE" = "BASICDM" ] ; then #basicdm data -# [nasadmin@mnemonic0 ~]$ server_stats server_2 -count 1 -terminationsummary no -# server_2 CPU Network Network dVol dVol -# Timestamp Util In Out Read Write -# % KiB/s KiB/s KiB/s KiB/s +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -count 1 -terminationsummary no +# server_2 CPU Network Network dVol dVol +# Timestamp Util In Out Read Write +# % KiB/s KiB/s KiB/s KiB/s # 20:42:26 9 16432 3404 1967 24889 member_elements_by_line=$(run_remote server_stats "$server" -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') @@ -519,10 +519,10 @@ for server in $nas_servers; do echo "${server}_stor_read.value $((graphs[4] * 1024))" echo "${server}_stor_write.value $((graphs[5] * 1024))" -# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor kernel.memory -count 1 -terminationsummary no -# server_2 Free Buffer Buffer Buffer Buffer Buffer Buffer Cache Encumbered FileResolve FileResolve FileResolve Free KiB Page Total Used KiB Memory -# Timestamp Buffer Cache High Cache Cache Cache Cache Low Watermark Memory Dropped Max Used Size Memory Util -# KiB Watermark Hits/s Hit % Hits/s Lookups/s Watermark Hits/s Hits/s KiB Entries Limit Entries KiB KiB % +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor kernel.memory -count 1 -terminationsummary no +# server_2 Free Buffer Buffer Buffer Buffer Buffer Buffer Cache Encumbered FileResolve FileResolve FileResolve Free KiB Page Total Used KiB Memory +# Timestamp Buffer Cache High Cache Cache Cache Cache Low Watermark Memory Dropped Max Used Size Memory Util +# KiB Watermark Hits/s Hit % Hits/s Lookups/s Watermark Hits/s Hits/s KiB Entries Limit Entries KiB KiB % # 20:44:14 3522944 0 96 11562 12010 0 0 3579268 0 0 0 3525848 8 6291456 2765608 44 member_elements_by_line=$(run_remote server_stats "$server" -monitor kernel.memory -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') @@ -530,7 +530,7 @@ for server in $nas_servers; do echo -e "\nmultigraph emc_vnx_memory" #Reserved for math - echo "${server}_total.value $((graphs[14] / 1))" + echo "${server}_total.value $((graphs[14] / 1))" echo "${server}_used.value $((graphs[15] / 1))" echo "${server}_free.value $((graphs[12] / 1))" echo "${server}_freebuffer.value $((graphs[1] / 1))" @@ -553,9 +553,9 @@ for server in $nas_servers; do if [ "$STATSTYPE" = "NFS" ] ; then #nfs.v3.op data # [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.v3.op -count 1 -terminationsummary no -# server_2 NFS Op NFS NFS Op NFS NFS Op % -# Timestamp Op Errors Op -# Calls/s diff uSec/Call +# server_2 NFS Op NFS NFS Op NFS NFS Op % +# Timestamp Op Errors Op +# Calls/s diff uSec/Call # 22:14:41 v3GetAttr 30 0 23 21 # v3Lookup 40 0 98070 27 # v3Access 50 0 20 34 @@ -571,7 +571,7 @@ for server in $nas_servers; do while IFS=$'\n' read -ra graphs ; do elements_array+=( $graphs ) done <<< "$member_elements_by_line" - + if [ "${#elements_array[@]}" -eq "0" ]; then LINES=0; fi echo "multigraph vnx_emc_v3_calls_s" @@ -593,9 +593,9 @@ for server in $nas_servers; do #nfs.v4.op data # [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.v4.op -count 1 -terminationsummary no -# server_2 NFS Op NFS NFS Op NFS NFS Op % -# Timestamp Op Errors Op -# Calls/s diff uSec/Call +# server_2 NFS Op NFS NFS Op NFS NFS Op % +# Timestamp Op Errors Op +# Calls/s diff uSec/Call # 22:13:14 v4Compound 2315 0 7913 30 # v4Access 246 0 5 3 # v4Close 133 0 11 2 @@ -643,9 +643,9 @@ for server in $nas_servers; do elements_array=() #nfs.client data -# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no -# server_2 Client NFS NFS NFS NFS NFS NFS NFS NFS -# Timestamp Total Read Write Suspicious Total Read Write Avg +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no +# server_2 Client NFS NFS NFS NFS NFS NFS NFS NFS +# Timestamp Total Read Write Suspicious Total Read Write Avg # Ops/s Ops/s Ops/s Ops diff KiB/s KiB/s KiB/s uSec/call # 20:26:38 id=192.168.1.223 2550 20 2196 13 4673 159 4514 1964 # id=192.168.1.2 691 4 5 1 1113 425 688 2404 @@ -687,9 +687,9 @@ for server in $nas_servers; do #nfs-std # bash-3.2$ server_stats server_2 -monitor nfs-std -# server_2 Total NFS NFS NFS Avg NFS NFS NFS Avg NFS -# Timestamp NFS Read Read Read Size Write Write Write Size Active -# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads +# server_2 Total NFS NFS NFS Avg NFS NFS NFS Avg NFS +# Timestamp NFS Read Read Read Size Write Write Write Size Active +# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads # 18:14:52 688 105 6396 62652 1 137 174763 3 member_elements_by_line=$(run_remote server_stats "$server" -monitor nfs-std -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') IFS=$' ' read -ra graphs <<< "$member_elements_by_line" @@ -700,12 +700,12 @@ for server in $nas_servers; do echo "${filtered_server}_rops.value ${graphs[2]}" echo "${filtered_server}_wops.value ${graphs[5]}" echo "${filtered_server}_tops.value ${graphs[1]}" - + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_b_s" echo "${filtered_server}_rbs.value $((graphs[3] * 1024))" echo "${filtered_server}_wbs.value $((graphs[6] * 1024))" echo "${filtered_server}_tbs.value 0" - + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_avg" echo "${filtered_server}_avg_readsize.value ${graphs[4]}" diff --git a/plugins/fan/ibmfan b/plugins/fan/ibmfan index 95d147a2..bba537b5 100755 --- a/plugins/fan/ibmfan +++ b/plugins/fan/ibmfan @@ -1,10 +1,10 @@ #!/bin/sh -# +# # Plugin to monitor fan speed on an IBM/Lenovo Laptop # # This plugin reads the current speed of the system fan from # the /proc file system. As it queries specific files provided -# by kernel modules for IBM/Lenovo Laptops, it probably only +# by kernel modules for IBM/Lenovo Laptops, it probably only # works for those, but it should be easy to adapt to others # if similar information is available for other types of laptops. # @@ -26,7 +26,7 @@ if [ "$1" = "autoconf" ]; then fi fi - + if [ "$1" = "config" ]; then echo 'graph_title Fan speed' echo 'graph_args --base 1000 -l 0' diff --git a/plugins/firebird/firebird b/plugins/firebird/firebird index 1e70bd79..7eabb3fe 100755 --- a/plugins/firebird/firebird +++ b/plugins/firebird/firebird @@ -7,7 +7,7 @@ # # "employee" must be an alias configured in the firebird aliases.conf file # -# You will also need to set +# You will also need to set # # [firebird_employee] # user root @@ -53,24 +53,24 @@ gstat=$(which gstat 2> /dev/null) ${gstat:=/opt/firebird/bin/gstat} -h ${db} | awk -F'[\t]+' \ '{ sub(/^ */,""); - if ($2 == "Oldest transaction") - { + if ($2 == "Oldest transaction") + { oldest_trans=$3; print "oldest_transaction.value " $3 } if ($2 == "Oldest active") { - oldest_active=$3 + oldest_active=$3 print "oldest_active.value " $3 } - if ($2 == "Oldest snapshot") + if ($2 == "Oldest snapshot") { - oldest_snapshot=$3 + oldest_snapshot=$3 print "oldest_snapshot.value " $3 } if ($2 == "Next transaction") { - next_transaction=$3 + next_transaction=$3 print "next_transaction.value " $3 print "oldest_trans_gap1.value " $3 - oldest_trans print "oldest_trans_gap2.value " $3 - oldest_snapshot diff --git a/plugins/freeradius/freeradius b/plugins/freeradius/freeradius index ae5e3016..b0c97422 100755 --- a/plugins/freeradius/freeradius +++ b/plugins/freeradius/freeradius @@ -42,7 +42,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title freeradius requests' echo 'graph_args --base 1000 -l 0 ' - if [ -n ${graph_period} ]; then + if [ -n ${graph_period} ]; then echo 'graph_period '${graph_period} fi echo 'graph_vlabel requests / ${graph_period}' diff --git a/plugins/ftp/proftpd_count b/plugins/ftp/proftpd_count index b0f10b50..f48e06a3 100755 --- a/plugins/ftp/proftpd_count +++ b/plugins/ftp/proftpd_count @@ -17,7 +17,7 @@ mktempfile () { mktemp -t "$1" -} +} LOGFILE=${logfile:-/var/log/proftpd/xferlog} LOGTAIL=${logtail:-$(which logtail)} diff --git a/plugins/ftp/pure-ftpd b/plugins/ftp/pure-ftpd index 2a699778..b77ed934 100755 --- a/plugins/ftp/pure-ftpd +++ b/plugins/ftp/pure-ftpd @@ -20,7 +20,7 @@ # # Configuration: # Maybe need to add following lines to plugins config file -# (e.g. /etc/munin/plugin-conf.d/pure-ftpd) to run pure-ftpwho +# (e.g. /etc/munin/plugin-conf.d/pure-ftpd) to run pure-ftpwho # as user with apropirate privilegs then restart munin-node. # # [pure-ftpd] diff --git a/plugins/ftp/pure-ftpd-bw b/plugins/ftp/pure-ftpd-bw index aa9112d2..10ef8e73 100755 --- a/plugins/ftp/pure-ftpd-bw +++ b/plugins/ftp/pure-ftpd-bw @@ -11,7 +11,7 @@ # # Configuration: # Maybe need to add following lines to plugins config file -# (e.g. /etc/munin/plugin-conf.d/pure-ftpd) to run pure-ftpwho +# (e.g. /etc/munin/plugin-conf.d/pure-ftpd) to run pure-ftpwho # as user with apropirate privilegs then restart munin-node. # # [pure-ftpd-bw] diff --git a/plugins/ftp/pureftpd_traffic b/plugins/ftp/pureftpd_traffic index c9ec4c73..d96898e5 100755 --- a/plugins/ftp/pureftpd_traffic +++ b/plugins/ftp/pureftpd_traffic @@ -54,7 +54,7 @@ if (!(-e $logfile)) { open LOG, "<$logfile" or print "Can't open logfile!\n"; my $last_line; while() { - $last_line = $_ if eof; + $last_line = $_ if eof; } close LOG; @em = split(/ /,$last_line); @@ -77,33 +77,33 @@ if (!(-e $logfile)) { $put=0, $mti=0, $dsm=0; - + open LOG, "<$logfile"; @log=; foreach $row (@log) { @parts=split(/ /,$row); $curr_ts = "$parts[3] $parts[4]"; $curr_ts =~ s/\[//g; - $curr_ts =~ s/\]//g; + $curr_ts =~ s/\]//g; if ( Date_Cmp($curr_ts,$last_ts) > 0 ) { if ( $parts[5]=~ /GET/ ) { $get+=int($parts[8]); if ($parts[2] eq "mti") { $mti+=int($parts[8]); - } + } if ($parts[2] eq "dsm") { $dsm+=int($parts[8]); - } + } } if ( $parts[5]=~ /PUT/ ) { $put-=int($parts[8]); if ($parts[2] eq "mti") { $mti-=int($parts[8]); - } + } if ($parts[2] eq "dsm") { $dsm-=int($parts[8]); - } + } } } } diff --git a/plugins/funkytown/denon_x311_volume b/plugins/funkytown/denon_x311_volume index 4dfd2e98..78fdc3e7 100755 --- a/plugins/funkytown/denon_x311_volume +++ b/plugins/funkytown/denon_x311_volume @@ -1,17 +1,17 @@ #!/usr/bin/gawk -f # Denon x311 volume-plugin for munin # Copyright (C) 2010 Kristian Lyngstol -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/plugins/games/game b/plugins/games/game index 9cb182ad..f248b5bd 100755 --- a/plugins/games/game +++ b/plugins/games/game @@ -45,7 +45,7 @@ function printMultigraph($ini_array, $machine_name, $title, $info, $max) { p("graph_category games"); p("graph_info $info"); p("graph_printf %6.0lf"); - + if (isset($ini_array['settings'][$machine_name . '_colour'])) p("players.colour " . $ini_array['settings'][$machine_name . '_colour']); @@ -106,8 +106,8 @@ if (isset($_SERVER['argv'][1])) { // Query the game servers $results = queryServers($ini_array); // Cache the query in the state file - $fp = fopen($state, 'w+') or die("I could not open state file."); - fwrite($fp, serialize($results)); + $fp = fopen($state, 'w+') or die("I could not open state file."); + fwrite($fp, serialize($results)); fclose($fp); @@ -175,7 +175,7 @@ $ini_array = parse_ini_file($config, true); // Load games.ini so we can show pretty game names $games = parse_ini_file('gameq/GameQ/games.ini', true); -$results = unserialize(file_get_contents($state)); +$results = unserialize(file_get_contents($state)); // Print individual game values foreach ($results as $name => $server){ diff --git a/plugins/geowebcache/geowebcache-bandwidth b/plugins/geowebcache/geowebcache-bandwidth index d2f9c0ce..3863510b 100755 --- a/plugins/geowebcache/geowebcache-bandwidth +++ b/plugins/geowebcache/geowebcache-bandwidth @@ -3,7 +3,7 @@ # Author Rodolphe Quideville # Licence : GPLv2 -# Code based on tomcat_volume plugin by +# Code based on tomcat_volume plugin by # Rune Nordbe Skillingstad =head1 NAME diff --git a/plugins/geowebcache/geowebcache-blankitems b/plugins/geowebcache/geowebcache-blankitems index a3c80a2f..68d00da6 100755 --- a/plugins/geowebcache/geowebcache-blankitems +++ b/plugins/geowebcache/geowebcache-blankitems @@ -3,7 +3,7 @@ # Author Rodolphe Quideville # Licence : GPLv2 -# Code based on tomcat_volume plugin by +# Code based on tomcat_volume plugin by # Rune Nordbe Skillingstad =head1 NAME diff --git a/plugins/geowebcache/geowebcache-cache-hits-ratio b/plugins/geowebcache/geowebcache-cache-hits-ratio index 270c846e..a14f4d5c 100755 --- a/plugins/geowebcache/geowebcache-cache-hits-ratio +++ b/plugins/geowebcache/geowebcache-cache-hits-ratio @@ -3,7 +3,7 @@ # Author Rodolphe Quideville # Licence : GPLv2 -# Code based on tomcat_volume plugin by +# Code based on tomcat_volume plugin by # Rune Nordbe Skillingstad =head1 NAME diff --git a/plugins/glance/glance_status b/plugins/glance/glance_status index 95917a3c..26a83e36 100755 --- a/plugins/glance/glance_status +++ b/plugins/glance/glance_status @@ -18,9 +18,9 @@ import sys import os -try: +try: from sqlalchemy.orm import exc, joinedload - + from glance.common.cfg import CommonConfigOpts from glance.registry.db import models from glance.registry.db.api import get_session, configure_db @@ -32,7 +32,7 @@ else: def load_conf(): CONF = CommonConfigOpts(project="glance", prog="glance-registry") CONF() - + # Hide missing logger warning message sys.stderr = open(os.devnull, 'w') configure_db(CONF) @@ -76,7 +76,7 @@ def print_values(): for image in images: n_image_by_status[image["status"]] = n_image_by_status.get(image["status"], 0) + 1 - for status in possible_status: + for status in possible_status: print '%s.value %s' % (status, n_image_by_status.get(status, 0)) if __name__ == '__main__': @@ -86,7 +86,7 @@ if __name__ == '__main__': if argv[1] == 'config': print_config() elif argv[1] == 'autoconf': - if not successful_import: + if not successful_import: print 'no (failed import glance and/or sqlachemy module)' sys.exit(0) try: diff --git a/plugins/google/google-rank b/plugins/google/google-rank index 00cafdba..9c6cba45 100755 --- a/plugins/google/google-rank +++ b/plugins/google/google-rank @@ -2,10 +2,10 @@ # Simple munin plugin to find the google rank for a URL/WORD combination # # THIS SCRIPT BREAKS THE TOS OF GOOGLE SO USE WITH CARE AND DON'T BLAME ME IF THINGS GO WRONG -# +# # (c) 2009 i.dobson@planet-ian.com # -# For each url/words that you want to watch you need to create a variable/word pair in your +# For each url/words that you want to watch you need to create a variable/word pair in your # munin-node configuration file for example # #[google_rank] @@ -19,18 +19,18 @@ # Version 0.5 24.1.2009 # Added loop to check the first 500 pages. Note the script sleeps 5 seconds between each page grab so # If the word/url your looking for is in the higher positions then you need to increase the timeout -# +# # Version 0.5 21.1.2009 -# Dump each page grabbed from google into separate files (helps with debugging) +# Dump each page grabbed from google into separate files (helps with debugging) # # Version 0.4 19.1.2009 # Fixed corrupt then empty cache file bug # -# Version 0.3 19.1.2009 +# Version 0.3 19.1.2009 # The script now grabs the google page based on the LASTHIT counter. # The script grabs the google page for URL1, then the next time it's called URL2 etc. If the url/word pair doesn't exist for LASTHIT then the script just dumps the cached data # -# Version 0.2 18.01.2009 +# Version 0.2 18.01.2009 # Cache added, the script only grabs the pages from google every 10 calls # The script still only checks to first 100 pages returned by google # @@ -70,7 +70,7 @@ if [ "$1" = "config" ]; then fi if [ "$WORD" = "" ]; then exit 0 - fi + fi VAR=`echo $URL.$WORD | sed -e "s/http:\/\///g"| sed -e "s/ /_/g"| sed -e "s/\./_/g"| sed -e "s/\-/_/g"` URL=`echo $URL| sed -e "s/http:\/\///g"` echo $VAR.label Pagerank $URL - $WORD @@ -108,7 +108,7 @@ if [ "$URL" != "" ]; then SEARCHWORD=`echo $WORD| sed -e "s/ /%20/g"` until [ "$FOUND" -ne "0" ]; do -#Grab page from google for the WORD/PAGE combination.Pipe it into awk to pull out the url's only, one per line. Then dump only the lines containing the URL defined +#Grab page from google for the WORD/PAGE combination.Pipe it into awk to pull out the url's only, one per line. Then dump only the lines containing the URL defined wget -q --user-agent=Firefox -O - http://www.google.com/search?q=$SEARCHWORD\&num=100\&hl=en\&safe=off\&pwst=1\&start=$start\&sa=N > /tmp/google_rank.$LASTHIT.data VALUE=`cat /tmp/google_rank.$LASTHIT.data|sed 's/
    /\n\1\n/g'|awk -v num=$num -v base=$base '{ if ( $1 ~ /^http/ ) print base,num++,$NF }'|awk '{ print $2 " " $3}'|grep -i $URL| awk '{ print $1}'` VALUE=`echo $VALUE| awk '{ print $1}'` @@ -117,7 +117,7 @@ until [ "$FOUND" -ne "0" ]; do let start="start + 100" sleep 5 else - FOUND=1 + FOUND=1 let VALUE="$VALUE + $start" fi ### echo Start=$start Value=$VALUE Found=$FOUND @@ -139,12 +139,12 @@ done #write data back rm /tmp/google_rank.cache - for iLoop in `seq 1 10`; do + for iLoop in `seq 1 10`; do echo ${Data[$iLoop]} >> /tmp/google_rank.cache done fi -#Reset counter to start +#Reset counter to start if [ "$LASTHIT" -gt 30 ]; then echo 0 > /tmp/google_rank.status fi diff --git a/plugins/google/googlecode b/plugins/google/googlecode index fed13890..7a419572 100755 --- a/plugins/google/googlecode +++ b/plugins/google/googlecode @@ -12,7 +12,7 @@ # (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 +# 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. # @@ -42,7 +42,7 @@ if [ "$1" = "autoconf" ]; then exit 0 fi -# Config. +# Config. if [ "$1" = "config" ]; then echo "graph_title Number of downloads of $PROJECTNAME from Google Code " echo "graph_args --base 1000 --lower-limit 0" diff --git a/plugins/gpu/amd_gpu_ b/plugins/gpu/amd_gpu_ index 3bbc2ae8..efdff9dc 100755 --- a/plugins/gpu/amd_gpu_ +++ b/plugins/gpu/amd_gpu_ @@ -8,13 +8,13 @@ amd_gpu_ - Wildcard plugin to monitor AMD GPUs. Uses aticonfig utility, usually bundled with AMD GPU driver, to obtain information. To use this plugin you have to make sure aticonfig will run without an active X -server (i.e. without anyone being logged in via the GUI). For more -information about this issue visit the link below: +server (i.e. without anyone being logged in via the GUI). For more +information about this issue visit the link below: http://www.mayankdaga.com/running-opencl-applications-remotely-on-amd-gpus/ =head1 CONFIGURATION -This is a wildcard plugin. The wildcard prefix link name should be the +This is a wildcard plugin. The wildcard prefix link name should be the value to monitor. This plugin uses the following configuration variables: @@ -54,7 +54,7 @@ faken@fakenmc.com =head1 LICENSE GNU General Public License, version 2 - http://www.gnu.org/licenses/gpl-2.0.html + http://www.gnu.org/licenses/gpl-2.0.html =head1 MAGIC MARKERS @@ -71,7 +71,7 @@ atiConfigExec=${aticonfexec:-'/usr/bin/aticonfig'} # Check if autoconf was requested if [ "$1" = "autoconf" ]; then - # Autoconf only returns yes if aticonfig exists and is executable + # Autoconf only returns yes if aticonfig exists and is executable if [ -x $atiConfigExec ]; then echo yes exit 0 @@ -122,7 +122,7 @@ if [ "$1" = "config" ]; then echo "temp${nGpusCounter}.info Temperature information for $gpuName" echo "temp${nGpusCounter}.label Temperature ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) - done + done ;; clocks) # First determine max clock for each GPU... @@ -149,7 +149,7 @@ if [ "$1" = "config" ]; then echo "coreclock${nGpusCounter}.info Core clock information for $gpuName" echo "coreclock${nGpusCounter}.label Core clock ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) - done + done ;; fan) echo 'graph_title GPU fan speed' @@ -164,14 +164,14 @@ if [ "$1" = "config" ]; then echo "fan${nGpusCounter}.info Fan speed information for $gpuName" echo "fan${nGpusCounter}.label Fan speed ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) - done + done ;; load) echo 'graph_title GPU load' echo 'graph_args -l 0 -u 100' echo 'graph_vlabel Percentage' echo 'graph_category htc' - echo "graph_info GPU load" + echo "graph_info GPU load" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do @@ -179,7 +179,7 @@ if [ "$1" = "config" ]; then echo "load${nGpusCounter}.info Load information for $gpuName" echo "load${nGpusCounter}.label Load ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) - done + done ;; vcore) echo 'graph_title GPU core voltage' @@ -193,7 +193,7 @@ if [ "$1" = "config" ]; then echo "vcore${nGpusCounter}.info Vcore information for $gpuName" echo "vcore${nGpusCounter}.label Core voltage ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) - done + done ;; *) echo "Can't run without a proper symlink. Exiting." diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index f50d1d26..839b4986 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -10,7 +10,7 @@ usually bundled with NVIDIA GPU driver, to obtain information. =head1 CONFIGURATION -This is a wildcard plugin. The wildcard prefix link name should be the +This is a wildcard plugin. The wildcard prefix link name should be the value to monitor. This plugin uses the following configuration variables: @@ -53,7 +53,7 @@ faken@fakenmc.com =head1 LICENSE GNU General Public License, version 2 - http://www.gnu.org/licenses/gpl-2.0.html + http://www.gnu.org/licenses/gpl-2.0.html =head1 MAGIC MARKERS @@ -70,7 +70,7 @@ nvSmiExec=${smiexec:-'/usr/bin/nvidia-smi'} # Check if autoconf was requested if [ "$1" = "autoconf" ]; then - # Autoconf only returns yes if nvidia-smi exists and is executable + # Autoconf only returns yes if nvidia-smi exists and is executable if [ -x "$nvSmiExec" ]; then echo yes exit 0 @@ -188,7 +188,7 @@ if [ "$1" = "config" ]; then gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.info GPU utilization information for $gpuName" : $((nGpusCounter=nGpusCounter+1)) - done + done ;; *) echo "Can't run without a proper symlink. Exiting." diff --git a/plugins/gpu/nvidia_smi_ b/plugins/gpu/nvidia_smi_ index f5395d5e..052dda07 100755 --- a/plugins/gpu/nvidia_smi_ +++ b/plugins/gpu/nvidia_smi_ @@ -38,7 +38,7 @@ my $runType = "normal"; my @runTypes = qw( normal config autoconf ); if ($#ARGV + 1 == 1) { - if (grep $_ eq $ARGV[0], @runTypes) + if (grep $_ eq $ARGV[0], @runTypes) { $runType = $ARGV[0]; } @@ -63,10 +63,10 @@ for (my $i = 0; $i < $gpuCount; $i++) { ($ret, my $handle) = nvmlDeviceGetHandleByIndex($i); next if $ret != $NVML_SUCCESS; - + ($ret, my $pciInfo) = nvmlDeviceGetPciInfo($handle); my $gpuName = $pciInfo->{'busId'} if $ret == $NVML_SUCCESS; - + if ($runType eq "config") { # only print the graph information once @@ -78,7 +78,7 @@ for (my $i = 0; $i < $gpuCount; $i++) print "graph_category sensors\n"; print "graph_info Information for NVIDIA GPUs using driver version $driverVersion\n"; } - + # metrics are collected for all the GPUs to a single graph print "GPU_UTIL_$i.label GPU$i - $gpuName : GPU utilization\n"; print "GPU_FANSPEED_$i.label GPU$i - $gpuName : fan speed\n"; diff --git a/plugins/groupwise/snmp__gwia_bytes_ b/plugins/groupwise/snmp__gwia_bytes_ index 8f2f3782..1f750992 100755 --- a/plugins/groupwise/snmp__gwia_bytes_ +++ b/plugins/groupwise/snmp__gwia_bytes_ @@ -23,8 +23,8 @@ # ------------------------------------------------------------ # Plugin to monitor Novell Groupwise Internet Agent (GWIA) # ------------------------------------------------------------ -# -# Management Information Base (MIB) GWIAMIB +# +# Management Information Base (MIB) GWIAMIB # # Naming Tree: 1.3.6.1.4.1.23 # iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) novell(23) @@ -32,7 +32,7 @@ # To see all values available for your GWIA, type # snmpwalk -v1 -c public -m GWIAMIB gwia # -# This plugin fetches: +# This plugin fetches: # # * gwiaGatewayName - 1.3.6.1.4.1.23.2.70.1.1. # * gwiaStatBytesIn - 1.3.6.1.4.1.23.2.70.1.6. @@ -40,31 +40,31 @@ # # Usage: # -------------- -# Link this file snmp__gwia_bytes_ to your nodes servicedir [/etc/munin/plugins] +# Link this file snmp__gwia_bytes_ to your nodes servicedir [/etc/munin/plugins] # -# as: -# snmp__gwia_bytes_ +# as: +# snmp__gwia_bytes_ # # with: -# = Name or IP-Number of host +# = Name or IP-Number of host # = table index of the GWIA Object -# +# # E.g. # ln -s /usr/share/munin/plugins/snmp__gwia_bytes_ \ # /etc/munin/plugins/snmp_foo.example.com_gwia_bytes_0 -# ...will monitor a single GWIA object on host foo.example.com. +# ...will monitor a single GWIA object on host foo.example.com. # # Parameters # community - Specify wich community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) -# pos - Specify which table Object to read (Default: Read from link in servicedir, -# +# pos - Specify which table Object to read (Default: Read from link in servicedir, +# # You may adjust settings to your need via configuration in plugin-conf.d/munin-node: # [snmp_*_gwia_bytes_*] # env.port # env.community -# env.pos +# env.pos # env.host # # Parameters can also be specified on a per GWIA basis, eg: @@ -96,8 +96,8 @@ my $GRAPH_VLABEL = "bytes per $GRAPH_PERIOD in(-) / out(+)"; my $BYTES_LABEL='Bytes'; if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") -{ - print "require 1.3.6.1.4.1.23.2.70.1.1. [.*]\n"; # gwiaGatewayName +{ + print "require 1.3.6.1.4.1.23.2.70.1.1. [.*]\n"; # gwiaGatewayName print "require 1.3.6.1.4.1.23.2.70.1.6. [\\d*]\n"; # gwiaStatBytesIn print "require 1.3.6.1.4.1.23.2.70.1.5. [\\d*]\n"; # gwiaStatBytesOut @@ -129,8 +129,8 @@ if (!defined ($session)) if (defined $ARGV[0] and $ARGV[0] eq "config") { - # get name of Internet Agent - my $gwname = &get_single ($session, "1.3.6.1.4.1.23.2.70.1.1.$pos"); # gwiaGatewayName + # get name of Internet Agent + my $gwname = &get_single ($session, "1.3.6.1.4.1.23.2.70.1.1.$pos"); # gwiaGatewayName # output to munin print "host_name $host graph_category mail diff --git a/plugins/groupwise/snmp__gwia_msgs_ b/plugins/groupwise/snmp__gwia_msgs_ index 4fb7d9ec..14a8c4fd 100755 --- a/plugins/groupwise/snmp__gwia_msgs_ +++ b/plugins/groupwise/snmp__gwia_msgs_ @@ -23,8 +23,8 @@ # ------------------------------------------------------------ # Plugin to monitor Novell Groupwise Internet Agent (GWIA) # ------------------------------------------------------------ -# -# Management Information Base (MIB) GWIAMIB +# +# Management Information Base (MIB) GWIAMIB # # Naming Tree: 1.3.6.1.4.1.23 # iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) novell(23) @@ -32,43 +32,43 @@ # To see all values available for your GWIA, type # snmpwalk -v1 -c public -m GWIAMIB gwia # -# This plugin fetches: +# This plugin fetches: # # * gwiaGatewayName - 1.3.6.1.4.1.23.2.70.1.1. # * gwiaStatMsgsOut - 1.3.6.1.4.1.23.2.70.1.7. # * gwiaStatMsgsIn - 1.3.6.1.4.1.23.2.70.1.8. # * gwiaStatStatusesOut - 1.3.6.1.4.1.23.2.70.1.9. -# * gwiaStatStatusesIn - 1.3.6.1.4.1.23.2.70.1.10. -# * gwiaStatErrorsOut - 1.3.6.1.4.1.23.2.70.1.11. +# * gwiaStatStatusesIn - 1.3.6.1.4.1.23.2.70.1.10. +# * gwiaStatErrorsOut - 1.3.6.1.4.1.23.2.70.1.11. # * gwiaStatErrorsIn - 1.3.6.1.4.1.23.2.70.1.12. # # Usage: # -------------- -# Link this file snmp__gwia_msgs_ to your nodes servicedir [/etc/munin/plugins] +# Link this file snmp__gwia_msgs_ to your nodes servicedir [/etc/munin/plugins] # -# as: -# snmp__gwia_msgs_ +# as: +# snmp__gwia_msgs_ # # with: -# = Name or IP-Number of host +# = Name or IP-Number of host # = table index of the GWIA Object -# +# # E.g. # ln -s /usr/share/munin/plugins/snmp__gwia_msgs_ \ # /etc/munin/plugins/snmp_foo.example.com_gwia_msgs_0 -# ...will monitor a single GWIA object on host foo.example.com. +# ...will monitor a single GWIA object on host foo.example.com. # # Parameters # community - Specify wich community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) -# pos - Specify which table Object to read (Default: Read from link in servicedir, -# +# pos - Specify which table Object to read (Default: Read from link in servicedir, +# # You may adjust settings to your need via configuration in plugin-conf.d/munin-node: # [snmp_*_gwia_msgs_*] # env.port # env.community -# env.pos +# env.pos # env.host # # Parameters can also be specified on a per GWIA basis, eg: @@ -104,8 +104,8 @@ my $ERRORS_LABEL='Errors'; my $ERRORS_CRITICAL=10; if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") -{ - print "require 1.3.6.1.4.1.23.2.70.1.1. [.*]\n"; # gwiaGatewayName +{ + print "require 1.3.6.1.4.1.23.2.70.1.1. [.*]\n"; # gwiaGatewayName print "require 1.3.6.1.4.1.23.2.70.1.7. [\\d*]\n"; # gwiaStatMsgsOut print "require 1.3.6.1.4.1.23.2.70.1.8. [\\d*]\n"; # gwiaStatMsgsIn print "require 1.3.6.1.4.1.23.2.70.1.11. [\\d*]\n"; # gwiaStatErrorsOut @@ -141,8 +141,8 @@ if (!defined ($session)) if (defined $ARGV[0] and $ARGV[0] eq "config") { - # get name of Internet Agent - my $gwname = &get_single ($session, "1.3.6.1.4.1.23.2.70.1.1.$pos"); # gwiaGatewayName + # get name of Internet Agent + my $gwname = &get_single ($session, "1.3.6.1.4.1.23.2.70.1.1.$pos"); # gwiaGatewayName # output to munin print "host_name $host diff --git a/plugins/groupwise/snmp__gwmta_msgs_ b/plugins/groupwise/snmp__gwmta_msgs_ index ee74f06a..184af277 100755 --- a/plugins/groupwise/snmp__gwmta_msgs_ +++ b/plugins/groupwise/snmp__gwmta_msgs_ @@ -23,8 +23,8 @@ # ------------------------------------------------------------ # Plugin to monitor Novell Groupwise MTA (GWMTA) # ------------------------------------------------------------ -# -# Management Information Base (MIB) GWMTA-MIB +# +# Management Information Base (MIB) GWMTA-MIB # # Naming Tree: 1.3.6.1.4.1.23 # iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) novell(23) @@ -32,40 +32,40 @@ # To see all values available for your GWMTA, type # snmpwalk -v1 -c public -m GWMTA-MIB gwmta # -# This plugin fetches: +# This plugin fetches: # -# * mtaDomainName 1.3.6.1.4.1.23.2.37.1.1.1.2. -# * mtaTenMinuteRoutedMsgs - 1.3.6.1.4.1.23.2.37.1.1.1.10. +# * mtaDomainName 1.3.6.1.4.1.23.2.37.1.1.1.2. +# * mtaTenMinuteRoutedMsgs - 1.3.6.1.4.1.23.2.37.1.1.1.10. # * mtaTenMinuteUndeliverableMsgs - 1.3.6.1.4.1.23.2.37.1.1.1.12. # * mtaTenMinuteErrorMsgs - 1.3.6.1.4.1.23.2.37.1.1.1.14. # # Usage: # -------------- -# Link this file snmp__gwmta_msgs_ to your nodes servicedir [/etc/munin/plugins] +# Link this file snmp__gwmta_msgs_ to your nodes servicedir [/etc/munin/plugins] # -# as: -# snmp__gwmta_msgs_ +# as: +# snmp__gwmta_msgs_ # # with: -# = Name or IP-Number of host +# = Name or IP-Number of host # = table index of the GWMTA Object -# +# # E.g. # ln -s /usr/share/munin/plugins/snmp__gwmta_msgs_ \ # /etc/munin/plugins/snmp_foo.example.com_gwmta_msgs_0 -# ...will monitor a single GWMTA object on host foo.example.com. +# ...will monitor a single GWMTA object on host foo.example.com. # # Parameters # community - Specify wich community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) -# pos - Specify which table Object to read (Default: Read from link in servicedir, -# +# pos - Specify which table Object to read (Default: Read from link in servicedir, +# # You may adjust settings to your need via configuration in plugin-conf.d/munin-node: # [snmp_*_gwmta_msgs_*] # env.port # env.community -# env.pos +# env.pos # env.host # # Parameters can also be specified on a per GWMTA basis, eg: @@ -104,8 +104,8 @@ my $ERRORS_CRITICAL=10; if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { - print "index 1.3.6.1.4.1.23.2.37.1.1.1.1.\n"; # mtaIndex - print "require 1.3.6.1.4.1.23.2.37.1.1.1.2. \n"; # mtaDomainName + print "index 1.3.6.1.4.1.23.2.37.1.1.1.1.\n"; # mtaIndex + print "require 1.3.6.1.4.1.23.2.37.1.1.1.2. \n"; # mtaDomainName print "require 1.3.6.1.4.1.23.2.37.1.1.1.10. [\\d+]\n"; # mtaTenMinuteRoutedMsgs print "require 1.3.6.1.4.1.23.2.37.1.1.1.12. [\\d+]\n"; # mtaTenMinuteUndeliverableMsgs print "require 1.3.6.1.4.1.23.2.37.1.1.1.14. [\\d+]\n"; # mtaTenMinuteErrorMsgs @@ -137,20 +137,20 @@ if (!defined ($session)) if (defined $ARGV[0] and $ARGV[0] eq "config") { - # get name of domain - my $domain = &get_single ($session, "1.3.6.1.4.1.23.2.37.1.1.1.2.$pos"); # mtaDomainName + # get name of domain + my $domain = &get_single ($session, "1.3.6.1.4.1.23.2.37.1.1.1.2.$pos"); # mtaDomainName # output to munin print "host_name $host graph_category mail graph_args --base 1000 graph_period $GRAPH_PERIOD -graph_title GWMTA load ($domain) -graph_info Monitors status of Groupwise MTA, here: $domain. It reports values for the last 10 minutes. +graph_title GWMTA load ($domain) +graph_info Monitors status of Groupwise MTA, here: $domain. It reports values for the last 10 minutes. graph_vlabel $GRAPH_VLABEL graph_args -l 0 routed.label $ROUTED_LABEL -routed.info mtaTenMinuteRoutedMsgs (1.3.6.1.4.1.23.2.37.1.1.1.10.) +routed.info mtaTenMinuteRoutedMsgs (1.3.6.1.4.1.23.2.37.1.1.1.10.) routed.critical $ROUTED_CRITICAL routed.type GAUGE routed.min 0 diff --git a/plugins/groupwise/snmp__gwpoa_ b/plugins/groupwise/snmp__gwpoa_ index 78051b3f..e17ca0c4 100755 --- a/plugins/groupwise/snmp__gwpoa_ +++ b/plugins/groupwise/snmp__gwpoa_ @@ -23,8 +23,8 @@ # ------------------------------------------------------------ # Plugin to monitor Novell Groupwise Post Office Agent (POA) # ------------------------------------------------------------ -# -# Management Information Base (MIB) GWPOA +# +# Management Information Base (MIB) GWPOA # # Naming Tree: 1.3.6.1.4.1.23 # iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) novell(23) @@ -32,7 +32,7 @@ # To see all values available for your GWPOA, type # snmpwalk -v1 -c public -m GWPOA-MIB gwpoa # -# This plugin fetches: +# This plugin fetches: # # * poaPostOfficeName - 1.3.6.1.4.1.23.2.38.1.1.1.2. # * poaTotalMsgs - 1.3.6.1.4.1.23.2.38.1.1.1.3. @@ -46,10 +46,10 @@ # with the hostname (host) and the table index (pos) of the POA-Object # defined in the linkage. # -# snmp__gwpoa_ +# snmp__gwpoa_ # # with: -# = Name or IP-Number of host +# = Name or IP-Number of host # = Table index of the POA Object # # @@ -57,7 +57,7 @@ # ln -s /usr/share/munin/plugins/snmp__gwpoa_ \ # /etc/munin/plugins/snmp_foo.example.com_gwpoa_1 # -# ...will monitor the first POA-Object on host foo.example.com. +# ...will monitor the first POA-Object on host foo.example.com. # # # Configuration: @@ -66,13 +66,13 @@ # community - Specify wich community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) -# pos - Specify which table Object to read (Default: Read from link in servicedir, -# +# pos - Specify which table Object to read (Default: Read from link in servicedir, +# # You may adjust settings via configuration in plugin-conf.d/munin-node: # [snmp_*_gwpoa_*] # env.port # env.community -# env.pos +# env.pos # env.host # # Parameters can also be specified on a per POA basis, eg: @@ -103,15 +103,15 @@ my $response; my $GRAPH_PERIOD = "minute"; my $GRAPH_VLABEL = "messages per $GRAPH_PERIOD"; my $TOTAL_LABEL = "TotalMsgs"; -my $UNDELIVERABLE_LABEL = "UndeliverableMsgs"; +my $UNDELIVERABLE_LABEL = "UndeliverableMsgs"; my $PROBLEM_LABEL = "ProblemMsgs"; my $ERRORS_LABEL = "AdmErrorMsgs"; my $ERRORS_CRITICAL = 10; if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { - print "index 1.3.6.1.4.1.23.2.38.1.1.1.1.\n"; # gwpoa - print "require 1.3.6.1.4.1.23.2.38.1.1.1.2. [.*]\n"; # poaPostOfficeName + print "index 1.3.6.1.4.1.23.2.38.1.1.1.1.\n"; # gwpoa + print "require 1.3.6.1.4.1.23.2.38.1.1.1.2. [.*]\n"; # poaPostOfficeName print "require 1.3.6.1.4.1.23.2.38.1.1.1.3. [\\d*]\n"; # poaTotalMsgs print "require 1.3.6.1.4.1.23.2.38.1.1.1.4. [\\d*]\n"; # poaProblemMsgs print "require 1.3.6.1.4.1.23.2.38.1.1.1.8. [\\d*]\n"; # poaUndeliverableMsgs @@ -145,8 +145,8 @@ if (!defined ($session)) if (defined $ARGV[0] and $ARGV[0] eq "config") { - # get Post Office Name - my $poname = &get_single ($session, "1.3.6.1.4.1.23.2.38.1.1.1.2.$pos"); # poaPostOfficeName + # get Post Office Name + my $poname = &get_single ($session, "1.3.6.1.4.1.23.2.38.1.1.1.2.$pos"); # poaPostOfficeName # output to munin print "host_name $host diff --git a/plugins/gunicorn/gunicorn_memory_status b/plugins/gunicorn/gunicorn_memory_status index 630af52e..b8948e35 100755 --- a/plugins/gunicorn/gunicorn_memory_status +++ b/plugins/gunicorn/gunicorn_memory_status @@ -9,10 +9,10 @@ Like Munin, this plugin is licensed under the GNU GPL v2 license http://www.opensource.org/licenses/GPL-2.0 - If you've put your gunicorn pid somewhere other than the + If you've put your gunicorn pid somewhere other than the default /var/run/gunicorn.pid, you can add a section like this to your munin-node's plugin configuration: - + [gunicorn_*] env.gunicorn_pid_path [path to your gunicorn pid] @@ -50,7 +50,7 @@ class GunicornMemoryStatus(): def _get_master_pid(self): master_pid_file = open(GUNICORN_PID_PATH) self.master_pid = master_pid_file.read().rstrip() - master_pid_file.close() + master_pid_file.close() return True def _get_total_memory(self): @@ -58,7 +58,7 @@ class GunicornMemoryStatus(): total = master +self. _get_worker_memory() total_in_mb = total / 1024 return total_in_mb - + def _get_master_memory(self): master = int(check_output( ['ps', '--pid', self.master_pid, '-o', 'rss', '--no-headers'])) diff --git a/plugins/gunicorn/gunicorn_status b/plugins/gunicorn/gunicorn_status index 471b291d..1d19179a 100755 --- a/plugins/gunicorn/gunicorn_status +++ b/plugins/gunicorn/gunicorn_status @@ -8,10 +8,10 @@ Like Munin, this plugin is licensed under the GNU GPL v2 license http://www.opensource.org/licenses/GPL-2.0 - If you've put your gunicorn pid somewhere other than the + If you've put your gunicorn pid somewhere other than the default /var/run/gunicorn.pid, you can add a section like this to your munin-node's plugin configuration: - + [gunicorn_*] env.gunicorn_pid_path [path to your gunicorn pid] @@ -59,7 +59,7 @@ class GunicornStatus(): def _get_master_pid(self): master_pid_file = open(GUNICORN_PID_PATH) self.master_pid = master_pid_file.read().rstrip() - master_pid_file.close() + master_pid_file.close() def _get_worker_pids(self, master_pid): children = check_output( diff --git a/plugins/haproxy/haproxy-bytes b/plugins/haproxy/haproxy-bytes index 303a849e..3ad78c0c 100755 --- a/plugins/haproxy/haproxy-bytes +++ b/plugins/haproxy/haproxy-bytes @@ -30,7 +30,7 @@ graph_vlabel=${title} case $1 in config) cat <) { + while () { my $url=$_; chomp($url); my $id=get_id($url); @@ -150,7 +150,7 @@ sub read_cache{ my %cache=(); if(-r $file){ open(FILE,'<'.$file); - while () { + while () { m/^(\S*)\s+(.*)$/; $cache{ $1 } = $2; } @@ -297,7 +297,7 @@ sub loadtime_config{ print "graph_total Total\n"; print "graph_info This graph is generated by a set of serial GETs to calculate the total time to load $urls{$id}. "; print "Note that browsers usually fork() the GET requests, resulting in a shorter total loading time.\n"; - + if(keys(%cache)>0){ for my $key ( sort reverse keys %cache ){ my $value=$cache{$key}; @@ -496,7 +496,7 @@ sub cache_values{ my $value=$cache{$key}; if($key =~ m/^([A-Za-z]+)\_(\S+)$/){ my $name=$2; - + if ($1 eq $type){ $name=get_fieldname($name); print $name . ".value " . $value . "\n"; @@ -609,7 +609,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { exit(0); } elsif($ARGV[0] and $ARGV[0] eq "cron") { - # This thing is run by cron and should write a cache file for munin-node to + # This thing is run by cron and should write a cache file for munin-node to # read from my $verbose=0; @@ -629,7 +629,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { while ( my ($id, $url) = each(%urls) ) { $verbose && print "Fetching $url (id: $id)... \n"; - + $t0=0; $status=0; %output=(); @@ -682,7 +682,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { $tag=$$link[0] . " " . $$link[1]; } $output{"tags_" . $$link[0] . "-" . $$link[1]}+=1; - + if(filter($tag)){ $verbose && print " Processing: " . $$link[0] . " " . $$link[1] . " " . $$link[2] . "\n"; @@ -693,7 +693,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { } my $suburl=$$link[2]; - + $t0 = [gettimeofday]; $response = $browser->get($suburl); $output{"loadtime_" . $host} += sprintf("%.6f",tv_interval ( $t0, [gettimeofday])); @@ -720,13 +720,13 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { while ( my ($id, $value) = each(%input) ) { $input{$id}="U"; } - + # Adding new values while ( my ($id, $value) = each(%output) ) { $input{$id}=$value; $verbose && print " Result: " . $id . " -> " . $value . "\n"; } - + # Writing the cache $verbose && print "Writing cache file: " . $cachefile . "... "; open(FILE,">".$cachefile); @@ -739,7 +739,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { exit(0); }elsif($ARGV[0] and $ARGV[0] eq "config") { my %urls=&read_urls($url_file); - + $debug && print "Reading cache file\n"; my $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id); my %cache=read_cache($cachefile); @@ -773,6 +773,6 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { } else { cache_values(\%cache, $type); } -} +} # vim:syntax=perl diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time index c8c4b9a0..8c796809 100755 --- a/plugins/http/http_request_time +++ b/plugins/http/http_request_time @@ -4,7 +4,7 @@ This plugin does http requests to specified URLs and takes the response time. Use it to monitor remote sites. - + LWP::UserAgent and Time::HiRes are required =head1 CONFIGURATION @@ -70,7 +70,7 @@ for (my $i = 1; $ENV{"url$i"}; $i++) my $proxy = $ENV{"url${i}_proxy"}; my $name = $ENV{"url${i}_name"} || clean($url); my $label = $ENV{"url${i}_label"} || $url; - + $URLS{$name}={ url=>$url, proxy=>$proxy, @@ -161,7 +161,7 @@ foreach my $name (keys %URLS) { if ($response->is_success) { $$url{'time'}=sprintf("%d",tv_interval($t1,$t2)*1000); - }; + }; }; print("multigraph http_request_time\n"); diff --git a/plugins/http/mongrel_memory b/plugins/http/mongrel_memory index 1638ba65..8448d5cd 100755 --- a/plugins/http/mongrel_memory +++ b/plugins/http/mongrel_memory @@ -4,26 +4,26 @@ # Copyright (C) 2009 Matthias Marschall - mm@agileweboperations.com # # Based on: -# mongrel_process_memory - A munin plugin to monitor memory size of +# mongrel_process_memory - A munin plugin to monitor memory size of # each individual mongrel process # Copyright (C) 2007 Ben VandenBos and Avvo, Inc. # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Author: Ben VandenBos # Contributors: Adam Jacob () -# Ryan Woodrum +# Ryan Woodrum # Matthias Marschall (mm@agileweboperations.com) # #%# family=auto @@ -31,7 +31,7 @@ module Munin class MongrelProcessMemory - + def run pid_port_map = get_pids() port_list = Hash.new @@ -40,7 +40,7 @@ module Munin puts "mongrel_#{port}.value #{rss}" end end - + def get_pids h = Hash.new pids = [] @@ -57,10 +57,10 @@ module Munin def autoconf get_pids().length > 0 end - + end end - + mpm = Munin::MongrelProcessMemory.new case ARGV[0] diff --git a/plugins/http/mongrel_process_memory b/plugins/http/mongrel_process_memory index b06267ed..0febcfe0 100755 --- a/plugins/http/mongrel_process_memory +++ b/plugins/http/mongrel_process_memory @@ -1,32 +1,32 @@ #!/usr/bin/env ruby # -# mongrel_process_memory - A munin plugin to monitor memory size of +# mongrel_process_memory - A munin plugin to monitor memory size of # each individual mongrel process # Copyright (C) 2007 Ben VandenBos and Avvo, Inc. # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Author: Ben VandenBos # Contributors: Adam Jacob () -# Ryan Woodrum +# Ryan Woodrum # #%# family=auto #%# capabilities=autoconf module Munin class MongrelProcessMemory - + def run h = get_pids() ps_output = "" @@ -49,10 +49,10 @@ module Munin port_list.sort.each do |port| puts "mongrel_#{port[0]}.value #{port[1]}" end - - end + + end end - + def get_pids h = Hash.new pids = [] @@ -68,10 +68,10 @@ module Munin def autoconf pids.length > 0 end - + end end - + mpm = Munin::MongrelProcessMemory.new case ARGV[0] diff --git a/plugins/http/multi_http_responsetime b/plugins/http/multi_http_responsetime index 94086e05..1596b197 100755 --- a/plugins/http/multi_http_responsetime +++ b/plugins/http/multi_http_responsetime @@ -1,7 +1,7 @@ #! /usr/bin/perl # This plugin based on http_responestime designed by Anders Nordby # -# It is written to control the quality of an internet conneting by +# It is written to control the quality of an internet conneting by # downloading a favicon.ico file from a lot - unlimited - count of # domains. # @@ -100,11 +100,11 @@ if ($ARGV[0] && $ARGV[0] eq "autoconf") { $vhost =~ s@^\w+://(.+?)/.*@\1@; $proto =~ s@^(\w+)://.*@\1@; -# If url_array[] is a domain, vhost will be contain the the strinf "http://" +# If url_array[] is a domain, vhost will be contain the the strinf "http://" if($vhost =~ /http/) { print "timespent$i.label $vhost\n"; } else { - print "timespent$i.label $proto://$vhost\n"; + print "timespent$i.label $proto://$vhost\n"; } print "timespent$i.info Ladezeit von $url_array[$i]/favicon.ico\n"; diff --git a/plugins/icecast/icecast2 b/plugins/icecast/icecast2 index 07de2966..2287302d 100755 --- a/plugins/icecast/icecast2 +++ b/plugins/icecast/icecast2 @@ -1,21 +1,21 @@ #!/bin/sh -# +# # Plugin to monitor icecast2 streams / listeners -# +# # Contributed by drew Roberts # # based on the postfix_mailqueue plugin as per below -# +# # Plugin to monitor postfix mail spools -# +# # Contributed by Nicolai Langfeldt -# +# # $Log$ # Revision 1.0 2008/07/04 16:02:36 zotz # Initial work # -# +# #%# family=auto #%# capabilities=autoconf diff --git a/plugins/icecast/icecast2_ b/plugins/icecast/icecast2_ index 4b0eac8e..91cd00fa 100755 --- a/plugins/icecast/icecast2_ +++ b/plugins/icecast/icecast2_ @@ -147,7 +147,7 @@ def hent_XML(): print "%s_%s_ogg.value %s" % (sourcename, bitrate, listeners) else: print sys.argv[1] - + if __name__ == "__main__": hent_XML() diff --git a/plugins/icecast/icecast2_all b/plugins/icecast/icecast2_all index 793403e8..21549d58 100755 --- a/plugins/icecast/icecast2_all +++ b/plugins/icecast/icecast2_all @@ -66,7 +66,7 @@ def hent_XML(): print "%s.label %s" % (source, "/" + source) else: print sys.argv[1] - + if __name__ == "__main__": hent_XML() diff --git a/plugins/icecast/icecast2_simple b/plugins/icecast/icecast2_simple index 5ebf8acf..dc6c2202 100755 --- a/plugins/icecast/icecast2_simple +++ b/plugins/icecast/icecast2_simple @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # # Plugin author: Gunnar Wolf -# +# # You are hereby granted authorization to copy, use, modify, distribute, # and in general do anything you please with this plugin. It is too simple # even to GPL-protect it. @@ -18,7 +18,7 @@ require 'open-uri' def get_conf # Default values - conf = {:host => '127.0.0.1', :port => 8000, + conf = {:host => '127.0.0.1', :port => 8000, :username => 'admin', :password => 'hackme' } conf.keys.each do |key| env_key = sprintf('icecast_%s', key) @@ -28,10 +28,10 @@ def get_conf end def get_data(conf) - begin - data = Hpricot(open(sprintf('http://%s:%s/admin/stats', + begin + data = Hpricot(open(sprintf('http://%s:%s/admin/stats', conf[:host], conf[:port]), - :http_basic_authentication=>[conf[:username], + :http_basic_authentication=>[conf[:username], conf[:password]])) rescue OpenURI::HTTPError puts "Cannot connect: HTTP connection error" diff --git a/plugins/icecast/icecast_ b/plugins/icecast/icecast_ index 80fbbfb4..6ec74fb1 100755 --- a/plugins/icecast/icecast_ +++ b/plugins/icecast/icecast_ @@ -50,7 +50,7 @@ pw = "yourpassword" #exclude = ("123.123.123.123",) exclude = () -# Exclude these sources from calculation. This is useful to excluse special sources like +# Exclude these sources from calculation. This is useful to excluse special sources like # fallback sources which doesn't expose the same informations and then break this script # Ever add fallback sources to this list #source_exclude = ["/fallback.mp3", "/fallback.ogg"] @@ -141,7 +141,7 @@ except IndexError: if plugin_name == "icecast_uptime": for s in sources: print "%s.value %s" % (s["mount"].strip("/").replace(".","_").replace("-","_"), int(s["connected"]) / 3600.) - + elif plugin_name == "icecast_traffic": f = opener.open("http://%s/admin/stats.xml" % server) tree = ElementTree() diff --git a/plugins/imapproxy/imapproxy_multi b/plugins/imapproxy/imapproxy_multi index 12afc642..16fc5dae 100755 --- a/plugins/imapproxy/imapproxy_multi +++ b/plugins/imapproxy/imapproxy_multi @@ -1,7 +1,7 @@ #!/usr/bin/env python """=cut -=head1 NAME +=head1 NAME imapproxy - Munin multigraph plugin to monitor imapproxy using pimpstat @@ -55,7 +55,7 @@ def print_config(): print "cache_misses.type DERIVE" print "cache_misses.label Cache Misses" print "cache_misses.min 0" - print + print print "multigraph imapproxy_connections" print "graph_title Connection Statistics For ImapProxy" print "graph_args -l 0 --base 1000" @@ -79,7 +79,7 @@ def print_fetch(): connections_created = 0 connections_reused = 0 connections = Popen( - "pimpstat -c | egrep '(Total (Reused|Created)|Cache (Hits|Misses))'", + "pimpstat -c | egrep '(Total (Reused|Created)|Cache (Hits|Misses))'", shell=True, stdout=PIPE ) diff --git a/plugins/ip6/ip6_ b/plugins/ip6/ip6_ index 20408378..54a5f1e3 100755 --- a/plugins/ip6/ip6_ +++ b/plugins/ip6/ip6_ @@ -1,6 +1,6 @@ #!/bin/sh # -# Patched version of ip_ plugin to support IPv6 and ip6tables. +# Patched version of ip_ plugin to support IPv6 and ip6tables. # Most of plugin done by munin core developers. Modified for IPv6 support # by Lasse Karstensen February 2009. # @@ -60,7 +60,7 @@ if [ "$1" = "config" ]; then echo 'in.label received' echo 'in.type DERIVE' echo 'in.min 0' - echo 'in.cdef in,8,*' + echo 'in.cdef in,8,*' exit 0 fi; diff --git a/plugins/ipvs/ipvs_active b/plugins/ipvs/ipvs_active index c6fd9255..ded74d7a 100755 --- a/plugins/ipvs/ipvs_active +++ b/plugins/ipvs/ipvs_active @@ -35,7 +35,7 @@ IPLIST=$ips if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -64,7 +64,7 @@ fi function get_ip { # Read the output ipvsadm -l -n | nl | while read line; do - # If match the ip, print the line number + # If match the ip, print the line number if ( echo $line | grep -e $IP > /dev/null ); then MAT=`echo $line | cut -d " " -f 1` echo $MAT @@ -84,7 +84,7 @@ for IP in $IPLIST; do # Parse lines while read line; do - + # Get line numbers N=`echo $line | cut -d " " -f 1` @@ -94,7 +94,7 @@ for IP in $IPLIST; do if ( echo $line | grep -e TCP -e UDP > /dev/null ); then break fi - + COUNT=`expr $COUNT + 1` fi done < $F1 diff --git a/plugins/ipvs/ipvs_bps b/plugins/ipvs/ipvs_bps index 70e045e3..c13c30d4 100755 --- a/plugins/ipvs/ipvs_bps +++ b/plugins/ipvs/ipvs_bps @@ -35,7 +35,7 @@ IPLIST=$ips if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi diff --git a/plugins/ipvs/ipvs_conn b/plugins/ipvs/ipvs_conn index fb480b92..cec4e391 100755 --- a/plugins/ipvs/ipvs_conn +++ b/plugins/ipvs/ipvs_conn @@ -35,7 +35,7 @@ IPLIST=$ips if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -67,7 +67,7 @@ fi function get_ip { # Read the output ipvsadm -l -n | nl | while read line; do - # If match the ip, print the line number + # If match the ip, print the line number if ( echo $line | grep -e $IP > /dev/null ); then MAT=`echo $line | cut -d " " -f 1` echo $MAT @@ -87,7 +87,7 @@ for IP in $IPLIST; do # Parse lines while read line; do - + # Get line numbers N=`echo $line | cut -d " " -f 1` @@ -97,7 +97,7 @@ for IP in $IPLIST; do if ( echo $line | grep -e TCP -e UDP > /dev/null ); then break fi - + # Get ActiveConn number NUM1=`echo $line | awk '{print $6}'` # Sum it @@ -106,7 +106,7 @@ for IP in $IPLIST; do NUM2=`echo $line | awk '{print $7}'` # Sum it INACTCONCNT=$(( INACTCONCNT + NUM2)) - + COUNT=`expr $COUNT + 1` fi done < $F1 diff --git a/plugins/ipvs/ipvs_cps b/plugins/ipvs/ipvs_cps index 41654535..502ff9f0 100755 --- a/plugins/ipvs/ipvs_cps +++ b/plugins/ipvs/ipvs_cps @@ -35,7 +35,7 @@ IPLIST=$ips if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi diff --git a/plugins/ircd/irc2 b/plugins/ircd/irc2 index 4a37c396..21361098 100755 --- a/plugins/ircd/irc2 +++ b/plugins/ircd/irc2 @@ -66,7 +66,7 @@ if($ARGV[0] and $ARGV[0] eq "config") { my %result; # We create a new PoCo-IRC object -my $irc = POE::Component::IRC->spawn( +my $irc = POE::Component::IRC->spawn( nick => $nickname, ircname => $ircname, server => $server, @@ -119,7 +119,7 @@ sub irc_001 { } -#irc_251: 'moo.us.p2p-network.net' 'There are 155 users and 3397 invisible on 16 servers' [There are 155 users and 3397 invisible on 16 servers] +#irc_251: 'moo.us.p2p-network.net' 'There are 155 users and 3397 invisible on 16 servers' [There are 155 users and 3397 invisible on 16 servers] # luserclient sub irc_251 { #print "In 251\n"; @@ -152,7 +152,7 @@ sub irc_251 { #printf "251 Got clients=%d servers=%d\n", ($result{'clients'} || -1), ($result{'servers'} || -1); } -#irc_252: 'moo.us.p2p-network.net' '18 :operator(s) online' [18, operator(s) online] +#irc_252: 'moo.us.p2p-network.net' '18 :operator(s) online' [18, operator(s) online] # opers sub irc_252 { my $sender = $_[SENDER]; @@ -165,7 +165,7 @@ sub irc_252 { #printf "254 Got channels %d\n", ($result{'channels'} || -1); } -#irc_253: 'moo.us.p2p-network.net' '1 :unknown connection(s)' [1, unknown connection(s)] +#irc_253: 'moo.us.p2p-network.net' '1 :unknown connection(s)' [1, unknown connection(s)] sub irc_253 { my $sender = $_[SENDER]; my $irc = $sender->get_heap(); @@ -177,7 +177,7 @@ sub irc_253 { #printf "254 Got channels %d\n", ($result{'channels'} || -1); } -#irc_254: 'moo.us.p2p-network.net' '1325 :channels formed' [1325, channels formed] +#irc_254: 'moo.us.p2p-network.net' '1325 :channels formed' [1325, channels formed] # luserchannels sub irc_254 { my $sender = $_[SENDER]; @@ -190,7 +190,7 @@ sub irc_254 { #printf "254 Got channels %d\n", ($result{'channels'} || -1); } -#irc_255: 'moo.us.p2p-network.net' 'I have 348 clients and 1 servers' [I have 348 clients and 1 servers] +#irc_255: 'moo.us.p2p-network.net' 'I have 348 clients and 1 servers' [I have 348 clients and 1 servers] # local clients/servers sub irc_255 { my $sender = $_[SENDER]; @@ -203,7 +203,7 @@ sub irc_255 { } } -#irc_265: 'moo.us.p2p-network.net' 'Current Local Users: 348 Max: 1900' [Current Local Users: 348 Max: 1900] +#irc_265: 'moo.us.p2p-network.net' 'Current Local Users: 348 Max: 1900' [Current Local Users: 348 Max: 1900] sub irc_265 { #print "In 265\n"; my $sender = $_[SENDER]; @@ -216,7 +216,7 @@ sub irc_265 { } } -#irc_266: 'moo.us.p2p-network.net' 'Current Global Users: 3552 Max: 8742' [Current Global Users: 3552 Max: 8742] +#irc_266: 'moo.us.p2p-network.net' 'Current Global Users: 3552 Max: 8742' [Current Global Users: 3552 Max: 8742] sub irc_266 { #print "In 266\n"; my $sender = $_[SENDER]; diff --git a/plugins/ircd/ircd b/plugins/ircd/ircd index 7770d354..4712403c 100755 --- a/plugins/ircd/ircd +++ b/plugins/ircd/ircd @@ -2,12 +2,12 @@ = 0 && $ARGV[0] eq "autoconf") { - + unless (-f $SMCF) { print "no\n"; exit 0; @@ -433,7 +433,7 @@ sub SaveState { printf FSTATE "%-20s %s\n", $k, $h->{$k}; } close FSTATE; - + Munin::Plugin::save_state(%$h); return 1; } @@ -516,7 +516,7 @@ __DATA__ conn Connections conn.info SMTP Connections per time unit msgs Messages -msgs.info Messages per time unit +msgs.info Messages per time unit rcpt Recipients rcpt.info Recipients per time unit @@ -556,8 +556,8 @@ kbytes.info Volume (KBytes) per time unit j-greyvalid Grey Validated records j-greyvalid.info Grey Validated records -j-greypend Grey Waiting records -j-greypend.info Grey Waiting records +j-greypend Grey Waiting records +j-greypend.info Grey Waiting records j-greywhitelist Grey Whitelisted records j-greywhitelist.info Grey Whitelisted records j-greyblacklist j-greyblacklist diff --git a/plugins/jenkins/jenkins_nodes_ b/plugins/jenkins/jenkins_nodes_ index 595e7f96..ab18cc20 100755 --- a/plugins/jenkins/jenkins_nodes_ +++ b/plugins/jenkins/jenkins_nodes_ @@ -157,15 +157,15 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_vlabel Executors\n"; print "graph_info The Graph shows the Jenkins Executors\n"; print "executors_offline.label offline\n"; - print "executors_offline.type GAUGE\n"; + print "executors_offline.type GAUGE\n"; print "executors_offline.draw AREA\n"; - print "executors_offline.colour 8A8A8A\n"; + print "executors_offline.colour 8A8A8A\n"; print "executors_busy.label busy\n"; - print "executors_busy.type GAUGE\n"; + print "executors_busy.type GAUGE\n"; print "executors_busy.draw STACK\n"; print "executors_idle.label idle\n"; - print "executors_idle.type GAUGE\n"; - print "executors_idle.draw STACK\n"; + print "executors_idle.type GAUGE\n"; + print "executors_idle.draw STACK\n"; } } else { @@ -249,8 +249,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } if( $type eq "executors" ) { - my $busyExecutors = $parsed->{'busyExecutors'}; - my $totalExecutors = 0; + my $busyExecutors = $parsed->{'busyExecutors'}; + my $totalExecutors = 0; my $offlineExecutors = 0; foreach my $cur(@{$parsed->{'computer'}}) { $totalExecutors += $cur->{'numExecutors'}; @@ -258,9 +258,9 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { $offlineExecutors += $cur->{'numExecutors'}; } } - print "executors_idle.value ", ($totalExecutors - $busyExecutors - $offlineExecutors), "\n"; - print "executors_busy.value $busyExecutors\n"; - print "executors_offline.value $offlineExecutors\n"; + print "executors_idle.value ", ($totalExecutors - $busyExecutors - $offlineExecutors), "\n"; + print "executors_busy.value $busyExecutors\n"; + print "executors_offline.value $offlineExecutors\n"; } } diff --git a/plugins/jmx/examples/java/java_process_memory.conf b/plugins/jmx/examples/java/java_process_memory.conf index cf872a9c..3885f791 100644 --- a/plugins/jmx/examples/java/java_process_memory.conf +++ b/plugins/jmx/examples/java/java_process_memory.conf @@ -42,5 +42,3 @@ os_memory_vm.label os vmem committed os_memory_vm.jmxObjectName java.lang:type=OperatingSystem os_memory_vm.jmxAttributeName CommittedVirtualMemorySize os_memory_vm.graph no - - diff --git a/plugins/jmx/examples/java/java_threads.conf b/plugins/jmx/examples/java/java_threads.conf index 42a9bcc1..06e39b3d 100644 --- a/plugins/jmx/examples/java/java_threads.conf +++ b/plugins/jmx/examples/java/java_threads.conf @@ -10,5 +10,3 @@ java_thread_count.jmxAttributeName ThreadCount java_thread_count_peak.label peak java_thread_count_peak.jmxObjectName java.lang:type=Threading java_thread_count_peak.jmxAttributeName PeakThreadCount - - diff --git a/plugins/jmx/examples/tomcat/catalina_times.conf b/plugins/jmx/examples/tomcat/catalina_times.conf index 5260c8b4..4e0572b7 100644 --- a/plugins/jmx/examples/tomcat/catalina_times.conf +++ b/plugins/jmx/examples/tomcat/catalina_times.conf @@ -10,7 +10,7 @@ catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestP catalina_request_count.jmxAttributeName requestCount catalina_request_count.graph no catalina_request_count.type DERIVE -catalina_request_count.min 0 +catalina_request_count.min 0 catalina_proc_time.label time catalina_proc_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor @@ -27,5 +27,3 @@ catalina_proc_tpr.cdef catalina_request_count,0,EQ,0,catalina_proc_time,catalina catalina_max_time.label peak catalina_max_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor catalina_max_time.jmxAttributeName maxTime - - diff --git a/plugins/jmx/readme.txt b/plugins/jmx/readme.txt index 44a01282..20f1e77d 100644 --- a/plugins/jmx/readme.txt +++ b/plugins/jmx/readme.txt @@ -5,7 +5,7 @@ As soon as JMX embedded in Java 5, any Java process may expose parameters to be look http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html and http://java.sun.com/jmx for details In Java version < 5 it is still possible to expose JMX interface using third party libraries -To see what can be monitored by JMX, run /bin/jconsole.exe and connect to +To see what can be monitored by JMX, run /bin/jconsole.exe and connect to the host/port you setup in your Java process. Some examples are: diff --git a/plugins/joomla/joomla-sessions b/plugins/joomla/joomla-sessions index e93f38dd..a75db535 100755 --- a/plugins/joomla/joomla-sessions +++ b/plugins/joomla/joomla-sessions @@ -90,7 +90,7 @@ EOC #Anonym Session count my $anonsessions = `$MYSQL $MYSQLOPTS -e 'SELECT COUNT( session_id ) FROM $DATABASE.jos_session WHERE usertype = "''"'`; -$anonsessions =~ /(\d+)/; +$anonsessions =~ /(\d+)/; print "anonsessions.value ".$1."\n"; #Registered count diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index d3f8e8ff..de221292 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -112,7 +112,7 @@ config_bea_jdk() echo 'Young_Pause.min 0' echo 'Old_Pause.label Old_GC Pause' echo 'Old_Pause.min 0' - + } print_sun_stats() @@ -126,7 +126,7 @@ ${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ EC = $5; \ EU = $6; \ OC = $7; \ - OU = $8; + OU = $8; \ PC = $9; \ PU = $10; \ YGC = $11; \ diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index de830218..c4c4c33a 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -117,7 +117,7 @@ config_sun_jdk() config_bea_jdk() { config_common - + echo "NurserySize.label NurserySize" echo "HeapSize.label HeapSize" echo "UsedHeapSize.label UsedHeapSize" @@ -137,7 +137,7 @@ ${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ EC = $5; \ EU = $6; \ OC = $7; \ - OU = $8; + OU = $8; \ PC = $9; \ PU = $10; \ \ diff --git a/plugins/jvm/jvm_sun_memory b/plugins/jvm/jvm_sun_memory index c622e1dc..a170561c 100755 --- a/plugins/jvm/jvm_sun_memory +++ b/plugins/jvm/jvm_sun_memory @@ -19,12 +19,12 @@ # env.graphtitle (default: "Sun Java") # You need to configure your Sun JVM with these options: -# -verbose:gc +# -verbose:gc # -Xloggc:/var/log/app/jvm/gc.log # -XX:+PrintGCTimeStamps # -XX:+PrintGCDetails -# History: +# History: # This plugin was developed by various people over some time - no logs # of this has been found. - In 2006 significant contributions was @@ -47,7 +47,7 @@ my $title = "$grtitle memory usage"; my $info = "Write som info about this graph..."; sub analyze_record { - # Match all interesting elements of a record and insert them + # Match all interesting elements of a record and insert them # into a hash my $record = shift; @@ -145,7 +145,7 @@ while () { ($now,undef) = split(/:/,$_,2); $now = $lastnow unless $now; } - + if (/.+Tenured.+/) { $record = $_; } elsif (/^:.+/) { diff --git a/plugins/jvm/jvm_sun_minorgcs b/plugins/jvm/jvm_sun_minorgcs index caa99a9a..23509a51 100755 --- a/plugins/jvm/jvm_sun_minorgcs +++ b/plugins/jvm/jvm_sun_minorgcs @@ -19,12 +19,12 @@ # env.grname (default: "sun-jvm". Used for state file-name) # You need to configure your Sun JVM with these options: -# -verbose:gc +# -verbose:gc # -Xloggc:/var/log/app/jvm/gc.log # -XX:+PrintGCTimeStamps # -XX:+PrintGCDetails -# History: +# History: # This plugin was developed by various people over some time - no logs # of this has been found. - In 2006 significant contributions was @@ -94,7 +94,7 @@ open (OUT, ">$statefile") or die "Could not open $statefile for reading: $!\n"; print OUT "$pos:$count:$timespent\n"; close OUT; -sub parseFile { +sub parseFile { my ($fname, $start, $count, $timespent) = @_; my @secs; diff --git a/plugins/jvm/jvm_sun_tenuredgcs b/plugins/jvm/jvm_sun_tenuredgcs index 9ea42e71..78487e06 100755 --- a/plugins/jvm/jvm_sun_tenuredgcs +++ b/plugins/jvm/jvm_sun_tenuredgcs @@ -17,12 +17,12 @@ # env.graphtitle (default: "Sun Java") # You need to configure your Sun JVM with these options: -# -verbose:gc +# -verbose:gc # -Xloggc:/var/log/app/jvm/gc.log # -XX:+PrintGCTimeStamps # -XX:+PrintGCDetails -# History: +# History: # This plugin was developed by various people over some time - no logs # of this has been found. - In 2006 significant contributions was diff --git a/plugins/kamailio/kamailio_memory b/plugins/kamailio/kamailio_memory index fa256a3c..83a3366a 100755 --- a/plugins/kamailio/kamailio_memory +++ b/plugins/kamailio/kamailio_memory @@ -31,9 +31,9 @@ GPLv2 use strict; -my %WANTED = ( "kamailio" => "ram_total", +my %WANTED = ( "kamailio" => "ram_total", "rtpproxy" => "ram_rtpproxy", - "freeswitch" => "ram_freeswitch", + "freeswitch" => "ram_freeswitch", ); my %VALUE = ( "ram_total" => 0, diff --git a/plugins/kamailio/kamailio_mysql_shared_memory b/plugins/kamailio/kamailio_mysql_shared_memory index d7405dfa..d163c947 100755 --- a/plugins/kamailio/kamailio_mysql_shared_memory +++ b/plugins/kamailio/kamailio_mysql_shared_memory @@ -93,9 +93,9 @@ use strict; my $MYSQLADMIN = $ENV{mysql} || "mysql"; my $COMMAND = "$MYSQLADMIN $ENV{mysqlauth} $ENV{kamailiodb} -e 'select * from statistics order by id desc limit 1\\G'"; -my %WANTED = ( "shm_free_used_size" => "shmem_total", +my %WANTED = ( "shm_free_used_size" => "shmem_total", "shm_real_used_size" => "shmem_real_used", - "shm_used_size" => "shmem_used", + "shm_used_size" => "shmem_used", ); my %VALUE = ( "shmem_total" => 0, diff --git a/plugins/kamailio/kamailio_transactions_users b/plugins/kamailio/kamailio_transactions_users index f7f68220..9af486ec 100755 --- a/plugins/kamailio/kamailio_transactions_users +++ b/plugins/kamailio/kamailio_transactions_users @@ -94,8 +94,8 @@ my $MYSQL = $ENV{mysql} || "mysql"; my $COMMAND = "$MYSQL $ENV{mysqlauth} $ENV{kamailiodb} -e 'select * from statistics order by id desc limit 1\\G'"; my %WANTED = ( "ul_users" => "users", - "ul_contact" => "contacts", - "tm_active" => "transactions", + "ul_contact" => "contacts", + "tm_active" => "transactions", ); my %VALUE = ( "users" => 0, diff --git a/plugins/keystone/keystone_stats b/plugins/keystone/keystone_stats index 4ce1d90c..ec0e596f 100755 --- a/plugins/keystone/keystone_stats +++ b/plugins/keystone/keystone_stats @@ -55,7 +55,7 @@ def get_status(): total['users'] += 1 if user['enabled']: enabled['users'] += 1 - + # Ldap and pam driver don't support get_all_tenants() # kvs and sql implement get_tenants() instead of get_all_tenants() # Whoo: None of backend implements the correct function @@ -89,7 +89,7 @@ if __name__ == '__main__': if sys.argv[1] == "config": print_config() elif sys.argv[1] == "autoconf": - if not successful_import: + if not successful_import: print 'no (failed import keystone module)' sys.exit(0) try: diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index f671a4c6..c9cad306 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -70,7 +70,7 @@ def find_vm_names(pids): cmdline = open("/proc/%s/cmdline" % pid, "r") result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result - + def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm @@ -85,7 +85,7 @@ def fetch(vms): for ( pid, name ) in vms.iteritems(): ( user, system ) = open("/proc/%s/stat" % pid, 'r').readline().split(' ')[13:15] print '%s_cpu.value %d' % ( name, int(user) + int(system) ) - + if __name__ == "__main__": if len(sys.argv) > 1: if sys.argv[1] in ['autoconf', 'detect']: diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 4bbf40a2..972f4983 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -37,7 +37,7 @@ graph_args --base 1024 print "%s_read.info I/O used by virtual machine %s" % (vm, vm) print "%s_write.label %s" % (vm, vm) print "%s_write.type COUNTER" % vm - print "%s_write.min 0" % vm + print "%s_write.min 0" % vm print "%s_write.draw LINE1" % vm print "%s_write.negative %s_read" % (vm, vm) print "%s_write.info I/O used by virtual machine %s" % (vm, vm) @@ -53,7 +53,7 @@ def clean_vm_name(vm_name): vm_name = re.sub(suffix,'',vm_name) return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) - + def fetch(vms): ''' Fetch values for a list of pids @param dictionnary {kvm_pid: cleaned vm name} @@ -64,11 +64,11 @@ def fetch(vms): for line in f.readlines(): if "read_bytes" in line: read = line.split()[1] - print "%s_read.value %s" % (vms[pid], read) + print "%s_read.value %s" % (vms[pid], read) if "write_bytes" in line: write = line.split()[1] - print "%s_write.value %s" % (vms[pid], write) - break + print "%s_write.value %s" % (vms[pid], write) + break f.close() def detect_kvm(): @@ -87,14 +87,14 @@ def find_vm_names(pids): cmdline = open("/proc/%s/cmdline" % pid, "r") result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result - + def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() - + if __name__ == "__main__": if len(sys.argv) > 1: if sys.argv[1] in ['autoconf', 'detect']: diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index 55f8e1f1..7e8d2793 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -50,7 +50,7 @@ def clean_vm_name(vm_name): vm_name = re.sub(suffix,'',vm_name) return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) - + def fetch(vms): ''' Fetch values for a list of pids @param dictionnary {kvm_pid: cleaned vm name} @@ -61,12 +61,12 @@ def fetch(vms): cmdline = open("/proc/%s/cmdline" % pid, "r") amount = re.sub(r"^.*-m\x00(.*)\x00-smp.*$",r"\1", cmdline.readline()) amount = int(amount) * 1024 * 1024 - print "%s_mem.value %s" % (vms[pid], amount) + print "%s_mem.value %s" % (vms[pid], amount) except: cmdline = open("/proc/%s/cmdline" % pid, "r") amount = re.sub(r"^.*-m\x00(\d+).*$",r"\1", cmdline.readline()) amount = int(amount) * 1024 * 1024 - print "%s_mem.value %s" % (vms[pid], amount) + print "%s_mem.value %s" % (vms[pid], amount) def detect_kvm(): ''' Check if kvm is installed @@ -84,14 +84,14 @@ def find_vm_names(pids): cmdline = open("/proc/%s/cmdline" % pid, "r") result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result - + def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' pid = Popen("pidof qemu-kvm qemu-system-x86_64 kvm", shell=True, stdout=PIPE) return pid.communicate()[0].split() - + if __name__ == "__main__": if len(sys.argv) > 1: if sys.argv[1] in ['autoconf', 'detect']: diff --git a/plugins/libvirt/libvirt b/plugins/libvirt/libvirt index 2ca31b3e..b77365dc 100755 --- a/plugins/libvirt/libvirt +++ b/plugins/libvirt/libvirt @@ -87,7 +87,7 @@ $show{'network_drops'} = $ENV{show_network_drops} || 1; sub init() { my $type=undef; - + if ($ARGV[0] and $ARGV[0] eq "config"){ $type="config"; } @@ -142,7 +142,7 @@ sub init() { if(defined($hash{$name}{'info'}{'memory'})){ $hash{$name}{'info'}{'memory_bytes'} = 1024 * $hash{$name}{'info'}{'memory'}; } - + # Extract network usage if(defined($hash{$name}{'xml'}{'devices'}{'interface'}{'bridge'})){ my $vif_id=0; @@ -204,7 +204,7 @@ sub init() { print "graph_category virtualization\n"; print "graph_args -l 0 --base 1000 --upper-limit 100\n"; #print "graph_width 550\n"; - + for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'block'})){ print $hash{$vm}{'label'} . ".label " . $hash{$vm}{'name'} . "\n"; @@ -214,14 +214,14 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk_utilization\n"; for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){ my $utilization=0; - + for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){ @@ -274,7 +274,7 @@ sub init() { print "graph_category virtualization\n"; print "graph_args -l 0 --base 1000 --upper-limit 100\n"; #print "graph_width 550\n"; - + if($devices>0){ if(defined($hash{$vm}{'devices'}{'block'})){ for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ @@ -286,25 +286,25 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ if($devices > 0){ print "multigraph libvirt_disk_utilization." . $hash{$vm}{'label'} . "\n"; if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){ - + for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ - + my $utilization=0; - + if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){ - + for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){ - + for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){ - + my $prev_ms_spent_doing_io=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'io_ticks'}; my $cur_ms_spent_doing_io=$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}{$slave_device}{'io_ticks'}; - + if($cur_ms_spent_doing_io > $prev_ms_spent_doing_io){ my $ticks=$cur_ms_spent_doing_io-$prev_ms_spent_doing_io; my $interval_ms=($time-$prev_time)*1000; @@ -343,7 +343,7 @@ sub init() { print "graph_vlabel read (-) / write (+)\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'block'})){ @@ -358,7 +358,7 @@ sub init() { print $hash{$vm}{'label'} . "_rd.min 0\n"; print $hash{$vm}{'label'} . "_rd.draw LINE2\n"; print $hash{$vm}{'label'} . "_rd.graph no\n"; - + print $hash{$vm}{'label'} . "_wr.label " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_wr.info I/O latency in seconds on " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_wr.min 0\n"; @@ -368,7 +368,7 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk_latency\n"; for my $vm (sort keys %hash) { @@ -382,9 +382,9 @@ sub init() { my $cur_total_time_spent_reading=0; my $cur_total_ios_read=0; my $cur_total_ios_written=0; - + my $devices=0; - + for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ $devices++; @@ -456,16 +456,16 @@ sub init() { print "graph_vlabel read (-) / write (+)\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + if(defined($hash{$vm}{'devices'}{'block'})){ for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ - + print $hash{$vm}{'label'} . "_" . $device . "_rd.label " . $device . "_rd\n"; print $hash{$vm}{'label'} . "_" . $device . "_rd.info I/O latency in seconds on " . $hash{$vm}{'name'} . ":" . $device . "\n"; print $hash{$vm}{'label'} . "_" . $device . "_rd.min 0\n"; print $hash{$vm}{'label'} . "_" . $device . "_rd.draw LINE2\n"; print $hash{$vm}{'label'} . "_" . $device . "_rd.graph no\n"; - + print $hash{$vm}{'label'} . "_" . $device . "_wr.label " . $device . "\n"; print $hash{$vm}{'label'} . "_" . $device . "_wr.info I/O latency in seconds on " . $hash{$vm}{'name'} . ":" . $device . "\n"; print $hash{$vm}{'label'} . "_" . $device . "_wr.min 0\n"; @@ -479,11 +479,11 @@ sub init() { print "multigraph libvirt_disk_latency.$hash{$vm}{'label'}\n"; if(defined($hash{$vm}{'devices'}{'block'}) && defined($prev_state_ref->{$vm}->{'devices'}->{'block'})){ for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ - + if(defined($prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device})){ - + for my $source_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}}){ - + my $prev_total_time_spent_writing=0; my $prev_total_time_spent_reading=0; my $prev_total_ios_read=0; @@ -492,7 +492,7 @@ sub init() { my $cur_total_time_spent_reading=0; my $cur_total_ios_read=0; my $cur_total_ios_written=0; - + for my $slave_device (keys %{$hash{$vm}{'devices'}{'block'}{$device}{'source'}{$source_device}{'slaves'}}){ my $prev_time_spent_writing=$prev_state_ref->{$vm}->{'devices'}->{'block'}->{$device}->{'source'}->{$source_device}->{'slaves'}->{$slave_device}->{'write_ticks'}; @@ -518,18 +518,18 @@ sub init() { my $read_latency=0; my $write_latency=0; - + if($prev_total_time_spent_reading > 0 && $prev_total_ios_read > 0 && ($cur_total_ios_read-$prev_total_ios_read) > 0){ $read_latency=(($cur_total_time_spent_reading-$prev_total_time_spent_reading)/($cur_total_ios_read-$prev_total_ios_read))/1000; } - + if($prev_total_time_spent_writing > 0 && $prev_total_ios_written > 0 && ($cur_total_ios_written-$prev_total_ios_written) > 0){ $write_latency=(($cur_total_time_spent_writing-$prev_total_time_spent_writing)/($cur_total_ios_written-$prev_total_ios_written))/1000; } - + print $hash{$vm}{'label'} . "_" . $device . "_rd.value " . $read_latency . "\n"; print $hash{$vm}{'label'} . "_" . $device . "_wr.value " . $write_latency . "\n"; - + } } } @@ -554,7 +554,7 @@ sub init() { print "graph_vlabel bytes read (-) / written (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vm (sort keys %hash) { my $devices=0; if(defined($hash{$vm}{'devices'}{'block'})){ @@ -565,14 +565,14 @@ sub init() { if($devices > 0){ if(defined($hash{$vm}{'devices'}{'block'})){ - + print $hash{$vm}{'label'} . "_rd_bytes.label " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_rd_bytes.type COUNTER\n"; print $hash{$vm}{'label'} . "_rd_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_rd_bytes.min 0\n"; print $hash{$vm}{'label'} . "_rd_bytes.draw LINE2\n"; print $hash{$vm}{'label'} . "_rd_bytes.graph no\n"; - + print $hash{$vm}{'label'} . "_wr_bytes.label " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_wr_bytes.type COUNTER\n"; print $hash{$vm}{'label'} . "_wr_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . "\n"; @@ -583,7 +583,7 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk\n"; for my $vm (sort keys %hash) { @@ -604,7 +604,7 @@ sub init() { # # Disk used, second level # - + for my $vm (sort keys %hash) { my $devices=0; if(defined($hash{$vm}{'devices'}{'block'})){ @@ -623,14 +623,14 @@ sub init() { print "graph_vlabel bytes read (-) / written (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ print $device . "_rd_bytes.label " . $device . "_rd\n"; print $device . "_rd_bytes.type COUNTER\n"; print $device . "_rd_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . "\n"; print $device . "_rd_bytes.min 0\n"; print $device . "_rd_bytes.graph no\n"; - + print $device . "_wr_bytes.label " . $device . "\n"; print $device . "_wr_bytes.type COUNTER\n"; print $device . "_wr_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . "\n"; @@ -639,7 +639,7 @@ sub init() { print $device . "_wr_bytes.negative " . $device . "_rd_bytes\n"; } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk.bytes_" . $hash{$vm}{'label'} . "\n"; if(defined($hash{$vm}{'devices'}{'block'})){ @@ -672,7 +672,7 @@ sub init() { print "graph_title Disk errors per domain\n"; print "graph_args --base 1000\n"; print "graph_category virtualization\n"; - + for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'block'})){ my $devices=0; @@ -691,7 +691,7 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk_errs\n"; for my $vm (sort keys %hash) { @@ -709,7 +709,7 @@ sub init() { # # Disk errors, second level # - + for my $vm (sort keys %hash) { my $devices=0; if(defined($hash{$vm}{'devices'}{'block'})){ @@ -724,9 +724,9 @@ sub init() { print "graph_title Disk errors for " . $hash{$vm}{'name'} . "\n"; print "graph_args --base 1000\n"; print "graph_category virtualization\n"; - + for my $device (keys %{$hash{$vm}{'devices'}{'block'}}){ - + print $device . "_errs.label " . $device . "\n"; print $device . "_errs.type COUNTER\n"; print $device . "_errs.info The number of errors by " . $hash{$vm}{'name'} . " on defice " . $device . "\n"; @@ -734,7 +734,7 @@ sub init() { print $device . "_errs.draw LINE2\n"; } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_disk_errs." . $hash{$vm}{'label'} . "\n"; if(defined($hash{$vm}{'devices'}{'block'})){ @@ -766,10 +766,10 @@ sub init() { print "graph_vlabel Bytes in (-) / out (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'network'})){ - + print $hash{$vm}{'label'} . "_rx_bytes.label " . $hash{$vm}{'name'} . "_rx\n"; print $hash{$vm}{'label'} . "_rx_bytes.type DERIVE\n"; print $hash{$vm}{'label'} . "_rx_bytes.info The number of bytes read by " . $hash{$vm}{'name'} . " in total.\n"; @@ -777,7 +777,7 @@ sub init() { print $hash{$vm}{'label'} . "_rx_bytes.draw LINE2\n"; print $hash{$vm}{'label'} . "_rx_bytes.graph no\n"; #print $hash{$vm}{'label'} . "_rx_bytes.cdef " . $hash{$vm}{'label'} . "_rx_bytes,8,*\n"; - + print $hash{$vm}{'label'} . "_tx_bytes.label " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_tx_bytes.type DERIVE\n"; print $hash{$vm}{'label'} . "_tx_bytes.info The number of bytes written by " . $hash{$vm}{'name'} . " in total.\n"; @@ -788,7 +788,7 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_network\n"; for my $vm (sort keys %hash) { @@ -819,10 +819,10 @@ sub init() { print "graph_vlabel Bits in (-) / out (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){ my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'}; - + print "rx_bytes_" . $vif_id . ".label " . $vif . "_rx\n"; print "rx_bytes_" . $vif_id . ".type DERIVE\n"; print "rx_bytes_" . $vif_id . ".info The number of bytes read by " . $hash{$vm}{'name'} . "\n"; @@ -872,17 +872,17 @@ sub init() { print "graph_vlabel Count in (-) / out (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vm (sort keys %hash) { if(defined($hash{$vm}{'devices'}{'network'})){ - + print $hash{$vm}{'label'} . "_rx_drop.label " . $hash{$vm}{'name'} . "_rx\n"; print $hash{$vm}{'label'} . "_rx_drop.type DERIVE\n"; print $hash{$vm}{'label'} . "_rx_drop.info The number of packets dropped at reception by " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_rx_drop.min 0\n"; print $hash{$vm}{'label'} . "_rx_drop.draw LINE2\n"; print $hash{$vm}{'label'} . "_rx_drop.graph no\n"; - + print $hash{$vm}{'label'} . "_tx_drop.label " . $hash{$vm}{'name'} . "\n"; print $hash{$vm}{'label'} . "_tx_drop.type DERIVE\n"; print $hash{$vm}{'label'} . "_tx_drop.info The number of packets dropped at transmission by " . $hash{$vm}{'name'} . "\n"; @@ -892,7 +892,7 @@ sub init() { } } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_network_drop\n"; for my $vm (sort keys %hash) { @@ -925,10 +925,10 @@ sub init() { print "graph_vlabel Count in (-) / out (+) per \${graph_period}\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + for my $vif (keys %{$hash{$vm}{'devices'}{'network'}}){ my $vif_id=$hash{$vm}{'devices'}{'network'}{$vif}{'vif_id'}; - + print "rx_drop_" . $vif_id . ".label " . $vif . "_rx\n"; print "rx_drop_" . $vif_id . ".type DERIVE\n"; print "rx_drop_" . $vif_id . ".info The number of packets dropped by " . $hash{$vm}{'name'} . ", nic " . $vif_id . "\n"; @@ -960,7 +960,7 @@ sub init() { # # CPU used # - + if($show{'cpu_used'} == 1){ # @@ -973,7 +973,7 @@ sub init() { print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + my $draw="AREA"; for my $vm (sort keys %hash) { print $hash{$vm}{'label'} . "_time.label " . $hash{$vm}{'name'} . "\n"; @@ -984,7 +984,7 @@ sub init() { $draw="STACK" if $draw eq "AREA"; } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_cpu\n"; for my $vm (sort keys %hash) { @@ -996,7 +996,7 @@ sub init() { # # CPU used, second level (pr virtual machine) # - + if($type eq "config"){ for my $vm (sort keys %hash) { print "multigraph libvirt_cpu.vm_" . $hash{$vm}{'label'} . "\n"; @@ -1004,7 +1004,7 @@ sub init() { print "graph_args --base 1000\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + print "time.label " . $hash{$vm}{'name'} . " (" . $hash{$vm}{'type'} . ")\n"; print "time.type DERIVE\n"; print "time.info The cpu time used by " . $hash{$vm}{'name'} . " in percent of the total available cpu time on the physical node. This domain has access to " . $hash{$vm}{'info'}{'nrVirtCpu'} . " VCPU(s) now, and $hash{$vm}{'maxvcpus'} at maximum. The scheduler for this domain is " . $hash{$vm}{'scheduler'} . ".\n"; @@ -1012,7 +1012,7 @@ sub init() { print "time.draw AREA\n"; print "\n"; } - + } elsif($type eq "fetch"){ for my $vm (sort keys %hash) { print "multigraph libvirt_cpu.vm_" . $hash{$vm}{'label'} . "\n"; @@ -1038,7 +1038,7 @@ sub init() { print "graph_args --base 1000\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + my $draw="AREA"; for my $vm (sort keys %hash) { print $hash{$vm}{'label'} . "_alloc.label " . $hash{$vm}{'name'} . "\n"; @@ -1049,7 +1049,7 @@ sub init() { $draw="STACK" if $draw eq "AREA"; } print "\n"; - + } elsif($type eq "fetch"){ print "multigraph libvirt_mem\n"; for my $vm (sort keys %hash) { @@ -1061,7 +1061,7 @@ sub init() { # # Memory allocated, second level (pr virtual machine) # - + if($type eq "config"){ for my $vm (sort keys %hash) { print "multigraph libvirt_mem.vm_" . $hash{$vm}{'label'} . "\n"; @@ -1069,7 +1069,7 @@ sub init() { print "graph_args --base 1000\n"; print "graph_category virtualization\n"; #print "graph_width 550\n"; - + print "mem.label " . $hash{$vm}{'name'} . " (" . $hash{$vm}{'type'} . ")\n"; print "mem.type GAUGE\n"; print "mem.info Amount of memory allocated to " . $hash{$vm}{'name'} . ". The maximum amount of memory for this domain is " . $hash{$vm}{'maxmem'}/1024 . " MB.\n"; @@ -1077,7 +1077,7 @@ sub init() { print "mem.draw AREA\n"; print "\n"; } - + } elsif($type eq "fetch"){ for my $vm (sort keys %hash) { print "multigraph libvirt_mem.vm_" . $hash{$vm}{'label'} . "\n"; @@ -1156,7 +1156,7 @@ sub parse_xml { my $teller = 0; my $fortsette = 1; - + while($fortsette){ if( $a[$i]{'devices'}[0]{'disk'}[$teller] ){ my $type=$a[$i]{'devices'}[0]{'disk'}[$teller]{'type'}; @@ -1173,7 +1173,7 @@ sub parse_xml { else{ $fortsette = 0; } - } + } } # Hack to extract network information and put it into the hash @@ -1182,7 +1182,7 @@ sub parse_xml { my $teller = 0; my $fortsette = 1; - + while($fortsette){ if( $a[$i]{'devices'}[0]{'interface'}[$teller] ){ my $type=$a[$i]{'devices'}[0]{'interface'}[$teller]{'type'}; @@ -1198,7 +1198,7 @@ sub parse_xml { else{ $fortsette = 0; } - } + } } } @@ -1213,8 +1213,8 @@ sub read_diskstats{ # Verify that $dev is a block device. if(-b $dev){ # Read minor and major number - my $rdev = stat($dev)->rdev; - $res{'major'} = $rdev >> 8; + my $rdev = stat($dev)->rdev; + $res{'major'} = $rdev >> 8; $res{'minor'} = $rdev & 0xff; # If major numer is 253, then proceed as dm-device @@ -1238,7 +1238,7 @@ sub read_diskstats{ while (my $line = ) { # 1 2 3 4 5 6 7 8 9 10 11 if($line =~ m/(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/){ - + # Name units description # ---- ----- ----------- # read I/Os requests number of read I/Os processed diff --git a/plugins/libvirt/munin-libvirtpy b/plugins/libvirt/munin-libvirtpy index 1620bbd3..b37e7a19 100755 --- a/plugins/libvirt/munin-libvirtpy +++ b/plugins/libvirt/munin-libvirtpy @@ -1,6 +1,6 @@ #!/usr/bin/python -# Revision 1.0 2008/05/16 - Steven Wagner +# Revision 1.0 2008/05/16 - Steven Wagner # First functional release. Works for me. # # Revision 0.5 2008/05/01 - Julien Rottenberg @@ -45,7 +45,7 @@ if len(sys.argv) == 2: print "%s.type COUNTER" %(nodeName) print "%s.label %s" %(nodeName, nodeName) sys.exit(1) - + for id in ids: dom = conn.lookupByID(id) state, maxMem, memory, numVirtCpu, cpuTime = dom.info() @@ -55,5 +55,3 @@ for id in ids: # print """Domain: %s, %s state (%s), %d CPUs, %d seconds, %d milliseconds, mem/max (%d/%d) """ \ # % (nodeName, ostype, state, numVirtCpu, cpuTime/float(1000000000), cpuTime/float(1000000), memory, maxMem ) print "%s.value %d" % (nodeName, cpuTime/float(1000000)) - - diff --git a/plugins/licensing/flexlm_ b/plugins/licensing/flexlm_ index 1ad006f4..ff7c3145 100755 --- a/plugins/licensing/flexlm_ +++ b/plugins/licensing/flexlm_ @@ -42,11 +42,11 @@ # - LMSTAT: The path to the lmstat binary # - LMLICFILE: The path to the FlexLM License File # - LMLOGARITHMIC If set then graph use a logarithmic scale -# +# # $Log$ # Revision 1.00 20090807 nassarmu # Initial public release. -# +# # Revision 1.10 20120625 nassarmu@msi.umn.edu # incorporate the rewrite by TSUCHIYA Masatoshi # diff --git a/plugins/load/load_extended b/plugins/load/load_extended index 3e7973ce..f0f7314b 100755 --- a/plugins/load/load_extended +++ b/plugins/load/load_extended @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the load average on a system. # # Usage: Link or copy into /etc/munin/node.d/ @@ -50,8 +50,8 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. - +# graphs should look. + if [ "$1" = "config" ]; then # The host name this plugin is for. (Can be overridden to have # one machine answer for several) @@ -69,7 +69,7 @@ if [ "$1" = "config" ]; then # Graph category. Defaults to 'other' echo 'graph_category system' # The fields. "label" is used in the legend. "label" is the only - # required subfield. + # required subfield. echo 'load1.label load 1 min avg' echo 'load1.draw AREA' echo 'load5.label load 5 min avg' diff --git a/plugins/logins/logins b/plugins/logins/logins index db7278b4..bde4b43d 100755 --- a/plugins/logins/logins +++ b/plugins/logins/logins @@ -17,10 +17,10 @@ EOF # can go into an include file labels=$((${#graph[*]}/(graph+1))) -for ((i=0; i/tasks count_processes () { [ -z "$1" ] && return 0 - + if [ -n "$cgrouppath" ]; then SYSFS=$cgrouppath/$1/tasks if [ -e $SYSFS ]; then return `wc -l < $SYSFS` fi fi - + for SYSFS in \ /sys/fs/cgroup/$1/tasks \ /sys/fs/cgroup/lxc/$1/tasks \ @@ -63,7 +63,7 @@ count_processes () { return `wc -l < $SYSFS` fi done - + return 0 } @@ -75,7 +75,7 @@ for guest in $guest_names; do fi done guest_names="$active" - + f_comm='lxc-cgroup ' @@ -97,9 +97,8 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel Number of processes' echo 'graph_category processes' - - for guest_name in $guest_names; - do + + for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" echo 'lxc_proc_'$guest'.label '$guest_name': processes' echo 'lxc_proc_'$guest'.type GAUGE' @@ -108,17 +107,16 @@ if [ "$1" = "config" ]; then exit 0 fi - for guest_name in $guest_names; - do + for guest_name in $guest_names; do guest="$(clean_fieldname $guest_name)" - + count_processes $guest_name tmp_g=$? if [ $tmp_g -eq 0 ]; then tmp_g=`$f_comm -n $guest_name tasks | wc -l` fi echo 'lxc_proc_'$guest'.value '$tmp_g - + done diff --git a/plugins/lxd/lxd_mem b/plugins/lxd/lxd_mem index f81a767d..23fb40f9 100755 --- a/plugins/lxd/lxd_mem +++ b/plugins/lxd/lxd_mem @@ -10,7 +10,7 @@ try: except: HAS_LIB=False errors.append("no pylxd module") - + c=None HAS_ACCESS=True try: diff --git a/plugins/mail/mail_connections b/plugins/mail/mail_connections index ade4f790..5c71ac87 100755 --- a/plugins/mail/mail_connections +++ b/plugins/mail/mail_connections @@ -29,7 +29,7 @@ fi if [ "$1" = "config" ]; then cat <{offset} ||= 0; diff --git a/plugins/mailman/mailman-queue-check b/plugins/mailman/mailman-queue-check index 3a9deab3..86a49773 100755 --- a/plugins/mailman/mailman-queue-check +++ b/plugins/mailman/mailman-queue-check @@ -56,5 +56,5 @@ echo "news.value ${array[4]}" echo "out.value ${array[5]}" echo "retry.value ${array[6]}" exit 0 - + diff --git a/plugins/mailman/mailman_subscribers b/plugins/mailman/mailman_subscribers index a5809d4f..3000d4e2 100755 --- a/plugins/mailman/mailman_subscribers +++ b/plugins/mailman/mailman_subscribers @@ -61,7 +61,7 @@ if ($ARGV[0] and $ARGV[0] eq "config" ){ print "graph_category mailinglist\n"; print "graph_total Total\n"; print 'graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/mailman/'."\n"; - + my $num =0; while (($list,$desc) = each(%lists)) { $label=$list; diff --git a/plugins/memcached/memcached_bytes_all b/plugins/memcached/memcached_bytes_all index 7b7bbe95..bfe5c4d5 100755 --- a/plugins/memcached/memcached_bytes_all +++ b/plugins/memcached/memcached_bytes_all @@ -1,7 +1,7 @@ #!/usr/bin/env perl # ex:ts=4 # Copyright © Nicolas BOUTHORS / Smile -# +# # Licence GPLv2 # # Based on a script distributed on munin-exchange. @@ -11,10 +11,10 @@ use warnings; use Cache::Memcached; -my %instances = (); +my %instances = (); # Will look into /etc for memcached config files and extract TCP listening port -# from the config file. +# from the config file. sub fetch_instances() { my @files = glob("/etc/memcached_*.conf"); undef $/; diff --git a/plugins/memcached/memcached_ext_bytes_ b/plugins/memcached/memcached_ext_bytes_ index 8dda1871..7b1606f8 100755 --- a/plugins/memcached/memcached_ext_bytes_ +++ b/plugins/memcached/memcached_ext_bytes_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_bytes_([\d\w]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_ext_connections_ b/plugins/memcached/memcached_ext_connections_ index 326c3afc..f4b0d497 100755 --- a/plugins/memcached/memcached_ext_connections_ +++ b/plugins/memcached/memcached_ext_connections_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_connections_([\w\d]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_ext_hits_ b/plugins/memcached/memcached_ext_hits_ index 2aca77a5..8e23ef98 100755 --- a/plugins/memcached/memcached_ext_hits_ +++ b/plugins/memcached/memcached_ext_hits_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_hits_([\w\d]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_ext_items_ b/plugins/memcached/memcached_ext_items_ index 19e359f4..53904e53 100755 --- a/plugins/memcached/memcached_ext_items_ +++ b/plugins/memcached/memcached_ext_items_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_items_([\w\d]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_ext_requests_ b/plugins/memcached/memcached_ext_requests_ index 9c1d9017..0172a2c2 100755 --- a/plugins/memcached/memcached_ext_requests_ +++ b/plugins/memcached/memcached_ext_requests_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_requests_([\w\d]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_ext_traffic_ b/plugins/memcached/memcached_ext_traffic_ index a9354ca0..f77e1d98 100755 --- a/plugins/memcached/memcached_ext_traffic_ +++ b/plugins/memcached/memcached_ext_traffic_ @@ -23,7 +23,7 @@ env.label "second local server" my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; unless( $label ){ - + if( $0 =~ /memcached_ext_traffic_([\w\d]+)$/ ){ $label = $1; } diff --git a/plugins/memcached/memcached_multi_ b/plugins/memcached/memcached_multi_ index f38affa0..5683b399 100755 --- a/plugins/memcached/memcached_multi_ +++ b/plugins/memcached/memcached_multi_ @@ -145,7 +145,7 @@ my %stats; my %items; # This gives us the memory size and usage per slab -# We track this so we can see what slab is being used the most and has no free chunks +# We track this so we can see what slab is being used the most and has no free chunks # so we can re-tune memcached to allocate more pages for the specified chunk size my %chnks; @@ -396,7 +396,7 @@ $graphs{slabitems} = { info => 'This graph shows you the number of items and reclaimed items per slab.', }, datasrc => [ - { name => 'number', label => 'Items', draw => 'AREA', + { name => 'number', label => 'Items', draw => 'AREA', info => 'This is the amount of items stored in this slab', min => '0' }, ], }; @@ -437,8 +437,8 @@ $graphs{slabunfetched} = { =head2 Config Check This block of code looks at the argument that is possibly supplied, - should it be config, it then checks to make sure the plugin - specified exists, assuming it does, it will run the do_config + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config subroutine for the plugin specified, otherwise it dies complaining about an unknown plugin. @@ -584,7 +584,7 @@ sub fetch_output { This subroutine prints out the return values for our non-multigraph root graphs. It takes one parameter $plugin and returns when completed. - $plugin; graph we are calling up to print data values for + $plugin; graph we are calling up to print data values for Example: print_root_output($plugin); @@ -939,7 +939,7 @@ sub print_submulti_config { # Lets set our graph reference, and main graph config for easy handling my $graph = $graphs{$sgraph}; my %graphconf = %{$graph->{config}}; - # Lets tell munin which graph we are graphing, and what our main graph config info is + # Lets tell munin which graph we are graphing, and what our main graph config info is print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n"; while ( my ($key, $value) = each(%graphconf)) { if ($key eq 'title') { diff --git a/plugins/memcached/memcached_servers_ b/plugins/memcached/memcached_servers_ index bf5dfcd2..fb80ee91 100755 --- a/plugins/memcached/memcached_servers_ +++ b/plugins/memcached/memcached_servers_ @@ -48,7 +48,7 @@ Link the plugin to get the desirec output, for example: memcached_multi_bytes =head1 ACKNOWLEDGEMENTS -This plugin is based on the available memcached plugins at +This plugin is based on the available memcached plugins at L =head1 AUTHORS diff --git a/plugins/memory/kmemsum b/plugins/memory/kmemsum index 74fe7106..6101d1c0 100755 --- a/plugins/memory/kmemsum +++ b/plugins/memory/kmemsum @@ -1,7 +1,7 @@ #!/bin/sh # Kernel Memory usage stats. -# Author: alex@trull.org -# +# Author: alex@trull.org +# # Based on the short script at http://wiki.freebsd.org/ZFSTuningGuide (20080820) # # Parameters: @@ -45,7 +45,7 @@ if [ "$1" = "config" ]; then echo 'text.info kmem text' echo 'text.draw AREA' echo 'data.label data' - echo 'data.info kmem data' + echo 'data.info kmem data' echo 'data.draw STACK' echo 'total.label total' echo 'total.info kmem total' @@ -61,4 +61,4 @@ echo "data.value $DATA" echo "total.value $TOTAL" echo "max.value $MAX" - + diff --git a/plugins/memory/proc_memory_status b/plugins/memory/proc_memory_status index 3ad2752c..e5e1aa65 100755 --- a/plugins/memory/proc_memory_status +++ b/plugins/memory/proc_memory_status @@ -33,28 +33,28 @@ if [ "$1" = "config" ]; then echo 'VmExe.label VmExe' echo 'VmExe.draw AREA' - echo "VmExe.info The size of the executable segment" - + echo "VmExe.info The size of the executable segment" + echo 'VmLib.label VmLib' echo 'VmLib.draw STACK' echo 'VmLib.info The size of the library code' - + echo 'VmStk.label VmStk' echo 'VmStk.draw STACK' echo 'VmStk.info The stack size' - + echo 'VmLck.label VmLck' echo 'VmLck.draw STACK' echo 'VmLck.info The amount of locked memory' - + echo 'VmData.label VmData' echo 'VmData.draw STACK' echo 'VmData.info The size of the Data segment' - + echo 'VmRSS.label VmRSS' echo 'VmRSS.draw LINE2' echo 'VmRSS.info The amount of memory mapped in RAM ( instead of swapped out)' - + echo 'VmSize.label VmSize' echo 'VmSize.draw LINE2' echo 'VmSize.info The size of the virtual memory allocated to the process' diff --git a/plugins/minecraft/bukkit-jsonapi-players b/plugins/minecraft/bukkit-jsonapi-players index adf8710d..c414f0a8 100755 --- a/plugins/minecraft/bukkit-jsonapi-players +++ b/plugins/minecraft/bukkit-jsonapi-players @@ -12,7 +12,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/bukkit-jsonapi-ramusage b/plugins/minecraft/bukkit-jsonapi-ramusage index 4de54e93..c4003234 100755 --- a/plugins/minecraft/bukkit-jsonapi-ramusage +++ b/plugins/minecraft/bukkit-jsonapi-ramusage @@ -12,7 +12,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/bukkit-jsonapi-tps b/plugins/minecraft/bukkit-jsonapi-tps index 597861ff..33fb0e8c 100755 --- a/plugins/minecraft/bukkit-jsonapi-tps +++ b/plugins/minecraft/bukkit-jsonapi-tps @@ -12,7 +12,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/bukkit-statistician-killshostile b/plugins/minecraft/bukkit-statistician-killshostile index 0e4640e1..6478c841 100755 --- a/plugins/minecraft/bukkit-statistician-killshostile +++ b/plugins/minecraft/bukkit-statistician-killshostile @@ -13,7 +13,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/bukkit-statistician-killsneutral b/plugins/minecraft/bukkit-statistician-killsneutral index cfa85f56..e0dae2cc 100755 --- a/plugins/minecraft/bukkit-statistician-killsneutral +++ b/plugins/minecraft/bukkit-statistician-killsneutral @@ -13,7 +13,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/bukkit-statistician-killspassive b/plugins/minecraft/bukkit-statistician-killspassive index 8344ab15..66801109 100755 --- a/plugins/minecraft/bukkit-statistician-killspassive +++ b/plugins/minecraft/bukkit-statistician-killspassive @@ -13,7 +13,7 @@ * * Author: Jonas Friedmann (http://frd.mn) * GitHub: https://github.com/yeahwhat-mc/munin-bukkit-plugins - * + * */ /** diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 5e97c060..0520935d 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -34,7 +34,7 @@ Some more instructions: http://wiki.natenom.name/minecraft/munin-plugin MC_PORT=${0##*_} - + if [ "$1" = "config" ] then echo "graph_title Minecraft-Server (Port $MC_PORT)" diff --git a/plugins/moblock/moblock_connections b/plugins/moblock/moblock_connections index 6f163e9c..3b34f14b 100755 --- a/plugins/moblock/moblock_connections +++ b/plugins/moblock/moblock_connections @@ -14,10 +14,10 @@ # Configurable variables # # logfile - Override default moblock logfile -# +# # Magic markers # -#%# family=auto +#%# family=auto #%# capabilities=autoconf # @@ -63,7 +63,7 @@ def fetch(debug=false) puts "blocked_out.value #{num_out}" puts "blocked_total.value #{num_total}" end - + # # If moblock executable on path then allow autoconfiguration # diff --git a/plugins/mod_jk/mod_jk b/plugins/mod_jk/mod_jk index f4358c0a..dfe1dd0e 100755 --- a/plugins/mod_jk/mod_jk +++ b/plugins/mod_jk/mod_jk @@ -81,7 +81,7 @@ for i in `echo $data | sed 's/worker/\nworker/g' | grep "^worker\..*\.state=.*"` do node=`echo $i | awk -F . '{print $2}'` status=`echo $i | awk -F = '{print $2}'` - + value=0 if [ `echo $status | sed 's/^OK/XOK/'` = "X$status" ] then @@ -97,7 +97,7 @@ do then value=3 fi - + echo "$node.value $value" done diff --git a/plugins/mogilefs/mogilefsd_activity b/plugins/mogilefs/mogilefsd_activity index a53edce6..11333d16 100755 --- a/plugins/mogilefs/mogilefsd_activity +++ b/plugins/mogilefs/mogilefsd_activity @@ -24,7 +24,7 @@ # Usage: # ln -s /usr/share/munin/plugins/mogilefsd_activity \ # /etc/munin/plugins/ -# +# # Configuration variables: # # host (default: '127.0.0.1') @@ -135,10 +135,10 @@ if($ARGV[0] and $ARGV[0] eq "config") { print "pending_queries.draw STACK\n"; exit 0; -} - +} + print %states; - + &query_mogilefsd($mogilefsd_host, $mogilefsd_port); foreach $key (@known_states) { diff --git a/plugins/mogilefs/mogilefsd_queries b/plugins/mogilefs/mogilefsd_queries index ac433ac0..5d788f1e 100755 --- a/plugins/mogilefs/mogilefsd_queries +++ b/plugins/mogilefs/mogilefsd_queries @@ -24,7 +24,7 @@ # Usage: # ln -s /usr/share/munin/plugins/mogilefsd_activity \ # /etc/munin/plugins/ -# +# # Configuration variables: # # host (default: '127.0.0.1') @@ -133,10 +133,10 @@ if($ARGV[0] and $ARGV[0] eq "config") { print "queries.min 0\n"; exit 0; -} - +} + print %states; - + &query_mogilefsd($mogilefsd_host, $mogilefsd_port); foreach $key (@known_states) { diff --git a/plugins/moinmoin/moinoin_pages b/plugins/moinmoin/moinoin_pages index e56cc2d5..2a2ec92d 100755 --- a/plugins/moinmoin/moinoin_pages +++ b/plugins/moinmoin/moinoin_pages @@ -15,7 +15,7 @@ # # Implementation notes # -------------------- -# +# # it is quite koumbit-specific: # 1. the wikifarm config is hardcoded # 2. it relies on the "wikilist.py" file to contain the list of wiki -> url patterns @@ -81,18 +81,18 @@ def main(): url = sub('\(([^\|]*)(\|[^\)]*\))+', '\\1', url) # remove common regexp patterns and slap a protocol to make this a real url url = sub('[\^\$]|(\.\*)', '', url) - + mod = getattr(__import__(name), 'Config') #print "Upgradeing wiki %s (%s)" % (getattr(mod, 'sitename'), url) - + request = RequestCLI(url) pagelist = request.rootpage.getPageList(user='') - + systemPages = [page for page in pagelist if wikiutil.isSystemPage(request, page)] print(name + '.value ' + str(len(pagelist)-len(systemPages))) #totalsize = reduce(operator.add, [Page(request, name).size() for name in pagelist]) - #print('Accumulated page sizes' + _formatInReadableUnits(totalsize)) + #print('Accumulated page sizes' + _formatInReadableUnits(totalsize)) def config(): print("""graph_title Wiki size diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index 80fdfbd2..3e6de845 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -13,10 +13,10 @@ :author: Stefan Andersen :license: The Beer Ware License (Revision 42) - wrote this file. As long - as you retain this notice you can do whatever you want - with this stuff. If we meet some day, and you think - this stuff is worth it, you can buy me a beer in return. + wrote this file. As long + as you retain this notice you can do whatever you want + with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. """ import os import sys @@ -51,7 +51,7 @@ graph_args --base 1000 graph_vlabel Replication lag (seconds) graph_category db """ - + for member in _get_members(): print "{0}.label {0}".format(member) diff --git a/plugins/mssql/microsoft-sql b/plugins/mssql/microsoft-sql index 181396ba..c56c703c 100755 --- a/plugins/mssql/microsoft-sql +++ b/plugins/mssql/microsoft-sql @@ -58,8 +58,8 @@ instance = 'AdventureWorks' # dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) -instance_name_query = "SELECT distinct instance_name - FROM sys.dm_os_performance_counters +instance_name_query = "SELECT distinct instance_name + FROM sys.dm_os_performance_counters WHERE instance_name = '#{instance}' and object_name = 'SQLServer:Databases' order by instance_name" @@ -91,7 +91,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" + puts "graph_args --base 1000 -r --lower-limit 0" puts "graph_title MSSQL Transactions/s" puts "graph_category db" puts "graph_info This graph shows transactions/s" diff --git a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio index 61ca3d7d..ca376f16 100755 --- a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +++ b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio @@ -81,7 +81,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" + puts "graph_args --base 1000 -r --lower-limit 0" puts "graph_title MSSQL Buffer Cache Hit Ratio " puts "graph_category db" puts "graph_info This graph shows Buffer Cache Hit Ratio" diff --git a/plugins/mssql/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes index f7143658..9e72eab4 100755 --- a/plugins/mssql/microsoft-sql-data-file-sizes +++ b/plugins/mssql/microsoft-sql-data-file-sizes @@ -58,7 +58,7 @@ instance = 'AdventureWorks' # dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) -instance_name_query = "SELECT distinct instance_name +instance_name_query = "SELECT distinct instance_name FROM sys.dm_os_performance_counters WHERE instance_name = '#{instance}'" @@ -89,7 +89,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title MSSQL DB File Sizes" puts "graph_category db" puts "graph_info This graph shows DB File Sizes (MB)" diff --git a/plugins/mssql/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size index 3bbf0c0e..14c7ffc2 100755 --- a/plugins/mssql/microsoft-sql-log-file-size +++ b/plugins/mssql/microsoft-sql-log-file-size @@ -58,7 +58,7 @@ instance = 'AdventureWorks' # dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) -instance_name_query = "SELECT distinct instance_name +instance_name_query = "SELECT distinct instance_name FROM sys.dm_os_performance_counters WHERE instance_name = '#{instance}'" @@ -89,7 +89,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title MSSQL DB Log File Sizes" puts "graph_category db" puts "graph_info This graph shows DB Log File Sizes (MB)" diff --git a/plugins/mumble/murmur-stats b/plugins/mumble/murmur-stats index bb87951d..03178659 100755 --- a/plugins/mumble/murmur-stats +++ b/plugins/mumble/murmur-stats @@ -41,4 +41,4 @@ print "uptime.value %.2f" % (float(meta.getUptime())/60/60/24) print "chancount.value %.1f" % (len(server.getChannels())/10) print "bancount.value %i" % (len(server.getBans())) -ice.shutdown() +ice.shutdown() diff --git a/plugins/munin/healthcheck_url b/plugins/munin/healthcheck_url index 6686f723..66d38700 100755 --- a/plugins/munin/healthcheck_url +++ b/plugins/munin/healthcheck_url @@ -17,14 +17,14 @@ #env.url_1 http://127.0.0.1/ #--------------------------------------------------- # -#check two site +#check two site #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.url_2 http://www.google.com/ #--------------------------------------------------- # -#check three site +#check three site #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ diff --git a/plugins/mysql/mysql-schema-size b/plugins/mysql/mysql-schema-size index d1227479..05a210f9 100755 --- a/plugins/mysql/mysql-schema-size +++ b/plugins/mysql/mysql-schema-size @@ -63,7 +63,7 @@ EOT; public function getSchemaSize() { $sql = <<getTableSize($schema,$table); $data_length = $row['data_length']; echo "$table.value $data_length\n"; @@ -74,7 +74,7 @@ EOT; public function getSchemaSize($schema) { $bind = array($schema); $sql = << "index", +my %WANTED = ( "Index" => "index", "Datas" => "datas", ); diff --git a/plugins/mysql/mysql_size_all b/plugins/mysql/mysql_size_all index c6f617b9..c5a39f84 100755 --- a/plugins/mysql/mysql_size_all +++ b/plugins/mysql/mysql_size_all @@ -48,7 +48,7 @@ use strict; my $COMMAND; my $MYSQLADMIN = $ENV{mysqladmin} || "mysql"; -my %WANTED = ( "Index" => "index", +my %WANTED = ( "Index" => "index", "Datas" => "datas", ); @@ -170,7 +170,7 @@ sub test_service { sub getDBList { my @dbs; foreach my $f (glob("/var/lib/mysql/*")) { - if (-d $f) { + if (-d $f) { $f =~ s/\@002d/-/g; $f =~ s!.*/!!; @dbs[$#dbs+1]=$f }; diff --git a/plugins/mysql/mysql_slave_threads b/plugins/mysql/mysql_slave_threads index f09f97af..6669c85d 100755 --- a/plugins/mysql/mysql_slave_threads +++ b/plugins/mysql/mysql_slave_threads @@ -41,7 +41,7 @@ EOC my $status = `$MYSQL $MYSQLOPTS -e 'SHOW SLAVE STATUS\\G'`; -$status =~ /Slave_IO_Running: (\w+)/; +$status =~ /Slave_IO_Running: (\w+)/; print 'io.value '.($1 eq 'Yes' ? 1 : 0)."\n"; $status =~ /Slave_SQL_Running: (\w+)/; print 'sql.value '.($1 eq 'Yes' ? 1 : 0)."\n"; diff --git a/plugins/mythtv/mythtv_programs b/plugins/mythtv/mythtv_programs index 826549a1..e205329b 100755 --- a/plugins/mythtv/mythtv_programs +++ b/plugins/mythtv/mythtv_programs @@ -2,8 +2,8 @@ # # Munin plugin for MythTV # This plugin can graph:- EPG programs per channel -# -# NOTE: This plugin needs to run as root so add the following to your munin-node config file +# +# NOTE: This plugin needs to run as root so add the following to your munin-node config file # [mythtv_status*] # user=root # @@ -48,8 +48,8 @@ my $Channel=""; print "cannot find MythTV configuration file my.txt\n"; exit 1; } - } - + } + #Config Options ##Configuration for encoder, no config data needs to read from anywhere if ($ARGV[0] and $ARGV[0] eq "config"){ @@ -71,16 +71,16 @@ my $Channel=""; print "Channel" . $Channel . "EPG.label EPG days for channel $gata\n"; $Ptr=0; } - } + } exit 0; } #Actually dump data to Munin - @result=SQLQuery("SELECT o.chanid, (UNIX_TIMESTAMP(MAX(c.endtime)) - - UNIX_TIMESTAMP(NOW()))/86400 - FROM channel o, program c + @result=SQLQuery("SELECT o.chanid, (UNIX_TIMESTAMP(MAX(c.endtime)) + - UNIX_TIMESTAMP(NOW()))/86400 + FROM channel o, program c WHERE o.chanid = c.chanid - AND o.visible = '1' + AND o.visible = '1' GROUP BY o.chanid"); my $Ptr=0; foreach $gata (@result) { @@ -89,10 +89,10 @@ my $Channel=""; $Ptr=1; } else { if ( $gata > 12 ) { - print "Channel" . $Channel . "EPG.value 12.0\n"; - } else { + print "Channel" . $Channel . "EPG.value 12.0\n"; + } else { print "Channel" . $Channel . "EPG.value $gata\n"; - } + } $Ptr=0; } } @@ -104,7 +104,7 @@ exit 0; sub PrepSQLRead { my $hostname = `hostname`; chomp($hostname); - + # Read the mysql.txt file in use by MythTV. Could be in a couple places, so try the usual suspects my $found = 0; my @mysql = ('/usr/local/share/mythtv/mysql.txt', @@ -153,7 +153,7 @@ sub SQLQuery { my ($QUERY) = @_; my @data; my $ref; - my $dbh = DBI->connect("DBI:mysql:$SQLDBName:$SQLServer", $SQLUser, $SQLPassword) + my $dbh = DBI->connect("DBI:mysql:$SQLDBName:$SQLServer", $SQLUser, $SQLPassword) or die "Couldn't connect to database: " . DBI->errstr; my $table_data = $dbh->prepare($QUERY) or die "Couldn't prepare statement: " . $dbh->errstr; $table_data->execute or die "Couldn't execute statement: " . $table_data->errstr; @@ -165,5 +165,5 @@ sub SQLQuery { return @data; } else { return 0; - } + } } diff --git a/plugins/mythtv/mythtv_status_ b/plugins/mythtv/mythtv_status_ index dbace488..dfaf31e0 100755 --- a/plugins/mythtv/mythtv_status_ +++ b/plugins/mythtv/mythtv_status_ @@ -2,19 +2,19 @@ # # Munin plugin for MythTV # This plugin can graph:- Encoder Status, Days Remaining in Schedule, Job schedule, Recording Schedule, Recorded Programes, Recorded hours -# +# # Create a symbolic link to mythtv_status_{GraphType} # Where {GraphType} can be encoder, epg, job, schedule, recorded # for example mythtv_status_encoder # -# NOTE: This plugin needs to run as root so add the following to your munin-node config file +# NOTE: This plugin needs to run as root so add the following to your munin-node config file # [mythtv_status*] # user=root # The http/xml status page must be enabled in the mythtv backend. # # $Log$ # Revision 0.1 2008/03/27 idobson -# Code for all options except recorded implemented +# Code for all options except recorded implemented # # Revision 0.2 2008/03/28 idobson # Tidied up the code abit/removed dead functions @@ -35,7 +35,7 @@ # Revision 0.7 2008/04/3 idobson # Now using SQL to read the number of days in the EPG # Changed recordings symlink to schedule, makes more sense -# +# # Revision 0.8 2008/04/6 idobson # Tidied up the SQL code abit, moved it into a function. # @@ -43,7 +43,7 @@ # Added a check that we got the XML data before trying to parse it. # # Revision 1.0 2008/04/15 idobson -# Fixed undef returned from SQL query, it now returns 0, +# Fixed undef returned from SQL query, it now returns 0, # added error handler to SQL sub. It just dies with an error text. # # Revision 1.1 2008/05/03 idobson @@ -94,7 +94,7 @@ my $RecHoursLiveTV=0; my $result=""; my $gata=""; my $VideoInput=1; - $GraphOption=`basename $0 | sed 's/^mythtv_status_//g' | tr '_' '-'` ; + $GraphOption=`basename $0 | sed 's/^mythtv_status_//g' | tr '_' '-'` ; chomp $GraphOption; PrepSQLRead(); @@ -103,7 +103,7 @@ PrepSQLRead(); if ($ARGV[0] and $ARGV[0] eq "autoconf" ) { print "yes\n"; exit 0; - } + } #Config Options ##Configuration for encoder, no config data needs to read from anywhere @@ -135,7 +135,7 @@ PrepSQLRead(); print "FreeEncoders.colour 00FF00\n"; print "HungEncoders.colour 000000\n"; print "HungEncoders.draw LINE1\n"; - print "HungEncoders.label Encoders that have hung (no DB update)\n"; + print "HungEncoders.label Encoders that have hung (no DB update)\n"; print "HungEncoders.warning 0:0\n"; } @@ -147,7 +147,7 @@ PrepSQLRead(); print "graph_title MythTV EPG days/Programs\n"; print "graph_category tv\n"; print "graph_vlabel Days\/Programs\n"; - + @result=SQLQuery("SELECT DISTINCT `sourceid` FROM `cardinput`"); $VideoInput = 1; foreach $gata (@result) { @@ -259,7 +259,7 @@ PrepSQLRead(); @result=SQLQuery("SELECT count(*) FROM `capturecard` "); my $FreeRecorders=$result[0]; my $ActiveTuners=0; - @result=SQLQuery("SELECT videosource.name, count( inuseprograms.recusage ) + @result=SQLQuery("SELECT videosource.name, count( inuseprograms.recusage ) FROM inuseprograms, channel, videosource WHERE inuseprograms.recusage = 'recorder' AND inuseprograms.chanid = channel.chanid @@ -283,8 +283,8 @@ PrepSQLRead(); } print "FreeEncoders.value $FreeRecorders\n"; @result=SQLQuery("SELECT count(inuseprograms.recusage) - from inuseprograms - where inuseprograms.recusage = 'recorder' + from inuseprograms + where inuseprograms.recusage = 'recorder' and (UNIX_TIMESTAMP( NOW( )) - UNIX_TIMESTAMP(inuseprograms.lastupdatetime)) / 60 > 20"); print "HungEncoders.value $result[0]\n"; } @@ -296,7 +296,7 @@ PrepSQLRead(); foreach $gata (@result) { print "EPGDays$VideoInput.value $gata\n"; $VideoInput++; - } + } #Get number of programs in EPG per video source $VideoInput = 1; @@ -324,7 +324,7 @@ PrepSQLRead(); @result=SQLQuery("SELECT COUNT(*) FROM `record`"); print "RecordingSchedules.value $result[0]\n"; -#Connect to mythtv using the MythTV object +#Connect to mythtv using the MythTV object my $Repeats=0; my $Recordings=0; my $Conflicts=0; @@ -374,7 +374,7 @@ exit 0; sub PrepSQLRead { my $hostname = `hostname`; chomp($hostname); - + # Read the mysql.txt file in use by MythTV. Could be in a couple places, so try the usual suspects my $found = 0; my @mysql = ('/usr/local/share/mythtv/mysql.txt', @@ -423,7 +423,7 @@ sub SQLQuery { my ($QUERY) = @_; my @data; my $ref; - my $dbh = DBI->connect_cached("DBI:mysql:$SQLDBName:$SQLServer", $SQLUser, $SQLPassword) + my $dbh = DBI->connect_cached("DBI:mysql:$SQLDBName:$SQLServer", $SQLUser, $SQLPassword) or die "Couldn't connect to database: " . DBI->errstr; my $table_data = $dbh->prepare($QUERY) or die "Couldn't prepare statement: " . $dbh->errstr; $table_data->execute or die "Couldn't execute statement: " . $table_data->errstr; @@ -435,7 +435,7 @@ sub SQLQuery { return @data; } else { return 0; - } + } } # Returns true if the show is scheduled to record diff --git a/plugins/nagios/nagios_multi_ b/plugins/nagios/nagios_multi_ index 20dbedc7..c34d99ec 100755 --- a/plugins/nagios/nagios_multi_ +++ b/plugins/nagios/nagios_multi_ @@ -14,7 +14,7 @@ A Plugin to monitor Nagios Servers and their Performance (Multigraph) =head2 MUNIN ENVIRONMENT CONFIGURATION EXPLANATION binary = location of your nagiostats binary including binary - passive = tell the plugin to graph passive results + passive = tell the plugin to graph passive results =head1 NODE CONFIGURATION @@ -60,7 +60,7 @@ Matt West < https://github.com/mhwest13/Nagios-Munin-Plugin > GPLv2 =head1 MAGIC MARKERS - + #%# family=auto #%# capabilities=autoconf suggest @@ -122,7 +122,7 @@ my $passive = $ENV{passive} || 'off'; my %graphs; -# main graph for service checks +# main graph for service checks $graphs{services} = { config => { args => '--lower-limit 0', @@ -207,7 +207,7 @@ $graphs{svcchkext} = { { name => 'AVGACTSVCEXT', label => 'Average Execution', min => '0', type => 'GAUGE', info => 'avg active service check execution time (ms).', draw => 'LINE2' }, ], }; -# main graph for host problems +# main graph for host problems $graphs{hosts} = { config => { args => '--lower-limit 0', @@ -290,7 +290,7 @@ $graphs{hostchkext} = { { name => 'AVGACTHSTEXT', label => 'Average Execution', min => '0', type => 'GAUGE', info => 'avg active host check execution time (ms).', draw => 'LINE2' }, ], }; -# main graph for host / service check counts +# main graph for host / service check counts $graphs{checks} = { config => { args => '--lower-limit 0', @@ -404,8 +404,8 @@ $graphs{extcmdcount} = { =head2 Config Check This block of code looks at the argument that is possibly supplied, - should it be config, it then checks to make sure the plugin - specified exists, assuming it does, it will run the do_config + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config subroutine for the plugin specified, otherwise it dies complaining about an unknown plugin. @@ -618,7 +618,7 @@ sub do_config { =cut sub print_sub_config { - # Lets get our plugin and subgraph, after that print for Munin to process it. + # Lets get our plugin and subgraph, after that print for Munin to process it. my ($plugin,$subgraph) = (@_); my $graph = $graphs{$subgraph}; print "multigraph nagios_$plugin.$subgraph\n"; diff --git a/plugins/nagios/nagiosstatus b/plugins/nagios/nagiosstatus index 57200814..051a1fcd 100755 --- a/plugins/nagios/nagiosstatus +++ b/plugins/nagios/nagiosstatus @@ -14,7 +14,7 @@ # Config variables: # # statuslog - Which logfile to use -# Might be /var/log/nagios2/nagios.log if +# Might be /var/log/nagios2/nagios.log if # /var/log/nagios/status.log is missing # # This program is free software; you can redistribute it and/or @@ -109,7 +109,7 @@ while() { if(/^\s+\}\s*$/) { $type = ""; } - + if($type) { push(@{$values{$type}}, $_); } diff --git a/plugins/netapp/snmp__netapp_cifs b/plugins/netapp/snmp__netapp_cifs index 75e7365a..0e4b8f6c 100755 --- a/plugins/netapp/snmp__netapp_cifs +++ b/plugins/netapp/snmp__netapp_cifs @@ -4,21 +4,21 @@ """ # The SNMP traps for the NetApp filer can be found in -# /net/netappfiler/vol0/etc/mib/traps.dat if the filer is +# /net/netappfiler/vol0/etc/mib/traps.dat if the filer is # NFS automounted mounted on server. # Example: the SNMP id for cpuBusyTimePerCent is -# snmp.1.3.6.1.4.1.789.1.2.1.3.0 -# and retrival of this value is done by +# snmp.1.3.6.1.4.1.789.1.2.1.3.0 +# and retrival of this value is done by # snmpget -v 1 -c public netappfiler 1.3.6.1.4.1.789.1.2.1.3.0 # -# Requires snmpget and assumes public community. +# Requires snmpget and assumes public community. import commands import sys # Provided a servername and a snmpid it returns the value stripped of bogus information. def snmpget(iservername,isnmpid): - runcmd = 'snmpget -v 1 -c public ' + iservername + ' ' + isnmpid + runcmd = 'snmpget -v 1 -c public ' + iservername + ' ' + isnmpid output = commands.getoutput(runcmd) return output.split()[3] @@ -34,14 +34,14 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print 'graph_args --base 1000 -l 0' print 'graph_vlabel number' print 'graph_category fs' - print 'graph_info This graph shows CIFS usage on '+servername + print 'graph_info This graph shows CIFS usage on '+servername print 'cifsConnectedUsers.label ConnectedUsers' print 'cifsConnectedUsers.info The current number of CIFS users on the filer' - + print 'cifsNSessions.label NumberOfSessions' print 'cifsNSessions.info The current number of active CIFS session on the filer' - + print 'cifsNOpenFiles.label NumberOfOpenfiles' print 'cifsNOpenFiles.info The number of open CIFS files and directories on the filer' sys.exit(0) diff --git a/plugins/netapp/snmp__netapp_cpu b/plugins/netapp/snmp__netapp_cpu index ea56b09b..5aef035c 100755 --- a/plugins/netapp/snmp__netapp_cpu +++ b/plugins/netapp/snmp__netapp_cpu @@ -4,14 +4,14 @@ """ # The SNMP traps for the NetApp filer can be found in -# /net/netappfiler/vol0/etc/mib/traps.dat if the filer is +# /net/netappfiler/vol0/etc/mib/traps.dat if the filer is # NFS automounted mounted on server. # Example: the SNMP id for cpuBusyTimePerCent is -# snmp.1.3.6.1.4.1.789.1.2.1.3.0 -# and retrival of this value is done by +# snmp.1.3.6.1.4.1.789.1.2.1.3.0 +# and retrival of this value is done by # snmpget -v 1 -c public netappfiler 1.3.6.1.4.1.789.1.2.1.3.0 # -# Requires snmpget and assumes public community. +# Requires snmpget and assumes public community. import commands import sys @@ -24,7 +24,7 @@ def snmpget(iservername,isnmpid): snmpid = "1.3.6.1.4.1.789.1.2.1.3.0" warning = 80 -critical = 95 +critical = 95 servername = sys.argv[0].split('_')[1] if len(sys.argv) == 2 and sys.argv[1] == "config": diff --git a/plugins/netapp/snmp__netapp_diskbusy b/plugins/netapp/snmp__netapp_diskbusy index 35a4b262..da44a4ec 100755 --- a/plugins/netapp/snmp__netapp_diskbusy +++ b/plugins/netapp/snmp__netapp_diskbusy @@ -64,7 +64,7 @@ sub do_collect sub do_config_root { # graph_category san # To show plugin in Gallery also in this category - + my ($host) = @_; print "multigraph diskbusy\n"; diff --git a/plugins/netapp/snmp__netapp_nfs3calls b/plugins/netapp/snmp__netapp_nfs3calls index 44acdb21..3f29fcf2 100755 --- a/plugins/netapp/snmp__netapp_nfs3calls +++ b/plugins/netapp/snmp__netapp_nfs3calls @@ -14,9 +14,9 @@ with SNMP agent daemon activated. See na_snmp(8) for details. Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. For this reason, this plugin will use SNMPv2 by default, which is -insecure because it doesn't encrypt the community string. +insecure because it doesn't encrypt the community string. -The following parameters will help you get this plugin working : +The following parameters will help you get this plugin working: [snmp_*] env.community MyCommunity diff --git a/plugins/netscaler/snmp__netscaler_connections b/plugins/netscaler/snmp__netscaler_connections index 9cc822f1..8b6952a1 100755 --- a/plugins/netscaler/snmp__netscaler_connections +++ b/plugins/netscaler/snmp__netscaler_connections @@ -23,9 +23,9 @@ # 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 . +# along with this program. If not, see . # -# ---------------------------------------------------- # +# ---------------------------------------------------- # =head1 NAME @@ -33,7 +33,7 @@ netscaler_conn - Munin plugin to monitor netscaler connections =head1 CONFIGURATION -Make a symlink from netscaler_conn_ to /etc/munin/plugins/netscaler_conn_. +Make a symlink from netscaler_conn_ to /etc/munin/plugins/netscaler_conn_. You can omit , then you need the env variable B. To configure the plugin, use ENV variables. @@ -173,7 +173,7 @@ $return_str .= "ssl.value $counter1\n"; &close_session($session); -print "$return_str"; +print "$return_str"; exit 0; # --------------------------- functions ------------------------- # @@ -203,7 +203,7 @@ sub close_session { sub get_buildversion { my ($session) = @_; my $build_version; - + my $result = $session->get_request( -varbindlist => [$oid_build_version] ); @@ -228,7 +228,7 @@ sub get_oid_values { if (!defined($result)) { return "na"; } - else { + else { $return_value = $result->{$oid_string}; return $return_value; } diff --git a/plugins/netscaler/snmp__netscaler_cpu b/plugins/netscaler/snmp__netscaler_cpu index e0ad7bb5..a13f447d 100755 --- a/plugins/netscaler/snmp__netscaler_cpu +++ b/plugins/netscaler/snmp__netscaler_cpu @@ -23,9 +23,9 @@ # 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 . +# along with this program. If not, see . # -# ---------------------------------------------------- # +# ---------------------------------------------------- # =head1 NAME @@ -33,7 +33,7 @@ netscaler_cpu - Munin plugin to monitor CPU usage =head1 CONFIGURATION -Make a symlink from netscaler_cpu_ to /etc/munin/plugins/netscaler_cpu_. +Make a symlink from netscaler_cpu_ to /etc/munin/plugins/netscaler_cpu_. You can omit , then you need the env variable B. To configure the plugin, use ENV variables. @@ -96,7 +96,7 @@ $o_community = undef; $o_port = 161; my $return_str = ""; -my @cpu_name = (); +my @cpu_name = (); # ---------------------------- snmp ---------------------------- # @@ -176,7 +176,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { &close_session($session); -print "$return_str"; +print "$return_str"; exit 0; # --------------------------- functions ------------------------- # @@ -206,7 +206,7 @@ sub close_session { sub get_buildversion { my ($session) = @_; my $build_version; - + my $result = $session->get_request( -varbindlist => [$oid_build_version] ); @@ -268,7 +268,7 @@ sub get_cpus { return 0; } else { - @cpu_name = (); + @cpu_name = (); foreach my $n ($session->var_bind_names()) { push @cpu_name, $session->var_bind_list()->{$n}; } diff --git a/plugins/network/bandwidth_ b/plugins/network/bandwidth_ index 09b3410c..031709b8 100755 --- a/plugins/network/bandwidth_ +++ b/plugins/network/bandwidth_ @@ -18,7 +18,7 @@ bandwidth_ to this file. E.g. Most likely usage is to monitor an interface connected to your ISP. -The suggest option will try and determine if you have any interfaces with a +The suggest option will try and determine if you have any interfaces with a public IP and if so it will suggest monitoring those interfaces. If all IP addresses are private the setup will have to be done manually. Suggest does not handle IPv6 addresses. @@ -192,12 +192,12 @@ EOM } sub read_traffic { - open( my $rx, "<", "/sys/class/net/$interface/statistics/rx_bytes" ) + open( my $rx, "<", "/sys/class/net/$interface/statistics/rx_bytes" ) || die "Unable to read: $!"; $counter_input = <$rx>; chomp $counter_input; close($rx); - open(my $tx , "<", "/sys/class/net/$interface/statistics/tx_bytes" ) + open(my $tx , "<", "/sys/class/net/$interface/statistics/tx_bytes" ) || die "Unable to read: $!"; $counter_output = <$tx>; chomp $counter_output; @@ -231,7 +231,7 @@ sub update_stats { else { if ( $perf_ref->{last}->{counter_input} > $counter_input ) { $input = - $counter_input + $counter_input + $rollover - $perf_ref->{last}->{counter_input}; } @@ -240,7 +240,7 @@ sub update_stats { } if ( $perf_ref->{last}->{counter_output} > $counter_output ) { $output = - $counter_output + $counter_output + $rollover - $perf_ref->{last}->{counter_output}; } diff --git a/plugins/network/bgpd b/plugins/network/bgpd index 8624a221..dc0c7515 100755 --- a/plugins/network/bgpd +++ b/plugins/network/bgpd @@ -1,7 +1,7 @@ #!/bin/sh # bgpd.sh -# +# # # Created by spleen. diff --git a/plugins/network/brc_rssi b/plugins/network/brc_rssi index 348f8263..01c76325 100755 --- a/plugins/network/brc_rssi +++ b/plugins/network/brc_rssi @@ -6,7 +6,7 @@ # NOTE: NEEDS NON FREE UTILITY "wl" # Configuration: # [brc_rssi] -# env.WIFISIDE eth0 # Set the WiFi side interface. Used to filter arp entries. +# env.WIFISIDE eth0 # Set the WiFi side interface. Used to filter arp entries. # # On a openwrt box defaults to "nvram get lan_ifname" otherwise # # no default. # @@ -58,7 +58,7 @@ EOF m=$(echo $M | tr -d ':') LABEL=$M NAME='' - + IP=$(echo "$ETHERS" | awk '/^'$M'/ { print $2; }') case $IP in '') :;; diff --git a/plugins/network/ddclient b/plugins/network/ddclient index cebf6db7..b77cd67f 100755 --- a/plugins/network/ddclient +++ b/plugins/network/ddclient @@ -5,7 +5,7 @@ # The base frame is copied from the proftp plugin # ########################################################################################## -# Folgende Eintraege in der Datei /etc/munin/plugin-conf.d/munin-node nicht vergessen ! # +# Folgende Eintraege in der Datei /etc/munin/plugin-conf.d/munin-node nicht vergessen ! # # Don't forget to add following lines to the file /etc/munin/plugin-conf.d/munin-node # # [quota] # # user root # @@ -50,7 +50,7 @@ fi # Zeitfenster vergroessert werden resize the reference periode if [ "${1//[^[:digit:]]}" != "" ]; then factor=${1//[^[:digit:]]} - else + else factor=1 fi @@ -77,10 +77,10 @@ fi # Nur zum Testen - for testing ony if [ "$factor" -gt 1 ]; then - echo "======================== Nur fuer Testzwecke ======================" + echo "======================== Nur fuer Testzwecke ======================" echo "Timestamp :" $Timestamp $(date -d "1970-01-01 UTC + $Timestamp seconds") echo "Ref_Timestamp:" $Ref_Timestamp $(date -d "1970-01-01 UTC + $Ref_Timestamp seconds") - echo "Zeitfenster :" $((Timeslot/60)) Minuten + echo "Zeitfenster :" $((Timeslot/60)) Minuten echo "Last_update :" $Last_update $(date -d "1970-01-01 UTC + $Last_update seconds") - echo "======================== for testing only ======================" + echo "======================== for testing only ======================" fi diff --git a/plugins/network/denyhosts b/plugins/network/denyhosts index d1456c4b..8617caed 100755 --- a/plugins/network/denyhosts +++ b/plugins/network/denyhosts @@ -1,6 +1,6 @@ #!/bin/bash # -# Plugin to monitor the number of hosts denied by DenyHosts +# Plugin to monitor the number of hosts denied by DenyHosts # # $Log$ # Revision 1.0 2009/06/05 16:00:00 tjansson diff --git a/plugins/network/fms b/plugins/network/fms index 29eeb219..2aaf70ee 100755 --- a/plugins/network/fms +++ b/plugins/network/fms @@ -68,7 +68,7 @@ # For autoconfiguration you'll also need the following Perl module: # # Proc::ProcessTable - Perl extension to access the unix process table -# http://search.cpan.org/perldoc?Proc::ProcessTable +# http://search.cpan.org/perldoc?Proc::ProcessTable # # On a Debian/Ubuntu system you can install this with the following command # (if APT is configured properly): @@ -213,7 +213,7 @@ if ( !( ( $usenetstat eq "yes" and defined($fmshost) and defined($fmsport) ) or # 2. Look for the FMS config file in ${FMS_DIR}/conf/fms.ini. # 3. Fetch host, port, admin username and password values from the # config file. - + # check that plugin is running with root privileges if ( $> == 0 ) { my $ProcTable = new Proc::ProcessTable; diff --git a/plugins/network/fms_apps b/plugins/network/fms_apps index 06beed5d..241958a4 100755 --- a/plugins/network/fms_apps +++ b/plugins/network/fms_apps @@ -11,7 +11,7 @@ # # libwww-perl (LWP) Perl library # Proc::ProcessTable Perl module -# +# # Tested with: # Debian Etch # Macromedia Flash Media Server 2.0.3 r68 @@ -32,7 +32,7 @@ # http://search.cpan.org/perldoc?LWP # # Proc::ProcessTable - Perl extension to access the unix process table -# http://search.cpan.org/perldoc?Proc::ProcessTable +# http://search.cpan.org/perldoc?Proc::ProcessTable # # On a Debian/Ubuntu system: # apt-get install libwww-perl libproc-process-perl @@ -150,7 +150,7 @@ if ( !( defined($host) and defined($port) and defined($username) and defined($pa # 2. Look for the FMS config file in ${FMS_DIR}/conf/fms.ini. # 3. Fetch host, port, admin username and password values from the # config file. - + # check that plugin is running with root privileges if ( $> == 0 ) { my $ProcTable = new Proc::ProcessTable; diff --git a/plugins/network/fms_apps_rate b/plugins/network/fms_apps_rate index 9a9b1573..d84fec3b 100755 --- a/plugins/network/fms_apps_rate +++ b/plugins/network/fms_apps_rate @@ -12,7 +12,7 @@ # # libwww-perl (LWP) Perl library # Proc::ProcessTable Perl module -# +# # Tested with: # Debian Etch # Macromedia Flash Media Server 2.0.3 r68 @@ -33,7 +33,7 @@ # http://search.cpan.org/perldoc?LWP # # Proc::ProcessTable - Perl extension to access the unix process table -# http://search.cpan.org/perldoc?Proc::ProcessTable +# http://search.cpan.org/perldoc?Proc::ProcessTable # # On a Debian/Ubuntu system: # apt-get install libwww-perl libproc-process-perl @@ -151,7 +151,7 @@ if ( !( defined($host) and defined($port) and defined($username) and defined($pa # 2. Look for the FMS config file in ${FMS_DIR}/conf/fms.ini. # 3. Fetch host, port, admin username and password values from the # config file. - + # check that plugin is running with root privileges if ( $> == 0 ) { my $ProcTable = new Proc::ProcessTable; diff --git a/plugins/network/fwbuilder_ b/plugins/network/fwbuilder_ index 23f8ba3c..d8ccfb4a 100755 --- a/plugins/network/fwbuilder_ +++ b/plugins/network/fwbuilder_ @@ -10,10 +10,10 @@ # Additionally, you need Accountingrules in fwbuilder # fwbuilder creates Chains in INPUT-, OUTPUT- and FORWARD-Chain # with Rules that "RETURN" -# You will have to specify rule options with name "ACCOUNTING" for the +# You will have to specify rule options with name "ACCOUNTING" for the # rules to use, otherwise no rules will be found. -# try "fwbuilder_ suggest" to see if all is ok. -# +# try "fwbuilder_ suggest" to see if all is ok. +# # # Furthermore, this plugin needs to be run as root for iptables to work # @@ -95,7 +95,7 @@ if [ "$1" = "config" ]; then echo 'in.label received' echo 'in.type DERIVE' echo 'in.min 0' - echo 'in.cdef in,8,*' + echo 'in.cdef in,8,*' exit 0 fi; diff --git a/plugins/network/hfsc b/plugins/network/hfsc index 14314069..fb1171d3 100755 --- a/plugins/network/hfsc +++ b/plugins/network/hfsc @@ -11,7 +11,7 @@ # http://www.elessar.one.pl/article_munin.php # ### -# Written by Rafal Rajs +# Written by Rafal Rajs # Date: 2007/06/19 # Email: elessar1@poczta.wp.pl # WWW: http://www.elessar.one.pl diff --git a/plugins/network/hfsc_sep b/plugins/network/hfsc_sep index 114458d6..355e1c13 100755 --- a/plugins/network/hfsc_sep +++ b/plugins/network/hfsc_sep @@ -11,7 +11,7 @@ # http://www.elessar.one.pl/article_munin.php # ### -# Written by Rafal Rajs +# Written by Rafal Rajs # Date: 2007/06/19 # Email: elessar1@poczta.wp.pl # WWW: http://www.elessar.one.pl diff --git a/plugins/network/hostsdeny b/plugins/network/hostsdeny index d5196556..3977be38 100755 --- a/plugins/network/hostsdeny +++ b/plugins/network/hostsdeny @@ -2,7 +2,7 @@ # # Plugin to monitor the number of hosts in /etc/hosts.deny # that are deined access to sshd -# +# # Based on denyhosts plugin by tjansson (2009) # # Copyright (C) 2009 Kåre Hartvig Jensen (kaare.hartvig.jensen@gmail.com) @@ -13,7 +13,7 @@ # (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 +# 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. # diff --git a/plugins/network/if b/plugins/network/if index d5086c85..3ad61159 100755 --- a/plugins/network/if +++ b/plugins/network/if @@ -89,20 +89,20 @@ my $graphs = 'per_if_fields' => [qw(rx_bytes tx_bytes)], 'general_fields' => [qw(rx_bytes tx_bytes)] }, - 'if_packets' => + 'if_packets' => { 'munin' => { 'category' => 'network', 'args' => '--base 1000', - 'title' => ':if: traffic, packets', + 'title' => ':if: traffic, packets', 'vlabel' => 'Packets in (-) / out (+), avg. per second', 'info' => 'This graph shows the traffic in packets of the :if:, averaged value per second from last update' }, 'per_if_fields' => [qw(rx_packets tx_packets rx_compressed tx_compressed rx_dropped tx_dropped multicast)], 'general_fields' => [qw(rx_packets tx_packets)] }, - 'if_errors' => + 'if_errors' => { 'munin' => { @@ -199,12 +199,12 @@ my $graphs = # { # 'type' - types: # 'percent', -# 'full' => +# 'full' => # { # 'source' => 'interface', # 'name' => 'bps' # }, -# 'part' => +# 'part' => # { # 'source' => 'field', # 'name' => 'tx_bytes' @@ -262,7 +262,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'multicast' => + 'multicast' => { 'munin' => { @@ -284,8 +284,8 @@ my $fields = 'difference' => 'per_secund' }, # -------------------------------------------------------------------------- - 'rx_bytes' => - { + 'rx_bytes' => + { 'munin' => { 'type' => 'GAUGE', @@ -335,9 +335,9 @@ my $fields = { 'munin' => { - 'type' => 'GAUGE', - 'draw' => 'LINE1', - 'label' => 'CRC errors' , + 'type' => 'GAUGE', + 'draw' => 'LINE1', + 'label' => 'CRC errors' , 'info' => 'CRC errors' }, 'source' => @@ -774,12 +774,12 @@ my $fields = 'calculated' => { 'type' => 'percent', - 'full' => + 'full' => { 'source' => 'interface', 'name' => 'bps' }, - 'part' => + 'part' => { 'source' => 'field', 'name' => 'rx_bytes' @@ -809,12 +809,12 @@ my $fields = 'calculated' => { 'type' => 'percent', - 'full' => + 'full' => { 'source' => 'interface', 'name' => 'bps' }, - 'part' => + 'part' => { 'source' => 'field', 'name' => 'tx_bytes' @@ -889,7 +889,7 @@ my $fields = 'peack_protect' => 'packet_size_range' }, # -------------------------------------------------------------------------- - 'retries' => + 'retries' => { 'munin' => { @@ -906,7 +906,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'nwid' => + 'nwid' => { 'munin' => { @@ -928,7 +928,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'misc' => + 'misc' => { 'munin' => { @@ -945,7 +945,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'fragment' => + 'fragment' => { 'munin' => { @@ -967,7 +967,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'beacon' => + 'beacon' => { 'munin' => { @@ -984,7 +984,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'crypt' => + 'crypt' => { 'munin' => { @@ -1006,7 +1006,7 @@ my $fields = 'difference' => 'count' }, # -------------------------------------------------------------------------- - 'rx_wifierr' => + 'rx_wifierr' => { 'munin' => { @@ -1031,7 +1031,7 @@ my $fields = } }, # -------------------------------------------------------------------------- - 'tx_wifierr' => + 'tx_wifierr' => { 'munin' => { @@ -1056,13 +1056,13 @@ my $fields = need_multigraph(); -if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) +if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) { printf("%s\n", -e $ifpath ? "yes" : "no ($ifpath not exists)"); exit (0); } $interfaces = get_interfaces(); -if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) +if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) { print_config(); exit (0); @@ -1124,7 +1124,7 @@ sub get_interfaces for (keys %{$interfaces}) { $interfaces->{$_}{'name'} = sprintf("[%${maxlen}s]", $interfaces->{$_}{'name'}); } } else { die "$ifpath not exists\n"; } - + return $interfaces; } @@ -1169,7 +1169,7 @@ sub get_peak_range $range->{'max'} = $interfaces->{$if}{'bps'}; $range->{'min'} = 0; } - # packets per sec + # packets per sec elsif($fields->{$field}{'peack_protect'} eq 'max_interface_pps' and defined ($interfaces->{$if}{'bps'})) { $range->{'max'} = $interfaces->{$if}{'bps'}/$min_packet_size; @@ -1283,10 +1283,10 @@ sub generate_field my ($config, $graph_name, $field, $if, $is_general_graph) = @_[0..4]; return '' unless(check_field_avialability($if, $field)); my $field_graph_name = $is_general_graph ? sprintf("%s_%s", $if, $field) : $field; - for my $option (keys %{$fields->{$field}{'munin'}}) + for my $option (keys %{$fields->{$field}{'munin'}}) { next if exists($config->{$graph_name}{'fields'}{$field_graph_name}{$option}); - $config->{$graph_name}{'fields'}{$field_graph_name}{$option} = replace_if_template($fields->{$field}{'munin'}{$option}, $interfaces->{$if}{'name'}); + $config->{$graph_name}{'fields'}{$field_graph_name}{$option} = replace_if_template($fields->{$field}{'munin'}{$option}, $interfaces->{$if}{'name'}); } if(exists($fields->{$field}{'cdef'})) { @@ -1300,13 +1300,13 @@ sub generate_field { $up_field = $fields->{$down_field}{'negative'}{'name'}; $up_field_name = $is_general_graph ? sprintf("%s_%s", $if, $up_field) : $up_field; - $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = + $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = concat_names($fields->{$down_field}{'munin'}{'label'}, $fields->{$up_field}{'munin'}{'label'}, $is_general_graph ? $if : ''); } elsif($fields->{$down_field}{'negative'}{'type'} eq 'dummy') { $up_field_name = $is_general_graph ? sprintf("%s_%s_dummy", $if, $down_field) : sprintf("%s_dummy", $down_field); - $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = + $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = concat_names($fields->{$down_field}{'munin'}{'label'}, $fields->{$down_field}{'munin'}{'label'}, $is_general_graph ? $if : ''); $config->{$graph_name}{'fields'}{$up_field_name}{'info'} = $fields->{$down_field}{'munin'}{'info'}; } @@ -1344,7 +1344,7 @@ sub generate_graph { for my $field (@{$graphs->{$graph}{'general_fields'}}) { - for my $general_if (keys %{$interfaces}) + for my $general_if (keys %{$interfaces}) { my $res_field = generate_field($config, $graph_name, $field, $general_if, 1); push(@order, $res_field) if $res_field ne ''; @@ -1361,9 +1361,9 @@ sub generate_graph } if(scalar(@order) > 0) { - for my $option (keys %{$graphs->{$graph}{'munin'}}) + for my $option (keys %{$graphs->{$graph}{'munin'}}) { - $config->{$graph_name}{'graph'}{$option} = replace_if_template($graphs->{$graph}{'munin'}{$option}, $is_general_graph ? 'All interfaces' : $interfaces->{$if}{'name'}); + $config->{$graph_name}{'graph'}{$option} = replace_if_template($graphs->{$graph}{'munin'}{$option}, $is_general_graph ? 'All interfaces' : $interfaces->{$if}{'name'}); } $config->{$graph_name}{'graph'}{'order'} = join(' ', @order); # if scalar(@order) > 1; unless($is_general_graph) @@ -1378,7 +1378,7 @@ sub generate_graphs { my ($config, $graph) = @_[0..1]; generate_graph($config, $graph, '', 1); - for my $if (keys %{$interfaces}) + for my $if (keys %{$interfaces}) { generate_graph($config, $graph, $if, 0); } @@ -1565,9 +1565,9 @@ sub print_values for my $field (@{$graphs->{$graph}{'general_fields'}}) { for my $if (keys %{$interfaces}) { prepare_value($values, $field, sprintf("%s_%s", $if, $field), $graph, $if, $data, $raw_data, $raw_prev_data); } } } - for my $if (keys %{$interfaces}) + for my $if (keys %{$interfaces}) { - for my $graph (keys %{$graphs}) + for my $graph (keys %{$graphs}) { my $graph_name = sprintf("%s.%s", $graph, $if); for my $field (@{$graphs->{$graph}{'per_if_fields'}}) diff --git a/plugins/network/if1sec-c.c b/plugins/network/if1sec-c.c index 4786d956..c31031ae 100644 --- a/plugins/network/if1sec-c.c +++ b/plugins/network/if1sec-c.c @@ -78,7 +78,7 @@ int config() { "down.graph no" "\n" "down.cdef down,8,*" "\n" "down.min 0" "\n" - + "up.label bps" "\n" "up.type DERIVE" "\n" "up.negative down" "\n" @@ -186,10 +186,10 @@ int acquire() { char if_id[64]; uint_fast64_t r_bytes, r_packets, r_errs, r_drop, r_fifo, r_frame, r_compressed, r_multicast; uint_fast64_t t_bytes, t_packets, t_errs, t_drop, t_fifo, t_frame, t_compressed, t_multicast; - sscanf(line, "%s" + sscanf(line, "%s" + " " + "%llu %llu %llu %llu %llu %llu %llu %llu" " " - "%llu %llu %llu %llu %llu %llu %llu %llu" - " " "%llu %llu %llu %llu %llu %llu %llu %llu" , if_id , &r_bytes, &r_packets, &r_errs, &r_drop, &r_fifo, &r_frame, &r_compressed, &r_multicast @@ -200,11 +200,11 @@ int acquire() { if_id[strlen(if_id) - 1] = '\0'; char out_buffer[1024]; - sprintf(out_buffer, + sprintf(out_buffer, "multigraph if_%s_1sec" "\n" "up.value %ld:%llu" "\n" "down.value %ld:%llu" "\n" - , if_id + , if_id , epoch, r_bytes , epoch, t_bytes ); @@ -276,7 +276,7 @@ int main(int argc, char **argv) { } /***** DEMO - + /proc/net/dev sample Inter-| Receive | Transmit diff --git a/plugins/network/if_uptime b/plugins/network/if_uptime index c5c51d2a..4dce3e7d 100755 --- a/plugins/network/if_uptime +++ b/plugins/network/if_uptime @@ -19,7 +19,7 @@ # # Plugin | Used | Suggestions # ------ | ---- | ----------- -# if_uptime | yes | +# if_uptime | yes | # # 7. Restart munin: "/etc/init.d/munin-node restart" # 8. Hold on for 5 minutes at most and watch the graph appear. @@ -117,7 +117,7 @@ $current_uptime EOF } -# Munin routines +# Munin routines case "$1" in autoconf) if [[ $(ifconfig &> /dev/null; echo "$?") == 0 ]]; then @@ -146,7 +146,7 @@ EOM done exit 0 ;; - *) + *) # Print data for Munin for (( i=0; i<"${#INTERFACES[*]}"; i++ )) do diff --git a/plugins/network/ifem_ b/plugins/network/ifem_ index 89d42bd2..13a0c60d 100755 --- a/plugins/network/ifem_ +++ b/plugins/network/ifem_ @@ -38,7 +38,7 @@ fi if [ "$1" = "config" ]; then - echo "graph_order rbytes obytes" + echo "graph_order rbytes obytes" echo "graph_title $INTERFACE traffic" echo 'graph_args --base 1000' echo 'graph_vlabel bits per ${graph_period} in (-) / out (+)' diff --git a/plugins/network/interfaces_linux_multi b/plugins/network/interfaces_linux_multi index 2b75c82c..d9515725 100755 --- a/plugins/network/interfaces_linux_multi +++ b/plugins/network/interfaces_linux_multi @@ -2,18 +2,18 @@ ######################################################################## # Copyright (c) 2012, Adrien Urban # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are -# met: -# +# met: +# # 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. +# notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the -# distribution. -# +# distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -439,7 +439,7 @@ EOF } sub check_req() { - my $data = get_data(); + my $data = get_data(); if (0 != scalar(keys %$data)) { return 1; } diff --git a/plugins/network/ip_forward_ b/plugins/network/ip_forward_ index d5ae66a2..374092f2 100755 --- a/plugins/network/ip_forward_ +++ b/plugins/network/ip_forward_ @@ -65,7 +65,7 @@ if [ "$1" = "config" ]; then echo 'in.label received' echo 'in.type DERIVE' echo 'in.min 0' - echo 'in.cdef in,8,*' + echo 'in.cdef in,8,*' exit 0 fi; diff --git a/plugins/network/ipfwnat_ b/plugins/network/ipfwnat_ index 3708729e..1ebba2de 100755 --- a/plugins/network/ipfwnat_ +++ b/plugins/network/ipfwnat_ @@ -17,7 +17,7 @@ # # Furthermore, nat configuration must contain "log" option. E.g. # -# ipfw nat 123 config ip 198.76.28.4 log +# ipfw nat 123 config ip 198.76.28.4 log # # This plugin is based on the if_ plugin. # @@ -45,7 +45,7 @@ if [ "$1" = "autoconf" ]; then echo "yes" exit 0 -fi +fi if [ "$1" = "suggest" ]; then $ipfw nat show config 2> /dev/null | /usr/bin/awk '/nat [0-9]+ .+ log/{print $3;}' diff --git a/plugins/network/ipt_basic_ b/plugins/network/ipt_basic_ index ec17102f..dd692fcd 100755 --- a/plugins/network/ipt_basic_ +++ b/plugins/network/ipt_basic_ @@ -22,7 +22,7 @@ # Revisions: # 2006.01.00 - First release. # 2006.11.26 - Use -j RETURN in rules, and sort interfaces -# +# # # Magic markers (optional - used by munin-config and some installation # scripts): @@ -94,7 +94,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000' echo 'graph_category network' echo 'graph_info This graph shows the traffic of the interfaces in bits per second, and should be precise above 50Mbps as well. All forwarded traffic is measured in the incoming counter of the given interface.' - + for iface in $IFACES; do echo "$iface.label ${iface}_received" echo "$iface.type DERIVE" diff --git a/plugins/network/ldap_connections b/plugins/network/ldap_connections index fc24b18b..49dd1cc6 100755 --- a/plugins/network/ldap_connections +++ b/plugins/network/ldap_connections @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the number of open connexions to LDAP # # $Log: ldap_connections,v $ @@ -34,14 +34,14 @@ # # # plugin-conf.d/-options: -# +# # netstat -- path to netstat executable # ports -- ldap ports used (389 and 636) # only used ones are graphed # socket -- ldapi socket (default: /var/run/openldap/ldapi) # # Parameters: -# +# # config (required) # autoconf (optional - used by munin-config) # diff --git a/plugins/network/netatalk b/plugins/network/netatalk index 9ac211eb..58c53c68 100755 --- a/plugins/network/netatalk +++ b/plugins/network/netatalk @@ -144,7 +144,7 @@ for shareName in `cat $defaultServer_volumesFile | grep "^[^#:]" | grep -oP "^([ for currentUid in $connectedUsers; do # For each connected users currentUserHomeDir=`getent passwd $currentUid | cut -d ':' -f6` # Fetch it's the home directory currentUserHomeDir=`readlink -f "$currentUserHomeDir"` # We want the realpath (resolves symbolic links and normalize the path) - + #FIX: We use pipe `lsof` outputs to `echo -e` with `xargs` because lsof "displays only printable ASCII characters" (cf. http://ftp.cerias.purdue.edu/pub/tools/unix/sysutils/lsof/00FAQ #14.5) # Then if a share with non-ASCII characters in it's path were to be opened, lsof would return them on \xNN form and grep wouldn't match: `echo -e /the/path` fixes this [ `$baseLsofCommand -F n | xargs -0 echo -e | grep "^n$currentUserHomeDir" | wc -l` -gt 0 ] && let openShares++ # If found in lsof output: increment the openShares counter diff --git a/plugins/network/nsd3 b/plugins/network/nsd3 index f2a4cddf..3b2e8b42 100755 --- a/plugins/network/nsd3 +++ b/plugins/network/nsd3 @@ -15,17 +15,17 @@ (http://nlnetlabs.nl/projects/nsd/) =head1 CONFIGURATION - + The plugin needs access to the nsd logfile and the nsd pid file to force the running nsd process to write the current statistics. - Tip: To see if it's already set up correctly, just run this plugin + Tip: To see if it's already set up correctly, just run this plugin with the parameter "autoconf". If you get a "yes", everything should work like a charm already. This configuration section shows the defaults of the plugin: - The stats line is a set of space-separated values that you wish to + The stats line is a set of space-separated values that you wish to retrieve from NSD. The format is VALUE=Caption. For spaces in a caption value, replace them with an underscore (_). @@ -34,7 +34,7 @@ env.pidfile /var/run/nsd3/nsd.pid env.stats "A=A AAAA=AAAA MX=MX PTR=PTR TYPE252=AXFR SNXD=NXDOMAIN RQ=Total_Successful" - If you need to set a user for the logfile to be readable, and most + If you need to set a user for the logfile to be readable, and most importantly, the process to receive the signal, you may specify it. For example: @@ -42,29 +42,29 @@ user nsd =head1 INTERPRETATION - - The plugin shows the number of queries that nsd has received, + + The plugin shows the number of queries that nsd has received, averaged over a period to gain the number of queries per second. For most servers, these values will be very low. In the event of a misconfiguration, the plugin will return undefined values. =head1 MAGIC MARKERS - + #%# family=auto #%# capabilities=autoconf =head1 VERSION v1.0.1 - + =head1 AUTHOR J.T.Sage - + =head1 LICENSE - + GPLv2 - + =cut """ diff --git a/plugins/network/qos_ b/plugins/network/qos_ index 06592904..973f5204 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -115,9 +115,9 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { last; } } - if($haschild == 1) { + if($haschild == 1) { $queues{$key}->{leaf} = 0; - next; + next; } $queues{$key}->{leaf} = 1; print $queues{$key}->{queue},$queues{$key}->{handle}, " "; diff --git a/plugins/network/radio b/plugins/network/radio index 4534506e..460f4b49 100755 --- a/plugins/network/radio +++ b/plugins/network/radio @@ -14,7 +14,7 @@ v0.2 - Updated to match output of current versions of ice- and shoutcast. */ // -------------- CONFIGURATION START --------------------------------------- - $cfg = array( + $cfg = array( // SERVER #1 array( "name" => "IceCast", // name for munin "type" => "ice", // server-type (ice/shout) diff --git a/plugins/network/shorewall-accounting_ b/plugins/network/shorewall-accounting_ index 9f6f9f21..4bb27d38 100755 --- a/plugins/network/shorewall-accounting_ +++ b/plugins/network/shorewall-accounting_ @@ -6,7 +6,7 @@ # Basically this plugin examines the output of "shorewall -x show accounting". # See http://atlee.ca/blog/2006/01/20/munin-shorewall/ for a description of # the original script by Chris AtLee. -# +# # Copyright 2010-2012 Lars Kruse # Copyright 2006 Chris AtLee # diff --git a/plugins/network/shorewall_ b/plugins/network/shorewall_ index a48f8007..b32bb7a0 100755 --- a/plugins/network/shorewall_ +++ b/plugins/network/shorewall_ @@ -1,11 +1,11 @@ #!/usr/bin/python # shorewall_ v2.0 - 30 Aug 2008 - Tanguy Pruvot -# +# # A munin plugin for tracking traffic as recorded by shorewall accounting rules -# +# # ex: ln -s /usr/share/munin/plugins/shorewall_ /etc/munin/plugins/shorewall_ftp # will log ftp* rules like ftp, ftp_input, ftp_output etc... -# +# # Basic Concept by Chris AtLee Released under the GPL v2 import sys, commands, re @@ -61,7 +61,7 @@ def getBytesByChain(): for name in chainNames: retval.append((name, chains[name])) return retval - + if len(sys.argv) > 1: if sys.argv[1] == "autoconf": print "yes" @@ -76,6 +76,6 @@ if len(sys.argv) > 1: print "%s.label %s" % (chain, chain) print "%s.cdef %s,8,*" % (chain, chain) sys.exit(0) - + for chain, bytes in getBytesByChain(): print "%s.value %i" % (chain, bytes) \ No newline at end of file diff --git a/plugins/network/shorewall_acc b/plugins/network/shorewall_acc index 2e60cd2d..5a5fc479 100755 --- a/plugins/network/shorewall_acc +++ b/plugins/network/shorewall_acc @@ -6,7 +6,7 @@ # Released under the GPL v2 import sys, commands, re accountingLineExp = re.compile(r"^\s*\d+[KMG]*\s+(\d+)([KMGT]*)\s+(\w+).*$") - + def getBytesByChain(): trafficCmd = "shorewall" status, output = commands.getstatusoutput("/sbin/shorewall show accounting 2>/dev/null") @@ -41,7 +41,7 @@ def getBytesByChain(): for name in chainNames: retval.append((name, chains[name])) return retval - + if len(sys.argv) > 1: if sys.argv[1] == "autoconf": print "yes" @@ -56,6 +56,6 @@ if len(sys.argv) > 1: print "%s.label %s" % (chain, chain) print "%s.cdef %s,8,*" % (chain, chain) sys.exit(0) - + for chain, bytes in getBytesByChain(): print "%s.value %i" % (chain, bytes) diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index f719291b..8906080c 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -42,14 +42,14 @@ case $1 in exit 0 ;; config) - + 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 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." - + # 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 DERIVE\n" $2 "_" $3 "_" $4 "_drops.min 0" }' exit 0 diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 8ae58204..0c5cadc2 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -42,14 +42,14 @@ case $1 in exit 0 ;; config) - + 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 packets per ${graph_period}' echo 'graph_category network' 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 DERIVE\n" $2 "_" $3 "_" $4 "_packets.min 0" }' exit 0 diff --git a/plugins/network/transmission b/plugins/network/transmission index 8d95b1de..ea3d15ad 100755 --- a/plugins/network/transmission +++ b/plugins/network/transmission @@ -13,7 +13,7 @@ This plugin implements the multigraph protocol and provides the following graphs transmission_throughput - monitor traffic volumes of Transmission torrents transmission_activity - plugin to monitor traffic speed of Transmission torrents -This plugin requires python and the transmissionrpc python module. +This plugin requires python and the transmissionrpc python module. See http://pypi.python.org/pypi/transmissionrpc/ =head1 CONFIGURATION @@ -64,13 +64,13 @@ find this plugin on github at http://github.com/VolatileMesh/munin-plugins =head1 CHANGELOG =head2 1.0 - 2010/11/12 - + first release =head2 1.1 - 2011/05/29 - + fix transmission error handling - + =cut """ __version__ = '1.1' @@ -138,7 +138,7 @@ def autoconf(): def fetch(): import transmissionrpc - + try: client = transmissionrpc.Client(host, port=port, user=user, password=passwd) except transmissionrpc.TransmissionError, err: @@ -148,20 +148,20 @@ def fetch(): stats = client.session_stats(10) print_values_throughput(stats) print_values_activity(stats) - - + + def print_values_activity(stats): print "multigraph {plugin_name}_activity".format(plugin_name=plugin_name) try: print "total.value %s" % stats.torrentCount except: print "total.value U" - + try: print "active.value %s" % stats.activeTorrentCount except: print "active.value U" - + try: print "paused.value %s" % stats.pausedTorrentCount except: @@ -174,12 +174,12 @@ def print_values_throughput(stats): print "down.value %s" % stats.cumulative_stats['downloadedBytes'] except: print "down.value U" - + try: print "up.value %s" % stats.cumulative_stats['uploadedBytes'] except: print "up.value U" - + def dumpstats(): @@ -192,7 +192,7 @@ def dumpstats(): stats = client.session_stats(10) print stats - + if __name__ == '__main__': if len(sys.argv)>1 : if sys.argv[1]=="dumpstats" : diff --git a/plugins/network/ubiquiti_airfiber_ b/plugins/network/ubiquiti_airfiber_ index 96e04ca2..233194fc 100755 --- a/plugins/network/ubiquiti_airfiber_ +++ b/plugins/network/ubiquiti_airfiber_ @@ -4,7 +4,7 @@ # Multigraph munin plugin to monitor Ubiquiti AirOS F (airFiber) devices various parameters. It needs # Perl's Net::Telnet or Net::OpenSSH to be able to connect. # -# To use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) +# To use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) # under the name "ubiquiti_airfiber_". Don't change this filename! Follow these steps: # # 1. Give names to your devices, in fqdn style. Like "master.wlan" or "slave.wlan". To make the @@ -22,7 +22,7 @@ # # 3. In /etc/munin/plugin-conf.d/munin-node add the following, to be able to contact # those devices via telnet (obviously replacing these with your own data): -# +# # [ubiquiti_airos_master.wlan] # user root # User and Group are required only if using any of the SSH modes to store # group root # the keys in /root/.ssh directory (or any user with homedir and shell) @@ -53,12 +53,12 @@ # # 5. Restart the munin node by 'service munin-node restart'. # -# If all went well, after 5 minutes or so you should have two additional nodes listed +# If all went well, after 5 minutes or so you should have two additional nodes listed # on the Web Interface of munin. # -# To use the script with public keys authentication and no password, set env.NetMode SSHkey, and -# create a pair of keys using command 'sudo ssh-keygen -t rsa'. This will generate in /root/.ssh -# directory two files, id_rsa and id_rsa.pub. Upload id_rsa.pub to your Ubiquiti device using +# To use the script with public keys authentication and no password, set env.NetMode SSHkey, and +# create a pair of keys using command 'sudo ssh-keygen -t rsa'. This will generate in /root/.ssh +# directory two files, id_rsa and id_rsa.pub. Upload id_rsa.pub to your Ubiquiti device using # Services > SSH Server > Authorized Keys window. Try to log in to the device by command line # first ('sudo ssh foobar@slave.wlan'), to save the RSA key fingerprint in the root account. # @@ -82,7 +82,7 @@ use warnings; my $NetMode = $ENV{'NetMode'}; if ($NetMode =~ /Telnet/) { use Net::Telnet; -} +} elsif ($NetMode =~ /SSH/) { use Net::OpenSSH; } @@ -350,20 +350,20 @@ if ($NetMode =~ /Telnet/) { elsif ($NetMode =~ /SSHPass/) { ## Initiate SSH Session using password authentication - $SSH = Net::OpenSSH->new($Hostname, + $SSH = Net::OpenSSH->new($Hostname, port => $Port, - user => $User, + user => $User, password => $Pass, timeout => 10, # master_stderr_discard => 1, master_opts => [-o => "StrictHostKeyChecking=no"]); - $SSH->error and + $SSH->error and # warn "Couldn't establish SSH connection: " . $SSH->error; &printResults(); # Nothing happens, except printing undefined results to munin } elsif ($NetMode =~ /SSHKey/) { - ## Initiate SSH Session using public key authentication + ## Initiate SSH Session using public key authentication $SSH = Net::OpenSSH->new($Hostname, port => $Port, user => $User, @@ -541,8 +541,8 @@ $ping_timelo = ($1 / 1000) if ($pinglo =~ m@min/avg/max.*\s\d+(?:\.\d+)?/(\d+(?: $packet_losslo = $1 if ($pinglo =~ /(\d+)% packet loss/); -chomp($load, $uptime, $ping_time, $packet_loss, $cpuuser, $cpusystem, $cpunice, $cpuidle, $cpuiowait, $cpuirq, $cpusoftirq, $ping_timelo, $packet_losslo, - $rxrate, $txrate, $rxpower0, $rxpower1, $powerout, $feet, $dist, $temp0, $temp1, +chomp($load, $uptime, $ping_time, $packet_loss, $cpuuser, $cpusystem, $cpunice, $cpuidle, $cpuiowait, $cpuirq, $cpusoftirq, $ping_timelo, $packet_losslo, + $rxrate, $txrate, $rxpower0, $rxpower1, $powerout, $feet, $dist, $temp0, $temp1, $rssi0, $rssi1, $baseline, $fade, $txfreq, $rxfreq, $txmodrate, $speed); &printResults(); diff --git a/plugins/network/ubiquiti_airos_ b/plugins/network/ubiquiti_airos_ index f1e7351d..9be5cca0 100755 --- a/plugins/network/ubiquiti_airos_ +++ b/plugins/network/ubiquiti_airos_ @@ -4,7 +4,7 @@ # Multigraph munin plugin to monitor Ubiquiti AirOS devices various parameters. It needs # Perl's Net::Telnet or Net::OpenSSH to be able to connect. # -# To use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) +# To use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) # under the name "ubiquiti_airos_". Don't change this filename! Follow these steps: # # 1. Give names to your devices, in fqdn style. Like "apo.wlan" or "cli.wlan". To make the @@ -53,12 +53,12 @@ # # 5. Restart the munin node by 'service munin-node restart'. # -# If all went well, after 5 minutes or so you should have two additional nodes listed +# If all went well, after 5 minutes or so you should have two additional nodes listed # on the Web Interface of munin. # -# To use the script with public keys authentication and no password, set env.NetMode SSHkey, and -# create a pair of keys using command 'sudo ssh-keygen -t rsa'. This will generate in /root/.ssh -# directory two files, id_rsa and id_rsa.pub. Upload id_rsa.pub to your Ubiquiti device using +# To use the script with public keys authentication and no password, set env.NetMode SSHkey, and +# create a pair of keys using command 'sudo ssh-keygen -t rsa'. This will generate in /root/.ssh +# directory two files, id_rsa and id_rsa.pub. Upload id_rsa.pub to your Ubiquiti device using # Services > SSH Server > Authorized Keys window. Try to log in to the device by command line # first ('sudo ssh foobar@cli.wlan'), to save the RSA key fingerprint in the root account. # @@ -89,7 +89,7 @@ use warnings; my $NetMode = $ENV{'NetMode'}; if ($NetMode =~ /Telnet/) { use Net::Telnet; -} +} elsif ($NetMode =~ /SSH/) { use Net::OpenSSH; } @@ -103,7 +103,7 @@ chomp ($HostLo); ############################################################################## ## Define variables my $graph_period = "second"; -my ($load, $uptime, $ping_time, $packet_loss, $amc, $amq, $conn, $mt, $mf, $mb, $errlrcvd, $errltrans, $errwrcvd, $errwtrans, $ping_timelo, $packet_losslo); +my ($load, $uptime, $ping_time, $packet_loss, $amc, $amq, $conn, $mt, $mf, $mb, $errlrcvd, $errltrans, $errwrcvd, $errwtrans, $ping_timelo, $packet_losslo); my ($trflup, $trfldown, $trfwup, $trfwdown, $freq, $txccq, $acttimeout, $txsignal, $noisefloor, $txrate, $rxrate, $errnwid); my ($errcrypt, $errfrag, $errretries, $errbmiss, $errother, $cpuuser, $cpusystem, $cpunice, $cpuidle, $cpuiowait, $cpuirq, $cpusoftirq); $load = $uptime = $ping_time = $packet_loss = $amc = $amq = $conn = $mt = $mf = $mb = $errlrcvd = $errltrans = $errwrcvd = $errwtrans = $ping_timelo = $packet_losslo = "U"; @@ -454,20 +454,20 @@ if ($NetMode =~ /Telnet/) { elsif ($NetMode =~ /SSHPass/) { ## Initiate SSH Session using password authentication - $SSH = Net::OpenSSH->new($Hostname, + $SSH = Net::OpenSSH->new($Hostname, port => $Port, - user => $User, + user => $User, password => $Pass, timeout => 10, # master_stderr_discard => 1, master_opts => [-o => "StrictHostKeyChecking=no"]); - $SSH->error and + $SSH->error and # warn "Couldn't establish SSH connection: " . $SSH->error; &printResults(); # Nothing happens, except printing undefined results to munin } elsif ($NetMode =~ /SSHKey/) { - ## Initiate SSH Session using public key authentication + ## Initiate SSH Session using public key authentication $SSH = Net::OpenSSH->new($Hostname, port => $Port, user => $User, @@ -666,8 +666,8 @@ my $pinglo = join(" ", @pinglo); $ping_timelo = ($1 / 1000) if ($pinglo =~ m@min/avg/max.*\s\d+(?:\.\d+)?/(\d+(?:\.\d+)?)/\d+(?:\.\d+)?@); $packet_losslo = $1 if ($pinglo =~ /(\d+)% packet loss/); -chomp($load, $uptime, $ping_time, $packet_loss, $amc, $amq, $conn, $mt, $mf, $mb, $errlrcvd, $errltrans, $ping_timelo, $packet_losslo, - $errwrcvd, $errwtrans, $trflup, $trfldown, $trfwup, $trfwdown, $freq, $txccq, $acttimeout, $txsignal, +chomp($load, $uptime, $ping_time, $packet_loss, $amc, $amq, $conn, $mt, $mf, $mb, $errlrcvd, $errltrans, $ping_timelo, $packet_losslo, + $errwrcvd, $errwtrans, $trflup, $trfldown, $trfwup, $trfwdown, $freq, $txccq, $acttimeout, $txsignal, $noisefloor, $txrate, $rxrate, $errnwid, $errcrypt, $errfrag, $errretries, $errbmiss, $errother, $cpuuser, $cpusystem, $cpunice, $cpuidle, $cpuiowait, $cpuirq, $cpusoftirq); @@ -688,7 +688,7 @@ sub printResults { print "multigraph airos_ack\n"; print "acttimeout.value " . $acttimeout . "\n"; print "\n"; - + print "multigraph airos_dbm\n"; print "txsignal.value " . $txsignal . "\n"; print "noisefloor.value " . $noisefloor . "\n"; diff --git a/plugins/network/umts_sig b/plugins/network/umts_sig index b66a9b55..674a697e 100755 --- a/plugins/network/umts_sig +++ b/plugins/network/umts_sig @@ -1,5 +1,5 @@ #!/bin/bash -# +# # A Munin Plugin to show umts signal strength using gcom # Created by Derik Vercueil # Based on a work of "auth" diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index e3c933c5..7e42fdea 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -115,7 +115,7 @@ EOF "*") echo "$0: unknown mode '$1'" >&2 exit 1 - esac + esac } fetch () { @@ -135,7 +135,7 @@ fetch () { "*") echo "$0: unknown mode '$1'" >&2 exit 1 - esac + esac } mode=`echo $0 | sed 's/.*_//'` diff --git a/plugins/network/vnstat b/plugins/network/vnstat index 65a52f8d..afdaf9f3 100755 --- a/plugins/network/vnstat +++ b/plugins/network/vnstat @@ -26,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'totalrx.info Total data received.' echo 'totalrx.cdef totalrx,1000000,*' exit 0 - + fi; diff --git a/plugins/network/vnstat_ b/plugins/network/vnstat_ index 9fb9c039..23388ae1 100755 --- a/plugins/network/vnstat_ +++ b/plugins/network/vnstat_ @@ -34,7 +34,7 @@ if [ "$1" = "config" ]; then echo 'totalrx.info Total data received.' echo 'totalrx.cdef totalrx,1000000,*' exit 0 - + fi; diff --git a/plugins/newznab/nn_ b/plugins/newznab/nn_ index 0bb1c601..41f13395 100755 --- a/plugins/newznab/nn_ +++ b/plugins/newznab/nn_ @@ -101,7 +101,7 @@ $graphs{releases} = { }, data_source_attrs => { min => '0', - + }, }, data_sources => [ @@ -254,7 +254,7 @@ sub main { 'config' => \&config, 'show' => \&show, ); - + die "Unknown command: $command" unless exists $commands{$command}; return $commands{$command}->($graph); } @@ -265,7 +265,7 @@ sub show { unless $graphs{$graph_name}; my $graph = $graphs{$graph_name}; - run_queries($graph_name); + run_queries($graph_name); for my $ds (@{$graph->{data_sources}}) { printf "%s.value %s\n", clean_fieldname($ds->{label}), ($data->{$ds->{name}} ? $data->{$ds->{name}} : '0'); } @@ -298,22 +298,22 @@ sub run_queries { } sub update_requests { - my ($dbh) = @_; + my ($dbh) = @_; my %queries = ( - request => 'select count(*) as requests from userrequests where timestamp > now() - INTERVAL 5 MINUTE;', - download => 'select count(*) as downloads from userdownloads where timestamp > now() - INTERVAL 5 MINUTE;', - ); + request => 'select count(*) as requests from userrequests where timestamp > now() - INTERVAL 5 MINUTE;', + download => 'select count(*) as downloads from userdownloads where timestamp > now() - INTERVAL 5 MINUTE;', + ); for my $name ( qw(request download) ) { - my $query = $queries{$name}; - my $sth = $dbh->prepare($query); - $sth->execute(); - while (my $row = $sth->fetch) { + my $query = $queries{$name}; + my $sth = $dbh->prepare($query); + $sth->execute(); + while (my $row = $sth->fetch) { $data->{$name} = $row->[0]; } - $sth->finish(); - } + $sth->finish(); + } } - + sub update_category { my ($dbh) = @_; my $sth = $dbh->prepare('select count(*) as releases, category.title, category.id from releases LEFT JOIN category ON releases.categoryID = category.ID group by releases.categoryID'); @@ -322,7 +322,7 @@ sub update_category { while (my $row = $sth->fetch) { $data->{$row->[2]} = $row->[0]; } - + } sub update_releases { my ($dbh) = @_; @@ -331,7 +331,7 @@ sub update_releases { $sth->execute(); }; if ($@) { die $@; } - my $row = $sth->fetchrow_hashref(); + my $row = $sth->fetchrow_hashref(); $data->{releases} = $row->{'releases'}; $sth->finish(); } @@ -343,7 +343,7 @@ sub update_users { $sth->execute(); }; if ($@) { die $@; } - my $row = $sth->fetchrow_hashref(); + my $row = $sth->fetchrow_hashref(); $data->{users} = $row->{'users'}; $sth->finish(); } diff --git a/plugins/nginx/nginx-combined b/plugins/nginx/nginx-combined index 0adf8362..e1006b01 100755 --- a/plugins/nginx/nginx-combined +++ b/plugins/nginx/nginx-combined @@ -10,7 +10,7 @@ # Copyright (C) 2010 Antnio P. P. Almeida # Copyright (C) 2010 Minato Miray -# Author: Antnio P. P. Almeida , +# Author: Antnio P. P. Almeida , # Author: Minato Miray ####################################### @@ -50,7 +50,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) print "no ($ret)\n"; exit 1; } - + my $ua = LWP::UserAgent->new(timeout => 30); my $response = $ua->request(HTTP::Request->new('GET',$URL)); @@ -75,15 +75,15 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) print "reqpsec.label Request/sec.\n"; print "reqpsec.info Request/sec.\n"; - print "reqpsec.draw LINE2\n"; + print "reqpsec.draw LINE2\n"; print "conpersec.label Connection/sec.\n"; print "conpersec.info Connection/sec.\n"; - print "conpersec.draw LINE2\n"; + print "conpersec.draw LINE2\n"; print "reqpcon.label Request/conn.\n"; print "reqpcon.info Request/conn.\n"; - print "reqpcon.draw LINE2\n"; + print "reqpcon.draw LINE2\n"; print "total.label Active connections\n"; print "total.info Active connections\n"; @@ -91,16 +91,16 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) print "reading.label Reading\n"; print "reading.info Reading\n"; - print "reading.draw LINE2\n"; + print "reading.draw LINE2\n"; print "writing.label Writing\n"; print "writing.info Writing\n"; - print "writing.draw LINE2\n"; + print "writing.draw LINE2\n"; print "waiting.label Waiting\n"; print "waiting.info Waiting\n"; - print "waiting.draw LINE2\n"; - + print "waiting.draw LINE2\n"; + exit 0; } @@ -145,11 +145,11 @@ if (defined $tmp2_conpsec && $tmp2_conpsec =~ /^[+-]?\d+$/ && $tmp2_conpsec > 0 $conpersec=$tmp2_conpsec-$tmp1_conpsec; } if (defined $tmp2_reqpsec && $tmp2_reqpsec =~ /^[+-]?\d+$/ && $tmp2_reqpsec > 0){ - $reqpsec=$tmp2_reqpsec-$tmp1_reqpsec; + $reqpsec=$tmp2_reqpsec-$tmp1_reqpsec; } if ($conpersec > 0){ $reqpcon=$reqpsec/$conpersec; -} +} print "reqpsec.value $reqpsec\n"; print "conpersec.value $conpersec\n"; diff --git a/plugins/nginx/nginx_byprojects/README.md b/plugins/nginx/nginx_byprojects/README.md index 7f93095f..0bde3568 100644 --- a/plugins/nginx/nginx_byprojects/README.md +++ b/plugins/nginx/nginx_byprojects/README.md @@ -2,21 +2,21 @@ Those plugins are used to monitor different projects or vhost (i.e. either different log files or using regular expression as filters) on the same web server. ## munin_byprojects_access -Count the number of hits per projects/vhost. +Count the number of hits per projects/vhost. ![byproject_access](https://www.mantor.org/~northox/misc/munin-plugins/nginx_byprojects_access1-month.png "byproject_access") ## munin_byprojects_bandwidth -Count the total bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. +Count the total bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_bandwidth-month.png "byproject_bandwidth") ## munin_byprojects_inout_bandwidth -Counts the in/out bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. +Counts the in/out bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_inout_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_inout_bandwidth-month.png "byproject_inout_bandwidth") ## Installation The setup is pretty straight forward. First you need to configure the plugin: -In your munin plugin configuration file (for example, a new dedicated /etc/munin/plugin-conf.d/nginx_byprojects), configure the plugins : +In your munin plugin configuration file (for example, a new dedicated /etc/munin/plugin-conf.d/nginx_byprojects), configure the plugins: [byprojects_*] env.logtail /usr/local/bin/logtail @@ -35,6 +35,6 @@ Then link the file just as any other plugins. ln -s /usr/local/sbin/ /usr/local/etc/munin/plugins/ And restart the munin-node service. - + ## License MIT diff --git a/plugins/nginx/nginx_byprojects/byprojects_access b/plugins/nginx/nginx_byprojects/byprojects_access index bc400504..f1246b62 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_access +++ b/plugins/nginx/nginx_byprojects/byprojects_access @@ -4,16 +4,16 @@ use JSON qw(decode_json); # # byprojects_access # -# Perl script to monitor access *byprojects* (e.g. vhost) from multiple files +# Perl script to monitor access *byprojects* (e.g. vhost) from multiple files # and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # # You need logtail (https://www.fourmilab.ch/webtools/logtail/) # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -73,7 +73,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_access.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail: $!"; while () { my $buf = $_; diff --git a/plugins/nginx/nginx_byprojects/byprojects_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_bandwidth index af245e96..52be2ced 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_bandwidth @@ -7,19 +7,19 @@ use JSON qw(decode_json); # Perl script to monitor total bandwidth *byprojects* (e.g. vhost) from multiple # files and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # # You need logtail (https://www.fourmilab.ch/webtools/logtail/) # -# Your nginx configuration should look like this (i.e. $request_length +# Your nginx configuration should look like this (i.e. $request_length # body_bytes_sent at the end): # log_format main '$remote_addr - $remote_user $time_local "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" $request_length $body_bytes_sent'; # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -80,7 +80,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_totalbandwidth.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail : $!"; while () { my $buf = $_; diff --git a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth index 21b36902..17f0772c 100755 --- a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth @@ -4,22 +4,22 @@ use JSON qw(decode_json); # # byprojects_inout_bandwidth # -# Perl script to monitor in/out bandwidth *byprojects* (e.g. vhost) from +# Perl script to monitor in/out bandwidth *byprojects* (e.g. vhost) from # multiple files and/or regex. # -# Danny Fullerton +# Danny Fullerton # Mantor Organization # This work is licensed under a MIT license. # # You need logtail (https://www.fourmilab.ch/webtools/logtail/) # -# Your nginx configuration should look like this (i.e. $request_length +# Your nginx configuration should look like this (i.e. $request_length # body_bytes_sent at the end): # log_format main '$remote_addr - $remote_user $time_local "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" $request_length $body_bytes_sent'; # -# Log can be gathered from multiple sources by simply specifying multiple log +# Log can be gathered from multiple sources by simply specifying multiple log # filename or using wildcards (glob). File content can be selected using regex. # # - 'prod' => [ {'path' => '/home/prod/log/access.log'} ], @@ -83,7 +83,7 @@ foreach my $project ( keys %logs ) { my @paths = glob $log->{'path'}; foreach my $path (@paths) { my $state = $statepath.'/'.$project.$x.'_inoutbandwidth.state'; - open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or + open(LT, "$logtail -f ".$log->{'path'}." -o $state |") or die "Can't open $logtail : $!"; while () { my $buf = $_; diff --git a/plugins/nginx/nginx_upstream b/plugins/nginx/nginx_upstream index 5528e9a1..0bca0f8d 100755 --- a/plugins/nginx/nginx_upstream +++ b/plugins/nginx/nginx_upstream @@ -44,7 +44,7 @@ totaltimeforrequests=0 numberofrequests=0 for line in lines: m = rg.search(line) - if m: + if m: word1=m.group(1) c1=m.group(2) float1=m.group(3) diff --git a/plugins/nginx/nginx_vhost_traffic b/plugins/nginx/nginx_vhost_traffic index a467d575..b512dce2 100755 --- a/plugins/nginx/nginx_vhost_traffic +++ b/plugins/nginx/nginx_vhost_traffic @@ -1,7 +1,7 @@ #!/bin/sh # # Script for monitoring nginx Virtual host output traffic -# +# # Requierements: logtail awk # one unique access log file with $bytes_sent value for more accuracy # check http://wiki.nginx.org/NginxHttpLogModule @@ -11,23 +11,23 @@ # # Virtual host list # env.vhosts "example.com example.net example.org" -# +# # Log path # env.logdir = /var/log/nginx -# env.flogfile = access.log +# env.flogfile = access.log # # Position of the $bytes_sent in the access.log file # env.bparam 11 # # Aggregate subdomains -# ex: example.com will match www.example.com, webmail.example.com and *example.com +# ex: example.com will match www.example.com, webmail.example.com and *example.com # BUG: will also match also www.bad-example.com -# env.aggregate true #change to false to disable aggregation +# env.aggregate true #change to false to disable aggregation # # To report bugs, improvements or get updates # see http://github.com/joanpc/nginix_vhost_traffic # -# inspired in postfix_filtered_awk +# inspired in postfix_filtered_awk # Copyright (c) 2010, Joan Perez i Cauhe LOGDIR=${logdir:-/var/log/nginx} @@ -48,8 +48,8 @@ case $1 in echo 'graph_category webserver' i=0 - for vhost in $VHOSTS - do + for vhost in $VHOSTS + do i=$(($i + 1)) echo vhost$i.label $vhost echo vhost$i.type ABSOLUTE @@ -57,7 +57,7 @@ case $1 in echo vhost$i.draw $DRAW DRAW=STACK done - + echo rest.label Rest echo rest.type ABSOLUTE echo rest.cdef rest,8,* @@ -72,16 +72,16 @@ export AGGREGATE # Awk Script $LOGTAIL ${ACCESS_LOG} -o $STATEFILE | awk ' -BEGIN { +BEGIN { split(ENVIRON["VHOSTS"], hosts) for (host in hosts) { track[hosts[host]] = host} -} +} { cn[$2]+=$ENVIRON["BPARAM"] } -END { - for (host in cn) { - if (match(ENVIRON["AGGREGATE"], "true")) { +END { + for (host in cn) { + if (match(ENVIRON["AGGREGATE"], "true")) { found = 0 for (vhost in track) { if (index(host, vhost)) { @@ -93,7 +93,7 @@ END { if (! found) rest+=cn[host] } else { if (host in track) { - res[host] += cn[host] + res[host] += cn[host] } else rest+=cn[host] } } diff --git a/plugins/nova/nova_floating_ips b/plugins/nova/nova_floating_ips index 6b7e95ba..ee04a083 100755 --- a/plugins/nova/nova_floating_ips +++ b/plugins/nova/nova_floating_ips @@ -72,7 +72,7 @@ if __name__ == '__main__': if sys.argv[1] == "config": print_config() elif sys.argv[1] == "autoconf": - if not successful_import: + if not successful_import: print 'no (failed import nova module)' sys.exit(0) else: diff --git a/plugins/nova/nova_instance_ b/plugins/nova/nova_instance_ index d34730b7..b5ffb60c 100755 --- a/plugins/nova/nova_instance_ +++ b/plugins/nova/nova_instance_ @@ -114,7 +114,7 @@ if __name__ == '__main__': elif argv[1] == 'suggest': print_suggest() elif argv[1] == 'autoconf': - if not successful_import: + if not successful_import: print 'no (failed import nova module)' sys.exit(0) else: diff --git a/plugins/nova/nova_instance_launched b/plugins/nova/nova_instance_launched index 2e80c2ce..f2c6fb6d 100755 --- a/plugins/nova/nova_instance_launched +++ b/plugins/nova/nova_instance_launched @@ -52,12 +52,12 @@ if __name__ == '__main__': if sys.argv[1] == "config": print_config() elif sys.argv[1]=="autoconf" : - if not successful_import: + if not successful_import: print 'no (failed import nova module)' sys.exit(0) else: print 'yes' elif successful_import: utils.default_flagfile() - flags.FLAGS(sys.argv) + flags.FLAGS(sys.argv) print_values() diff --git a/plugins/nova/nova_instance_timing b/plugins/nova/nova_instance_timing index 8e59ca0e..de53b909 100755 --- a/plugins/nova/nova_instance_timing +++ b/plugins/nova/nova_instance_timing @@ -64,7 +64,7 @@ if __name__ == '__main__': if sys.argv[1] == "config": print_config() elif sys.argv[1] == "autoconf": - if not successful_import: + if not successful_import: print 'no (failed import nova module)' sys.exit(0) else: diff --git a/plugins/nova/nova_services b/plugins/nova/nova_services index d12823ea..47baa0b1 100755 --- a/plugins/nova/nova_services +++ b/plugins/nova/nova_services @@ -76,7 +76,7 @@ if __name__ == '__main__': if sys.argv[1] == "config": print_config() elif sys.argv[1] == "autoconf": - if not successful_import: + if not successful_import: print 'no (failed import nova module]' else: print 'yes' diff --git a/plugins/nscd/nscd_ b/plugins/nscd/nscd_ index a6e8877b..b8919a55 100755 --- a/plugins/nscd/nscd_ +++ b/plugins/nscd/nscd_ @@ -41,7 +41,7 @@ MODE=$(basename $0 | sed 's/^nscd_//g' | tr '_' '.') case $1 in autoconf) - [ -r "$NSCD_CFG" ] && [ $AUTOCONF_CHECK -gt 0 ] && echo yes || echo "no (nscd config not found or no database enabled)" + [ -r "$NSCD_CFG" ] && [ $AUTOCONF_CHECK -gt 0 ] && echo yes || echo "no (nscd config not found or no database enabled)" exit 0 ;; suggest) diff --git a/plugins/ntp/ntp_kernel_pll_prec b/plugins/ntp/ntp_kernel_pll_prec index 52fbd984..26fe2139 100755 --- a/plugins/ntp/ntp_kernel_pll_prec +++ b/plugins/ntp/ntp_kernel_pll_prec @@ -37,7 +37,7 @@ EOF if [ "$1" = "autoconf" ]; then { ntpq -c kerninfo; ntpdc -c kerninfo; } 2>/dev/null | awk 'BEGIN { ev=1; } - /^precision:/ { ev=0; } + /^precision:/ { ev=0; } END { if (ev == 0) { print "yes";} else { print "no"; } exit ev; }' exit 0 fi diff --git a/plugins/ntp/ntp_kernel_pll_tol b/plugins/ntp/ntp_kernel_pll_tol index 481c8a48..e1d48e6c 100755 --- a/plugins/ntp/ntp_kernel_pll_tol +++ b/plugins/ntp/ntp_kernel_pll_tol @@ -35,9 +35,9 @@ None known EOF if [ "$1" = "autoconf" ]; then - { ntpq -c kerninfo; ntpdc -c kerninfo; } 2>/dev/null | + { ntpq -c kerninfo; ntpdc -c kerninfo; } 2>/dev/null | awk 'BEGIN { ev=1; } - /^frequency tolerance:/ { ev=0; } + /^frequency tolerance:/ { ev=0; } END { if (ev == 0) { print "yes";} else { print "no"; } exit ev; }' exit 0 fi diff --git a/plugins/ntp/ntp_peers b/plugins/ntp/ntp_peers index 174a0c26..3cd798bb 100755 --- a/plugins/ntp/ntp_peers +++ b/plugins/ntp/ntp_peers @@ -39,7 +39,7 @@ # # Change log # v1.0.0 2008-07-21 Chris Hastie -# initial release +# initial release # # v1.1.0 2010-12-07 Uffe Norberg # - Changed default statedir to /var/lib/munin/plugin-state (Debian default) @@ -107,7 +107,7 @@ open(SERVICE, "$COMMAND |") while () { if (/^[-+*#](\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s+\S+){7}\s+(\S+)/) { my $name = &lookupname($1); - $peers{$1}{'value'} = $3; + $peers{$1}{'value'} = $3; } } close(SERVICE); @@ -140,7 +140,7 @@ foreach my $peer (keys %peers) { # save list of peer IPs and hostnames if(-l $statefile) { die("$statefile is a symbolic link, refusing to touch it."); -} +} open (OUT, ">$statefile") or exit 4; foreach my $i (keys %peers) { print OUT "$i:" . $peers{$i}{'name'} . "\n"; @@ -155,12 +155,12 @@ sub by_name { # create a valid munin field name from the hostname sub sanitize_field () { my $field = shift; - + # replace illegal characters with an underscore $field =~ s/[^A-Za-z0-9_]/_/g; # prepend an underscore if name starts with a number $field =~ s/^([^A-Za-z_])/_$1/; - + # truncate to 19 characters if (length($field) > 19) { $field = substr($field, 0, 19); @@ -181,11 +181,11 @@ sub lookupname () { my $ip = shift; # have we already got it? if ($peers{$ip}{'name'}) { - return $peers{$ip}{'name'}; + return $peers{$ip}{'name'}; } # else look it up - my $iaddr = inet_aton($ip); - my $name = gethostbyaddr($iaddr, AF_INET) || $ip; + my $iaddr = inet_aton($ip); + my $name = gethostbyaddr($iaddr, AF_INET) || $ip; # add to cache $peers{$ip}{'name'} = $name; return $name; diff --git a/plugins/ntp/ntp_peers_ipv6 b/plugins/ntp/ntp_peers_ipv6 index d98701e0..e422f012 100755 --- a/plugins/ntp/ntp_peers_ipv6 +++ b/plugins/ntp/ntp_peers_ipv6 @@ -29,7 +29,7 @@ # # Change log # v1.0.0 2008-07-21 Chris Hastie -# initial release +# initial release # v1.0.1 2009-06-05 Tony Hoyle # ipv6 support. Remove dns lookups. # @@ -75,7 +75,7 @@ open(SERVICE, "$COMMAND |") while () { if(/^\s*\d+\s+(\d+)\s+/) { my ($name, $offset) = &lookupip($1); - $peers{$name} = $offset; + $peers{$name} = $offset; } } close(SERVICE); @@ -106,12 +106,12 @@ foreach my $peer (keys %peers) { # create a valid munin field name from the hostname sub sanitize_field () { my $field = shift; - + # replace illegal characters with an underscore $field =~ s/[^A-Za-z0-9_]/_/g; # prepend an underscore if name starts with a number $field =~ s/^([^A-Za-z_])/_$1/; - + # truncate to 19 characters if (length($field) > 19) { $field = substr($field, 0, 19); diff --git a/plugins/ntp/ntp_pool_score_ b/plugins/ntp/ntp_pool_score_ index 1e5ce7d4..44c2633c 100755 --- a/plugins/ntp/ntp_pool_score_ +++ b/plugins/ntp/ntp_pool_score_ @@ -5,7 +5,7 @@ =head1 NAME -ntp_pool_score_ - Wildcard plugin to monitor the score assigned to a server +ntp_pool_score_ - Wildcard plugin to monitor the score assigned to a server from pool.ntp.org . This is achieved by fetching the cvs data from http://www.pool.ntp.org/scores/IP_ADDRESS/log?limit=1 using wget. @@ -38,7 +38,7 @@ tocho AT tochev DOT net =head1 LICENSE GNU General Public License, version 2 - http://www.gnu.org/licenses/gpl-2.0.html + http://www.gnu.org/licenses/gpl-2.0.html =head1 MAGIC MARKERS diff --git a/plugins/ntp/ntp_queries b/plugins/ntp/ntp_queries index 18a25b22..68c155f8 100755 --- a/plugins/ntp/ntp_queries +++ b/plugins/ntp/ntp_queries @@ -27,7 +27,7 @@ # # Change log # v1.0.0 2009-03-11 Chris Hastie -# initial release +# initial release # # # @@ -86,20 +86,19 @@ while () { } if (/^bad version:\s*(\d*)/) { $queries += $1; - } + } # if (/^access denied:\s*(\d*)/) { # $queries += $1; - # } + # } # if (/^bad length or format:\s*(\d*)/) { # $queries += $1; - # } + # } # if (/^bad authentication:\s*(\d*)/) { # $queries += $1; - # } + # } # if (/^rate exceeded:\s*(\d*)/) { # $queries += $1; - # } - + # } } close(SERVICE); diff --git a/plugins/omreport/omreport_pwrmon_current b/plugins/omreport/omreport_pwrmon_current index b8e8a0cc..af0eb858 100755 --- a/plugins/omreport/omreport_pwrmon_current +++ b/plugins/omreport/omreport_pwrmon_current @@ -2,7 +2,7 @@ # # Copyright (C) 2009 Andrew Chadwick, University of Oxford # Based on work by Rackspace US, Inc. , (C) 2008. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/omreport/omreport_pwrmon_power b/plugins/omreport/omreport_pwrmon_power index c5c2ddbe..a58ec270 100755 --- a/plugins/omreport/omreport_pwrmon_power +++ b/plugins/omreport/omreport_pwrmon_power @@ -2,7 +2,7 @@ # # Copyright (C) 2009 Andrew Chadwick, University of Oxford # Based on work by Rackspace US, Inc. , (C) 2008. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/omreport/omreport_storage_temp b/plugins/omreport/omreport_storage_temp index 0c8be449..ca67cc71 100755 --- a/plugins/omreport/omreport_storage_temp +++ b/plugins/omreport/omreport_storage_temp @@ -2,7 +2,7 @@ # # Copyright (C) 2009 Andrew Chadwick, University of Oxford # Based on work by Rackspace US, Inc. , (C) 2008. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/omreport/omreport_temp b/plugins/omreport/omreport_temp index f7bcb3f9..01eeee16 100755 --- a/plugins/omreport/omreport_temp +++ b/plugins/omreport/omreport_temp @@ -14,11 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, see http://www.gnu.org/licenses/gpl.txt -# +# # # This plugin will graph the chassis temp sensors on a Dell PowerEdge Server # via the omreport tool. It has been tested on the following chassis: -# +# # PE2650/6650 # PE2850/6850 # PE2950 diff --git a/plugins/openvz/openvz-load-avg b/plugins/openvz/openvz-load-avg index 77dbcb42..c624ad85 100755 --- a/plugins/openvz/openvz-load-avg +++ b/plugins/openvz/openvz-load-avg @@ -1,12 +1,12 @@ #!/usr/bin/perl -# +# # Copyright (c) 2008, Maxime Besson # Copyright (c) 2011, David Bernard -# +# # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. -# +# # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -15,7 +15,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -# +# # Graph VE load averages on an OpenVZ server # # This plugin needs root privileges to call vzlist, so add this : @@ -62,7 +62,7 @@ if ( $arg eq "autoconf" ) { # Dynamic config my @result=`/usr/sbin/vzlist -H -a -s veid -o veid,name`; my $A=0; # draw an area only under the first line - + for (@result ) { ( my $veid,my $label ) = /^\s*(\d+)\s*([\w\-]+)\s*$/ ; my $name = $label; diff --git a/plugins/openvz/vpsmem b/plugins/openvz/vpsmem index c9d9d789..dc3aad03 100755 --- a/plugins/openvz/vpsmem +++ b/plugins/openvz/vpsmem @@ -2,7 +2,7 @@ # # Monitors memory usage in openVZ or Virtuozzo # based on http://www.huschi.net/archiv/speicher-anzeigen-mit-vzfree.html -# Author: Michael Richter, http://osor.de/ +# Author: Michael Richter, http://osor.de/ # Cleaned up and translated to english by: Marian Sigler , 2010-08-13 # #%# capabilities=autoconf diff --git a/plugins/openwrt/snmp__memory_openwrt b/plugins/openwrt/snmp__memory_openwrt index af496ec0..2be0186e 100755 --- a/plugins/openwrt/snmp__memory_openwrt +++ b/plugins/openwrt/snmp__memory_openwrt @@ -72,7 +72,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n" unless ($host eq 'localhost'); print <<'EOC'; graph_title Memory usage -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel kB graph_category memory graph_info This graph shows total and used memory on the host. diff --git a/plugins/oracle/oracle-pga-monitor b/plugins/oracle/oracle-pga-monitor index bc723a0f..b7fd0915 100755 --- a/plugins/oracle/oracle-pga-monitor +++ b/plugins/oracle/oracle-pga-monitor @@ -7,18 +7,18 @@ # Version: 1.0 # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# # # Prerequistes: # 1) env.ORACLE_HOME set in munin-node @@ -44,7 +44,7 @@ # dbport : port used by the monitored instance (notice: numeric value) # dbname : database to be monitored # dbhost : host or ip address of db instance -# +# # #%# family=auto #%# capabilities=autoconf @@ -72,7 +72,7 @@ end # # Queries # -pga_target_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024),value),2)) pga_target +pga_target_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024),value),2)) pga_target from V$PGASTAT where name = 'aggregate PGA target parameter'" pga_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024),value),2)) pga @@ -97,7 +97,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title Oracle PGA from #{dbname}" puts "graph_category db" puts "graph_info This graph shows the PGA memory usage (in MB)" diff --git a/plugins/oracle/oracle-sga b/plugins/oracle/oracle-sga index dddbdc04..a00ceeb1 100755 --- a/plugins/oracle/oracle-sga +++ b/plugins/oracle/oracle-sga @@ -7,18 +7,18 @@ # Version: 1.0 # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# # # Prerequistes: # 1) env.ORACLE_HOME set in munin-node @@ -43,7 +43,7 @@ # dbport : port used by the monitored instance (notice: numeric value) # dbname : database to be monitored # dbhost : host or ip address of db instance -# +# # #%# family=auto #%# capabilities=autoconf @@ -71,7 +71,7 @@ end # # Queries # -shared_pool_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, 'shared pool', +shared_pool_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, 'shared pool', decode(name, 'library cache',0, 'dictionary chace',0, 'free memory',0, @@ -96,7 +96,7 @@ large_pool_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, 'large pool', (bytes)/ log_buffer_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, NULL, decode(name, 'log_buffer', (bytes)/(1024*1024),0),0)),2)) sga_lbuffer - from V$SGASTAT" + from V$SGASTAT" memory_components = { "fixed_area" => fixed_area_query, @@ -122,7 +122,7 @@ if ARGV[0] == "autoconf" # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title Oracle SGA from #{dbname}" puts "graph_category db" puts "graph_info This graph shows the SGA memory usage (in MB)" @@ -138,7 +138,7 @@ elsif ARGV[0] == "config" # make sure fixed_area is at the bottom of the stack if ( m == 'fixed_area' ) puts "#{m}.draw AREA" - else + else puts "#{m}.draw STACK" end end diff --git a/plugins/oracle/oracle__connections b/plugins/oracle/oracle__connections index 5119e840..a96b3b23 100755 --- a/plugins/oracle/oracle__connections +++ b/plugins/oracle/oracle__connections @@ -30,7 +30,7 @@ # 'oracle'. Anyway, Munin must be told which user # this plugin should be run as. # dbpass - The corresponding password, if -# applicable. Default to undef. +# applicable. Default to undef. # # showusers - If set to 1 show usernames and num. of connections. # Default is not show users (0). @@ -137,9 +137,9 @@ if ( $showusers ) { $sth->execute(); my $total = 0; while ( my ($datname,$curr_conn) = $sth->fetchrow_array ) { - print "$datname.value $curr_conn\n"; + print "$datname.value $curr_conn\n"; $total = $total+$curr_conn; - } + } print "total.value $total\n"; } else { my $sql = "select count(username) from v\$session where username is not null"; diff --git a/plugins/oracle/oracle_connections b/plugins/oracle/oracle_connections index a05f5d8c..587b34f8 100755 --- a/plugins/oracle/oracle_connections +++ b/plugins/oracle/oracle_connections @@ -25,7 +25,7 @@ # 'oracle'. Anyway, Munin must be told which user # this plugin should be run as. # oracle_pass - The corresponding password, if -# applicable. Default to undef. +# applicable. Default to undef. # # SHOW_ORACLE_USERS - If set to 1 show usernames and num. of connections. # Default is not show users (0). @@ -119,21 +119,21 @@ EOF` set pagesize 0 select username, count(username) from v\$session where username is not null group by username; EOF - echo $WARN_CRIT| awk '{ print "total.warning " $1 "\ntotal.critical " $2 }' + echo $WARN_CRIT| awk '{ print "total.warning " $1 "\ntotal.critical " $2 }' else echo "connections.label active connections" echo "connections.info active connections" echo "connections.type GAUGE" - echo $WARN_CRIT| awk '{ print "connections.warning " $1 "\nconnections.critical " $2 }' + echo $WARN_CRIT| awk '{ print "connections.warning " $1 "\nconnections.critical " $2 }' fi fi -if [ $SHOW_ORACLE_USERS -eq 1 ] +if [ $SHOW_ORACLE_USERS -eq 1 ] then sqlplus -s ${oracle_user}/${oracle_pass}@$ORACLE_SID << EOF | \ grep -v '^$'|awk 'BEGIN { total=0 } { - print $1 ".value " $2 + print $1 ".value " $2 total=total+$2 } END { print "total.value " total }' set pagesize 0 select username, count(username) from v\$session where username is not null group by username; @@ -145,7 +145,7 @@ set pagesize 0 select count(username) from v\$session where username is not null; EOF fi - + echo "max_connections.value" "`sqlplus -s ${oracle_user}/${oracle_pass}@$ORACLE_SID << EOF | \ grep -v '^$' | awk '{print $1 }' set pagesize 0 diff --git a/plugins/oracle/oracle_sysstat b/plugins/oracle/oracle_sysstat index 85ddd1f9..1ea0526a 100755 --- a/plugins/oracle/oracle_sysstat +++ b/plugins/oracle/oracle_sysstat @@ -84,7 +84,7 @@ default: asmusage Module name(s) to exclude seperated by white-space. - By default, asmusage module is excluded because another privilege + By default, asmusage module is excluded because another privilege is necessary to connect ASM instance. env.include_module: @@ -92,21 +92,21 @@ default: none Module name(s) to include seperated by white-space. - If both include_module and exclude_module are set, exclude will be + If both include_module and exclude_module are set, exclude will be ignored. - env.plugin_name: + env.plugin_name: example: env.plugin_name oracle_sysstat_2 default: program name (usually oracle_sysstat) - Used for internal graph name. + Used for internal graph name. It will be useful to monitor multi-instance databases. - env.db_name: + env.db_name: example: env.db_name dbname default: none - Used for graph title. + Used for graph title. It will be useful to monitor multi-instance databases. =head1 NOTES @@ -133,7 +133,7 @@ . "${MUNIN_LIBDIR:-}/plugins/plugin.sh" is_multigraph "$@" -# Like perl 'use strict;' +# Like perl 'use strict;' set -o nounset # Global variables @@ -661,7 +661,7 @@ sqlplus_variables=" set numwidth 30 " -# Functions +# Functions autoconf() { if which sqlplus >/dev/null ; then @@ -688,7 +688,7 @@ fetch() { do_config() { local label_max_length=45 local field type draw label - local fields= + local fields= getfield echo "multigraph ${plugin_name}_${module}" @@ -697,7 +697,7 @@ do_config() { echo "${global_attrs[$module]}" | sed -e 's/^ *//' -e '/^$/d' # print data source attributes - # split line into field,type,draw,label + # split line into field,type,draw,label while read -r field type draw label do [ -z "$field" ] && continue @@ -731,7 +731,7 @@ module_list() { echo "$include_module" else for i in $exclude_module - do + do # remove excluded modules unset -v "global_attrs[$i]" done @@ -768,8 +768,8 @@ getvalue_sysstat() { echo "${sqlplus_variables} VAR vf VARCHAR2(64) VAR vl VARCHAR2(64) - EXEC :vf := '${field}' - EXEC :vl := '${label}' + EXEC :vf := '${field}' + EXEC :vl := '${label}' SELECT :vf || '.value ' || value FROM @@ -789,8 +789,8 @@ getvalue_sgainfo() { echo "${sqlplus_variables} VAR vf VARCHAR2(64) VAR vl VARCHAR2(64) - EXEC :vf := '${field}' - EXEC :vl := '${label}' + EXEC :vf := '${field}' + EXEC :vl := '${label}' SELECT :vf || '.value ' || bytes FROM @@ -810,8 +810,8 @@ getvalue_pgastat() { echo "${sqlplus_variables} VAR vf VARCHAR2(64) VAR vl VARCHAR2(64) - EXEC :vf := '${field}' - EXEC :vl := '${label}' + EXEC :vf := '${field}' + EXEC :vl := '${label}' SELECT :vf || '.value ' || value FROM @@ -831,8 +831,8 @@ getvalue_cputime() { echo "${sqlplus_variables} VAR vf VARCHAR2(64) VAR vl VARCHAR2(64) - EXEC :vf := '${field}' - EXEC :vl := '${label}' + EXEC :vf := '${field}' + EXEC :vl := '${label}' SELECT :vf || '.value ' || ROUND( value / 1000000 ) FROM @@ -853,11 +853,11 @@ FROM WHERE name = 'physical reads cache' ; -SELECT +SELECT 'buf_logical.value ' || ( sd.value + sc.value ) -FROM - v\$sysstat sd, v\$sysstat sc -WHERE +FROM + v\$sysstat sd, v\$sysstat sc +WHERE sd.name = 'db block gets from cache' AND sc.name = 'consistent gets from cache' ; SELECT 'lib_pins.value ' || SUM(pins) FROM v\$librarycache; @@ -892,7 +892,7 @@ ${sqlplus_variables} SELECT REGEXP_REPLACE( du.username, '^[^A-Za-z_]|[^A-Za-z0-9_]', '_' ) || '.value ' || count(vs.username) -FROM +FROM ( SELECT username FROM @@ -952,7 +952,7 @@ FROM ) en LEFT JOIN v\$session se ON - en.wait_class = se.wait_class AND + en.wait_class = se.wait_class AND se.username is not null AND se.wait_time = 0 GROUP BY @@ -1010,12 +1010,12 @@ getfield_eventwait2() { data_attrs[$module]=$( ${sqlplus} "${oracle_auth}" < GPLv2 =head1 MAGIC MARKERS - + #%# family=auto #%# capabilities=autoconf suggest @@ -193,8 +193,8 @@ $graphs{udp4} = { =head2 Config Check This block of code looks at the argument that is possibly supplied, - should it be config, it then checks to make sure the plugin - specified exists, assuming it does, it will run the do_config + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config subroutine for the plugin specified, otherwise it dies complaining about an unknown plugin. @@ -313,7 +313,7 @@ sub print_output { This subroutine prints out the main config information for all of the graphs. It takes one parameters, $plugin - $plugin; graph being called up to print config for + $plugin; graph being called up to print config for Example: print_config($plugin); diff --git a/plugins/other/pid b/plugins/other/pid index 09e2c2b0..f1381a5a 100755 --- a/plugins/other/pid +++ b/plugins/other/pid @@ -8,7 +8,7 @@ GRAPH="pid" SECTION="system" run_autoconf() { - echo yes + echo yes } run_config() { diff --git a/plugins/other/port_ b/plugins/other/port_ index 7249cbac..32f70ab2 100755 --- a/plugins/other/port_ +++ b/plugins/other/port_ @@ -55,7 +55,7 @@ class PortMonit < Munin::Plugin :type => :GAUGE, :min => 0 def retrieve_values - + @_netstat = %x[netstat -n -P tcp | egrep "\.#{PORT} "].split( "\n" ) @@ -73,7 +73,7 @@ class PortMonit < Munin::Plugin @_result = 0 source.each { |obj| @_result += 1 if obj.match( regex ) } - + return @_result end end diff --git a/plugins/other/proc_ b/plugins/other/proc_ index 6577bdb6..473da807 100755 --- a/plugins/other/proc_ +++ b/plugins/other/proc_ @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/perl # -*- perl -*- # # proc_ - Munin plugin to for Process information @@ -7,17 +7,17 @@ # # Author: Kristian Lyngstøl # Author: Trygve Vea -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -39,7 +39,7 @@ The configuration section shows the defaults env.procname init env.category Process Info -env.procname defines the processname as seen inside the parenthesis of the +env.procname defines the processname as seen inside the parenthesis of the second column in /proc/pid/stat. If you don't get the data you expect, you can check if the value is what you expect here. @@ -121,15 +121,15 @@ my @field_parameters = ('graph', 'min', 'max', 'draw', 'cdef', 'warning', 'colour', 'info', 'type'); # Data structure that defines all possible graphs (aspects) and how they # are to be plotted. Every top-level entry is a graph/aspect. Each top-level graph -# MUST have title set and 'values'. -# +# MUST have title set and 'values'. +# # Graphs with 'DEBUG' set to anything is omitted from 'suggest'. -# +# # 'rpn' on values allows easy access to graphs consisting of multiple # values from procstats. (Reverse polish notation). The RPN # implementation only accepts +-*/ and procstats-values. # -# Any value left undefined will be left up to Munin to define/ignore/yell +# Any value left undefined will be left up to Munin to define/ignore/yell # about. # # See munin documentation or rrdgraph/rrdtool for more information. @@ -258,7 +258,7 @@ sub autoconf exit 0; } -# Suggest relevant aspects/values of $self. +# Suggest relevant aspects/values of $self. # 'DEBUG'-graphs are excluded. sub suggest { @@ -292,7 +292,7 @@ sub print_if_exist } # Walk through the relevant aspect and print all top-level configuration -# values and value-definitions. +# values and value-definitions. sub get_config { my $graph = $_[0]; @@ -328,7 +328,7 @@ sub get_config } } -# Read and verify the aspect ($self). +# Read and verify the aspect ($self). sub set_aspect { $self = $0; diff --git a/plugins/other/s9y b/plugins/other/s9y index f66b688e..a3b75b24 100755 --- a/plugins/other/s9y +++ b/plugins/other/s9y @@ -1,8 +1,8 @@ #!/bin/bash -# $Id: s9y 7 2010-02-24 22:15:37Z root $ +# $Id: s9y 7 2010-02-24 22:15:37Z root $ # $Rev: 7 $ # $Author: root $ -# $Date: 2010-02-24 23:15:37 +0100 (Mi, 24. Feb 2010) $ +# $Date: 2010-02-24 23:15:37 +0100 (Mi, 24. Feb 2010) $ # # : < -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and splitted by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_openbsd b/plugins/pf/pf_openbsd index 03160e14..0d7c1680 100755 --- a/plugins/pf/pf_openbsd +++ b/plugins/pf/pf_openbsd @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and splitted by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_packets b/plugins/pf/pf_packets index 7995aee6..117fe0a5 100755 --- a/plugins/pf/pf_packets +++ b/plugins/pf/pf_packets @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and splitted by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_states b/plugins/pf/pf_states index 5e57ea60..762b2c03 100755 --- a/plugins/pf/pf_states +++ b/plugins/pf/pf_states @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and splitted by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/php/eaccelerator b/plugins/php/eaccelerator index 9a9fcbc7..f4f8e111 100755 --- a/plugins/php/eaccelerator +++ b/plugins/php/eaccelerator @@ -18,8 +18,8 @@ # - php file placed on web server , paste there below code (strip hash files first) # # 0,"memoryAvailable"=>0,"memoryAllocated"=>0,"cachedScripts"=>0,"removedScripts"=>0,"cachedKeys"=>0); # if(!function_exists("eaccelerator_info")) # $info = $keys; @@ -35,7 +35,7 @@ # http://localhost/eaccelerator_status.php # if you see the plain text with values then its working ok! # if you see the plain text and all values are zero then probalby eaccelerator is not enabled. -# - installed wget +# - installed wget # ################################################################# # @@ -53,8 +53,8 @@ WGET_FLAGS="-Yoff"; # refer to wget manual, you may set extra parameters like di # Changelog # # Revision 0.1 Tue 03 Feb 2009 02:16:02 PM CET _KaszpiR_ -# - initial release, -# +# - initial release, +# ################################################################# @@ -79,7 +79,7 @@ if [ "$1" = "config" ]; then echo 'graph_args -l 0' echo 'graph_category webserver' echo 'graph_info This graph shows performance of the eaccelerator module on WWW server.' - + echo 'memorysize.label total' echo 'memorysize.draw AREA' echo 'memorysize.min 0' @@ -91,7 +91,7 @@ if [ "$1" = "config" ]; then # echo "memoryallocated.warning 92" # echo "memoryallocated.critical 98" echo 'memoryallocated.info Memory allocated .' - + echo 'memoryavailable.label available' echo 'memoryavailable.min 0' echo 'memoryavailable.info Memory available .' diff --git a/plugins/php/eaccelerator-python b/plugins/php/eaccelerator-python index dc2bad21..098f204f 100755 --- a/plugins/php/eaccelerator-python +++ b/plugins/php/eaccelerator-python @@ -2,7 +2,7 @@ ''' Plugin to monitor performance of eaccelerator module for PHP. -To use: +To use: 1. Copy script to munin plugins folder 2. Symbolically link to eacc_memory and eacc_cached * eacc_memory shows memory usage @@ -33,27 +33,27 @@ config = { 'graph_info This graph shows memory performance of PHP eaccelerator module\n' + 'graphs_args -1 0\n' + 'graph_category webserver\n' + - + 'memorysize.label total\n' + 'memorysize.draw AREA\n' + 'memorysize.min 0\n' + 'memorysize.info Total memory\n' + - + 'memoryallocated.label allocated\n' + 'memoryallocated.draw LINE1\n' + 'memoryallocated.min 0\n' + 'memoryallocated.info Memory allocated', - 'cached': + 'cached': 'graph_title eacceleratory cached scripts\n' + 'graph_info This graph shows how many scripts are cached by PHP eaccelerator module\n' + 'graphs_args -1 0\n' + 'graph_category webserver\n' + - + 'cachedscripts.label cached scripts\n' + 'cachedscripts.draw LINE1\n' + 'cachedscripts.min 0\n' + 'cachedscripts.info Cached scripts\n' + - + 'removedscripts.label removed scripts\n' + 'removedscripts.draw LINE1\n' + 'removedscripts.min 0\n' + @@ -68,16 +68,16 @@ def get_stats(): if 'auth_user' in os.environ and 'auth_pwd' in os.environ: fetcher.add_credentials(os.environ['auth_user'], os.environ['auth_pwd']) resp, content = fetcher.request(os.environ["cpanel"]) - + if resp['status'] != '200': content = '0 0 0 0' - + bits = content.split(' ') return {'memorysize': bits[0], 'memoryallocated': bits[1], 'cachedscripts': bits[2], 'removedscripts': bits[3]} def print_stats(command): stats = get_stats() - + for var in command_vars[command]: print "%s.value %s" % (var, stats[var]) @@ -87,19 +87,19 @@ if __name__ == "__main__": except ImportError: print "httplib2 not found" sys.exit(1) - + if os.environ['cpanel'] == '': print "env.cpanel not defined in munin config" sys.exit() underscore = sys.argv[0].find('_') - + if underscore == -1: print "Symbolically link this file to eacc_memory or eacc_cached" sys.exit(1) else: command = sys.argv[0][underscore+1:] - + if len(sys.argv) > 1 and sys.argv[1] != '': if sys.argv[1] == 'config': print_config(command) diff --git a/plugins/php/eaccelerator-usage b/plugins/php/eaccelerator-usage index f95a19d1..090c5256 100755 --- a/plugins/php/eaccelerator-usage +++ b/plugins/php/eaccelerator-usage @@ -89,7 +89,7 @@ if ($resp->is_success) { print "cached.value $v\n" if $b =~ /cached/i; print "removed.value $v\n" if $b =~ /removed/i; print "keys.value $v\n" if $b =~ /keys/i; - + } $b = $_; } diff --git a/plugins/php/php5-fpm_status b/plugins/php/php5-fpm_status index 73cc92c4..8af29db8 100755 --- a/plugins/php/php5-fpm_status +++ b/plugins/php/php5-fpm_status @@ -3,7 +3,7 @@ # Plugin to monitor php5-fpm process manager, php5-fpm 5.3.3 is required # 20100726 21:15:39 radar AT aol DOT pl # modified by Daniel Caillibaud on 20110926 -# +# # /etc/php5/fpm/php5-fpm.conf: # # pm.status_path = /fpm-status diff --git a/plugins/php/php_apc_ b/plugins/php/php_apc_ index f0824171..82c00177 100755 --- a/plugins/php/php_apc_ +++ b/plugins/php/php_apc_ @@ -2,7 +2,7 @@ ################################################################# # # Script to monitor apc usage -# +# ################################################################# # # Parameters understood: config, autoconf and suggest @@ -10,9 +10,9 @@ ################################################################# # # Configuration section -# +# # Configuration example -# +# # [php_apc_*] # user root # env.URL http://localhost/php_apc.php # URL to fetch APC status @@ -30,7 +30,7 @@ #%# family=auto #%# capabilities=autoconf suggest -# URL to the script to check APC status (defaults to +# URL to the script to check APC status (defaults to # 'http://localhost/php_apc.php' if not configured) URL=${URL:-'http://localhost/php_apc.php'} diff --git a/plugins/php/php_eaccelerator b/plugins/php/php_eaccelerator index 34edc549..7c08adda 100755 --- a/plugins/php/php_eaccelerator +++ b/plugins/php/php_eaccelerator @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -# Monitor your EAccelerator usage. +# Monitor your EAccelerator usage. # Requires: ruby # Mandatory Parameters @@ -35,11 +35,11 @@ if ARGV[0]=="config" print "EAccelerator Monitoring\n" print "graph_title PHP Eaccelerator\n" print "graph_category webserver\n" - print "Memoryusagepercentage.label Memory Usage %\n" - print "Memoryusagepercentage.warning 95\n" - print "Memoryusagepercentage.critical 95\n" - print "Memoryusage.label Memory Usage MB\n" - print "Memorymax.label Cache Size MB\n" + print "Memoryusagepercentage.label Memory Usage %\n" + print "Memoryusagepercentage.warning 95\n" + print "Memoryusagepercentage.critical 95\n" + print "Memoryusage.label Memory Usage MB\n" + print "Memorymax.label Cache Size MB\n" print "Freememory.label Free Memory MB\n" print "Cachedscripts.label Cached Scripts\n" print "Removedscripts.label Removed Scripts\n" diff --git a/plugins/php/php_errors_ b/plugins/php/php_errors_ index dd073a58..fd4f43fe 100755 --- a/plugins/php/php_errors_ +++ b/plugins/php/php_errors_ @@ -30,8 +30,8 @@ Revision 0.1 2011/06/17 12:00:00 Ulrich Lusseau . $MUNIN_LIBDIR/plugins/plugin.sh LOGS=${logfile:-/var/log/apache2/error.log} - - + + if [[ $1 == autoconf ]]; then for LOG in $LOGS; do if [[ ! -r $LOGS ]]; then @@ -43,7 +43,7 @@ if [[ $1 == autoconf ]]; then echo yes exit 0 fi - + if [[ $1 == config ]]; then echo 'graph_title PHP Errors from ' $LOGS echo 'graph_args --base 1000 -l 0' diff --git a/plugins/php/php_opcache.php b/plugins/php/php_opcache.php index a4437a35..b45a0c14 100755 --- a/plugins/php/php_opcache.php +++ b/plugins/php/php_opcache.php @@ -3,7 +3,7 @@ * Part of Munin PHP OPcache plugin - Refer to php_opcache for installation instructions. */ -if (function_exists('opcache_get_status')) +if (function_exists('opcache_get_status')) { $data = opcache_get_status(false); $output = array( diff --git a/plugins/php/php_time_execution b/plugins/php/php_time_execution index bae1252f..431754c6 100755 --- a/plugins/php/php_time_execution +++ b/plugins/php/php_time_execution @@ -31,12 +31,12 @@ #env.sitename mon-code #env.nbrpage 10 # - + LOG=${logfile:-/var/log/apache2/access.log} NAME=${sitename:undefined} NBRPAGE=${nbrpage} - - + + if [ "$1" = "autoconf" ]; then if [ -r "$LOG" ]; then echo yes @@ -46,13 +46,13 @@ if [ "$1" = "autoconf" ]; then exit 1 fi fi - + if [ "$1" = "config" ]; then - + echo 'graph_title Time to generate PHP page ' $NAME 'v2' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Time in microsecond' - + echo "graph_category webserver" echo "graph_info This graph shows load time in ms of $target" echo "minloadtime.label Min time" @@ -61,9 +61,9 @@ if [ "$1" = "config" ]; then echo "avgloadtime.info Avg time" echo "maxloadtime.label Max time" echo "maxloadtime.info Max time" - + exit 0 fi - + awk '($4 ~ /[0-9]+\/[0-9]+/ && $8 !~ /\.(jpg|JPG|jpeg|JPEG|gif|GIF|png|PNG|txt|TXT|css|CSS|js|JS|zip|ZIP|bmp|BMP)$/)' $LOG | sed -e :a -e '$q;N;'$NBRPAGE',$D;ba' | awk '{print $4}' | awk -F\/ ' MIN=="" || $2 < MIN {MIN=$2} MAX=="" || $2 > MAX {MAX=$2} {SUM += $2} END {print "minloadtime.value ",MIN/1000,"\navgloadtime.value ",SUM/(NR*1000),"\nmaxloadtime.value ",MAX/1000}' - + diff --git a/plugins/php/php_xcache b/plugins/php/php_xcache index 148ad864..bfa3cc38 100755 --- a/plugins/php/php_xcache +++ b/plugins/php/php_xcache @@ -7,7 +7,7 @@ require LWP::UserAgent; # # Installation / Configuration # -# - place munin_xcache.php in a directory on your webserver +# - place munin_xcache.php in a directory on your webserver # - add the url config to plugin-conf.d/munin-node # # diff --git a/plugins/ping/fping_ b/plugins/ping/fping_ index 2c7efd4a..f7c66d80 100755 --- a/plugins/ping/fping_ +++ b/plugins/ping/fping_ @@ -4,7 +4,7 @@ # Author : Thomas VIAL # Author URL : http://tvi.al # Usage : ln -s /path/to/fping_ /etc/munin/plugins/fping_www.google.com -# Explanation : Will graph connection to www.google.com +# Explanation : Will graph connection to www.google.com # Requirements : # * fping # @@ -47,5 +47,5 @@ if [ $status -eq 0 ]; then else # Failure echo "success.value 0" - echo "failure.value 100" + echo "failure.value 100" fi diff --git a/plugins/ping/multi_tcp_ping b/plugins/ping/multi_tcp_ping index 1ab779a4..cd07de4e 100755 --- a/plugins/ping/multi_tcp_ping +++ b/plugins/ping/multi_tcp_ping @@ -91,7 +91,7 @@ $cmd_arg = $ARGV[0] || ''; config() if($cmd_arg eq "config"); autoconf() if ($cmd_arg eq 'autoconf'); -for my $host (@hosts) { +for my $host (@hosts) { threads->new(\&ping_host, $host) } diff --git a/plugins/ping/multiping b/plugins/ping/multiping index 1fb56458..a6f9bfa3 100755 --- a/plugins/ping/multiping +++ b/plugins/ping/multiping @@ -82,7 +82,7 @@ if ((exists $ARGV[0]) && ($ARGV[0] eq "config")) { print "graph_category network\n"; print "graph_info This graph shows ping RTT statistics.\n"; for (my $site=1; $site<=$#hosts+1; $site++) { - my $item = lc($hosts[$site-1]); + my $item = lc($hosts[$site-1]); $item =~ s/\.//g; print "$item.label $names[$site-1]\n"; print "$item.info Ping RTT statistics for $hosts[$site-1].\n"; @@ -94,7 +94,7 @@ if ((exists $ARGV[0]) && ($ARGV[0] eq "config")) { } for (my $site=1; $site<=$#hosts+1; $site++) { - my $item = lc($hosts[$site-1]); + my $item = lc($hosts[$site-1]); $item =~ s/\.//g; my $host = $hosts[$site-1]; my @ping = `$ping_cmd $ping_args $host $ping_args2`; diff --git a/plugins/ping/ping-with-ceil b/plugins/ping/ping-with-ceil index 5735e10a..d9f10c05 100755 --- a/plugins/ping/ping-with-ceil +++ b/plugins/ping/ping-with-ceil @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Python (2.5+) version of the plugin to monitor ping times. +# Python (2.5+) version of the plugin to monitor ping times. # Evolution from the standard shipped with munin by adding a ceil. # # Thanks to "Jimmy Olsen" for the base. @@ -82,5 +82,5 @@ for line in out.split("\n"): print "ping.value", "%.6f" % (v / 1000) EOF -) | python +) | python diff --git a/plugins/poseidon/snmp__poseidon-sensors b/plugins/poseidon/snmp__poseidon-sensors index 0fe28a8d..aea79be5 100755 --- a/plugins/poseidon/snmp__poseidon-sensors +++ b/plugins/poseidon/snmp__poseidon-sensors @@ -29,7 +29,7 @@ E_OK="0" # everything went allright E_UNKNOWN="1" # "catch all" for otherwise unhandled errors -E_ARG="81" # invalid argument +E_ARG="81" # invalid argument E_USAGE="82" # wrong program name or arguments E_SNMPGET="83" # error while executing the 'snmpget' utility @@ -57,12 +57,12 @@ SENS_VALUE_OID=".1.3.6.1.4.1.21796.3.3.3.1.6." # integer (decimal * 10) SENS_ID_OID=".1.3.6.1.4.1.21796.3.3.99.1.2.1.4." # unique sensor ID (integer) #+ representation of the #+ temperature (integer) -SENS_UNIT_OID=".1.3.6.1.4.1.21796.3.3.3.1.9." # 0=°C,1=°F,2=°K,3=%,4=V,5=mA, +SENS_UNIT_OID=".1.3.6.1.4.1.21796.3.3.3.1.9." # 0=°C,1=°F,2=°K,3=%,4=V,5=mA, RTS_OUTPUT_OID=".1.3.6.1.4.1.21796.3.3.2.1.2." # binary input state (integer) #+ 6=unknown, 7=pulse, 8=switch # define some Poseidon specific stuff: -STATE_OK="1" +STATE_OK="1" STATE_WARN="2" STATE_CRIT="3" UNITS=("C" "F" "K" "%" "V" "mA" "unknown" "pulse" "switch") @@ -100,13 +100,13 @@ EOT cat <<- EOT multigraph $sensorType graph_title $sensorLocation - $sensorType [$sensorUnit] -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel $sensorUnit graph_category sensors graph_info This graph shows $sensorType history. EOT fi - _firstSensor="0" + _firstSensor="0" cat <<- EOT $sensorType$_sensorNr.label $sensorName $sensorType$_sensorNr.info This graph shows $sensorType$_sensorNr history. @@ -158,7 +158,7 @@ EOT # getSensorData(sensorNr) # fetch state, value, name unit and sensor type for the sensor -#+ with the number "sensorNr" +#+ with the number "sensorNr" getSensorData() { _sensorNr=$1 sensorState=`snmpGet $hostAddr $SENS_STATE_OID$_sensorNr || err \ @@ -172,7 +172,7 @@ getSensorData() { } # getSystemInfo() -# fetch general information about the system +# fetch general information about the system getSystemInfo() { sensorLocation="`snmpGet $hostAddr $SYS_LOC_OID`" } @@ -185,22 +185,22 @@ snmpGet () { _host="$1" _exit="0" - # fetch the requested OID + # fetch the requested OID _longValue="`snmpget -O v\ -m $MIBS\ -v $SNMPVERSION\ -c $SNMPCOMMUNITY\ $_host $_oid 2>/dev/null`" - + _exitStatus="$?" echo ${_longValue#*:} # remove the type from the answer return $_exitStatus -} +} # get unit (string) -#+ find out the unit of the output of a given sensor. possible units are: -#+ "C" "F" "K" "%" "V" "mA" "unknown" "pulse" "switch" +#+ find out the unit of the output of a given sensor. possible units are: +#+ "C" "F" "K" "%" "V" "mA" "unknown" "pulse" "switch" getSensorUnitString () { _sensorNr=$1 _sensorUnit=`snmpGet $hostAddr $SENS_UNIT_OID$_sensorNr || err \ @@ -209,7 +209,7 @@ getSensorUnitString () { } # get type (string) -#+ find out what type of sensor we are dealing with. possible types are: +#+ find out what type of sensor we are dealing with. possible types are: #+ "Temp" "Hum" "Volt" "Curr" "Unkn" "Pulse" "Switch" getSensorType () { _sensorNr=$1 @@ -223,19 +223,19 @@ getSensorType () { #+ in the array for the respective unit getAvailableSensorsByType () { _thisSensorNr="1" - + # initial fetch _snmpget=`snmpGet $hostAddr $SENS_UNIT_OID$_thisSensorNr` _nextSensorExits=$? _unit=`echo "$_snmpget" | tr -d " "` - # add next sensor if it exists - while [ true ]; do + # add next sensor if it exists + while [ true ]; do # add sensors of the same type to a list - sensorsOfType[$_unit]="${sensorsOfType[$_unit]} $_thisSensorNr" - + sensorsOfType[$_unit]="${sensorsOfType[$_unit]} $_thisSensorNr" + # fetch next sensor - _thisSensorNr=$(($_thisSensorNr+1)) + _thisSensorNr=$(($_thisSensorNr+1)) _snmpget=`snmpGet $hostAddr $SENS_UNIT_OID$_thisSensorNr` _nextSensorExits=$? @@ -243,7 +243,7 @@ getAvailableSensorsByType () { if [ $_nextSensorExits -ne 0 ]; then break fi - + _unit=`echo "$_snmpget" |cut -d" " -f 4` done } @@ -264,7 +264,7 @@ sanitize () { } # usage () -# print usage +# print usage usage () { echo "usage: snmp__poseidon-sensors [config|snmpconf]" 1>&2 exit $E_USAGE @@ -279,7 +279,7 @@ err () { else _errorMsg="Fatal: An unknown error occurred! Exiting..." _exitCode="$E_UNKNOWN" - + fi # print error message to STDERR ... @@ -291,13 +291,13 @@ err () { #==============================================================================# # SNMP Config -MIBS=":" # we don't use any configured MIBs so we don't +MIBS=":" # we don't use any configured MIBs so we don't #+ have to deal with errors in the MIBs -if [ -z $SNMPVERSION ]; then +if [ -z $SNMPVERSION ]; then SNMPVERSION="1" # as of firmware 3.1.5 only SNMPv1 is supported fi -if [ -z $SNMPCOMMUNITY ]; then +if [ -z $SNMPCOMMUNITY ]; then SNMPCOMMUNITY="public" # SNMP community string to read from the device fi @@ -326,11 +326,11 @@ hostAddr="`basename "$0" | cut -d "_" -f 2`" if [ "$myName" = "snmp_poseidon-sensors" ]; then hostAddr=`sanitize $hostAddr || err \ "Fatal: Invalid argument \"$hostAddr\"! Exiting..." $E_ARG` - if [ -z "$hostAddr" ]; then + if [ -z "$hostAddr" ]; then usage munin fi getAvailableSensorsByType - if [ "$1" = "config" ]; then + if [ "$1" = "config" ]; then printMuninConfig exit $E_OK elif [ "$1" = "snmpconfig" ]; then diff --git a/plugins/postfix/greyfix b/plugins/postfix/greyfix index 87d0d4fd..75fd84c3 100755 --- a/plugins/postfix/greyfix +++ b/plugins/postfix/greyfix @@ -21,7 +21,7 @@ $ ln -s /path/to/plugin/greyfix /etc/munin/plugins/greyfix Configuration ============= -There are some settings that can be tweaked by adding statements to the +There are some settings that can be tweaked by adding statements to the munin-node config: [greyfix] @@ -36,10 +36,10 @@ env.num_steps 47 # graph the greylisted triplets separate from the whitelisted ones (default: yes) env.greylist_step no -Please note that the last step has no end date, so it includes all triplets -older than the second last step. I.e., the defaults (as named above) create a -graph that shows 10 steps of one week each, and one last step for everything -older than 10 weeks. Also, the separate greylist step is not considered +Please note that the last step has no end date, so it includes all triplets +older than the second last step. I.e., the defaults (as named above) create a +graph that shows 10 steps of one week each, and one last step for everything +older than 10 weeks. Also, the separate greylist step is not considered when applying num_steps. """ @@ -72,7 +72,7 @@ def greyfix_parse_triplets(): if greyfix.returncode > 0: print '# greyfix exited with exit code %i' % (greyfix.returncode) sys.exit(greyfix.returncode) - + triplets = [] for line in stdout.split("\n"): triplet = line.split("\t") diff --git a/plugins/postfix/policyd-spf-python b/plugins/postfix/policyd-spf-python index 93c53b5e..3c169b40 100755 --- a/plugins/postfix/policyd-spf-python +++ b/plugins/postfix/policyd-spf-python @@ -1,14 +1,14 @@ #! /bin/bash # # Munin plugin to monitor postfix-policyd-spf-python results -# Contributed by Alexander Koch +# Contributed by Alexander Koch # # This plugin is published under the terms of the MIT License. -# +# # Parameters understood: # config (required) # autoconf (optional - used by munin-config) -# +# # Config variables: # logfile - Where to find the postfix log (mail.log) # diff --git a/plugins/postfix/postfix-queue-size b/plugins/postfix/postfix-queue-size index f471b260..5f9db598 100755 --- a/plugins/postfix/postfix-queue-size +++ b/plugins/postfix/postfix-queue-size @@ -91,7 +91,7 @@ SPOOLDIR=${spooldir:-$POSTCONFSPOOL} case $1 in autoconf|detect) - + if [ -d $SPOOLDIR ] ; then echo yes exit 0 diff --git a/plugins/postfix/postfix_filtered b/plugins/postfix/postfix_filtered index 8cb70370..c9eda5f4 100755 --- a/plugins/postfix/postfix_filtered +++ b/plugins/postfix/postfix_filtered @@ -17,9 +17,9 @@ # env.policy my policy string # # When env.policy is set, this plugin will match the string you supply as env.policy and return the number of instances -# of that string as an output called "policy.value". +# of that string as an output called "policy.value". # -# If you are NOt using a postfix policy daemon, as above, use the line +# If you are NOt using a postfix policy daemon, as above, use the line # # [postfix*] # env.policy none @@ -29,7 +29,7 @@ -POLICY=${policy} +POLICY=${policy} if [ "$POLICY" = "none" ] then POLICY="" @@ -67,11 +67,11 @@ if [ "$1" = "config" ]; then echo 'allowed.label Allowed' echo 'allowed.min 0' echo 'allowed.type ABSOLUTE' - + echo 'rbl.label RBL blocked' echo 'rbl.min 0' echo 'rbl.type ABSOLUTE' - + if [ -z "$POLICY" ] then echo "empty" > /dev/null @@ -84,15 +84,15 @@ if [ "$1" = "config" ]; then echo 'helo.label HELO rejected' echo 'helo.min 0' echo 'helo.type ABSOLUTE' - + echo 'client.label Client rejected' echo 'client.min 0' echo 'client.type ABSOLUTE' - + echo 'sender.label Sender rejected' echo 'sender.min 0' echo 'sender.type ABSOLUTE' - + echo 'recipient.label Recipient unknown' echo 'recipient.min 0' echo 'recipient.type ABSOLUTE' diff --git a/plugins/postfix/postfix_filtered_awk b/plugins/postfix/postfix_filtered_awk index fde5dbc0..b054c111 100755 --- a/plugins/postfix/postfix_filtered_awk +++ b/plugins/postfix/postfix_filtered_awk @@ -17,9 +17,9 @@ # env.policy my policy string # # When env.policy is set, this plugin will match the string you supply as env.policy and return the number of instances -# of that string as an output called "policy.value". +# of that string as an output called "policy.value". # -# If you are NOT using a postfix policy daemon, as above, use the line +# If you are NOT using a postfix policy daemon, as above, use the line # # [postfix_filtered] # env.policy none @@ -58,7 +58,7 @@ if [ "$1" = "config" ]; then if [ -z "$POLICY" ] then echo 'graph_order rbl helo client sender recipient relay allowed' - + else echo 'graph_order rbl policy helo client sender recipient relay allowed' echo 'policy.label policy blocked' @@ -98,11 +98,11 @@ fi $LOGTAIL ${MAIL_LOG} $STATEFILE | \ -awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; ne= 0; nf= 0; ng= 0; nh= 0 ; st= ENVIRON["POLICY"] } +awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; ne= 0; nf= 0; ng= 0; nh= 0 ; st= ENVIRON["POLICY"] } - { - if (index($0, "queued as")) { na++ } - else if (index($0, "Relay access denied")) { nb++ } + { + if (index($0, "queued as")) { na++ } + else if (index($0, "Relay access denied")) { nb++ } else if (index($0, "blocked using")) { nc++ } else if (index($0, "Helo command rejected")) { nd++ } else if (index($0, "Client host rejected")) { ne++ } diff --git a/plugins/postfix/postfix_mailfiltered b/plugins/postfix/postfix_mailfiltered index f96687df..8689bdd7 100755 --- a/plugins/postfix/postfix_mailfiltered +++ b/plugins/postfix/postfix_mailfiltered @@ -10,8 +10,8 @@ mktempfile () { -mktemp -t -} + mktemp -t +} MAIL_LOG=${logfile:-/var/log/mail.log} LOGTAIL=${logtail:-`which logtail`} diff --git a/plugins/postfix/postfix_mailqueuelog_ b/plugins/postfix/postfix_mailqueuelog_ index 4ccc8f6a..a4be671a 100755 --- a/plugins/postfix/postfix_mailqueuelog_ +++ b/plugins/postfix/postfix_mailqueuelog_ @@ -171,7 +171,7 @@ foreach my $i (@status_list) #if(-l $statefile) { # die("$statefile is a symbolic link, refusing to touch it."); -#} +#} #open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n"; #print OUT "sum:$sum\n"; #foreach my $i (@status_list) @@ -180,8 +180,8 @@ foreach my $i (@status_list) #} #close OUT; -sub parseLogfile -{ +sub parseLogfile +{ my ($fname) = @_; # the search parts diff --git a/plugins/postfix/postfix_mailstats_ b/plugins/postfix/postfix_mailstats_ index b3a7f489..24d0029e 100755 --- a/plugins/postfix/postfix_mailstats_ +++ b/plugins/postfix/postfix_mailstats_ @@ -169,7 +169,7 @@ foreach my $i (sort keys %{$rejects}) if (-l $statefile) { die ("$statefile is a symbolic link, refusing to touch it."); -} +} open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n"; print OUT "$pos:$delivered\n"; foreach my $i (sort keys %{$rejects}) @@ -178,31 +178,31 @@ foreach my $i (sort keys %{$rejects}) } close OUT; -sub parseLogfile -{ +sub parseLogfile +{ my ($fname, $start, $stop) = @_; open (LOGFILE, $fname) or die "Unable to open logfile $fname for reading: $!\n"; seek (LOGFILE, $start, 0) or die "Unable to seek to $start in $fname: $!\n"; - while (tell (LOGFILE) < $stop) + while (tell (LOGFILE) < $stop) { my $line = ; chomp ($line); if ($line =~ /$postfix\/qmgr.*from=.*size=[0-9]*/ || - $line =~ /$postfix\/smtp.* status=sent /) + $line =~ /$postfix\/smtp.* status=sent /) { $delivered++; - } + } elsif ($line =~ /$postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ || $line =~ /$postfix\/cleanup.* reject: (\S+)/) { $rejects->{$1}++; } } - close(LOGFILE) or warn "Error closing $fname: $!\n"; + close(LOGFILE) or warn "Error closing $fname: $!\n"; } # vim:syntax=perl diff --git a/plugins/postfix/postfix_mailvolume_multi b/plugins/postfix/postfix_mailvolume_multi index 4e7401e5..5ef5c201 100755 --- a/plugins/postfix/postfix_mailvolume_multi +++ b/plugins/postfix/postfix_mailvolume_multi @@ -145,7 +145,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") print "no (logfile '$logfile' not found)\n"; } } - else + else { print "no (postfix not found)\n"; } @@ -193,7 +193,7 @@ if (!$pos) # No state file present. Avoid startup spike: Do not read log # file up to now, but remember how large it is now, and next # time read from there. - + $pos = (stat $logfile)[7]; # File size foreach $syslog_name (@postfix_syslog_name) { diff --git a/plugins/postfix/postfix_stats b/plugins/postfix/postfix_stats index 89ac0fdb..d3f51fbc 100755 --- a/plugins/postfix/postfix_stats +++ b/plugins/postfix/postfix_stats @@ -21,11 +21,11 @@ There is no default configuration. This is an example config for Ubuntu: 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 may +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 it was manually -downloaded and installed, or "pflogsumm" if it was installed by a package +downloaded and installed, or "pflogsumm" if it was installed by a package manager (like apt-get). Use the last part of the symlink name for filtering by hostname from logfile, @@ -136,7 +136,7 @@ else fi # Parse value from Raw result -# +# # Return digit if regex are parsed correctly # # Return U (undefined) if any error occurs diff --git a/plugins/postfix/postgrey b/plugins/postfix/postgrey index e5c9ebc6..0fc3bbef 100755 --- a/plugins/postfix/postgrey +++ b/plugins/postfix/postgrey @@ -10,8 +10,8 @@ mktempfile () { -mktemp -t -} + mktemp -t +} MAIL_LOG=${logfile:-/var/log/mail.log} STATEFILE=$MUNIN_PLUGSTATE/postgrey.offset @@ -69,7 +69,7 @@ then fi delayed=`grep 'Recipient address rejected.*Greylisted' ${TEMP_FILE} | wc -l` - passed=`grep 'postgrey\[[0-9]*\]: delayed [0-9]* seconds:' ${TEMP_FILE} | wc -l` + passed=`grep 'postgrey\[[0-9]*\]: delayed [0-9]* seconds:' ${TEMP_FILE} | wc -l` whitelisted=`grep 'postgrey\[[0-9]*\]: whitelisted:' ${TEMP_FILE} | wc -l` /bin/rm -f $TEMP_FILE diff --git a/plugins/postfix/postgrey-new b/plugins/postfix/postgrey-new index 3a8944a1..a56e0f12 100755 --- a/plugins/postfix/postgrey-new +++ b/plugins/postfix/postgrey-new @@ -62,7 +62,7 @@ if(defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_category mail\n"; print "graph_vlabel Count\n"; print "graph_scale no\n"; - + print "greylisted.label greylisted_reason_new\n"; print "greylisted.type GAUGE\n"; print "greylisted.draw AREA\n"; @@ -93,7 +93,7 @@ if( -f $statefile) { chomp($retry_old); chomp($passes_white_old); close STATE; -} +} @@ -122,7 +122,7 @@ while(my $line = ) $passes_white++; } } - + } } diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 7e429671..69dacafd 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -31,7 +31,7 @@ my $plugin_title = ($db_pool) ? $plugin_suffix." ".$pool_name : $pool_name; if (!$pool_name) { print "Cannot get pool name\n"; - exit 1; + exit 1; } # command line arguments for autconf and config @@ -164,7 +164,7 @@ if (defined($ARGV[0])) } # connect to data -my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass) +my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass) or die ("Cannot connect to database"); # go trough each set and get the data foreach my $get ('pools', 'stats') @@ -256,7 +256,7 @@ another example, where different pgbouncers (and so munin plugins) connecting to env.pgbouncer_port 6542 env.pgbouncer_host localhost env.pgbouncer_pool dbname - + [pgbouncer_webmain] env.pgbouncer_pass barfoo env.pgbouncer_user bar @@ -276,7 +276,7 @@ This graph will show the average bytes sent and received by the pgbouncer for th =head2 Average connections -This graph will show the average amount of connections to the pgbouncer for this pool +This graph will show the average amount of connections to the pgbouncer for this pool =head2 Average query time diff --git a/plugins/postgresql/postgres_block_read_ b/plugins/postgresql/postgres_block_read_ index 62a6f4b5..2ea8da8a 100755 --- a/plugins/postgresql/postgres_block_read_ +++ b/plugins/postgresql/postgres_block_read_ @@ -28,8 +28,8 @@ # This should, however, be given through autoconf and suggest. # # If required, give username, password and/or Postgresql server -# host through environment variables. -# +# host through environment variables. +# # You must also activate Postgresql statistics. See # http://www.postgresql.org/docs/7.4/interactive/monitoring-stats.html # for how to enable this. Specifically, the following lines must @@ -46,11 +46,11 @@ # Config variables: # # dbhost - Which database server to use. Defaults to -# 'localhost'. +# 'localhost'. # dbuser - A Postgresql user account with read permission to # the given database. Defaults to # 'postgres'. Anyway, Munin must be told which user -# this plugin should be run as. +# this plugin should be run as. # dbpass - The corresponding password, if # applicable. Default to undef. Remember that # pg_hba.conf must be configured accordingly. @@ -128,7 +128,7 @@ if ($configure) { graph_title Postgres data reads from $dbname graph_args --base 1000 graph_vlabel Blocks read per \${graph_period} -graph_category db +graph_category db graph_info Shows number of blocks read from disk and from memory from_disk.label Read from disk from_disk.info Read from disk diff --git a/plugins/postgresql/postgres_queries2_ b/plugins/postgresql/postgres_queries2_ index 5c8185f2..dab14abb 100755 --- a/plugins/postgresql/postgres_queries2_ +++ b/plugins/postgresql/postgres_queries2_ @@ -14,7 +14,7 @@ cat << EOF graph_title Postgres queries2 on $db graph_args --base 1000 graph_vlabel Queries per \${graph_period} -graph_category db +graph_category db graph_info Shows number of select, insert, update and delete queries sel_seq.label s_selects sel_seq.info Sequential selects on all tables @@ -47,7 +47,7 @@ deletes.min 0 EOF else psql -At $db << EOF -select +select 'sel_seq.value ' || SUM(seq_scan) || E'\n' || 'sel_seq_rows.value ' || SUM(seq_tup_read) || E'\n' || 'sel_idx.value ' || SUM(idx_scan) || E'\n' || @@ -57,7 +57,7 @@ select 'deletes.value ' || SUM(n_tup_del) from pg_stat_all_tables; EOF - + # my $sql = "SELECT SUM(seq_scan),SUM(seq_tup_read), "; # $sql .= "SUM(idx_scan),SUM(idx_tup_fetch), "; # $sql .= "SUM(n_tup_ins),SUM(n_tup_upd),SUM(n_tup_del) "; diff --git a/plugins/postgresql/postgres_queries3_ b/plugins/postgresql/postgres_queries3_ index 6d4b873e..c3b18169 100755 --- a/plugins/postgresql/postgres_queries3_ +++ b/plugins/postgresql/postgres_queries3_ @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# postgres_queries3: see stats on number of rows +# postgres_queries3: see stats on number of rows # read, inserted, updated and deleted on a per table basis # # Author: @@ -27,10 +27,10 @@ # On debian systems install libipc-run3-perl # # Log info: -# 20140701 - Initial -# 20140924 - +# 20140701 - Initial +# 20140924 - # -ignore internal pg tables -# -missing stuff in config +# -missing stuff in config use strict; @@ -40,7 +40,7 @@ use IPC::Run3 qw( run3 ); my %values; my $query = \<new( vlabel => 'Size', paramdatabase => 1, basequery => "SELECT CASE WHEN relkind = 'r' OR relkind = 't' THEN 'db_detail_data' WHEN relkind = 'i' THEN 'db_detail_index' WHEN relkind = 'v' THEN 'db_detail_view' WHEN relkind = 'S' THEN 'db_detail_sequence' ELSE 'db_detail_other' END AS state, - SUM(relpages::bigint * 8 * 1024) AS size + SUM(relpages::bigint * 8 * 1024) AS size FROM pg_class pg, pg_namespace pgn WHERE pg.relnamespace = pgn.oid AND pgn.nspname NOT IN ('information_schema', 'pg_catalog') GROUP BY state", configquery => [ "VALUES ('db_detail_data','Data size'),('db_detail_index','Index size'),('db_detail_sequence','Sequence size'),('db_detail_view','View size'),('db_detail_other','Other size')", diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index ed387631..1b6d88da 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -105,17 +105,17 @@ _EOM my $metadatabase_pages = 0; my $metadatabase_indexes = 0; my @names = $dbh->tables; - + # Find relfilenode and relpages from the given table my $q_ind = "SELECT relkind, relfilenode, relpages FROM pg_class WHERE relname = ? UNION SELECT relkind, relfilenode, relpages FROM pg_class - WHERE relfilenode IN (SELECT indexrelid FROM pg_index + WHERE relfilenode IN (SELECT indexrelid FROM pg_index WHERE indrelid IN (SELECT relfilenode FROM pg_class WHERE relname = ?))"; my $sth = $dbh->prepare ($q_ind) or die $dbh->errstr; - + # Iterate over the tables in the database foreach my $table (@names) { my $meta = 1; @@ -127,7 +127,7 @@ _EOM # "public" tables are the user data $meta = 0 if $table =~ /^public\./; $table =~ s/^.*\.//; - + # Call the query with $table twice for each side of the UNION $sth->execute ($table, $table) or die $dbh->errstr; while (my ($relkind, $relfilenode, $relpages) = $sth->fetchrow_array) { @@ -139,8 +139,8 @@ _EOM $metatable_indexes += $relpages if $meta == 1; } # Define the query - my $q2 = "SELECT SUM(relpages) - FROM pg_class + my $q2 = "SELECT SUM(relpages) + FROM pg_class WHERE relname IN (?, ?)"; my $sth2 = $dbh->prepare ($q2); $sth2->execute ("pg_toast_${relfilenode}", @@ -159,7 +159,7 @@ _EOM $database_indexes += $table_indexes; $metadatabase_pages += $metatable_pages; $metadatabase_indexes += $metatable_indexes; - } + } $sth->finish; $dbh->disconnect; print "size\.value " . $database_pages * 8192 . "\n"; diff --git a/plugins/postgresql/slony_lag_events_ b/plugins/postgresql/slony_lag_events_ index 13d88f83..f85d6272 100755 --- a/plugins/postgresql/slony_lag_events_ +++ b/plugins/postgresql/slony_lag_events_ @@ -14,7 +14,7 @@ # st_last_received_event_ts: the timestamp on the sl_event in that pair # st_lag_num_events: difference between st_last_event and st_last_received # st_lag_time: difference between st_last_event_ts and st_last_received_ts -# +# # # Configuration variables: # @@ -54,7 +54,7 @@ if [ "$1" = "config" ]; then echo "graph_title Slony lag events for ${PGDATABASE}" echo 'graph_vlabel event' - psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host + psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host do test -z "${node_id}" && continue echo "${node_id}.label ${host}" @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then fi psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT st_received, st_lag_num_events FROM ${PGSCHEMA}.sl_status ORDER BY 1;" | while read node_id sep event -do +do test -z "${node_id}" && continue echo "${node_id}.value ${event}" done diff --git a/plugins/postgresql/slony_lag_time b/plugins/postgresql/slony_lag_time index 3ba34422..ddbcd99f 100755 --- a/plugins/postgresql/slony_lag_time +++ b/plugins/postgresql/slony_lag_time @@ -1,6 +1,6 @@ #!/bin/bash # -# plugin to monitor difference between st_last_event_ts and st_last_received_ts +# plugin to monitor difference between st_last_event_ts and st_last_received_ts # in sl_status table (based on slony_ and slony_lag_) # # http://blog.endpoint.com/2009/07/slony-slstatus-and-diagnosing.html @@ -14,7 +14,7 @@ # st_last_received_event_ts: the timestamp on the sl_event in that pair # st_lag_num_events: difference between st_last_event and st_last_received # st_lag_time: difference between st_last_event_ts and st_last_received_ts -# +# # # Configuration variables: # @@ -54,7 +54,7 @@ if [ "$1" = "config" ]; then echo "graph_title Slony lag time for ${PGDATABASE}" echo "graph_vlabel \${graph_period}" - psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host + psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host do test -z "${node_id}" && continue echo "${node_id}.label ${host}" @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then fi psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT st_received, extract(epoch FROM st_lag_time)::integer FROM ${PGSCHEMA}.sl_status ORDER BY 1;" | while read node_id sep time -do +do test -z "${node_id}" && continue echo "${node_id}.value ${time}" done diff --git a/plugins/postgresql/slony_lag_time_ b/plugins/postgresql/slony_lag_time_ index c671f657..0ab90119 100755 --- a/plugins/postgresql/slony_lag_time_ +++ b/plugins/postgresql/slony_lag_time_ @@ -1,6 +1,6 @@ #!/bin/bash # -# plugin to monitor difference between st_last_event_ts and st_last_received_ts +# plugin to monitor difference between st_last_event_ts and st_last_received_ts # in sl_status table (based on slony_ and slony_lag_) # # http://blog.endpoint.com/2009/07/slony-slstatus-and-diagnosing.html @@ -14,7 +14,7 @@ # st_last_received_event_ts: the timestamp on the sl_event in that pair # st_lag_num_events: difference between st_last_event and st_last_received # st_lag_time: difference between st_last_event_ts and st_last_received_ts -# +# # # Configuration variables: # @@ -54,7 +54,7 @@ if [ "$1" = "config" ]; then echo "graph_title Slony lag time for ${PGDATABASE}" echo "graph_vlabel \${graph_period}" - psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host + psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT no_id,regexp_replace(pa_conninfo, '.*host=(.*?) .*$', '\\1') FROM ${PGSCHEMA}.sl_node JOIN ${PGSCHEMA}.sl_path ON (pa_server=no_id) WHERE pa_client=${PGSCHEMA}.getlocalnodeid('${PGSCHEMA}'::name);" | while read node_id sep host do test -z "${node_id}" && continue echo "${node_id}.label ${host}" @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then fi psql -h ${PGHOST} -d ${PGDATABASE} -U ${PGUSER} -tc "SELECT st_received, extract(epoch FROM st_lag_time)::integer FROM ${PGSCHEMA}.sl_status ORDER BY 1;" | while read node_id sep time -do +do test -z "${node_id}" && continue echo "${node_id}.value ${time}" done diff --git a/plugins/power/apcupsd_pct b/plugins/power/apcupsd_pct index dec45cfb..2e4452aa 100755 --- a/plugins/power/apcupsd_pct +++ b/plugins/power/apcupsd_pct @@ -208,14 +208,14 @@ munin plugin to monitor APC UPS via apcupsd by apcaccess. =head1 INSTALLATION cp apcupsd_pct $MUNIN_LIBDIR/plugsin/ - + cd YOUR_MUNIN_PLUGINS_DIR (make symbolic links different name) ln -s $MUNIN_LIBDIR/plugsin/apcupsd_pct apcupsd_pct ln -s $MUNIN_LIBDIR/plugsin/apcupsd_pct apcupsd_volt ln -s $MUNIN_LIBDIR/plugsin/apcupsd_pct apcupsd_time ln -s $MUNIN_LIBDIR/plugsin/apcupsd_pwr apcupsd_pwr - + restart munin-node =head1 REPOSITORY diff --git a/plugins/power/apcupsd_ww b/plugins/power/apcupsd_ww index b50067b3..4a657f28 100755 --- a/plugins/power/apcupsd_ww +++ b/plugins/power/apcupsd_ww @@ -1,15 +1,15 @@ #!/usr/bin/perl -w -# +# # Plugin to monitor apcupsd via apcaccess # # Version 1.3 -# +# # Copyright (C) 2005-2008 Behan Webster # Licenced under GPL 2.0 # # Written by: Behan Webster # German translation by: Bianco Veigel -# +# #%# family=auto #%# capabilities=autoconf @@ -28,7 +28,7 @@ my $language = $ENV{LANG} || 'en'; # # UPSNAME : Elfhild # MODEL : SMART-UPS 1400 RM XL -# STATUS : ONLINE +# STATUS : ONLINE # LINEV : 123.5 Volts # LOADPCT : 24.9 Percent Load Capacity # BCHARGE : 100.0 Percent diff --git a/plugins/power/currentcost b/plugins/power/currentcost index 956acfc8..41437c51 100755 --- a/plugins/power/currentcost +++ b/plugins/power/currentcost @@ -314,8 +314,8 @@ As per L 00014 days since birth
    14 the time - 07 - 07 + 07 + 07 CC02 name of this device @@ -336,71 +336,71 @@ As per L 000.0 total Kwh used in 2 hour blocks - 000.1 - 000.1 - 000.0 - 000.0 - 000.0 - 000.0 - 000.1 - 000.1 - 000.1 - 000.1 - 000.0 - 000.0 + 000.1 + 000.1 + 000.0 + 000.0 + 000.0 + 000.0 + 000.1 + 000.1 + 000.1 + 000.1 + 000.0 + 000.0 0000 total Kwh used per day(s) - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 0000 total Kwh used per month(s) - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 + 0000 0000000 total Kwh used per year(s) - 0000000 - 0000000 - 0000000 + 0000000 + 0000000 + 0000000 @@ -427,7 +427,7 @@ For full definition, see L 00000 end of message - + =cut sub collect_cc128_data { diff --git a/plugins/power/nut b/plugins/power/nut index 0bd56048..8c3f158d 100755 --- a/plugins/power/nut +++ b/plugins/power/nut @@ -41,7 +41,7 @@ load=$(upsc $UPS | grep ups.load: | cut -d" " -f2) charge=$(upsc $UPS | grep battery.charge: | cut -d" " -f2) } -# Munin routines +# Munin routines case "$1" in autoconf) grep ^MONITOR < /etc/nut/upsmon.conf &> /dev/null @@ -78,8 +78,8 @@ EOM grep ^MONITOR < /etc/nut/upsmon.conf | cut -d" " -f2 | sed 's|@|_AT_|g' exit 0 ;; - *) - + *) + FETCH_DATA # Print data for Munin cat << EOM diff --git a/plugins/power/snmp__ipoman_ b/plugins/power/snmp__ipoman_ index 9524460b..011852e1 100755 --- a/plugins/power/snmp__ipoman_ +++ b/plugins/power/snmp__ipoman_ @@ -3,8 +3,8 @@ # What is snmp__ipoman_ # ---------------------- # snmp__ipoman is a munin plugin written for the Ingrasys IpomanII 1202 -# Power Distribution Unit. It should work on any PDU conforming to -# the IPOMANII-MIB. +# Power Distribution Unit. It should work on any PDU conforming to +# the IPOMANII-MIB. # # How do I use it # --------------- @@ -12,7 +12,7 @@ # how: # # 1. Copy snmp__ipoman_ to the directory where all your munin plugins -# reside, for example /usr/share/munin/plugins. +# reside, for example /usr/share/munin/plugins. # # 2. Make the following symlinks to snmp__ipoman_ in that same directory # @@ -40,7 +40,7 @@ # output current and power usage for all available outlets of the # ipoman, and current, power usage and voltage/frequency on all inlets # of the ipoman. -# +# # 5. Restart munin-node # # 6. Make an entry in your munin server's munin.conf: @@ -48,7 +48,7 @@ # [] # address
    # use_node_name no -# +# # 7. Done. # # Copyright (C) 2009 Rien Broekstra @@ -67,21 +67,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Munin plugin to monitor power consumption and current of the sockets of an -# Ingrasys IpomanII 1202 Power Distribution Unit, or any power distribution +# Munin plugin to monitor power consumption and current of the sockets of an +# Ingrasys IpomanII 1202 Power Distribution Unit, or any power distribution # unit that conforms to IPOMANII-MIB via SNMP. # # Parameters: # -# config -# snmpconf +# config +# snmpconf # # Relevant OID's under .iso.org.dod.internet.private.enterprises.ingrasys.product.pduAgent.iPoManII # .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletNumber.0 # .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletStatusTable.ipmDeviceOutletStatusEntry.outletStatusIndex.1 # .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletStatusTable.ipmDeviceOutletStatusEntry.outletStatusCurrent.1 # .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletStatusTable.ipmDeviceOutletStatusEntry.outletStatusKwatt.1 -# .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletStatusTable.ipmDeviceOutletStatusEntry.outletStatusWH.1 +# .ipmObjects.ipmDevice.ipmDeviceOutlet.ipmDeviceOutletStatusTable.ipmDeviceOutletStatusEntry.outletStatusWH.1 # # Version 0.1, Aug 4, 2009 # @@ -134,15 +134,15 @@ if (!defined($graphtype)) { # # The relevant OID's on the IPOMAN # -my $oid_inletnumber = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.1.0"; -my $oid_inletindextable = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.2.1.1."; +my $oid_inletnumber = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.1.0"; +my $oid_inletindextable = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.2.1.1."; my $oid_inletvoltage = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.3.1.2."; my $oid_inletcurrent = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.3.1.3."; my $oid_inletfrequency = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.3.1.4."; my $oid_inletenergy = ".1.3.6.1.4.1.2468.1.4.2.1.3.1.3.1.5."; -my $oid_outletnumber = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.1.0"; -my $oid_outletindextable = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.3.1.1."; +my $oid_outletnumber = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.1.0"; +my $oid_outletindextable = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.3.1.1."; my $oid_outletdescription = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.2.1.2."; my $oid_outletcurrent = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.3.1.3."; my $oid_outletenergy = ".1.3.6.1.4.1.2468.1.4.2.1.3.2.3.1.4."; @@ -219,7 +219,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows the tension and frequency to inlet $socketnumber on the Power Distribution Unit\n"; - + print "voltage.label Tension (V)\n"; print "voltage.draw LINE2\n"; print "voltage.type GAUGE\n"; @@ -235,7 +235,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows the delivered current to inlet $socketnumber on the Power Distribution Unit\n"; - + print "current.label Current (A)\n"; print "current.draw AREA\n"; print "current.type GAUGE\n"; @@ -247,43 +247,43 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows the delivered apparent and real power to inlet $socketnumber of the Power Distribution Unit\n"; - + print "apparentpower.label Apparent power (kVA)\n"; print "apparentpower.draw LINE3\n"; print "apparentpower.type GAUGE\n"; - + print "realpower.label Real power (kW)\n"; print "realpower.draw AREA\n"; print "realpower.type COUNTER\n"; - + exit 0; } elsif ($graphtype eq "outletcurrent") { print "graph_title Outlet $socketnumber current\n"; - + print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows the delivered current to outlet $socketnumber of the Power Distribution Unit\n"; - + print "current.label Delivered current (A)\n"; print "current.draw AREA\n"; print "current.type GAUGE\n"; } elsif ($graphtype eq "outletpower") { print "graph_title Outlet $socketnumber power\n"; - + print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows the delivered apparent and real power to outlet $socketnumber of the Power Distribution Unit\n"; - + print "apparentpower.label Apparent power (kVA)\n"; print "apparentpower.draw LINE3\n"; print "apparentpower.type GAUGE\n"; - + print "realpower.label Real power (kW)\n"; print "realpower.draw AREA\n"; print "realpower.type COUNTER\n"; - + exit 0; } exit 0; @@ -342,7 +342,7 @@ elsif ($graphtype eq "inletcurrent") { } elsif ($graphtype eq "inletpower") { my ($current, $energy, $voltage, $apparentpower); - + if (defined ($response = $session->get_request($oid_inletcurrent.$socketnumber))) { $current = $response->{$oid_inletcurrent.$socketnumber}; } @@ -402,7 +402,7 @@ elsif ($graphtype eq "outletcurrent") { } elsif ($graphtype eq "outletpower") { my ($current, $energy, $voltage, $apparentpower); - + if (defined ($response = $session->get_request($oid_outletcurrent.$socketnumber))) { $current = $response->{$oid_outletcurrent.$socketnumber}; } diff --git a/plugins/power/snmp__sentry b/plugins/power/snmp__sentry index 4631b0c4..32ddece0 100755 --- a/plugins/power/snmp__sentry +++ b/plugins/power/snmp__sentry @@ -31,9 +31,9 @@ Sentry3-MIB::infeedID.1.3 = STRING: AC Sentry3-MIB::infeedName.1.1 = STRING: Master_X Sentry3-MIB::infeedName.1.2 = STRING: Master_Y Sentry3-MIB::infeedName.1.3 = STRING: Master_Z -Sentry3-MIB::infeedCapabilities.1.1 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) -Sentry3-MIB::infeedCapabilities.1.2 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) -Sentry3-MIB::infeedCapabilities.1.3 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) +Sentry3-MIB::infeedCapabilities.1.1 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) +Sentry3-MIB::infeedCapabilities.1.2 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) +Sentry3-MIB::infeedCapabilities.1.3 = BITS: C6 00 onSense(0) loadSense(1) voltageSense(5) powerSense(6) Sentry3-MIB::infeedStatus.1.1 = INTEGER: on(1) Sentry3-MIB::infeedStatus.1.2 = INTEGER: on(1) Sentry3-MIB::infeedStatus.1.3 = INTEGER: on(1) @@ -153,7 +153,7 @@ graph_info This shows the amperage drawn on your PDU. Per NEC, a PDU should not my $infeedName = $sentry_h->{$k}->{'infeedName'}; my $critical = ($sentry_h->{$k}->{'infeedCapacity'})*.9; # 90% of capacity my $warning = $sentry_h->{$k}->{'infeedLoadHighThresh'}; # 80% of capacity - + print "$infeedName.critical $critical\n"; print "$infeedName.draw LINE1\n"; print "$infeedName.label $infeedName\n"; @@ -175,7 +175,7 @@ graph_info Power factor represents the efficiency of the components connected to foreach my $k ( keys %{$sentry_h} ) { my $infeedName = $sentry_h->{$k}->{'infeedName'}; - + print "$infeedName.draw LINE1\n"; print "$infeedName.label $infeedName\n"; print "$infeedName.type GAUGE\n"; @@ -194,7 +194,7 @@ graph_info Crest factor relates the peak value of a signal to its root mean squa foreach my $k ( keys %{$sentry_h} ) { my $infeedName = $sentry_h->{$k}->{'infeedName'}; - + print "$infeedName.draw LINE1\n"; print "$infeedName.label $infeedName\n"; print "$infeedName.type GAUGE\n"; diff --git a/plugins/power5/consumed_cpu_cycles b/plugins/power5/consumed_cpu_cycles index 950c31a6..070f04bd 100755 --- a/plugins/power5/consumed_cpu_cycles +++ b/plugins/power5/consumed_cpu_cycles @@ -42,9 +42,9 @@ if [ "$1" = "config" ]; then echo 'graph_category cpu' echo 'graph_vlabel CPU cycles' echo 'graph_info This graph shows the CPU cycles on an uncapped LPAR' - + echo 'cpuCycles.label used CPU cycles' - + exit 0 fi diff --git a/plugins/power5/cpu_in_lpar b/plugins/power5/cpu_in_lpar index aab20829..9d91f63a 100755 --- a/plugins/power5/cpu_in_lpar +++ b/plugins/power5/cpu_in_lpar @@ -41,10 +41,10 @@ if [ "$1" = "config" ]; then echo 'graph_category cpu' echo 'graph_vlabel CPUs in LPAR' echo 'graph_info This graph shows potential and active processors for a LPAR.' - + echo 'potentialLparCpu.label partition potential processors' echo 'activeLparCpu.label partition active processors' - + exit 0 fi diff --git a/plugins/power5/weight_of_a_lpar b/plugins/power5/weight_of_a_lpar index 2d2e2b05..fbf32209 100755 --- a/plugins/power5/weight_of_a_lpar +++ b/plugins/power5/weight_of_a_lpar @@ -43,9 +43,9 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the weight of an uncapped LPAR' echo 'weight.min 0' echo 'weight.max 255' - + echo 'LparWeight.label weight' - + exit 0 fi diff --git a/plugins/powermta/pmta_ b/plugins/powermta/pmta_ index cace6ce9..6c1e70db 100755 --- a/plugins/powermta/pmta_ +++ b/plugins/powermta/pmta_ @@ -12,21 +12,21 @@ # Copyright (c) 2011, emarsys eMarketing Systems AG # All rights reserved. # -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that # the following conditions are met: # -# Redistributions of source code must retain the above copyright notice, this list of conditions and the -# following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other materials provided with the -# distribution. Neither the name of the emarsys eMarketing Systems AG nor the names of its contributors may +# Redistributions of source code must retain the above copyright notice, this list of conditions and the +# following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other materials provided with the +# distribution. Neither the name of the emarsys eMarketing Systems AG nor the names of its contributors may # be used to endorse or promote products derived from this software without specific prior written permission. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # @@ -53,7 +53,7 @@ # lrwxrwxrwx 1 root root 5 2011-11-15 16:31 pmta_top_domains -> pmta_ # # o alternative multi-host configuration: -# you can also link the pmta_ script to various hosts and configure munin to respond +# you can also link the pmta_ script to various hosts and configure munin to respond # to multiple REMOTEHOSTs, just link pmta_ in this schema and configure munin # accordingly - DO NOT USE DOTS IN YOUR IDENTIFIER VARIABLE, USE UNDERSCORE INSTEAD! # for example: @@ -63,7 +63,7 @@ # lrwxrwxrwx 1 root root 5 2011-11-18 12:18 mailhost4_domain_com_pmta_top_domains -> pmta_ # [...] # -# conf.: +# conf.: # [mailhost1_domain_com_pmta*] # env.REMOTEHOST somehostORip # env.PORT portnumber @@ -91,18 +91,18 @@ fi fetch_xml() { # o arg1 specifies xpath or false # o arg2 specifies sed/regex or false - # o arg3 specifies category ('status', 'domains',..) or false + # o arg3 specifies category ('status', 'domains',..) or false HTTPQUERY="GET /${3}?format=xml HTTP/1.1\n\n" REMOTE=`echo ${HTTPQUERY} | nc -q 1 -w 5 ${REMOTEHOST} ${PORT} | tail -1` XML_DATA=${REMOTE} - SED="s/.*<$2>\([^<]*\)<\/$2>.*/\1/p" - + SED="s/.*<$2>\([^<]*\)<\/$2>.*/\1/p" + if [ "$3" = "status" ]; then RET=`echo $XML_DATA | xpath -q -e $1 | sed -n $SED | cut -f1 -d '.'` elif [ "$3" = "domains" ]; then RET=`echo $XML_DATA | xpath -q -e '//domain/*[self::name or self::rcp]' | sed 's/<[^>]*>//g' | sed 's/\./_/g'` - fi - + fi + if [ "$RET" ]; then echo $RET return 0 @@ -122,15 +122,15 @@ case $NAME_SELF in CONF_TITLE="powermta ${WHAT}bound traffic" CONF_LABEL="traffic_${WHAT}" CONF_SELF="traffic_${WHAT}bound" - - if [ -z $1 ]; then - GET=`fetch_xml //traffic//lastMin/${WHAT} kb status` + + if [ -z $1 ]; then + GET=`fetch_xml //traffic//lastMin/${WHAT} kb status` RETURN=`echo $GET / 1024 | bc` - RCPT=`fetch_xml //traffic//lastMin/${WHAT} rcp status` - MSGS=`fetch_xml //traffic//lastMin/${WHAT} msg status` + RCPT=`fetch_xml //traffic//lastMin/${WHAT} rcp status` + MSGS=`fetch_xml //traffic//lastMin/${WHAT} msg status` echo "megabytes.value $RETURN" echo "recipients.value $RCPT" - echo "messages.value $MSGS" + echo "messages.value $MSGS" exit 0 elif [ $1 = config ]; then echo "megabytes.label megabytes" @@ -154,10 +154,10 @@ case $NAME_SELF in CONF_TITLE=`echo "powermta ${WHAT}bound connections" | tr [:upper:] [:lower:]` CONF_LABEL=`echo "conn_${WHAT}" | tr [:upper:] [:lower:]` CONF_SELF=`echo "connections_${WHAT}bound" | tr [:upper:] [:lower:]` - if [ -z $1 ]; then - GET=`fetch_xml //conn//smtp${WHAT} cur status` + if [ -z $1 ]; then + GET=`fetch_xml //conn//smtp${WHAT} cur status` echo "${CONF_SELF}.value $GET" - exit 0 + exit 0 elif [ $1 = config ]; then echo "${CONF_SELF}.label ${WHAT}bound connections" echo "${CONF_SELF}.draw LINE1" @@ -165,7 +165,7 @@ case $NAME_SELF in AUTOLABEL="no" fi ;; - *pmta_queue_domains | *pmta_queue_recipients | *pmta_queue_megabytes) + *pmta_queue_domains | *pmta_queue_recipients | *pmta_queue_megabytes) if [ `expr match "$NAME_SELF" ".*pmta_queue_domains"` != 0 ]; then WHAT="domains" elif [ `expr match "$NAME_SELF" ".*pmta_queue_recipients"` != 0 ]; then @@ -175,28 +175,28 @@ case $NAME_SELF in UNIT_KB="true" fi CONF_TITLE="powermta ${WHAT} in queue" - CONF_LABEL="queue_${WHAT}" - CONF_SELF="${WHAT}" - + CONF_LABEL="queue_${WHAT}" + CONF_SELF="${WHAT}" + if [ -z $1 ]; then if [ !$UNIT_KB ]; then - GET=`fetch_xml //queue/smtp dom status` + GET=`fetch_xml //queue/smtp dom status` echo "${CONF_SELF}.value $GET" - exit 0 - else - GET=`fetch_xml //queue/smtp kb status` + exit 0 + else + GET=`fetch_xml //queue/smtp kb status` RETURN=`echo $GET / 1024 | bc` echo "${CONF_SELF}.value $RETURN" - exit 0 + exit 0 fi - fi + fi ;; *pmta_top_domains) CONF_TITLE="powermta top 10 domains by recipients" CONF_LABEL="top_domains" CONF_SELF="domains" AUTOLABEL="no" - + GET=`fetch_xml false false domains` if [ -z $1 ]; then for values in $GET; do @@ -204,7 +204,7 @@ case $NAME_SELF in echo $values else echo -n "$values.value#" - fi + fi done | tr ' ' '\n' | tr '#' ' ' # restore newline - replace hashtag with whitespace exit 0 elif [ $1 = config ]; then @@ -239,11 +239,11 @@ case $1 in fi fi exit 0 - ;; + ;; autoconf) # tell munin this script supports autoconfiguration: echo "yes" - ;; + ;; *) exit 1 ;; diff --git a/plugins/powermta/powermta_vmta_recpients b/plugins/powermta/powermta_vmta_recpients index f4fb697e..5d7d2752 100755 --- a/plugins/powermta/powermta_vmta_recpients +++ b/plugins/powermta/powermta_vmta_recpients @@ -39,4 +39,4 @@ domain=`echo "${queue[7]}" | awk -F" " '{print $1}' | cut -d/ -f1 | sed -e 's/[\ recpts=${queue[8]} conns=${queue[10]} echo $domain'.value '$recpts -done +done diff --git a/plugins/printer/hp2600_count_ b/plugins/printer/hp2600_count_ index 185db3de..3ebffdd8 100755 --- a/plugins/printer/hp2600_count_ +++ b/plugins/printer/hp2600_count_ @@ -15,11 +15,11 @@ get_data (){ do_stats () { count=1 - for I in `get_data`; do - [ $count = 3 ] && echo "countb.value $I" - [ $count = 6 ] && echo "county.value $I" - [ $count = 9 ] && echo "countc.value $I" - [ $count = 12 ] && echo "countm.value $I" + for I in `get_data`; do + [ $count = 3 ] && echo "countb.value $I" + [ $count = 6 ] && echo "county.value $I" + [ $count = 9 ] && echo "countc.value $I" + [ $count = 12 ] && echo "countm.value $I" count=$(($count + 1)) done @@ -33,7 +33,7 @@ graph_title HP 2600 pages by cartridge statistics graph_vlabel Count (Pages) graph_category printing graph_info Pages count by color. -graph_args -l 0 +graph_args -l 0 graph_scale no line.label --- line.line 0 diff --git a/plugins/printer/hp2600_status_ b/plugins/printer/hp2600_status_ index dcb36a24..a02e91c0 100755 --- a/plugins/printer/hp2600_status_ +++ b/plugins/printer/hp2600_status_ @@ -15,11 +15,11 @@ get_data (){ do_stats () { count=1 - for I in `get_data`; do - [ $count = 1 ] && echo "statb.value $I" - [ $count = 4 ] && echo "staty.value $I" - [ $count = 7 ] && echo "statc.value $I" - [ $count = 10 ] && echo "statm.value $I" + for I in `get_data`; do + [ $count = 1 ] && echo "statb.value $I" + [ $count = 4 ] && echo "staty.value $I" + [ $count = 7 ] && echo "statc.value $I" + [ $count = 10 ] && echo "statm.value $I" count=$(($count + 1)) done @@ -29,11 +29,11 @@ case $1 in config) cat <<'EOF' host_name printers -graph_title HP 2600 cartridge status +graph_title HP 2600 cartridge status graph_vlabel Status (%) graph_category printing graph_info Toner status. -graph_args --upper-limit 400 -l 0 +graph_args --upper-limit 400 -l 0 line.label --- line.line 400 statb.label Black diff --git a/plugins/printer/snmp__hpclj b/plugins/printer/snmp__hpclj index ffa6083d..5b5b9e79 100755 --- a/plugins/printer/snmp__hpclj +++ b/plugins/printer/snmp__hpclj @@ -140,7 +140,7 @@ tray3.min 0 tray3.max 100 "; } - + print "multigraph hpclj_pagecount graph_category printing graph_title HP Printer Page Counters @@ -182,11 +182,11 @@ sub printPercentageValue { my $field = $_[0]; my $oid_cur = $_[1]; my $oid_max = $_[2]; - + if(not oidExists($oid_cur) || not oidExists($oid_max)){ return(0); } - + my $val_max = $session->get_single($oid_max) || 'U'; my $val_cur = $session->get_single($oid_cur); if ($val_max ne 'U') { @@ -199,11 +199,11 @@ sub printValue { } my $field = $_[0]; my $oid = $_[1]; - + if(not oidExists($oid)){ return(0); } - + my $val_cur = $session->get_single($oid) || 'U'; if ($val_cur ne 'U') { print $field, ".value ", $val_cur, "\n"; @@ -215,7 +215,7 @@ sub oidExists { } my $oid = $_[0]; my $val = $session->get_single($oid); - + if(!length $val || $val eq 'noSuchInstance' || $val eq 'U'){ return(0); }else{ diff --git a/plugins/printer/xerox-wc3220 b/plugins/printer/xerox-wc3220 index dc695333..983217e6 100755 --- a/plugins/printer/xerox-wc3220 +++ b/plugins/printer/xerox-wc3220 @@ -27,7 +27,7 @@ As is. #%# family=contrib #%# capabilities=autoconf - + =cut case $1 in @@ -51,7 +51,7 @@ esac wget -q -o /dev/null -O $TMP_DIR/$PRINTER_IP-Supplies.html http://$PRINTER_IP/status/Supplies.html -TONER_STR=$(grep "" "$TMP_DIR/$PRINTER_IP-Supplies.html") +TONER_STR=$(grep "" "$TMP_DIR/$PRINTER_IP-Supplies.html") #99% echo -n "black.value " @@ -59,4 +59,4 @@ echo $TONER_STR | egrep -o "5%>[0-9]{1,2}" | egrep -o "[0-9]{1,2}$" rm $TMP_DIR/$PRINTER_IP-Supplies.html - + diff --git a/plugins/printer/xerox-wc7232-consumables b/plugins/printer/xerox-wc7232-consumables index d427da3f..72c8ef30 100755 --- a/plugins/printer/xerox-wc7232-consumables +++ b/plugins/printer/xerox-wc7232-consumables @@ -27,7 +27,7 @@ As is. #%# family=contrib #%# capabilities=autoconf - + =cut case $1 in @@ -84,10 +84,10 @@ wget -q -o /dev/null -O $TMP_DIR/$PRINTER_IP-stsply.htm http://$PRINTER_IP/stspl #toner and drum cartriges have both status and percentage -TONER_STR=$(grep Toner "$TMP_DIR/$PRINTER_IP-stsply.htm") +TONER_STR=$(grep Toner "$TMP_DIR/$PRINTER_IP-stsply.htm") #info=info.concat([['Toner Cartridges',[['Cyan Toner [C]',0,77],['Magenta Toner [M]',7,1],['Yellow Toner [Y]',7,1],['Black Toner [K]',0,39]],3]]); -DRUM_STR=$(grep Drum "$TMP_DIR/$PRINTER_IP-stsply.htm") +DRUM_STR=$(grep Drum "$TMP_DIR/$PRINTER_IP-stsply.htm") #info=info.concat([['Drum Cartridges',[['Drum Cartridges',0,79]],1]]); echo -n "cyan.value " @@ -106,4 +106,4 @@ echo -n "drum.value " echo $DRUM_STR | egrep -o "s',[0-9],[0-9]{1,2}" | egrep -o "[0-9]{1,2}$" rm $TMP_DIR/$PRINTER_IP-stsply.htm - + diff --git a/plugins/qmail/qremote b/plugins/qmail/qremote index 2abe07ce..ddf8ec21 100755 --- a/plugins/qmail/qremote +++ b/plugins/qmail/qremote @@ -17,7 +17,7 @@ if [ "$1" = "config" ]; then cat < 0; my $where; @@ -82,7 +82,7 @@ sub do_stats { } } close $fh; - + save_state($stop_at); foreach my $rc (sort {$a<=>$b} keys %REJECT_CODES) { @@ -108,7 +108,7 @@ graph_total Total my $type; foreach $k (sort {$a<=>$b} keys %REJECT_CODES) { - print + print "r$k.label $REJECT_CODES{$k} r$k.type ABSOLUTE r$k.min 0 diff --git a/plugins/rabbitmq/rabbitmq_connections b/plugins/rabbitmq/rabbitmq_connections index f7adbc5a..fd755968 100755 --- a/plugins/rabbitmq/rabbitmq_connections +++ b/plugins/rabbitmq/rabbitmq_connections @@ -53,7 +53,7 @@ case $(whoami) in esac # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. if [ "$1" = "config" ]; then CONN_WARN=${conn_warn:-500} diff --git a/plugins/rabbitmq/rabbitmq_consumers b/plugins/rabbitmq/rabbitmq_consumers index 76ce6759..b3ac555b 100755 --- a/plugins/rabbitmq/rabbitmq_consumers +++ b/plugins/rabbitmq/rabbitmq_consumers @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the queues of a virtual_host in RabbitMQ # # Usage: Link or copy into /etc/munin/node.d/ @@ -26,14 +26,14 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. HOME=/tmp/ VHOST=${vhost:-"/"} QUEUES=$(HOME=$HOME rabbitmqctl list_queues -p $VHOST name | \ grep -v '^Listing' | \ grep -v 'done\.$' | sed -e 's/[.=-]/_/g' ) - + if [ "$1" = "config" ]; then QUEUE_WARN=${queue_warn:-100} QUEUE_CRIT=${queue_crit:-500} diff --git a/plugins/rabbitmq/rabbitmq_messages b/plugins/rabbitmq/rabbitmq_messages index a1b8f688..ca7cece9 100755 --- a/plugins/rabbitmq/rabbitmq_messages +++ b/plugins/rabbitmq/rabbitmq_messages @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the queues of a virtual_host in RabbitMQ # # Usage: Link or copy into /etc/munin/node.d/ @@ -26,14 +26,14 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. HOME=/tmp/ VHOST=${vhost:-"/"} QUEUES=$(HOME=$HOME rabbitmqctl list_queues -p $VHOST name | \ grep -v '^Listing' | \ grep -v 'done\.$' | sed -e 's/[.=-]/_/g' ) - + if [ "$1" = "config" ]; then QUEUE_WARN=${queue_warn:-10000} QUEUE_CRIT=${queue_crit:-20000} diff --git a/plugins/rabbitmq/rabbitmq_messages_unacknowledged b/plugins/rabbitmq/rabbitmq_messages_unacknowledged index d5d8f60f..a36a0a4d 100755 --- a/plugins/rabbitmq/rabbitmq_messages_unacknowledged +++ b/plugins/rabbitmq/rabbitmq_messages_unacknowledged @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the queues of a virtual_host in RabbitMQ # # Usage: Link or copy into /etc/munin/node.d/ @@ -26,14 +26,14 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. HOME=/tmp/ VHOST=${vhost:-"/"} QUEUES=$(HOME=$HOME rabbitmqctl list_queues -p $VHOST name | \ grep -v '^Listing' | \ grep -v 'done\.$' | sed -e 's/[.=-]/_/g' ) - + if [ "$1" = "config" ]; then QUEUE_WARN=${queue_warn:-10000} QUEUE_CRIT=${queue_crit:-20000} diff --git a/plugins/rabbitmq/rabbitmq_messages_uncommitted b/plugins/rabbitmq/rabbitmq_messages_uncommitted index 179d2e60..c3e6fadf 100755 --- a/plugins/rabbitmq/rabbitmq_messages_uncommitted +++ b/plugins/rabbitmq/rabbitmq_messages_uncommitted @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the queues of a virtual_host in RabbitMQ # # Usage: Link or copy into /etc/munin/node.d/ @@ -26,14 +26,14 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. HOME=/tmp/ VHOST=${vhost:-"/"} QUEUES=$(HOME=$HOME rabbitmqctl list_queues -p $VHOST name | \ grep -v '^Listing' | \ grep -v 'done\.$' | sed -e 's/[.=-]/_/g' ) - + if [ "$1" = "config" ]; then QUEUE_WARN=${queue_warn:-10000} QUEUE_CRIT=${queue_crit:-20000} diff --git a/plugins/rabbitmq/rabbitmq_queue_memory b/plugins/rabbitmq/rabbitmq_queue_memory index fef2cc7b..253a15a6 100755 --- a/plugins/rabbitmq/rabbitmq_queue_memory +++ b/plugins/rabbitmq/rabbitmq_queue_memory @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the queues of a virtual_host in RabbitMQ # # Usage: Link or copy into /etc/munin/node.d/ @@ -26,14 +26,14 @@ if [ "$1" = "autoconf" ]; then fi # If run with the "config"-parameter, give out information on how the -# graphs should look. +# graphs should look. HOME=/tmp/ VHOST=${vhost:-"/"} QUEUES=$(HOME=$HOME rabbitmqctl list_queues -p $VHOST name | \ grep -v '^Listing' | \ grep -v 'done\.$' | sed -e 's/[.=-]/_/g' ) - + if [ "$1" = "config" ]; then QUEUE_WARN=${queue_warn:-104857600} # 100 MB QUEUE_CRIT=${queue_crit:-209715200} # 200 MB diff --git a/plugins/rackspace/README b/plugins/rackspace/README index 9faf8c8d..25ba1fd1 100644 --- a/plugins/rackspace/README +++ b/plugins/rackspace/README @@ -1,5 +1,5 @@ ====================================================================================== -These plugins are made to monitor RackSpace Cloudfiles storage usage and files +These plugins are made to monitor RackSpace Cloudfiles storage usage and files count. ====================================================================================== diff --git a/plugins/rackspace/rackspace_cdn_count.php b/plugins/rackspace/rackspace_cdn_count.php index d1ee8cbb..111d6590 100755 --- a/plugins/rackspace/rackspace_cdn_count.php +++ b/plugins/rackspace/rackspace_cdn_count.php @@ -5,7 +5,7 @@ # Parameters: # # config (required) -# +# # #%# family=manual diff --git a/plugins/raspberry-pi/cpu_freq_1sec b/plugins/raspberry-pi/cpu_freq_1sec index 62a26523..6bd0d6bf 100755 --- a/plugins/raspberry-pi/cpu_freq_1sec +++ b/plugins/raspberry-pi/cpu_freq_1sec @@ -1,8 +1,8 @@ #! /bin/sh -# -# This is a small supersampling plugin that does +# +# This is a small supersampling plugin that does # cpu sampling every 1 second. -# +# # (c) 2013 - LGPL - Steve Schnepp pluginfull="$0" # full name of plugin diff --git a/plugins/reddit_karma/reddit_karma_ b/plugins/reddit_karma/reddit_karma_ index 9ff633c7..3d3ead68 100755 --- a/plugins/reddit_karma/reddit_karma_ +++ b/plugins/reddit_karma/reddit_karma_ @@ -3,7 +3,7 @@ # reddit_karma_ ########################## # Munin Plugin to track the karma activity of a Reddit user. -# +# # Copyright 2012 Mark Caudill # # This program is free software: you can redistribute it and/or modify @@ -12,7 +12,7 @@ # (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 +# 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. # diff --git a/plugins/redis/redis_ b/plugins/redis/redis_ index 55474435..77c506d2 100755 --- a/plugins/redis/redis_ +++ b/plugins/redis/redis_ @@ -128,7 +128,7 @@ switch ($0) { print "missratio.draw STACK\n"; exit 0; } - + my $total = $hash->{'keyspace_hits'} + $hash->{'keyspace_misses'}; my $hitratio = 0; my $missratio = 0; @@ -177,7 +177,7 @@ switch ($0) { print "used_memory_rss.value ". $hash->{'used_memory_rss'} ."\n"; print "used_memory_peak.value ". $hash->{'used_memory_peak'} ."\n"; } - + case "used_keys" { my $dbs; foreach my $key (keys %{$hash}) { @@ -210,18 +210,18 @@ switch ($0) { close ($sock); sub get_conn { - + my $sock; - + if( $UNIX_SOCKET && -S $UNIX_SOCKET ){ - + $sock = IO::Socket::UNIX->new( Type => SOCK_STREAM(), Peer => $UNIX_SOCKET, ); - + }else{ - + $sock = IO::Socket::INET->new( PeerAddr => $HOST, PeerPort => $PORT, @@ -229,7 +229,7 @@ sub get_conn { Proto => 'tcp' ); } - + if ( defined( $PASSWORD ) ) { print $sock "AUTH ", $PASSWORD, "\r\n"; my $result = <$sock> || die "can't read socket: $!"; diff --git a/plugins/redis/resque b/plugins/redis/resque index 23b75c73..c1a0dc58 100755 --- a/plugins/redis/resque +++ b/plugins/redis/resque @@ -120,8 +120,8 @@ elsif ($opt eq 'queues_size') { my $name = $queue; $name =~ s/:/_/; - my $size = $r->llen("${NAMESPACE}:queue:${queue}") || 0; - + my $size = $r->llen("${NAMESPACE}:queue:${queue}") || 0; + $total += $size; print "${name}_size.value ${size}\n"; diff --git a/plugins/router/ag241-adsl b/plugins/router/ag241-adsl index 9a3d7e63..19ebcbf4 100755 --- a/plugins/router/ag241-adsl +++ b/plugins/router/ag241-adsl @@ -9,7 +9,7 @@ # as per the instructions on the munin wiki # http://munin.projects.linpro.no/wiki/Using_SNMP_plugins # By default the SNMP server is disabled, you can enable it in the web admin -# You will need to set up the "virtual node" configuration as detailed +# You will need to set up the "virtual node" configuration as detailed # for snmp plugins # Plugin will require some configuration in /etc/munin/plugin-conf.d/ag241_MODEMADDRESS @@ -35,7 +35,7 @@ require 'net/http' #default parameters -host = nil +host = nil port = ENV['port'] || 80 user = ENV['user'] || 'admin' pass = ENV['pass'] || 'forhax' #dont remember what the default admin password was diff --git a/plugins/router/avm-fritzbox-wan-traffic b/plugins/router/avm-fritzbox-wan-traffic index adda3a73..164e9faf 100755 --- a/plugins/router/avm-fritzbox-wan-traffic +++ b/plugins/router/avm-fritzbox-wan-traffic @@ -6,7 +6,7 @@ # # Author: Andreas Kreisl # -# Licence: Creative Commons - Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +# Licence: Creative Commons - Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) # http://creativecommons.org/licenses/by-sa/3.0/ # ############################## diff --git a/plugins/router/cisco-epc3010_ b/plugins/router/cisco-epc3010_ index e1b83638..d795587e 100755 --- a/plugins/router/cisco-epc3010_ +++ b/plugins/router/cisco-epc3010_ @@ -5,7 +5,7 @@ =head1 NAME -epc3010_ - munin-plugin to monitor Upstream/Downstream Power Levels and Signal to Noise Ratio on Cisco EPC3010 EuroDocsis 3.0 Data Modem +epc3010_ - munin-plugin to monitor Upstream/Downstream Power Levels and Signal to Noise Ratio on Cisco EPC3010 EuroDocsis 3.0 Data Modem =head1 CONFIGURATION @@ -43,9 +43,9 @@ DIRECTION=${0##*epc3010_} # Check if argument is autoconfig or config case "$1" in -autoconfig) +autoconfig) # Does your network have a Cisco EPC3010? - curl -s http://192.168.100.1/Docsis_system.asp | grep -q "Cisco EPC3010" + curl -s http://192.168.100.1/Docsis_system.asp | grep -q "Cisco EPC3010" if [ $? -eq 0 ]; then echo "yes" exit 0 @@ -54,7 +54,7 @@ autoconfig) exit 1 fi ;; -config) +config) if [ $DIRECTION == "downstream" ]; then printf "graph_title Cisco EPC3010 Downstream measurements\n"; printf "graph_args -l 0 --base 1000\n" diff --git a/plugins/router/conexant_adsl b/plugins/router/conexant_adsl index acf4b186..71ca6053 100755 --- a/plugins/router/conexant_adsl +++ b/plugins/router/conexant_adsl @@ -1,6 +1,6 @@ #!/bin/bash -# -# +# +# # Script to show adsl router stats for routers with Conexant based chips and the standard Conexant web admin gui like the eTec EpicRouter... # # Parameters understood: diff --git a/plugins/router/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin index 202e0538..7379c73d 100755 --- a/plugins/router/d-link-dir-655-router-statistics-plugin +++ b/plugins/router/d-link-dir-655-router-statistics-plugin @@ -43,15 +43,15 @@ def output agent = Mechanize.new x = agent.get(router_path) salt = x.body.match(/salt = "(.*)"/)[1] - + # pad the pasword to length 16 pad_size = (16 - password.length) padded_password = password + "\x01" * pad_size - + # pad it the rest of the way, length 64 for user salted_password = salt + padded_password + ("\x01" * (63 - salt.length - padded_password.length)) + "U" login_hash = salt + Digest::MD5.hexdigest(salted_password) - + # authenticate against the router using the hash that we just built login_path = "#{router_path}/post_login.xml?hash=#{login_hash}" x = agent.get(login_path) @@ -120,7 +120,7 @@ def output puts "#{i}_coll.value " + nics[i]["tx_collisions"] puts "#{i}_err.value " + nics[i]["rx_errors"] end - + # graph stats for each interface for i in [ "LAN", "WAN", "WLAN" ] do puts "multigraph if_packets.#{i}" @@ -215,7 +215,7 @@ def config puts 'send.type DERIVE' puts 'send.negative recv' puts 'send.min 0' - + puts "multigraph if_drop.#{i}" puts "graph_title D-Link DIR-655 #{i} drops" puts 'graph_category network' @@ -229,7 +229,7 @@ def config puts 'send.type DERIVE' puts 'send.negative recv' puts 'send.min 0' - + puts "multigraph if_collerr.#{i}" puts "graph_title D-Link DIR-655 #{i} collisions & errors" puts 'graph_category network' diff --git a/plugins/router/dsl-connection-speed b/plugins/router/dsl-connection-speed index 06984f1e..eb5a03c4 100755 --- a/plugins/router/dsl-connection-speed +++ b/plugins/router/dsl-connection-speed @@ -28,7 +28,7 @@ fi # create temp file for storing wget output TMPFILE=$(mktemp) -# if we have auth variables then add them to +# if we have auth variables then add them to # wget cmdline if [[ "$DSLUSER" != "" && "$DSLPASS" != "" ]] then diff --git a/plugins/router/dsl-stats b/plugins/router/dsl-stats index 58b4bf20..cfdeaf9c 100755 --- a/plugins/router/dsl-stats +++ b/plugins/router/dsl-stats @@ -41,7 +41,7 @@ fi TMPFILE=$(mktemp) -# if we have auth variables then add them to +# if we have auth variables then add them to # wget cmdline if [[ "$DSLUSER" != "" && "$DSLPASS" != "" ]] then diff --git a/plugins/router/freeboxuptime b/plugins/router/freeboxuptime index c7dcb654..7202f82d 100755 --- a/plugins/router/freeboxuptime +++ b/plugins/router/freeboxuptime @@ -22,7 +22,7 @@ # Running: HP embedded # OS details: HP Onboard Administrator management console # Uptime: 7.226 days (since Thu Dec 9 21:01:44 2010) -# +# # OS detection performed. Please report any incorrect results at http://nmap.org/submit/ . # Nmap done: 1 IP address (1 host up) scanned in 29.279 seconds # ---------------------------------------------------------------------------------------------------- @@ -71,7 +71,7 @@ if [ "$1" = "autoconf" ]; then echo "no (Freebox not reachable)" exit 2 else - echo yes + echo yes exit 0 fi fi @@ -87,7 +87,7 @@ if [ "$1" = "config" ]; then graph_info="Shows the uptime of your freebox (cache: ${CACHE_HOURS}h" if [ -f $CACHE_FILE ]; then lastCheck=$(stat -c %z $CACHE_FILE | cut -d"." -f1) - lastReboot=$(awk -F"@" '{print $2}' $CACHE_FILE) + lastReboot=$(awk -F"@" '{print $2}' $CACHE_FILE) graph_info="${graph_info} - last check: ${lastCheck} - last reboot: $lastReboot" else graph_info="${graph_info})" diff --git a/plugins/router/snmp__juniper b/plugins/router/snmp__juniper index da332ea8..e6568461 100755 --- a/plugins/router/snmp__juniper +++ b/plugins/router/snmp__juniper @@ -126,7 +126,7 @@ class JunOSSnmpClient(object): return int(varBindTable[0][1]) - + def get_data(self): devs = self.get_devices() @@ -179,7 +179,7 @@ else: if not (host and port and community): print "# Bad configuration. Cannot run with Host=%s, port=%s and community=%s" % (host, port, community) sys.exit(1) - + if "config" in sys.argv[1:]: c.print_config() else: diff --git a/plugins/router/snmp__juniper_spu b/plugins/router/snmp__juniper_spu index 920f0b83..5ceab231 100755 --- a/plugins/router/snmp__juniper_spu +++ b/plugins/router/snmp__juniper_spu @@ -222,7 +222,7 @@ else: if not (host and port and community): print "# Bad configuration. Cannot run with Host=%s, port=%s and community=%s" % (host, port, community) sys.exit(1) - + if "config" in sys.argv[1:]: c.print_config() else: diff --git a/plugins/router/snmp__linksys_poe b/plugins/router/snmp__linksys_poe index 886326db..0e5c9a4d 100755 --- a/plugins/router/snmp__linksys_poe +++ b/plugins/router/snmp__linksys_poe @@ -33,7 +33,7 @@ PoE ports. Information is gathered from Linksys' private MIB space, so it's probably only applicable to Linksys devices. I have been unable to get an actual copy of -the appropriate MIB, so I don't know the actual names of the values I'm +the appropriate MIB, so I don't know the actual names of the values I'm retrieving. =head1 MAGIC MARKERS diff --git a/plugins/router/speedport_300 b/plugins/router/speedport_300 index 4b5cd531..7f780b1d 100755 --- a/plugins/router/speedport_300 +++ b/plugins/router/speedport_300 @@ -1,8 +1,8 @@ #!/bin/bash # # -# Munin plugin to show the up- / download stream of the actual -# internet connection by reading the top_status.htm from the +# Munin plugin to show the up- / download stream of the actual +# internet connection by reading the top_status.htm from the # Speedport 300 # # diff --git a/plugins/router/tg585v7__ b/plugins/router/tg585v7__ index d410e893..1bf4e576 100755 --- a/plugins/router/tg585v7__ +++ b/plugins/router/tg585v7__ @@ -13,7 +13,7 @@ Requires perl and either WWW::Mechanize or Net::Telnet. =head1 CONFIGURATION The plugin needs HTML access to the router. If you can get to http://YOUR_ROUTER/, -and are greeting with a page titled "THOMSON TG585 v7", then you can probably use this plugin. +and are greeting with a page titled "THOMSON TG585 v7", then you can probably use this plugin. This is a wildcard plugin, so you will need to create symlinks to this plugin (or create copies if your filesystem doesn't support linking). Links should be of the form: diff --git a/plugins/rsync/rsyncd_bytes b/plugins/rsync/rsyncd_bytes index d290282b..985086e7 100755 --- a/plugins/rsync/rsyncd_bytes +++ b/plugins/rsync/rsyncd_bytes @@ -12,7 +12,7 @@ mktempfile () { mktemp -t $1 -} +} RSYNCD_LOG=${logfile:-/var/log/rsyncd.log} LOGTAIL=${logtail:-`which logtail`} diff --git a/plugins/rsync/rsyncd_count b/plugins/rsync/rsyncd_count index 9b3a8afd..ec04c3f4 100755 --- a/plugins/rsync/rsyncd_count +++ b/plugins/rsync/rsyncd_count @@ -12,7 +12,7 @@ mktempfile () { mktemp -t $1 -} +} RSYNCD_LOG=${logfile:-/var/log/rsyncd.log} LOGTAIL=${logtail:-`which logtail`} diff --git a/plugins/rtorrent/rtom_mem b/plugins/rtorrent/rtom_mem index eb8a5850..afb00cef 100755 --- a/plugins/rtorrent/rtom_mem +++ b/plugins/rtorrent/rtom_mem @@ -34,7 +34,7 @@ # user username # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket -# env.category Category +# env.category Category # # [rtom_mem] # env.ip 127.0.0.1 diff --git a/plugins/s3/s3_items b/plugins/s3/s3_items index 44511240..d31c149f 100755 --- a/plugins/s3/s3_items +++ b/plugins/s3/s3_items @@ -3,7 +3,7 @@ use strict; -my $s3_id = exists $ENV{'s3_id'} ? $ENV{'s3_id'} : "user"; +my $s3_id = exists $ENV{'s3_id'} ? $ENV{'s3_id'} : "user"; my $s3cmd = 's3curl.pl --id ' . $s3_id . ' http://s3.amazonaws.com/'; @@ -29,14 +29,14 @@ sub get_bucket_list() { my @bucket_list = (); my $pos = 0; - + while ($str =~ /.([\w._-]+)<\/Name>/) { $bucket_list[$pos++] = $1; $str = $'; - } + } - return @bucket_list; + return @bucket_list; } @@ -47,16 +47,16 @@ my ($name) = @_; my $stats = `$s3cmd_local`; my %res; - - $res{'size'} = 0; - $res{'count'} = 0; - + + $res{'size'} = 0; + $res{'count'} = 0; + while ($stats =~ /.([\w._-]+)<\/Size>/) { $stats = $'; - + $res{'size'} += $1; $res{'count'}++; - } + } return %res; } @@ -69,32 +69,32 @@ if ( $ARGV[0] eq "config" ) { print "graph_category cloud\n"; print "graph_vlabel items\n"; print 'graph_info Plugin available at http://www.ohardt.com/dev/munin/' . "\n"; - + my @bucket_list = get_bucket_list(); - + my $bucket_name; - + foreach $bucket_name ( @bucket_list ) { - - print $bucket_name . ".label Bucket " . $bucket_name . "\n"; - - } + + print $bucket_name . ".label Bucket " . $bucket_name . "\n"; + + } exit; - - + + } my @bucket_list = get_bucket_list(); my $bucket_name; - + foreach $bucket_name ( @bucket_list ) { - - my %stats = get_bucket_stats( $bucket_name ); - + + my %stats = get_bucket_stats( $bucket_name ); + print $bucket_name . ".value " . $stats{'count'} . "\n"; } - \ No newline at end of file + diff --git a/plugins/s3/s3_storage b/plugins/s3/s3_storage index ac73a125..3fda1d7c 100755 --- a/plugins/s3/s3_storage +++ b/plugins/s3/s3_storage @@ -3,7 +3,7 @@ use strict; use warnings; -my $s3_id = exists $ENV{'s3_id'} ? $ENV{'s3_id'} : "user"; +my $s3_id = exists $ENV{'s3_id'} ? $ENV{'s3_id'} : "user"; my $s3curl = "perl s3-curl/s3curl.pl --id $s3_id -- -s -S"; sub get_bucket_list() @@ -11,13 +11,13 @@ sub get_bucket_list() my $buckets = `$s3curl http://s3.amazonaws.com`; my $str = $buckets; my @bucket_list; - + while ($buckets =~ s/.([\w._-]+)<\/Name>//) { push @bucket_list, $1; - } + } - return @bucket_list; + return @bucket_list; } my @bucket_list = split /\s+/, ($ENV{'buckets'} || ''); @@ -45,9 +45,9 @@ sub get_bucket_stats my ($name) = @_; my $stats = `$s3curl http://$name.s3.amazonaws.com`; my %res; - + $res{'size'} = 0; - + while ($stats =~ s/.([\w._-]+)<\/Size>//) { $res{'size'} += $1; @@ -65,18 +65,18 @@ if ($ARGV[0] and $ARGV[0] eq "config") print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; print "graph_info Plugin available at https://github.com/aptivate/munin-contrib/blob/master/plugins/s3/s3_storage\n"; - + foreach my $bucket_name (@bucket_list) { print "$bucket_name.label Bucket $bucket_name\n"; - } + } exit; } foreach my $bucket_name (@bucket_list) { - my %stats = get_bucket_stats($bucket_name); + my %stats = get_bucket_stats($bucket_name); print "$bucket_name.value " . $stats{'size'} . "\n"; } diff --git a/plugins/sabnzbd/sabnzbd_dataleft b/plugins/sabnzbd/sabnzbd_dataleft index 1f56ea8b..3eed92c8 100755 --- a/plugins/sabnzbd/sabnzbd_dataleft +++ b/plugins/sabnzbd/sabnzbd_dataleft @@ -6,7 +6,7 @@ # # SABnzbd : http://sabnzbd.org/ # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -26,7 +26,7 @@ # [sabnzbd*] # env.host http://host:port/ # env.api apikey -# +# use strict; use XML::Simple; @@ -62,7 +62,7 @@ my $req = $get->get($sURL); my $vals = $req->content(); my $xmlvals = $xml->XMLin($vals); -#get/output vals +#get/output vals my $left = $xmlvals->{mbleft}; $left =~ /(\d+)\./; print "rem.value ".$1."\n"; diff --git a/plugins/sabnzbd/sabnzbd_speed b/plugins/sabnzbd/sabnzbd_speed index fb466c7b..af48066d 100755 --- a/plugins/sabnzbd/sabnzbd_speed +++ b/plugins/sabnzbd/sabnzbd_speed @@ -6,7 +6,7 @@ # # SABnzbd : http://sabnzbd.org/ # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -26,7 +26,7 @@ # [sabnzbd*] # env.host http://host:port/ # env.api apikey -# +# use strict; use XML::Simple; @@ -65,4 +65,4 @@ my $xmlvals = $xml->XMLin($vals); #get/output vals my $speed = $xmlvals->{kbpersec}; $speed =~ /(\d+)\./; -print "speed.value ".$1."\n"; +print "speed.value ".$1."\n"; diff --git a/plugins/samba/samba_locked b/plugins/samba/samba_locked index d591510e..a9b00e18 100755 --- a/plugins/samba/samba_locked +++ b/plugins/samba/samba_locked @@ -8,8 +8,8 @@ # autoconf (optional - used by munin-config) # # $Log$ -# Revision 1.0 2007/04/16 Jon Higgs -# Initial Release - Adapted from jimmyo's processses plugin. +# Revision 1.0 2007/04/16 Jon Higgs +# Initial Release - Adapted from jimmyo's processses plugin. # # Magick markers (optional - used by munin-config and som installation # scripts): @@ -17,7 +17,7 @@ #%# capabilities=autoconf if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -25,7 +25,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Samba Locked Files' echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel number of locked files' + echo 'graph_vlabel number of locked files' echo 'graph_category fs' echo 'graph_info This graph shows the number locked Samba Files.' echo 'samba_locked.label Locked Files' diff --git a/plugins/samba/samba_users b/plugins/samba/samba_users index 4bf00d56..e125e52c 100755 --- a/plugins/samba/samba_users +++ b/plugins/samba/samba_users @@ -8,13 +8,13 @@ # autoconf (optional - used by munin-config) # # $Log$ -# Revision 1.0 2007/04/16 Jon Higgs -# Initial Release - Adapted from jimmyo's processses plugin. +# Revision 1.0 2007/04/16 Jon Higgs +# Initial Release - Adapted from jimmyo's processses plugin. # # Revision 1.1 2014/07/24 MangaII # Add exit 0 # WARNING : Samba 3.6 and newer block access to smbstatus for no root user -# On Debian make a "chmod a+w /run/samba/sessionid.tdb" +# On Debian make a "chmod a+w /run/samba/sessionid.tdb" # smbstatus must open this file with RW option # # Magick markers (optional - used by munin-config and som installation @@ -23,7 +23,7 @@ #%# capabilities=autoconf if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -31,7 +31,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Samba Users' echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel number of Samba users.' + echo 'graph_vlabel number of Samba users.' echo 'graph_category fs' echo 'graph_info This graph shows the number Samba users.' echo 'samba_users.label Samba Users' diff --git a/plugins/san/emc_comprehensive/emc_comprehensive b/plugins/san/emc_comprehensive/emc_comprehensive index 174944aa..2d5c3742 100755 --- a/plugins/san/emc_comprehensive/emc_comprehensive +++ b/plugins/san/emc_comprehensive/emc_comprehensive @@ -46,14 +46,14 @@ FILE: for my $file (@files) { my $object_name = shift @row; my $epoch = shift @row; my $owner_array_name = shift @row; - + # Ignore if too old next if ($epoch <= $spool_fetch_epoch); # Don't do too much work : 4h each time is enough $first_epoch ||= $epoch; next if $epoch > $first_epoch + 60 * 60 * 4; - + # Store Values for (my $idx = 0; $idx < $nb_headers; $idx ++) { @@ -91,26 +91,26 @@ multigraph san graph_title Vue globale graph_order \ cpu_sp_a=san.cpu.sp_a \ - cpu_sp_b=san.cpu.sp_b + cpu_sp_b=san.cpu.sp_b EOF ; # CPU my @object_names = keys %$values; - gen_multigraph( - $values, + gen_multigraph( + $values, "cpu", [ grep { /^SP / } @object_names ], ); # DISK - gen_multigraph( + gen_multigraph( $values, "disk", [ grep { /^Bus \d+/ } @object_names ], ); # Port - gen_multigraph( + gen_multigraph( $values, "port", [ grep { /^Port / } @object_names ], @@ -118,7 +118,7 @@ EOF ); # Pool - gen_multigraph( + gen_multigraph( $values, "pool", [ grep { /^Pool / } @object_names ], @@ -130,17 +130,17 @@ multigraph san.lun graph_title Luns Global graph_order \ cpu_sp_a=san.cpu.sp_a \ - cpu_sp_b=san.cpu.sp_b + cpu_sp_b=san.cpu.sp_b EOF ; # LUN Per Host my %host_seen; - my @hosts = grep { $_ ne "" } grep { ! $host_seen{$_}++ } + my @hosts = grep { $_ ne "" } grep { ! $host_seen{$_}++ } map { $1 if /^\w+ \[\d+; (\w+)/ } @object_names; for my $host (@hosts) { my $host_field = $host; $host_field =~ tr/./_/; - gen_multigraph( + gen_multigraph( $values, "lun.$host_field", [ grep { /^\w+ \[\d+; $host/ } @object_names ], @@ -153,7 +153,7 @@ EOF last; } -sub gen_multigraph +sub gen_multigraph { my ($values, $category, $object_names, $convert_to_label, $convert_to_field) = @_; @@ -169,7 +169,7 @@ multigraph san.$category graph_title $category Global graph_order \ cpu_sp_a=san.cpu.sp_a \ - cpu_sp_b=san.cpu.sp_b + cpu_sp_b=san.cpu.sp_b EOF ; @@ -191,7 +191,7 @@ EOF } } -sub hash_field_name +sub hash_field_name { my $name = shift; $name = lc($name); @@ -212,7 +212,7 @@ sub trim sub file_mtime { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat(shift); - return $mtime; + return $mtime; } __DATA__ @@ -254,7 +254,7 @@ EOF print ".\n"; my %MONTHS = get_months(); -sub convert_to_epoch +sub convert_to_epoch { # converts "05/12/2011 03:57" to EPOCH my ($date, $time) = split(/ /); @@ -302,7 +302,7 @@ multigraph san graph_title Vue globale graph_order \ cpu_sp_a=san.cpu.cpu_sp_a \ - cpu_sp_b=san.cpu.cpu_sp_b + cpu_sp_b=san.cpu.cpu_sp_b multigraph san.cpu graph_title Utilization (%) cpu_sp_a.label Utilization (%) for SP A diff --git a/plugins/scalix/scalix_clients b/plugins/scalix/scalix_clients index a4bdfc88..d4880116 100755 --- a/plugins/scalix/scalix_clients +++ b/plugins/scalix/scalix_clients @@ -1,12 +1,12 @@ #!/bin/sh -# +# # Plugin to monitor the Scalix email system: Client statistics # # (C) 2008 P.Holzleitner # #%# family=contrib -OMSTAT="/opt/scalix/bin/omstat" +OMSTAT="/opt/scalix/bin/omstat" if [ "$1" = "config" ]; then diff --git a/plugins/scalix/scalix_indexwork b/plugins/scalix/scalix_indexwork index 2b1a0a57..4b53a052 100755 --- a/plugins/scalix/scalix_indexwork +++ b/plugins/scalix/scalix_indexwork @@ -1,12 +1,12 @@ #!/usr/bin/perl -# +# # Plugin to monitor the Scalix email system. # # $Log$ # # # Parameters: -# +# # config (required) # autoconf (optional - used by munin-config) # diff --git a/plugins/scalix/scalix_processes b/plugins/scalix/scalix_processes index 6c39052e..6a620c6e 100755 --- a/plugins/scalix/scalix_processes +++ b/plugins/scalix/scalix_processes @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the Scalix email system: Process statistics # # (C) 2008 P.Holzleitner diff --git a/plugins/scalix/scalix_queues b/plugins/scalix/scalix_queues index 50ea7607..802a44bc 100755 --- a/plugins/scalix/scalix_queues +++ b/plugins/scalix/scalix_queues @@ -1,5 +1,5 @@ #!/bin/sh -# +# # Plugin to monitor the Scalix email system: Queue statistics # # (C) 2008 P.Holzleitner @@ -7,7 +7,7 @@ # #%# family=contrib -OMSTAT="/opt/scalix/bin/omstat" +OMSTAT="/opt/scalix/bin/omstat" if [ "$1" = "config" ]; then diff --git a/plugins/security/forefront_ b/plugins/security/forefront_ index 951db439..eb9b61d4 100755 --- a/plugins/security/forefront_ +++ b/plugins/security/forefront_ @@ -3,7 +3,7 @@ # Plugin to monitor Forefront Client Security status in MOM database # # Copyright (c) 2008 Rune Nordbe Skillingstad - -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. @@ -18,17 +18,17 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # -# Parameters: +# Parameters: # # config # autoconf # suggest # # Config variables -# +# # dsn - If DSN name differs from hostname # dbuser - Valid MS SQL user (Windows authentication is possible using "DOMAIN\user") -# dbpass - Password +# dbpass - Password # # Install guide: # This plugin relies on correct configured ODBC for the MOM database @@ -36,28 +36,28 @@ # * Install and configure FreeTDS and DBD::Sybase (packages tdsodbc and libdbd-sybase-perl on Ubuntu) # - DBD::Sybase is preferred over ODBC because of strange TEXT field handling in DBD::ODBC # -# Example +# Example # /etc/freetds/freetds.conf: # [MyHost] # host = MyHost.domain.tld # port = 1433 # tds version = 7.0 -# +# # Copy this script to /usr/share/munin/plugins and run "munin-node-configure --shell" # If freetds.conf has one or more lines containing "host = ", the output will be something like this: # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.Domain.tld_computers # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.domain.tld_deployments # ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.domain.tld_status # -# To manually add, symlink forefront_ to forefront_MyHost.domain.tld_computers, -# forefront_MyHost.domain.tld_deployments and forefront_MyHost.domain.tld_status +# To manually add, symlink forefront_ to forefront_MyHost.domain.tld_computers, +# forefront_MyHost.domain.tld_deployments and forefront_MyHost.domain.tld_status # # Add your DSN and user/password to /etc/munin/plugin-conf.d/munin-node: # [forefront_MyHost.domain.tld_*] # env.dsn MyHost # env.dbuser # env.dbpass -# +# # On your munin server, add this to /etc/munin/munin.conf # # [MyHost.domain.tld] @@ -211,7 +211,7 @@ EOF my %profiles = &deployments_general(); foreach my $policy (sort(keys(%profiles))) { my $field = encode_base64($policy); - chomp($field); + chomp($field); print $field . ".label " . $profiles{$policy}{'name'} . "\n"; print $field . ".draw LINE2\n"; print $field . ".info Numer of computers having the " .$profiles{$policy}{'name'} . " profile.\n"; @@ -268,7 +268,7 @@ sub status_fetch { while(my @row = $sth->fetchrow_array) { $alerts{$row[0]} = 0; } - $sth = $dbh->prepare("SELECT al.Level, COUNT(a.AlertLevel) FROM Alert a, AlertLevel al WHERE a.AlertLevel = al.Level AND a.ResolutionState <> 255 GROUP BY al.Level", + $sth = $dbh->prepare("SELECT al.Level, COUNT(a.AlertLevel) FROM Alert a, AlertLevel al WHERE a.AlertLevel = al.Level AND a.ResolutionState <> 255 GROUP BY al.Level", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { @@ -300,7 +300,7 @@ sub deployments_general { $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'instance'} = ""; $profiles{'d3b75be9-7125-4db1-8b24-93004bd9d88e'}{'count'} = 0; my $dbh = DBI->connect("DBI:Sybase:$dsn", $dbuser, $dbpass, { PrintError => 1, AutoCommit => 1}); - + my $sth = $dbh->prepare("SELECT Id, Name, LatestInstanceID From fcs_Profiles", {odbc_exec_direct => 1}); $sth->execute(); while(my @row = $sth->fetchrow_array) { diff --git a/plugins/senderscore/senderscore b/plugins/senderscore/senderscore index 4c6eb236..a7c2886f 100755 --- a/plugins/senderscore/senderscore +++ b/plugins/senderscore/senderscore @@ -2,7 +2,7 @@ # # Here's a plugin which lets you monitor your senderscore.org reputation. For # people sending a large amount of email - this might be useful. -# +# # Config env var used: # [senderscore] # env.senderscore_check_ip xxx.yyy.zzz.ttt @@ -75,7 +75,7 @@ if __name__ == "__main__": if os.environ['senderscore_check_ip'] == '': print "env.senderscore_check_ip not defined in munin config" sys.exit(1) - + ip = os.environ['senderscore_check_ip'] if len(sys.argv) > 1 and sys.argv[1] != '': diff --git a/plugins/sendmail/sendmail_mailq b/plugins/sendmail/sendmail_mailq index 64139226..e2bd69f7 100755 --- a/plugins/sendmail/sendmail_mailq +++ b/plugins/sendmail/sendmail_mailq @@ -46,7 +46,7 @@ Jose-Marcio Martins da Cruz - mailto:Jose-Marcio.Martins@mines-paristech.fr Ecole Nationale Superieure des Mines de Paris -=head1 VERSION +=head1 VERSION 1.0 - Jan, 04, 2014 diff --git a/plugins/sensors/alertme_keyfobsathome b/plugins/sensors/alertme_keyfobsathome index 1fbe3a1b..35179606 100755 --- a/plugins/sensors/alertme_keyfobsathome +++ b/plugins/sensors/alertme_keyfobsathome @@ -4,7 +4,7 @@ # alertme_power relies on http://code.google.com/p/alertmepi/ # to have been installed and working # -# 20110120 - update to use correct mixed case for AlertMe and remove cruft +# 20110120 - update to use correct mixed case for AlertMe and remove cruft # left over from creating this plugin from another script # # add to the plugins-conf.d/munin so that it can read the /etc/alertme files @@ -26,7 +26,7 @@ echo -n "KeyfobsAtHome.value " KFC=`/usr/local/bin/alertmepi.pl -k | wc -l` if [ $? -eq 0 ] ; then - echo $KFC + echo $KFC else echo U fi diff --git a/plugins/sensors/alertme_power b/plugins/sensors/alertme_power index d64aec40..303d03c8 100755 --- a/plugins/sensors/alertme_power +++ b/plugins/sensors/alertme_power @@ -4,7 +4,7 @@ # alertme_power relies on http://code.google.com/p/alertmepi/ # to have been installed and working # -# 20110120 - update to use correct mixed case for AlertMe and remove cruft +# 20110120 - update to use correct mixed case for AlertMe and remove cruft # left over from creating this plugin from another script # # add to the plugins-conf.d/munin so that it can read the /etc/alertme files diff --git a/plugins/sensors/allnet__ b/plugins/sensors/allnet__ index d27d309f..dcd70a6d 100755 --- a/plugins/sensors/allnet__ +++ b/plugins/sensors/allnet__ @@ -77,7 +77,7 @@ use XML::Simple; { package AuthAgent; use base 'LWP::UserAgent'; - + sub get_basic_credentials { if (defined($ENV{'username'}) && defined($ENV{'password'})) { return $ENV{'username'}, $ENV{'password'}; diff --git a/plugins/sensors/ip_thermo_125 b/plugins/sensors/ip_thermo_125 index 01dbe765..3ba4fce3 100755 --- a/plugins/sensors/ip_thermo_125 +++ b/plugins/sensors/ip_thermo_125 @@ -1,7 +1,7 @@ #!/usr/bin/perl # # Copyright (C) 2008 Yuriy Sabov -# Version 0.1 +# Version 0.1 # # Plugin to fetch temperature from "IP Thermo 125" ethernet thermometer # available at http://www.procontrol.hu/GyartasFejlesztes/Termekeink/IPThermoSimple/IPThermo125_eng.htm @@ -18,7 +18,7 @@ my ($hostname, $port, $line, $telnet); # "C" = Celsius, "F" = Fahrenheit -my $unit = $ENV{unit} || "C"; +my $unit = $ENV{unit} || "C"; $hostname = "10.10.10.10"; $port = 23; @@ -38,7 +38,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_args --base 1000 -l 0\n"; print "graph_category sensors\n"; print "graph_info This graph shows temperature using IP Thermo 125 server.\n"; - + if ($unit =~ /F/) { print "graph_vlabel temp in °F\n"; @@ -48,8 +48,8 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_vlabel temp in °C\n"; } print "temperature.label temperature\n"; - - exit 0 + + exit 0; } $telnet = new Net::Telnet (Telnetmode => 0); diff --git a/plugins/sensors/mbmon b/plugins/sensors/mbmon index da8c4480..0044bafe 100755 --- a/plugins/sensors/mbmon +++ b/plugins/sensors/mbmon @@ -37,16 +37,16 @@ temp2.label Temperature2 EOF ## more info from mbmon -# fan0 -# fan1 -# fan2 -# vc0 -# vc1 -# v33 -# v50p -# V12P -# V12N -# V50N +# fan0 +# fan1 +# fan2 +# vc0 +# vc1 +# v33 +# v50p +# V12P +# V12N +# V50N } else { my(@res) = `$mbmon -r -c 1`; diff --git a/plugins/sensors/nvclock b/plugins/sensors/nvclock index c15e59b7..c140c7c6 100755 --- a/plugins/sensors/nvclock +++ b/plugins/sensors/nvclock @@ -1,5 +1,5 @@ #!/bin/sh -# +# # plugin to monitor a nvidia graphic card temperature(s) # dynamically shows the sensor name (gpu, board...) # written by Dju diff --git a/plugins/sensors/voltcraft_tcm220_ b/plugins/sensors/voltcraft_tcm220_ index 130503ee..b2da4e2c 100755 --- a/plugins/sensors/voltcraft_tcm220_ +++ b/plugins/sensors/voltcraft_tcm220_ @@ -1,10 +1,10 @@ #!/usr/bin/perl -w ########################################################################################################## # -# Munin plugin to monitor values reported by a Voltcraft TCM 220 or 320 temperature swicth module -# connected to a serial port. A cable needs to be soldered to the module's dedicated data output pads: -# pad no. 9 (GND) to pin no. 5 of the serial port, pad no. 17 (DATA_OUT) to pin no. 2 of the serial port -# and pad no. 18 (CLOCK) to pin no. 3 of the serial port. For more details, check out the documentation +# Munin plugin to monitor values reported by a Voltcraft TCM 220 or 320 temperature swicth module +# connected to a serial port. A cable needs to be soldered to the module's dedicated data output pads: +# pad no. 9 (GND) to pin no. 5 of the serial port, pad no. 17 (DATA_OUT) to pin no. 2 of the serial port +# and pad no. 18 (CLOCK) to pin no. 3 of the serial port. For more details, check out the documentation # that came with your Voltcraft TCM 220/320 temperature swicth module. # # In order to use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins) @@ -15,7 +15,7 @@ # # Important: make sure to use the same names in your symlinks and other config places! # -# 2. In /etc/munin/plugin-conf.d/munin-node add the following, to be able to contact the modules via +# 2. In /etc/munin/plugin-conf.d/munin-node add the following, to be able to contact the modules via # serial ports (obviously replacing these with your own data): # # [voltcraft_tcm220_serverroom1] @@ -30,11 +30,11 @@ # # 3. Restart the munin node by 'service munin-node restart'. # -# If all went well, after 5 minutes or so you should have tne new module's graphs listed on the Web +# If all went well, after 5 minutes or so you should have tne new module's graphs listed on the Web # Interface of Munin. # # Note: the plugin waits maximum 11 seconds for the module to report the current temperature values to -# the serial port. If no value is reported, or the serial cable is unplugged, it returns Undefined to +# the serial port. If no value is reported, or the serial cable is unplugged, it returns Undefined to # Munin. According to the documentation, the module can be configured to report values every 2 seconds # instead the 10 seconds default - this can be used to speed up the data acquisition process, however # it can decrease battery life (with default values, two AAA alkaline batteries last cca 2 years). @@ -119,7 +119,7 @@ my $ch1temp = "U"; my $ch2temp = "U"; if ($response // "") { - my $hex = unpack 'H*', $response; + my $hex = unpack 'H*', $response; # print "$hex\n"; #$hex should be something like "abc102999babc2017677" for +29.9 and +17.6 my $ch1sum = substr($hex, 4, 1) + substr($hex, 5, 1) + substr($hex, 6, 1) + substr($hex, 7, 1); diff --git a/plugins/sfsnmp/snmp__sfsnmp_fan b/plugins/sfsnmp/snmp__sfsnmp_fan index fac3751d..09f6894e 100755 --- a/plugins/sfsnmp/snmp__sfsnmp_fan +++ b/plugins/sfsnmp/snmp__sfsnmp_fan @@ -5,17 +5,17 @@ # Copyright (C) 2010 Nils Henrik Tvetene # # Author: Nils Henrik Tvetene -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -53,7 +53,7 @@ Nils Henrik Tvetene GPLv2 -=cut +=cut use strict; use warnings; diff --git a/plugins/sfsnmp/snmp__sfsnmp_temp b/plugins/sfsnmp/snmp__sfsnmp_temp index 9161b6a9..a40de986 100755 --- a/plugins/sfsnmp/snmp__sfsnmp_temp +++ b/plugins/sfsnmp/snmp__sfsnmp_temp @@ -5,17 +5,17 @@ # Copyright (C) 2010 Nils Henrik Tvetene # # Author: Nils Henrik Tvetene -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -53,7 +53,7 @@ Nils Henrik Tvetene GPLv2 -=cut +=cut use strict; use warnings; diff --git a/plugins/sfsnmp/snmp__sfsnmp_volt b/plugins/sfsnmp/snmp__sfsnmp_volt index ecb0c7f6..4c274bdd 100755 --- a/plugins/sfsnmp/snmp__sfsnmp_volt +++ b/plugins/sfsnmp/snmp__sfsnmp_volt @@ -5,17 +5,17 @@ # Copyright (C) 2010 Nils Henrik Tvetene # # Author: Nils Henrik Tvetene -# +# # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -53,7 +53,7 @@ Nils Henrik Tvetene GPLv2 -=cut +=cut use strict; use warnings; diff --git a/plugins/sge/sge_queue_ b/plugins/sge/sge_queue_ index 791ee9ae..6b57ab6b 100755 --- a/plugins/sge/sge_queue_ +++ b/plugins/sge/sge_queue_ @@ -22,7 +22,7 @@ # # env.sge_settings - Path to SGE settings.sh script, defaults to /opt/sge/default/common/settings.sh # env.title - Graph title, overrides "SGE Queue state". -# env.options - Additional command line options to qstat. +# env.options - Additional command line options to qstat. # # Revisions: # v1.0 2009-07-19 @@ -93,7 +93,7 @@ if [ "$1" = "autoconf" ]; then if which qstat > /dev/null; then echo "yes" exit 0 - else + else echo "no" exit 1 fi @@ -102,8 +102,8 @@ fi # env.options OPTIONS="-g c -q $QUEUE $options" -# qstat -g c example output: -# CLUSTER QUEUE CQLOAD USED RES AVAIL TOTAL aoACDS cdsuE +# qstat -g c example output: +# CLUSTER QUEUE CQLOAD USED RES AVAIL TOTAL aoACDS cdsuE # -------------------------------------------------------------------------------- # all.q 0.00 16 8 48 64 0 0 qstat $OPTIONS | tail -n 1 | awk '{print "unavailable.value " $8 "\nreserved.value " $4 "\nused.value " $3 "\nfree.value " $5-$4}' diff --git a/plugins/sge/sge_queue_xml_ b/plugins/sge/sge_queue_xml_ index b20d088f..6adc3e2c 100755 --- a/plugins/sge/sge_queue_xml_ +++ b/plugins/sge/sge_queue_xml_ @@ -21,7 +21,7 @@ # # env.sge_settings - Path to SGE settings.sh script, defaults to /opt/sge/default/common/settings.sh # env.title - Graph title, overrides "SGE Queue state". -# env.options - Additional command line options to qstat. +# env.options - Additional command line options to qstat. # env.queues - list of queues to summarize # # Revisions: @@ -35,7 +35,7 @@ SGE_SETTINGS=${sge_settings:-/opt/sge/default/common/settings.sh} -# queues to monitor +# queues to monitor # priority 1: queue name in symlink QUEUE=${0##*_} # priority 2: queue names from environment @@ -92,17 +92,17 @@ if [ "$1" = "autoconf" ]; then if [ -n "$QSTAT" -a -n "$XMLSTARLET" ]; then echo "yes" exit 0 - else + else echo "no" exit 1 fi fi -# check requirements -[ -z "$QSTAT" ] && { echo "qstat not found" 1>&2 ; exit 1; } -[ -z "$XMLSTARLET" ] && { echo "xmlstarlet not found" 1>&2 ; exit 1; } +# check requirements +[ -z "$QSTAT" ] && { echo "qstat not found" 1>&2 ; exit 1; } +[ -z "$XMLSTARLET" ] && { echo "xmlstarlet not found" 1>&2 ; exit 1; } -ALL_QUEUES=$( $QSTAT -g c -xml | $XMLSTARLET sel -T -t -m "//cluster_queue_summary/name" -v "node()" -o "," ) +ALL_QUEUES=$( $QSTAT -g c -xml | $XMLSTARLET sel -T -t -m "//cluster_queue_summary/name" -v "node()" -o "," ) [ "$QUEUE" == "Summary" ] && QUEUE="$ALL_QUEUES" if [ "$1" = "suggest" ]; then @@ -130,7 +130,7 @@ xmldemangle() { printvalues() { local IFSBAK=$IFS; unset IFS for i in ${!_SGE_QUEUE_KEYS[@]} - do + do echo "${_SGE_QUEUE_KEYS[$i]}.value ${_SGE_QUEUE_VALUES[$i]}" done IFS=$IFSBAK @@ -153,7 +153,7 @@ do xmldemangle || echo "Error on QUEUE: $qu" 1>&2 for i in ${!_SGE_QUEUE_KEYS[@]} - do + do _SGE_QUEUE_VALUES[$i]=${_SGE_QUEUE_VALUES[$i]:-0} let "_SGE_QUEUE_VALUES[$i] += ${_SGE_XML[$i]:-0}" done diff --git a/plugins/shoutcast/shoutcast b/plugins/shoutcast/shoutcast index 386bf225..b27bb3c7 100755 --- a/plugins/shoutcast/shoutcast +++ b/plugins/shoutcast/shoutcast @@ -85,7 +85,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "ax_used_connections.label Peak Listeners\n"; print "ax_used_connections.type GAUGE\n"; - # DJ-Online tag must be eq to Peak Listeners + # DJ-Online tag must be eq to Peak Listeners print "djonline_tag.draw AREA\n"; print "djonline_tag.colour e5ff60\n"; print "djonline_tag.min 0\n"; diff --git a/plugins/shoutcast/shoutcast2_multi b/plugins/shoutcast/shoutcast2_multi index 5f922ba1..310a2cf7 100755 --- a/plugins/shoutcast/shoutcast2_multi +++ b/plugins/shoutcast/shoutcast2_multi @@ -200,7 +200,7 @@ sub print_active_data { foreach my $sid (sort keys %{$sidDataRef}) { print "multigraph shoutcast2_active.active_sid_$sid\n"; foreach my $dsrc (@{$graphsRef->{sid_active}->{datasrc}}) { - print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; + print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; if ($sidDataRef->{$sid}->{$dsrc->{xmlkey}} == 1) { $globalActive = 1; } @@ -216,7 +216,7 @@ sub print_active_data { =head2 print_listener_data This subroutine prints out the listener graph values for each stream and ultimately - adds all of the current users together to show that against the maxserver count in + adds all of the current users together to show that against the maxserver count in the global graph. Clicking on the global graph will reveal a bit more information about the users on a stream by stream basis. @@ -228,7 +228,7 @@ sub print_listener_data { foreach my $sid (sort keys %{$sidDataRef}) { print "multigraph shoutcast2_listeners.listeners_sid_$sid\n"; foreach my $dsrc (@{$graphsRef->{sid_listeners}->{datasrc}}) { - print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; + print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; if ($dsrc->{name} eq 'currlisteners') { $globalListeners += $sidDataRef->{$sid}->{$dsrc->{xmlkey}}; } @@ -283,7 +283,7 @@ sub config { } print_active_config($sidDataRef); print_listener_config($sidDataRef); - return; + return; } =head2 print_active_config @@ -409,7 +409,7 @@ sub fetch_sid_data { my $response = $ua->get($url); if ($response->is_success) { my $returnRef = XMLin($response->decoded_content); - return (1, $returnRef); + return (1, $returnRef); } else { return (0, $response->status_line); } @@ -431,7 +431,7 @@ sub fetch_admin_data { if ($response->is_success) { my $returnRef = XMLin($response->decoded_content); if (($returnRef->{STREAMCONFIGS}->{TOTALCONFIGS} > 0) && (defined($returnRef->{STREAMCONFIGS}->{STREAMCONFIG}))) { - return (1, $returnRef); + return (1, $returnRef); } else { return (0, 'Unable to Detect any Stream Configurations'); } diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes index 81d67811..e4f38a24 100755 --- a/plugins/sickbeard/sickbeard_episodes +++ b/plugins/sickbeard/sickbeard_episodes @@ -7,7 +7,7 @@ # # Sick-Beard : http://sickbeard.com/ # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -27,7 +27,7 @@ # [sickbeard*] # env.host http://host:port/ # env.api apikey -# +# use strict; use JSON::Any; diff --git a/plugins/sickbeard/sickbeard_shows b/plugins/sickbeard/sickbeard_shows index 2407d83e..ed755ac5 100755 --- a/plugins/sickbeard/sickbeard_shows +++ b/plugins/sickbeard/sickbeard_shows @@ -7,7 +7,7 @@ # # Sick-Beard : http://sickbeard.com/ # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -27,7 +27,7 @@ # [sickbeard*] # env.host http://host:port/ # env.api apikey -# +# use strict; use JSON::Any; diff --git a/plugins/smf/smf_errors b/plugins/smf/smf_errors index 5a2dc249..a5f88bfa 100755 --- a/plugins/smf/smf_errors +++ b/plugins/smf/smf_errors @@ -53,5 +53,5 @@ EOC #Errors count my $errors = `$MYSQL $MYSQLOPTS -e 'SELECT COUNT(*) FROM ${DATABASE}.${PREFIX}log_errors'`; -$errors =~ /(\d+)/; +$errors =~ /(\d+)/; print "errors.value ".$1."\n"; diff --git a/plugins/smf/smf_mail_queue b/plugins/smf/smf_mail_queue index 99def0a7..4d40c605 100755 --- a/plugins/smf/smf_mail_queue +++ b/plugins/smf/smf_mail_queue @@ -53,5 +53,5 @@ EOC #Emails count my $emails = `$MYSQL $MYSQLOPTS -e 'SELECT COUNT(*) FROM ${DATABASE}.${PREFIX}mail_queue'`; -$emails =~ /(\d+)/; +$emails =~ /(\d+)/; print "emails.value ".$1."\n"; diff --git a/plugins/smf/smf_online b/plugins/smf/smf_online index e9e609b7..2739e5a2 100755 --- a/plugins/smf/smf_online +++ b/plugins/smf/smf_online @@ -65,12 +65,12 @@ EOC #Guests count my $guests = `$MYSQL $MYSQLOPTS -e 'SELECT COUNT(*) FROM ${DATABASE}.${PREFIX}log_online WHERE id_member = 0 AND id_spider = 0'`; -$guests =~ /(\d+)/; +$guests =~ /(\d+)/; print "guests.value ".$1."\n"; #Spiders count my $spiders = `$MYSQL $MYSQLOPTS -e 'SELECT COUNT(*) FROM ${DATABASE}.${PREFIX}log_online WHERE id_spider > 0'`; -$spiders =~ /(\d+)/; +$spiders =~ /(\d+)/; print "spiders.value ".$1."\n"; #Users count diff --git a/plugins/smf/smf_stats b/plugins/smf/smf_stats index 7e25f691..47758732 100755 --- a/plugins/smf/smf_stats +++ b/plugins/smf/smf_stats @@ -59,12 +59,12 @@ EOC #Members count my $members = `$MYSQL $MYSQLOPTS -e 'SELECT value FROM ${DATABASE}.${PREFIX}settings WHERE variable = "totalMembers"'`; -$members =~ /(\d+)/; +$members =~ /(\d+)/; print "members.value ".$1."\n"; #Messages count my $messages = `$MYSQL $MYSQLOPTS -e 'SELECT value FROM ${DATABASE}.${PREFIX}settings WHERE variable="totalMessages"'`; -$messages =~ /(\d+)/; +$messages =~ /(\d+)/; print "messages.value ".$1."\n"; #Topics count diff --git a/plugins/smstools/smstools_ b/plugins/smstools/smstools_ index 0066d9b8..e5e4644d 100755 --- a/plugins/smstools/smstools_ +++ b/plugins/smstools/smstools_ @@ -16,7 +16,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# +# # Plugin to monitor an smstools installation # # Usage: Link or copy into the munin-node plugin directory, name it according @@ -85,8 +85,8 @@ fi # If run with the "config"-parameter, give out information on how the -# graphs should look. - +# graphs should look. + if [ "$1" = "config" ]; then echo 'graph_title SMSTools Report for '${MODEM} echo 'graph_args --base 1000 -l 0' diff --git a/plugins/snmp/multi_snmp_querier b/plugins/snmp/multi_snmp_querier index 0048c6af..4894ff77 100755 --- a/plugins/snmp/multi_snmp_querier +++ b/plugins/snmp/multi_snmp_querier @@ -6,8 +6,8 @@ multi_snmp_querier - Munin plugin to query several SNMP hosts =head1 SYNOPSIS -This plugin is meant to be called from Munin. You should at least set the -'hosts' environment variable from Munin's configuration (i.e. +This plugin is meant to be called from Munin. You should at least set the +'hosts' environment variable from Munin's configuration (i.e. /etc/munin/munin.conf) to specify which hosts and how to query. =head1 DESCRIPTION @@ -20,7 +20,7 @@ This plugin is meant to be called from Munin. You should at least set the Which SNMP OID should we query on; it defaults to 1.3.6.1.2.1.43.10.2.1.4.1.1 (total printed pages - of course, it only -makes sense to query a printer on this ;-) ). +makes sense to query a printer on this ;-) ). Other known and useful OIDs for printers are 1.3.6.1.2.1.43.11.1.1.9.1.1 (total number of pages printed with this @@ -111,7 +111,7 @@ $oid = $ENV{snmp_oid} || $defaults{oid}; total => 'Total', units => 'pages' }, - '1.3.6.1.2.1.43.11.1.1.8.1.1' => + '1.3.6.1.2.1.43.11.1.1.8.1.1' => { title => 'Total projected capacity of this cartridge', vlabel => 'Total capacity', category => 'printing', @@ -120,7 +120,7 @@ $oid = $ENV{snmp_oid} || $defaults{oid}; total => 'Total', units => 'pages' }, - '1.3.6.1.2.1.43.11.1.1.9.1.1' => + '1.3.6.1.2.1.43.11.1.1.9.1.1' => { title => 'Pages printed with this cartridge', vlabel => 'Printed pages', category => 'printing', @@ -129,7 +129,7 @@ $oid = $ENV{snmp_oid} || $defaults{oid}; total => 'Total', units => 'pages' }, - 'default' => + 'default' => { title => "Results for SNMP OID $oid", vlabel => 'units', category => 'Other', @@ -144,7 +144,7 @@ die "Hosts not set - cannot continue" unless @hosts; $cmd_arg = $ARGV[0] || ''; if($cmd_arg eq "config") { my $labels = $known_oids{$oid} || $known_oids{default}; - # See http://munin.projects.linpro.no/wiki/HowToWritePlugins for + # See http://munin.projects.linpro.no/wiki/HowToWritePlugins for # explanation on the following fields print "graph_title $labels->{title}\n"; print "graph_args --base 1000 -l 0\n"; @@ -184,7 +184,7 @@ sub ck_alive{ $ping = Net::Ping->new("tcp", 1); $ping->ping($host); } - + sub get_hosts { # Hosts are defined in the 'hosts' environment variable. It's a list of # hosts (and optionally ports) - We parse the list and arrange it neatly diff --git a/plugins/snmp/snmp__brocade_ifs b/plugins/snmp/snmp__brocade_ifs index de555afd..b43691d2 100755 --- a/plugins/snmp/snmp__brocade_ifs +++ b/plugins/snmp/snmp__brocade_ifs @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Munin plugin which reports selected counters regarding ports on a +Munin plugin which reports selected counters regarding ports on a Brocade SAN FC-switch. Only enabled ports are considered. The counters shown: @@ -14,7 +14,7 @@ enc_out: Encoding errors outside FC frame. enc_out_per_mframe: As above, but per million frames of traffic. If there is a high number for this counter, it could reflect: - - If there is also a high value for + - If there is also a high value for rx_crcs for the port, then there is likely a GBIC/SFP problem. - If there the value of rx_crcs for the port @@ -22,7 +22,7 @@ enc_out_per_mframe: As above, but per million frames of traffic. problem. rx_crcs: CRC errors detected in received frames. - Together with enc_out errors, CRC errors + Together with enc_out errors, CRC errors indicate a GBIC/SFP problem. bits: Number of bits transmitted(tx)/received(rx) @@ -38,7 +38,7 @@ snmp_HOSTNAME_brocade_ifs """ # Note: In the SNMP output from brocade switches, the interesting -# counters are named with numbers starting with 1, while the +# counters are named with numbers starting with 1, while the # ports' real names on the box and in the administration interface # start with 0. And there doesn't seem to be a way to map between # ifDesc and the interesting crc and enc_out counters :-( @@ -63,7 +63,7 @@ snmp_HOSTNAME_brocade_ifs # Released according to the "New BSD License" AKA the 3-clause # BSD License: -# ==================================================================== +# ==================================================================== # Copyright (c) 2011, Danish National Board of Health. # All rights reserved. # @@ -88,7 +88,7 @@ snmp_HOSTNAME_brocade_ifs # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ==================================================================== +# ==================================================================== # $Id: brocade_san_switch_ports_ 15443 2011-03-03 12:23:56Z tra $ @@ -140,7 +140,7 @@ def debug(msg): print('Debug: %s\n' % msg) # Break OID-string in to a tuple of elements -def oidstr2tuple(oidstr): +def oidstr2tuple(oidstr): int_list = [ int(s) for s in oidstr.split('.') ] return tuple(int_list) diff --git a/plugins/snmp/snmp__cpu_usage b/plugins/snmp/snmp__cpu_usage index 00aa86f2..2bb58ed6 100755 --- a/plugins/snmp/snmp__cpu_usage +++ b/plugins/snmp/snmp__cpu_usage @@ -119,7 +119,7 @@ my $cpuIdle = "1.3.6.1.4.1.2021.11.11"; my $cpu = 0; -my %cpuCounters = ( +my %cpuCounters = ( cpuUser => "1.3.6.1.4.1.2021.11.9.$cpu", # The percentage of CPU time spent processing user-level code, calculated over the last minute cpuSystem => "1.3.6.1.4.1.2021.11.10.$cpu", # The percentage of CPU time spent processing system-level code, calculated over the last minute cpuIdle => "1.3.6.1.4.1.2021.11.11.$cpu", # The percentage of processor time spent idle, calculated over the last minute @@ -147,7 +147,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") foreach my $c (keys %cpuCounters) { print $c . ".label $c\n"; print $c . ".type GAUGE\n"; - if ($firstCounter) { + if ($firstCounter) { print $c . ".draw AREA\n"; $firstCounter = 0; } else { @@ -156,7 +156,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") print $c . ".min 0\n"; } - exit 0; + exit 0; } foreach my $c (keys %cpuCounters) { diff --git a/plugins/snmp/snmp__cyberpower b/plugins/snmp/snmp__cyberpower index 8b3c9ee3..c6797bb8 100755 --- a/plugins/snmp/snmp__cyberpower +++ b/plugins/snmp/snmp__cyberpower @@ -75,7 +75,7 @@ charge.type GAUGE charge.min 0 charge.max 100 "; - + print "multigraph cyberpower_runtime graph_title CyberPower UPS Runtime graph_info This graph shows expected runtime informatiom. @@ -86,7 +86,7 @@ graph_vlabel minutes runtime.draw AREA runtime.type GAUGE "; - + print "multigraph cyberpower_voltage graph_title CyberPower UPS Voltages graph_info This graph shows voltage information. @@ -101,7 +101,7 @@ input.type GAUGE output.draw LINE1 output.type GAUGE "; - + if(oidExists(oid_cps_env_temp) && oidExists(oid_cps_env_humidity)){ print "multigraph cyberpower_environment graph_title CyberPower UPS Environment @@ -120,7 +120,7 @@ humidity.min 0 humidity.max 100 "; } - + exit 0; } @@ -177,7 +177,7 @@ sub oidExists { } my $oid = $_[0]; my $val = $session->get_single($oid); - + if(!length $val || $val eq 'noSuchInstance' || $val eq 'U'){ return(0); }else{ diff --git a/plugins/snmp/snmp__fn b/plugins/snmp/snmp__fn index e6122725..521b19a9 100755 --- a/plugins/snmp/snmp__fn +++ b/plugins/snmp/snmp__fn @@ -3,11 +3,11 @@ # File: snmp__fn # Description: SNMP plugin to monitor open sessions, sslvpn, CPU and Memory on a # Fortinet Fortigate firewall. -# +# # Author: Thom Diener # License: This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated +# as published by the Free Software Foundation; version 2 dated # June, 1991. # # Version: v1.00 30.10.2011 First draft of the fortigate plugin @@ -22,21 +22,21 @@ # (Example: ln -s /usr/share/munin/plugins/snmp__fn \ # /etc/munin/plugins/snmp_foo.example.com_fn) # -# Add global community string +# Add global community string # vi /etc/munin/plugin-conf.d/munin-node # [snmp_*] # env.community private # timeout 45 # In case low latency or timeout # -# Fortigate Activate snmp on your Fortigate firewall. -# Fortigate documentation at https://support.fortinet.com +# Fortigate Activate snmp on your Fortigate firewall. +# Fortigate documentation at https://support.fortinet.com # # MIB Download and copy the original Fortigate MIB definition files to: # /usr/share/snmp/mibs/FORTINET-CORE-MIB.mib.txt # /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib # -# Testing This plugin has been tested with the following OS/software: -# +# Testing This plugin has been tested with the following OS/software: +# # Appliance/Firmware: # Fortigate-50B 3.00-b0662(MR6 Patch 1) work with v1.00-1.02 # Fortigate-50B 3.00-b0678(MR6 Patch 6) work with v1.00-1.02 @@ -44,10 +44,10 @@ # Fortigate-50B 4.00-b0217(MR1 Patch 10) work with v1.00-1.02 # Fortigate-50B 4.00-b0217(MR2 Patch 4) work with v1.00-1.02 # Fortigate-50B 4.00-b0521(MR3 Patch 6) work with v1.03 -# +# # Munin-Version: -# Munin 1.4.4 (1.4.4-1ubuntu1) -# OS-Version: +# Munin 1.4.4 (1.4.4-1ubuntu1) +# OS-Version: # Ubuntu 10.04.3 LTS (lucid) x86_32/64 # #%# family=manual @@ -148,7 +148,7 @@ config() echo "graph_title $UNIT - SSLvpn Sessions" echo 'graph_category fw' echo 'graph_vlabel Sessions/Users' - echo 'graph_info Loged in users with SSLvpn (WebSession or Tunnel-Mode)' + echo 'graph_info Loged in users with SSLvpn (WebSession or Tunnel-Mode)' echo 'fortiuser.label Users' echo 'fortiuser.info Loged in SSLvpn users' echo 'fortiwebs.label WebSessions' diff --git a/plugins/snmp/snmp__webthermometer b/plugins/snmp/snmp__webthermometer index 34a2a827..b0b4f51b 100755 --- a/plugins/snmp/snmp__webthermometer +++ b/plugins/snmp/snmp__webthermometer @@ -72,7 +72,7 @@ if [ "$1" = "config" ]; then SENSOR_2_CRITICAL=`$SNMPGET WebGraph-2xThermometer-MIB::wtWebioAn2GraphAlarmMax.2 | sed s/.*STRING:// | sed s/\"//g` # echo the result to munin - echo "graph_title $GRAPH_TITLE" + echo "graph_title $GRAPH_TITLE" echo "graph_info $GRAPH_INFO" echo "graph_vlabel $GRAPH_VLABEL" echo "Sensor_1.label $SENSOR_1_LABEL" diff --git a/plugins/snmp/snmp__wmsconnectedplayers b/plugins/snmp/snmp__wmsconnectedplayers index 506f31f4..2c3f9a1a 100755 --- a/plugins/snmp/snmp__wmsconnectedplayers +++ b/plugins/snmp/snmp__wmsconnectedplayers @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -86,7 +86,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsConnectedPlayers -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsConnectedPlayers graph_category streaming graph_info This graph shows wmsConnectedPlayers. diff --git a/plugins/snmp/snmp__wmsplayerallocatedbandwidth b/plugins/snmp/snmp__wmsplayerallocatedbandwidth index a29cc123..8f5209ee 100755 --- a/plugins/snmp/snmp__wmsplayerallocatedbandwidth +++ b/plugins/snmp/snmp__wmsplayerallocatedbandwidth @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -92,7 +92,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsPlayerAllocatedBandwidth -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsPlayerAllocatedBandwidth graph_category streaming graph_info This graph shows wmsPlayerAllocatedBandwidth. diff --git a/plugins/snmp/snmp__wmsstreaminghttpplayers b/plugins/snmp/snmp__wmsstreaminghttpplayers index c3a8492e..3118478c 100755 --- a/plugins/snmp/snmp__wmsstreaminghttpplayers +++ b/plugins/snmp/snmp__wmsstreaminghttpplayers @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -87,7 +87,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsStreamingHttpPlayers -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsStreamingHttpPlayers graph_category streaming graph_info This graph shows wmsStreamingHttpPlayers. diff --git a/plugins/snmp/snmp__wmsstreamingmmsplayers b/plugins/snmp/snmp__wmsstreamingmmsplayers index 2608e731..29f8e258 100755 --- a/plugins/snmp/snmp__wmsstreamingmmsplayers +++ b/plugins/snmp/snmp__wmsstreamingmmsplayers @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -87,7 +87,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsStreamingMmsPlayers -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsStreamingMmsPlayers graph_category streaming graph_info This graph shows wmsStreamingMmsPlayers. diff --git a/plugins/snmp/snmp__wmsstreamingplayers b/plugins/snmp/snmp__wmsstreamingplayers index 7f93b605..2cf4c8c6 100755 --- a/plugins/snmp/snmp__wmsstreamingplayers +++ b/plugins/snmp/snmp__wmsstreamingplayers @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -84,7 +84,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsStreamingPlayers -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsStreamingPlayers graph_category streaming graph_info This graph shows wmsStreamingPlayers. diff --git a/plugins/snmp/snmp__wmsstreamingrtspplayers b/plugins/snmp/snmp__wmsstreamingrtspplayers index 333a8283..300b24cb 100755 --- a/plugins/snmp/snmp__wmsstreamingrtspplayers +++ b/plugins/snmp/snmp__wmsstreamingrtspplayers @@ -16,13 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Derived from SNMP plugin code written by +# Derived from SNMP plugin code written by # Jimmy Olsen, Dagfinn Ilmari Mannsaaker # ####################################################################### # # You must enable SNMP for Windows Media Services by running: -# +# # regsvr32 "%systemroot%\system32\windows media\server\wmssnmp.dll # ####################################################################### @@ -86,7 +86,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; print "graph_title wmsStreamingRtspPlayers -graph_args --base 1000 -l 0 +graph_args --base 1000 -l 0 graph_vlabel wmsStreamingRtspPlayers graph_category streaming graph_info This graph shows wmsStreamingRtspPlayers. diff --git a/plugins/solaris/forks b/plugins/solaris/forks index 1744bdf3..97874d4b 100755 --- a/plugins/solaris/forks +++ b/plugins/solaris/forks @@ -66,7 +66,7 @@ config() { echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' # print data source attributes - # split line into field,type,draw,label + # split line into field,type,draw,label local field type draw label echo "$data_attr" | while read -r field type draw label do diff --git a/plugins/solaris/fsstat_act_ b/plugins/solaris/fsstat_act_ index 3332e0a8..71b6f759 100755 --- a/plugins/solaris/fsstat_act_ +++ b/plugins/solaris/fsstat_act_ @@ -12,9 +12,9 @@ Tested with Solaris 10 and 11. And should work with Solaris 10 11/06 or above. Note: - In Solaris 11, fsstat command can get stats for each non-global zones in + In Solaris 11, fsstat command can get stats for each non-global zones in global zone. (see man fsstat) - In global zone, this plugin gets stats of only global zone. + In global zone, this plugin gets stats of only global zone. In non-global zones, this plugin reports stats of the non-global zones. =head1 CONFIGURATION @@ -63,7 +63,7 @@ stat_regexp='/^(?!(class|crtime|snaptime|.*_bytes))/' # Graph settings global_attr=" graph_title File system statictics - Activities of $( echo "$fs_type" | tr '[:lower:]' '[:upper:]' ) - graph_category disk + graph_category disk graph_args --base 1000 graph_vlabel Counts per second graph_info File system statictics - Activities of $( echo "$fs_type" | tr '[:lower:]' '[:upper:]' ) @@ -78,7 +78,7 @@ get_zone_id() { zoneid=0 osver=$( uname -r | cut -d. -f2 ) - + if [ "$osver" -ge 11 ]; then zonename=$( zonename ) zoneid=$( /usr/sbin/zoneadm list -p | awk -F: '$2 == "'"$zonename"'" { print $1 }' ) @@ -101,7 +101,7 @@ suggest() { kstat -p "unix:${zone_id}:${name_regexp}:/^(read_bytes|write_bytes)\$/" \ | sed -e 's/vopstats_//' -e 's/:/ /g' \ | awk '{ - sum[ $3 ] += $5 + sum[ $3 ] += $5 } END { for ( i in sum ) { diff --git a/plugins/solaris/fsstat_bytes b/plugins/solaris/fsstat_bytes index 4fb5e7e1..45d256c0 100755 --- a/plugins/solaris/fsstat_bytes +++ b/plugins/solaris/fsstat_bytes @@ -9,9 +9,9 @@ Tested with Solaris 10 and 11. Note: - In Solaris 11, fsstat command can get stats for each non-global zones in + In Solaris 11, fsstat command can get stats for each non-global zones in global zone. (see man fsstat) - In global zone, this plugin gets stats of only global zone. + In global zone, this plugin gets stats of only global zone. In non-global zones, this plugin reports stats of the non-global zones. =head1 CONFIGURATION @@ -86,7 +86,7 @@ get_zone_id() { zoneid=0 osver=$( uname -r | cut -d. -f2 ) - + if [ "$osver" -ge 11 ]; then zonename=$( zonename ) zoneid=$( /usr/sbin/zoneadm list -p | awk -F: '$2 == "'"$zonename"'" { print $1 }' ) diff --git a/plugins/solaris/interrupts b/plugins/solaris/interrupts index 7f1f49cc..50169246 100755 --- a/plugins/solaris/interrupts +++ b/plugins/solaris/interrupts @@ -78,7 +78,7 @@ config() { echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' # print data source attributes - # split line into field,type,draw,label + # split line into field,type,draw,label local field type draw label echo "$data_attr" | while read -r field type draw label do diff --git a/plugins/solaris/io_disk b/plugins/solaris/io_disk index 34ec68f0..164d2df6 100755 --- a/plugins/solaris/io_disk +++ b/plugins/solaris/io_disk @@ -8,7 +8,7 @@ These functions are implemented: ops : similar to iostat r/s, w/s - bytes : similar to iostat kr/s, kw/s + bytes : similar to iostat kr/s, kw/s busy : similar to iostat %b, %w (%w usually indicates 0 in Sol10,11) queue : similar to iostat actv, wait latency : similar to iostat asvc_t, wsvc_t @@ -25,11 +25,11 @@ cd /path/to/munin/etc/plugins ln -s /path/to/munin/lib/plugins/io_disk . - The RRD files generated by io_busy_, io_ops_, io_bytes_ can be taken over + The RRD files generated by io_busy_, io_ops_, io_bytes_ can be taken over by this plugin. Thus, please remove symlinks of those plugins before using this plugin. - By default, this plugin monitors disk devices. And also it can monitor + By default, this plugin monitors disk devices. And also it can monitor NFS and Tape devices as much as io_* plugins with setting environments. Note that instance names of nfs (e.g. nfs1) can be changed after reboot or @@ -41,7 +41,7 @@ example: env.class /disk|nfs|tape/ default: disk - env.module - Module name. Only used in internal graph name. + env.module - Module name. Only used in internal graph name. example: env.module something default: sd @@ -102,7 +102,7 @@ functions='ops bytes busy queue latency size' : "${graph_width:=}" # Create map of instance name (e.g. sd0) and logical device name (e.g. c0t0d0) -# Example: +# Example: # name_sd1=c0t0d0 # name_ssd2=c0tAB_1234d0 (shorten long target) # ... @@ -213,7 +213,7 @@ do_config() { latency|size) for stat in reads writes do - echo "${dev}_${stat}.label dummy" + echo "${dev}_${stat}.label dummy" echo "${dev}_${stat}.graph no" echo "${dev}_${stat}.type DERIVE" echo "${dev}_${stat}.min 0" @@ -243,7 +243,7 @@ do_config() { esac # Print data attributes - echo "${dev}_${conf_out}.label dummy" + echo "${dev}_${conf_out}.label dummy" echo "${dev}_${conf_out}.graph no" echo "${dev}_${conf_out}.type DERIVE" echo "${dev}_${conf_out}.min 0" diff --git a/plugins/solaris/solaris-memstat b/plugins/solaris/solaris-memstat index 71957f53..d6c58931 100755 --- a/plugins/solaris/solaris-memstat +++ b/plugins/solaris/solaris-memstat @@ -98,11 +98,11 @@ BEGIN { phys=0 } -/^Kernel/ { kernel=$2 } +/^Kernel/ { kernel=$2 } /^ZFS File Data/ { zfs=$4 } -/^Anon/ { anon=$2 } +/^Anon/ { anon=$2 } /^Exec and libs/ { exec=$4 } -/^Page cache/ { cacheused=$3 } +/^Page cache/ { cacheused=$3 } /^Free \(cachelist\)/ { cachefree=$3 } /^Free \(freelist\)/ { free=$3 } diff --git a/plugins/solr/solr b/plugins/solr/solr index 647ea5e9..836da03a 100755 --- a/plugins/solr/solr +++ b/plugins/solr/solr @@ -1,5 +1,5 @@ #!/usr/bin/python -import sys, os +import sys, os import urllib2 try: from xml.etree import cElementTree as ET @@ -12,7 +12,7 @@ except ImportError: SOLR_PORT=8389 SOLR_HOST="localhost" # Parameters: -# +# # config (required) # autoconf (optional - used by munin-config) # @@ -84,7 +84,7 @@ if len(sys.argv) > 1: sys.exit(0) elif sys.argv[1] == "config": params = parseArgs() - + print 'graph_title Solr %s' % params['valueName'] print 'graph_args -l 0 ' print 'graph_vlabel Size %s' % params['valueName'] diff --git a/plugins/solr/solr-stats b/plugins/solr/solr-stats index e023d339..424d9e39 100755 --- a/plugins/solr/solr-stats +++ b/plugins/solr/solr-stats @@ -9,7 +9,7 @@ * Verify the server where the munin-node instance is can access that URL * * You need to have a PHP 5.2.6+ CLI installed too - * + * * Once the plugin is available you can simlink it with the following naming convention : * solr-[name of the core]-[name of the stats section - ex.: CORE]-[name of the entry in the xml - ex.: searcher]-[name of the stat to graph - ex.: numDocs] */ diff --git a/plugins/solr/solr4_ b/plugins/solr/solr4_ index 247c0810..3de709e3 100755 --- a/plugins/solr/solr4_ +++ b/plugins/solr/solr4_ @@ -1,7 +1,7 @@ #!/usr/bin/env python # # Copyright (c) 2013, Antonio Verni, me.verni@gmail.com -# +# # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation @@ -32,7 +32,7 @@ # # Example: # [solr_*] -# env.host_port solrhost:8080 +# env.host_port solrhost:8080 # env.url /solr # env.qpshandler_select /select # @@ -301,7 +301,7 @@ graph_vlabel docs docs.label Docs graph_category search""" -INDEXSIZE_GRAPH_TPL = """graph_args --base 1024 -l 0 +INDEXSIZE_GRAPH_TPL = """graph_args --base 1024 -l 0 graph_vlabel Bytes graph_title Index Size graph_category search @@ -313,7 +313,7 @@ xmx.colour ff0000 """ INDEXSIZECORE_GRAPH_TPL = """{core}.label {core} -{core}.draw STACK""" +{core}.draw STACK""" MEMORYUSAGE_GRAPH_TPL = """graph_args --base 1024 -l 0 --upper-limit {availableram} graph_vlabel Bytes @@ -387,10 +387,10 @@ class SolrMuninGraph: cores = self._getCores() graph = [QPSCORE_GRAPH_TPL.format(core=c, gtype='LINESTACK1') for pos,c in enumerate(cores) ] return QPSMAIN_GRAPH_TPL.format( - cores_qps_graphs='\n'.join(graph), - handler=self.params['params']['handler'], - core=self.params['core'], - cores_qps_cdefs='%s,%s' % (','.join(map(lambda x: 'qps_%s' % x, cores)),','.join(['+']*(len(cores)-1))), + cores_qps_graphs='\n'.join(graph), + handler=self.params['params']['handler'], + core=self.params['core'], + cores_qps_cdefs='%s,%s' % (','.join(map(lambda x: 'qps_%s' % x, cores)),','.join(['+']*(len(cores)-1))), gorder=','.join(cores) ) @@ -481,7 +481,7 @@ if __name__ == '__main__': SOLR_HOST_PORT = os.environ.get('host_port', 'localhost:8080').replace('http://', '') SOLR_URL = os.environ.get('url', '/solr') if SOLR_URL[0] != '/': - SOLR_URL = '/' + SOLR_URL + SOLR_URL = '/' + SOLR_URL mb = SolrMuninGraph(SOLR_HOST_PORT, SOLR_URL, params) if hasattr(mb, params['op']): print getattr(mb, params['op'])(params['type']) diff --git a/plugins/solr/solrmulticore b/plugins/solr/solrmulticore index b210246f..a3c22149 100755 --- a/plugins/solr/solrmulticore +++ b/plugins/solr/solrmulticore @@ -1,42 +1,42 @@ #!/usr/bin/python -# +# # Copyright (C) Rodolphe Franceschi -# +# # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# +# +# # This plugin monitors a SOLR server configured for multicore by automatically # getting core names from SOLR default page. -# +# # Tested on SOLR 1.4.0 -# +# # Parameters: # config (required) # autoconf (optional - used by munin-config) # # For the full list of options, refer to PLUGINOPTIONSLIST variable -# +# # Example of symlink creation on Debian Lenny # ln -s /usr/share/munin/plugins/solrmulticore_ /etc/munin/plugins/solrmulticore_avgRequestsPerSecond -# +# # Magic markers (Used by munin-config and some installation scripts. # Optional): #%# family=auto #%# capabilities=autoconf -import sys, os +import sys, os import urllib2 import HTMLParser, urllib @@ -101,7 +101,7 @@ def parseArgs(): params = { } params['valueName'] = parts[1] - + # Automatic / Manual Mode for core names if (len(SOLR_CORES) == 0): params['cores'] = getSolrCoreNameList() @@ -173,7 +173,7 @@ if len(sys.argv) > 1: params = parseArgs() for core in params['cores']: #print core, params['valueName'] - queues = fetchUrl(core, PLUGINOPTIONSLIST[params['valueName']]['xmlpath']) + queues = fetchUrl(core, PLUGINOPTIONSLIST[params['valueName']]['xmlpath']) searcher = getEntry(queues, PLUGINOPTIONSLIST[params['valueName']]['xmlparententryname']) value = getValue(searcher, params['valueName']).strip() print "%s.value %s" % (core, value) diff --git a/plugins/solr/wfsolr_ b/plugins/solr/wfsolr_ index fa5d5025..708aaa1d 100755 --- a/plugins/solr/wfsolr_ +++ b/plugins/solr/wfsolr_ @@ -14,7 +14,7 @@ * * You need to have a PHP 5.2.6+ CLI installed too with curl extension or * allow_url_fopen directive on - * + * * Once the plugin is available you can symlink it with the following naming convention : * wfsolr-[name of the core]-[name of the stats section - ex.: CORE]-[name of the entry in the xml - ex.: searcher]-[name of the stat to graph - ex.: numDocs] * diff --git a/plugins/sphinx/sphinx_documents b/plugins/sphinx/sphinx_documents index 9b73e15d..3a4cb67a 100755 --- a/plugins/sphinx/sphinx_documents +++ b/plugins/sphinx/sphinx_documents @@ -27,7 +27,7 @@ done exit 0;; esac -for i in `ls $idxpath/*.sph`; do +for i in `ls $idxpath/*.sph`; do echo -n "`basename $i .sph`.value " echo `indextool --dumpheader $i | grep ^total-doc | cut -f2 -d\:` done diff --git a/plugins/spotweb/spotweb_average b/plugins/spotweb/spotweb_average index 4e906ac0..a05b7784 100755 --- a/plugins/spotweb/spotweb_average +++ b/plugins/spotweb/spotweb_average @@ -8,7 +8,7 @@ # Original idea : smeerbartje (http://gathering.tweakers.net/forum/myreact/190949) # slommer (http://gathering.tweakers.net/forum/myreact/353335) # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -68,7 +68,7 @@ EOC # Spot count my $spots = `$MYSQL $MYSQLOPTS -e 'SELECT count(*) as spots FROM $DATABASE.spots WHERE stamp > $FROMSTAMP;'`; -$spots =~ /(\d+)/; +$spots =~ /(\d+)/; print "spots.value ".$1."\n"; # Downloaded diff --git a/plugins/spotweb/spotweb_cat_average b/plugins/spotweb/spotweb_cat_average index 0929fc19..4349151e 100755 --- a/plugins/spotweb/spotweb_cat_average +++ b/plugins/spotweb/spotweb_cat_average @@ -6,7 +6,7 @@ # # Spotweb : http://github.com/spotweb/spotweb # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -69,13 +69,13 @@ EOC # Output of values. We just call a subroutine here to prevent much duplicate code -if($COUNTPORN eq 'yes') { +if($COUNTPORN eq 'yes') { &print_value(0); } -else { +else { # 11-04-2011 - New pr0n filters # &print_value(0, " AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%'"); - &print_value(0, " AND subcatz NOT LIKE '%4%' AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%' AND subcatd NOT LIKE '%72%' AND subcatd NOT LIKE '%73%' AND subcatd NOT LIKE '%74%' AND subcatd NOT LIKE '%75%' AND subcatd NOT LIKE '%76%' AND subcatd NOT LIKE '%77%' AND subcatd NOT LIKE '%78%' AND subcatd NOT LIKE '%79%' AND subcatd NOT LIKE '%80%' AND subcatd NOT LIKE '%81%' AND subcatd NOT LIKE '%82%' AND subcatd NOT LIKE '%83%' AND subcatd NOT LIKE '%84%' AND subcatd NOT LIKE '%85%' AND subcatd NOT LIKE '%86%' AND subcatd NOT LIKE '%87%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%89%'"); + &print_value(0, " AND subcatz NOT LIKE '%4%' AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%' AND subcatd NOT LIKE '%72%' AND subcatd NOT LIKE '%73%' AND subcatd NOT LIKE '%74%' AND subcatd NOT LIKE '%75%' AND subcatd NOT LIKE '%76%' AND subcatd NOT LIKE '%77%' AND subcatd NOT LIKE '%78%' AND subcatd NOT LIKE '%79%' AND subcatd NOT LIKE '%80%' AND subcatd NOT LIKE '%81%' AND subcatd NOT LIKE '%82%' AND subcatd NOT LIKE '%83%' AND subcatd NOT LIKE '%84%' AND subcatd NOT LIKE '%85%' AND subcatd NOT LIKE '%86%' AND subcatd NOT LIKE '%87%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%89%'"); } &print_value(1); &print_value(2); @@ -85,6 +85,6 @@ else { # Subroutine to print the values sub print_value { my $spots = `$MYSQL $MYSQLOPTS -e "SELECT count(*) as spots FROM $DATABASE.spots WHERE stamp > $FROMSTAMP AND category = $_[0]$_[1];"`; - $spots =~ /(\d+)/; - print "cat".$_[0].".value ".$1."\n"; + $spots =~ /(\d+)/; + print "cat".$_[0].".value ".$1."\n"; } diff --git a/plugins/spotweb/spotweb_cat_total b/plugins/spotweb/spotweb_cat_total index 3b5f789d..ed09e79c 100755 --- a/plugins/spotweb/spotweb_cat_total +++ b/plugins/spotweb/spotweb_cat_total @@ -62,10 +62,10 @@ exit 0; if($COUNTPORN eq 'yes') { &print_value(0); } -else { +else { # 11-04-2011 - Added the new pr0n filters -# &print_value(0, " AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%'"); - &print_value(0, " AND (subcatz IS NULL OR subcatz NOT LIKE '%z4%') AND subcatd NOT LIKE '%d23%' AND subcatd NOT LIKE '%d24%' AND subcatd NOT LIKE '%d25%' AND subcatd NOT LIKE '%d26%' AND subcatd NOT LIKE '%d72%' AND subcatd NOT LIKE '%d73%' AND subcatd NOT LIKE '%d74%' AND subcatd NOT LIKE '%d75%' AND subcatd NOT LIKE '%d76%' AND subcatd NOT LIKE '%d77%' AND subcatd NOT LIKE '%d78%' AND subcatd NOT LIKE '%d79%' AND subcatd NOT LIKE '%d80%' AND subcatd NOT LIKE '%d81%' AND subcatd NOT LIKE '%d82%' AND subcatd NOT LIKE '%d83%' AND subcatd NOT LIKE '%d84%' AND subcatd NOT LIKE '%d85%' AND subcatd NOT LIKE '%d86%' AND subcatd NOT LIKE '%d87%' AND subcatd NOT LIKE '%d88%' AND subcatd NOT LIKE '%d88%' AND subcatd NOT LIKE '%d89%'"); +# &print_value(0, " AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%'"); + &print_value(0, " AND (subcatz IS NULL OR subcatz NOT LIKE '%z4%') AND subcatd NOT LIKE '%d23%' AND subcatd NOT LIKE '%d24%' AND subcatd NOT LIKE '%d25%' AND subcatd NOT LIKE '%d26%' AND subcatd NOT LIKE '%d72%' AND subcatd NOT LIKE '%d73%' AND subcatd NOT LIKE '%d74%' AND subcatd NOT LIKE '%d75%' AND subcatd NOT LIKE '%d76%' AND subcatd NOT LIKE '%d77%' AND subcatd NOT LIKE '%d78%' AND subcatd NOT LIKE '%d79%' AND subcatd NOT LIKE '%d80%' AND subcatd NOT LIKE '%d81%' AND subcatd NOT LIKE '%d82%' AND subcatd NOT LIKE '%d83%' AND subcatd NOT LIKE '%d84%' AND subcatd NOT LIKE '%d85%' AND subcatd NOT LIKE '%d86%' AND subcatd NOT LIKE '%d87%' AND subcatd NOT LIKE '%d88%' AND subcatd NOT LIKE '%d88%' AND subcatd NOT LIKE '%d89%'"); } &print_value(1); &print_value(2); @@ -73,6 +73,6 @@ else { sub print_value { my $spots = `$MYSQL $MYSQLOPTS -e "SELECT count(*) as spots FROM $DATABASE.spots WHERE category = $_[0]$_[1];"`; - $spots =~ /(\d+)/; - print "cat".$_[0].".value ".$1."\n"; + $spots =~ /(\d+)/; + print "cat".$_[0].".value ".$1."\n"; } diff --git a/plugins/spotweb/spotweb_catsize_average b/plugins/spotweb/spotweb_catsize_average index f938954f..2f3f65ed 100755 --- a/plugins/spotweb/spotweb_catsize_average +++ b/plugins/spotweb/spotweb_catsize_average @@ -6,7 +6,7 @@ # # Spotweb : http://github.com/spotweb/spotweb # -# This program is free software: you can redistribute it and/or modify +# 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. @@ -68,13 +68,13 @@ EOC } # Output of values. We just call a subroutine here to prevent much duplicate code -if($COUNTPORN eq 'yes') { +if($COUNTPORN eq 'yes') { &print_value(0); } -else { +else { # 11-04-2011 - New pr0n filters # &print_value(0, " AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%'"); - &print_value(0, " AND (subcatz IS NULL OR subcatz NOT LIKE '%4%') AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%' AND subcatd NOT LIKE '%72%' AND subcatd NOT LIKE '%73%' AND subcatd NOT LIKE '%74%' AND subcatd NOT LIKE '%75%' AND subcatd NOT LIKE '%76%' AND subcatd NOT LIKE '%77%' AND subcatd NOT LIKE '%78%' AND subcatd NOT LIKE '%79%' AND subcatd NOT LIKE '%80%' AND subcatd NOT LIKE '%81%' AND subcatd NOT LIKE '%82%' AND subcatd NOT LIKE '%83%' AND subcatd NOT LIKE '%84%' AND subcatd NOT LIKE '%85%' AND subcatd NOT LIKE '%86%' AND subcatd NOT LIKE '%87%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%89%'"); + &print_value(0, " AND (subcatz IS NULL OR subcatz NOT LIKE '%4%') AND subcatd NOT LIKE '%23%' AND subcatd NOT LIKE '%24%' AND subcatd NOT LIKE '%25%' AND subcatd NOT LIKE '%26%' AND subcatd NOT LIKE '%72%' AND subcatd NOT LIKE '%73%' AND subcatd NOT LIKE '%74%' AND subcatd NOT LIKE '%75%' AND subcatd NOT LIKE '%76%' AND subcatd NOT LIKE '%77%' AND subcatd NOT LIKE '%78%' AND subcatd NOT LIKE '%79%' AND subcatd NOT LIKE '%80%' AND subcatd NOT LIKE '%81%' AND subcatd NOT LIKE '%82%' AND subcatd NOT LIKE '%83%' AND subcatd NOT LIKE '%84%' AND subcatd NOT LIKE '%85%' AND subcatd NOT LIKE '%86%' AND subcatd NOT LIKE '%87%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%88%' AND subcatd NOT LIKE '%89%'"); } &print_value(1); &print_value(2); @@ -89,6 +89,6 @@ sub print_value { print "cat".$_[0].".value 0\n"; } else { - print "cat".$_[0].".value ".$1."\n"; + print "cat".$_[0].".value ".$1."\n"; } } diff --git a/plugins/spotweb/spotweb_total b/plugins/spotweb/spotweb_total index 10d839e4..c61a7321 100755 --- a/plugins/spotweb/spotweb_total +++ b/plugins/spotweb/spotweb_total @@ -67,7 +67,7 @@ EOC # Spot count my $spots = `$MYSQL $MYSQLOPTS -e 'SELECT count(*) as spots FROM $DATABASE.spots;'`; -$spots =~ /(\d+)/; +$spots =~ /(\d+)/; print "spots.value ".$1."\n"; if($SPOTSFULL eq 'yes') { diff --git a/plugins/squid/squid b/plugins/squid/squid index 1f404431..3e894771 100755 --- a/plugins/squid/squid +++ b/plugins/squid/squid @@ -11,7 +11,7 @@ Any system running squid and avialable cache_object:// =head1 CONFIGURATION -The following environment settings are the default configuration. +The following environment settings are the default configuration. [squid] env.squidhost localhost @@ -82,9 +82,9 @@ _ return sprintf($r_text, $squid_host, $what, &make_auth_header()); } -sub make_auth_header +sub make_auth_header { - if(defined $squid_passwd && $squid_passwd ne "") + if(defined $squid_passwd && $squid_passwd ne "") { my $h_text = <<_; Authorization: Basic %s @@ -99,22 +99,22 @@ _ sub squid_requst { my $what = $_[0]; - die "Could not connect: $!\n" unless my $cachemgr = IO::Socket::INET->new(PeerAddr => $squid_host, - PeerPort => $squid_port, - Proto => 'tcp', + die "Could not connect: $!\n" unless my $cachemgr = IO::Socket::INET->new(PeerAddr => $squid_host, + PeerPort => $squid_port, + Proto => 'tcp', Timeout => $squid_request_timeout); my $request = &make_request($what); $cachemgr->syswrite($request, length($request)); return $cachemgr; } -if($ARGV[0] and $ARGV[0] eq "autoconf") +if($ARGV[0] and $ARGV[0] eq "autoconf") { if (squid_requst('menu')->getline ne "") { print "yes\n"; } exit 0; } -if($ARGV[0] and $ARGV[0] eq "config") +if($ARGV[0] and $ARGV[0] eq "config") { my %config; # ------------------------------------------------- squid_efficiency_ ----------------------------------------- @@ -349,7 +349,7 @@ my $cm; my %data; # ----------------- squid_efficiency_ and squid_memory -------------------- $cm = squid_requst('info'); -while (my $line = $cm->getline) +while (my $line = $cm->getline) { # ----------------- squid_efficiency_ -------------------- if($line =~ /Hits as % of all requests:.*/) { ($data{'squid_efficiency_5'}{'all'}, $data{'squid_efficiency_60'}{'all'}) = $line =~ /([\d.]+)%/g; next; } @@ -392,7 +392,7 @@ while (my $line = $cm->getline) # ----------------- squid_ipcache -------------------- $cm = squid_requst('ipcache'); -while (my $line = $cm->getline) +while (my $line = $cm->getline) { if($line =~ /IPcache Entries:.*/) { $data{'squid_ipcache'}{'entries'} = ($line =~ /(\d+)/g)[0]; next; } if($line =~ /IPcache Requests:.*/) { $data{'squid_ipcache'}{'request'} = ($line =~ /(\d+)/g)[0]; next; } diff --git a/plugins/squid/squid_efficiency b/plugins/squid/squid_efficiency index 6bbd28c4..286c9f3a 100755 --- a/plugins/squid/squid_efficiency +++ b/plugins/squid/squid_efficiency @@ -51,7 +51,7 @@ # # [squid_efficiency] # env.squidhost yourhost.example.com -# env.squidport 8080 +# env.squidport 8080 # host=${squidhost:-localhost} diff --git a/plugins/squid/squid_times b/plugins/squid/squid_times index 1a4b6d10..3e5311fc 100755 --- a/plugins/squid/squid_times +++ b/plugins/squid/squid_times @@ -4,7 +4,7 @@ # License GPL V2 or higher # # Abstract -# munin plugin that logs the cache mean services times +# munin plugin that logs the cache mean services times # Requires netcat (here nc) # # Authors @@ -19,7 +19,7 @@ port=${squidport:-3128} if [ "$1" = "autoconf" ]; then SQUID_STATS=`printf "GET cache_object://$host/info HTTP/1.0\n\n" | netcat $host $port` if [ -n "${SQUID_STATS}" ]; then - echo yes + echo yes exit 0 else echo "no (HTTP GET failed)" @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel median response times (s)' echo 'mean_http.label Http' - echo 'mean_cmis.label Cache misses' + echo 'mean_cmis.label Cache misses' echo 'mean_chits.label Cache hits' echo 'mean_nhits.label Near hits' echo 'mean_nmr.label Not-modified replies' @@ -43,7 +43,7 @@ if [ "$1" = "config" ]; then echo 'mean_icpq.label Icp queries' exit 0 -fi +fi SQUID_TIME=$(printf "GET cache_object://$host/info HTTP/1.0\n\n" | nc $host $port) @@ -57,7 +57,7 @@ SQUID_TIME_ICP_QUERIES=$(echo "$SQUID_TIME" | grep "ICP Queries" | cut -d':' -f2 echo "mean_http.value $SQUID_TIME_HTTP" echo "mean_cmis.value $SQUID_TIME_CACHE_MISSES" -echo "mean_chits.value $SQUID_TIME_CACHE_HITS" +echo "mean_chits.value $SQUID_TIME_CACHE_HITS" echo "mean_nhits.value $SQUID_TIME_NEAR_HITS" echo "mean_nmr.value $SQUID_TIME_NM_REPLIES" echo "mean_dnsl.value $SQUID_TIME_DNS_LOOKUPS" diff --git a/plugins/ssh/hostdenied b/plugins/ssh/hostdenied index 9996f2d6..611c632e 100755 --- a/plugins/ssh/hostdenied +++ b/plugins/ssh/hostdenied @@ -13,7 +13,7 @@ # (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 +# 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. # @@ -22,19 +22,19 @@ # # ------------------------------------------------------------------------------------------------------ # 20100310 v1.01 ls -# as threatened, shows now "temperatures" of active hosts.deny lines. Recent additions are +# as threatened, shows now "temperatures" of active hosts.deny lines. Recent additions are # displayed in bright red, turning to blue as older the addition rules are. # This requires denyhosts to add line to hosts.deny in a specific format. Also, times are currently # hardcoded, and not a lot of flexibility adjusting them through parameters. # A line in hosts.deny should come with a comment, looking like: # # DenyHosts: Sat Mar 6 01:11:57 2010 | sshd: 87.101.51.198 # 8 graphs are drawn from that depicting number of rules in 24 h increments. Different colours are -# assigned to graphs which are <24h, 24-48h, 48-72h ... old. The last (coldest) graph shows rules +# assigned to graphs which are <24h, 24-48h, 48-72h ... old. The last (coldest) graph shows rules # which have been added > 168h ago. # I'm considerering to change age granularity to hours, rather than days, and plot many graphs (64 or 128, -# which are nice for colour calculations), showing more of a colour cloud than discernible areas. +# which are nice for colour calculations), showing more of a colour cloud than discernible areas. # The plugin must have permission to read /etc/hosts.deny, of course. -# 20100308, v1.0, ls +# 20100308, v1.0, ls # Will probably add multiple stacked graphs, indicative for addition/removal date of denies, # instead of a boring single area graph. # ------------------------------------------------------------------------------------------------------ @@ -80,7 +80,7 @@ run_fetch() { TOTAL=0 NOW=$(date +%s) sed -n 's/^\# DenyHosts: //;s/ | .*//gp' $DENY | # strip all but date - while read DATE; do + while read DATE; do echo $(((NOW - $(date -d "$DATE" +%s))/86400)) # calculate rule age done > $STATEFILE # rather than going through temp file, the age could be for AGE in {0..6} ; do # used to increment an array element with that index. diff --git a/plugins/ssh/openssh-denyhosts b/plugins/ssh/openssh-denyhosts index 46c13bcd..9990975c 100755 --- a/plugins/ssh/openssh-denyhosts +++ b/plugins/ssh/openssh-denyhosts @@ -11,8 +11,8 @@ # mktempfile () { -mktemp -t -} +mktemp -t +} AUTH_LOG=${logfile:-/var/log/auth.log} STATEFILE=$MUNIN_PLUGSTATE/sshd.offset @@ -59,7 +59,7 @@ then $LOGTAIL ${AUTH_LOG} $STATEFILE | grep 'sshd' > ${TEMP_FILE} refused=`grep -ic 'refused' ${TEMP_FILE}` - accepted=`grep -ic 'accepted' ${TEMP_FILE}` + accepted=`grep -ic 'accepted' ${TEMP_FILE}` invalid=`grep -ic 'invalid user' ${TEMP_FILE}` failed=`grep -ic 'failed password' ${TEMP_FILE}` diff --git a/plugins/swap/swapspace-info b/plugins/swap/swapspace-info index 766afc36..a6d82cc3 100755 --- a/plugins/swap/swapspace-info +++ b/plugins/swap/swapspace-info @@ -44,7 +44,7 @@ http://www.brendangregg.com/K9Toolkit/swapinfo =head1 LICENSE -GPL 2. +GPL 2. =cut @@ -87,7 +87,7 @@ sub value my %VMinfo; # --- Fetch VM info --- - foreach my $count (0..12) + foreach my $count (0..12) { # # The values are counters that increment each second, here we @@ -97,16 +97,16 @@ sub value foreach my $var ("swap_avail","swap_alloc","swap_free") { $VMnow{$var} = $Kstat->{unix}->{0}->{vminfo}->{$var}; - unless ($count) + unless ($count) { $VMold{$var} = $VMnow{$var}; next; } - if (($VMnow{$var} != $VMold{$var}) && (! $VMinfo{$var})) + if (($VMnow{$var} != $VMold{$var}) && (! $VMinfo{$var})) { $VMinfo{$var} = $VMnow{$var} - $VMold{$var}; } - } + } select(undef, undef, undef, 0.1); $Kstat->update(); } @@ -152,7 +152,7 @@ sub config "Unalloc ", "Avail ", "\n"; - + print "Alloc.label Alloc \n"; print "Alloc.draw \n"; print "Alloc.info Swap used.\n"; diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index 5a30f27b..dabc504e 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -11,13 +11,13 @@ This plugin requires the curl utility : https://curl.haxx.se/ Available plugins : strelaysrv_goroutine # -strelaysrv_num # +strelaysrv_num # strelaysrv_proxied # strelaysrv_transfer # strelaysrv_uptime # =head1 CONFIGURATION -To make the plugin connect to the Syncthing relay server one has to use this type of +To make the plugin connect to the Syncthing relay server one has to use this type of configuration [strelaysrv_*] @@ -88,7 +88,7 @@ goroutine() { graph_title Syncthing relay go routines graph_vlabel number of go routines graph_category network -strelaysrv_goroutine.label routines +strelaysrv_goroutine.label routines EOM exit 0;; *) @@ -139,9 +139,9 @@ EOM cd "$(dirname "$0")" || exit CURL=$(which curl) -JQ=$(which jq) +JQ=$(which jq) -case $(basename "$0") in +case $(basename "$0") in strelaysrv_num) num "$1" exit 0;; diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index 7bfd0cd0..8c7fc8a9 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -11,14 +11,14 @@ This plugin requires the jq utility : https://stedolan.github.io/jq/ This plugin requires the cURL utility : https://curl.haxx.se/ Available plugins : -syncthing_cpu # +syncthing_cpu # syncthing_mem # syncthing_goroutine # syncthing_transfer # syncthing_uptime # =head1 CONFIGURATION -To make the plugin connect to the Syncthing server one has to use this type of +To make the plugin connect to the Syncthing server one has to use this type of configuration [syncthing_*] @@ -109,7 +109,7 @@ goroutine() { graph_title Syncthing server go routines graph_vlabel number of go routines graph_category network -syncthing_goroutine.label routines +syncthing_goroutine.label routines EOM exit 0;; *) @@ -152,7 +152,7 @@ cd "$(dirname "$0")" || exit CURL=$(which curl) JQ=$(which jq) -case $(basename "$0") in +case $(basename "$0") in syncthing_cpu) cpu "$1" exit 0;; diff --git a/plugins/synology/snmp__synology_hddtemp b/plugins/synology/snmp__synology_hddtemp index 57931c3d..17310197 100755 --- a/plugins/synology/snmp__synology_hddtemp +++ b/plugins/synology/snmp__synology_hddtemp @@ -4,7 +4,7 @@ =head1 NAME -snmp__syno_hddtemp - Munin plugin to monitor the temperature of +snmp__syno_hddtemp - Munin plugin to monitor the temperature of harddisks in an Synology NAS. =head1 APPLICABLE SYSTEMS @@ -33,7 +33,7 @@ The temperature of each disk installed in °C. =head1 MIB INFORMATION -This plugin requires support for the synoDisk. It reports +This plugin requires support for the synoDisk. It reports the temperature of the installed disks. =head1 MAGIC MARKERS diff --git a/plugins/synology/snmp__synology_temperature b/plugins/synology/snmp__synology_temperature index 522dfa75..a871897e 100755 --- a/plugins/synology/snmp__synology_temperature +++ b/plugins/synology/snmp__synology_temperature @@ -4,7 +4,7 @@ =head1 NAME -snmp__syno_temperature - Munin plugin to retrieve current temperature from a +snmp__syno_temperature - Munin plugin to retrieve current temperature from a Synology NAS. =head1 APPLICABLE SYSTEMS diff --git a/plugins/synology/snmp__synology_ups b/plugins/synology/snmp__synology_ups index 0b1ca5cb..2fd6e3f5 100755 --- a/plugins/synology/snmp__synology_ups +++ b/plugins/synology/snmp__synology_ups @@ -4,7 +4,7 @@ =head1 NAME -snmp__syno_ups - Munin plugin to retrieve various information of the +snmp__syno_ups - Munin plugin to retrieve various information of the UPS attached to a Synology NAS. =head1 APPLICABLE SYSTEMS diff --git a/plugins/system/auth b/plugins/system/auth index 7edd39ad..5782cf68 100755 --- a/plugins/system/auth +++ b/plugins/system/auth @@ -1,5 +1,5 @@ #!/bin/bash -# +# # A Munin Plugin to show auth stuff # Created by Dominik Schulz # http://developer.gauner.org/munin/ diff --git a/plugins/system/blockhosts b/plugins/system/blockhosts index 652752e5..400d7404 100755 --- a/plugins/system/blockhosts +++ b/plugins/system/blockhosts @@ -2,7 +2,7 @@ # Plugin to monitor the number of hosts denied by BlockHosts # # $Log$ -# based on: +# based on: # denyhosts plugin # Revision 1.0 2009/06/05 16:00:00 tjansson # @@ -11,7 +11,7 @@ # Parameters: # config (required) # autoconf (optional - used by munin-config) - + LOG=/etc/hosts.allow if [ "$1" = "autoconf" ]; then @@ -23,9 +23,9 @@ if [ "$1" = "autoconf" ]; then exit 1 fi fi - + if [ "$1" = "config" ]; then - + echo 'graph_title Hosts denied by BlockHosts' echo 'graph_args -l 0' echo 'graph_vlabel denied hosts ' @@ -34,6 +34,6 @@ if [ "$1" = "config" ]; then echo 'HostsWatched.label Hosts watched by BlockHosts' exit 0 fi - + echo HostsDenied.value `egrep -c " : deny" $LOG` echo HostsWatched.value `egrep -c "#bh: ip:" $LOG` diff --git a/plugins/system/cpu_linux_multi b/plugins/system/cpu_linux_multi index b45e8b47..a3363451 100755 --- a/plugins/system/cpu_linux_multi +++ b/plugins/system/cpu_linux_multi @@ -2,18 +2,18 @@ ######################################################################## # Copyright (c) 2012, Adrien Urban # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are -# met: -# +# met: +# # 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. +# notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the -# distribution. -# +# distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR diff --git a/plugins/system/fresh-backups b/plugins/system/fresh-backups index e1215458..efde7930 100755 --- a/plugins/system/fresh-backups +++ b/plugins/system/fresh-backups @@ -24,7 +24,7 @@ daily archives. 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. diff --git a/plugins/system/hugepages b/plugins/system/hugepages index 78eca8f5..40d1b4e3 100755 --- a/plugins/system/hugepages +++ b/plugins/system/hugepages @@ -9,7 +9,7 @@ # transparent_hugepages kernel command line parameter). All values are # shown in (KiBi/MeBi/GiBi)Bytes. # -# This plugin is used like many other munin plugins: put it in +# This plugin is used like many other munin plugins: put it in # /usr/share/munin/plugins (or another appropriate location) # and create a symlink in /etc/munin/plugins: # > ln -s /usr/share/munin/plugins/hugepages /etc/munin/plugins diff --git a/plugins/system/irq b/plugins/system/irq index 31f613e4..6949bea1 100755 --- a/plugins/system/irq +++ b/plugins/system/irq @@ -114,7 +114,7 @@ my $irq_types = 'sirq' => 'Software interrupts' }; -my $irq_descriptions = +my $irq_descriptions = { 'HI' => 'High priority tasklets', 'TIMER' => 'Timer bottom half', @@ -127,7 +127,7 @@ my $irq_descriptions = # ----------------- main ---------------- need_multigraph(); # -- autoconf -- -if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) +if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) { printf("%s\n", (-e "/proc/interrupts" and -e "/proc/softirqs") ? "yes" : "no (stats not exists)"); exit (0); @@ -309,15 +309,15 @@ sub prepare_graphs $gr->{'irq'}{'fields'}{$down_field_name}{'type'} = $fields->{'irq_sirq'}{'type'}; $gr->{'irq'}{'fields'}{$up_field_name}{'draw'} = $fields->{'irq_sirq'}{'draw'}; $gr->{'irq'}{'fields'}{$down_field_name}{'draw'} = $fields->{'irq_sirq'}{'draw'}; - + $gr->{'irq'}{'fields'}{$up_field_name}{'label'} = replace($fields->{'irq_sirq'}{'label'}, ':cpu:', $i); $gr->{'irq'}{'fields'}{$up_field_name}{'info'} = replace($fields->{'irq_sirq'}{'info'} , ':cpu:', $i); $gr->{'irq'}{'fields'}{$down_field_name}{'label'} = 'NaN'; $gr->{'irq'}{'fields'}{$down_field_name}{'info'} = 'NaN'; - + $gr->{'irq'}{'fields'}{$up_field_name}{'negative'} = $down_field_name; $gr->{'irq'}{'fields'}{$down_field_name}{'graph'} = 'no'; - + # --- child graphs --- for my $irq_type (qw(irq sirq)) { @@ -335,7 +335,7 @@ sub prepare_graphs my @irq_names = keys %{$IRQi->{'stat'}{$irq_type}{$i}}; # names split for better sorting for my $irq_name (( - (sort {int $a <=> int $b} grep{/^\d/} @irq_names), + (sort {int $a <=> int $b} grep{/^\d/} @irq_names), (sort grep{!/(^\d|ERR|MIS)/} @irq_names), (sort grep{/(ERR|MIS)/ } @irq_names) )) @@ -347,7 +347,7 @@ sub prepare_graphs { $gr->{$graph_name}{'fields'}{$field_name}{$fo} = replace($fields->{'irq'}{$fo}, ':irq:', $irq_name); $gr->{$graph_name}{'fields'}{$field_name}{$fo} = replace($gr->{$graph_name}{'fields'}{$field_name}{$fo}, - ':irqinfo:', + ':irqinfo:', exists($IRQi->{'description'}{$irq_type}{$irq_name}) ? $IRQi->{'description'}{$irq_type}{$irq_name} : ''); diff --git a/plugins/system/pagefaults_by_process b/plugins/system/pagefaults_by_process index 9396d42c..22eedcd0 100755 --- a/plugins/system/pagefaults_by_process +++ b/plugins/system/pagefaults_by_process @@ -18,7 +18,7 @@ # that die may not appear on the graph, and anyway their last chunk of # CPU usage before they died is lost. You could modify this plugin to # read SAR/psacct records if you care about that. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/system/raminfo b/plugins/system/raminfo index d0d0f77d..4f513c42 100755 --- a/plugins/system/raminfo +++ b/plugins/system/raminfo @@ -7,7 +7,7 @@ # RAM Used Anon, Exec + Libs, Page cache # RAM Avail Free memory that can be immediately used # -# Core logic developed by Brendan Gregg. +# Core logic developed by Brendan Gregg. # REFERENCE: http://www.brendangregg.com/k9toolkit.html - the swap diagram. # # COPYRIGHT: Copyright (c) 2004 Brendan Gregg. @@ -39,7 +39,7 @@ raminfo - Plugin for monitoring memory usage =head1 AUTHOR Christian Braum, chrisi_braum@web.de - + Core logic developed by Brendan Gregg. See K9Toolkit: http://www.brendangregg.com/K9Toolkit/swapinfo @@ -103,7 +103,7 @@ sub value # get value for variables my %VMinfo; my %VMold; - foreach my $count (0..12) + foreach my $count (0..12) { # # The values are counters that increment each second, here we @@ -111,7 +111,7 @@ sub value # get value for variables # (reading them once then again a second later was not reliable). # - foreach my $var ( "freemem" ) + foreach my $var ( "freemem" ) { $VMnow{$var} = $Kstat->{unix}->{0}->{vminfo}->{$var}; unless ($count) { @@ -148,18 +148,18 @@ sub value # get value for variables my $ram_used = $pagestotal - $freemem - $ram_kernel - $ram_locked; ### format values - + my $freemem_B = sprintf( "%d ", $freemem * $PAGETOBYTE ); my $pp_kernel_B = sprintf( "%d ", $pp_kernel * $PAGETOBYTE ); my $pageslocked_B = sprintf( "%d ", $pageslocked * $PAGETOBYTE ); my $pagestotal_B = sprintf( "%d ", $pagestotal * $PAGETOBYTE ); my $ram_unusable_B = sprintf( "%d ", $ram_unusable * $PAGETOBYTE ); - my $ram_kernel_B = sprintf( "%d ", $ram_kernel * $PAGETOBYTE ); + my $ram_kernel_B = sprintf( "%d ", $ram_kernel * $PAGETOBYTE ); my $ram_locked_B = sprintf( "%d ", $ram_locked * $PAGETOBYTE ); my $ram_used_B = sprintf( "%d ", $ram_used * $PAGETOBYTE ); my $ram_total_B = sprintf( "%d ", $ram_total * $PAGETOBYTE ); - # --- assign the variables --- + # --- assign the variables --- $h_ramvalues{"Unusable.value"} = "$ram_unusable_B"; $h_ramvalues{"Kernel.value"} = "$ram_kernel_B"; $h_ramvalues{"Locked.value"} = "$ram_locked_B"; @@ -201,7 +201,7 @@ sub config # print config message and exit. "Used ", "Avail ", "\n"; - + print "Unusable.label Unusable \n"; print "Unusable.draw AREA \n"; print "Unusable.info RAM consumed by the OBP and TSBs.\n"; diff --git a/plugins/system/read_serial_temperature b/plugins/system/read_serial_temperature index 4fef783a..dd4fcf44 100755 --- a/plugins/system/read_serial_temperature +++ b/plugins/system/read_serial_temperature @@ -2,7 +2,7 @@ """ Paul Wiegmans (p.wiegmans@bonhoeffer.nl) 2009 dec 18 -This munin-node plugin reads a temperature value from a serial port, +This munin-node plugin reads a temperature value from a serial port, provided by a Arduino with temperature sensor. For details see: http://amber.bonhoeffer.nl/temperatuur/ @@ -29,7 +29,7 @@ def gettemperature(): t += 1 ser.close() -# shamelessly copied from weather_temp_ +# shamelessly copied from weather_temp_ if len(sys.argv) == 2 and sys.argv[1] == "autoconf": @@ -50,4 +50,4 @@ else: print 'temperature.value %s' % gettemperature() - + diff --git a/plugins/system/total_by_process_ b/plugins/system/total_by_process_ index af4b4003..d374f463 100755 --- a/plugins/system/total_by_process_ +++ b/plugins/system/total_by_process_ @@ -18,7 +18,7 @@ # that die may not appear on the graph, and anyway their last chunk of # CPU usage before they died is lost. You could modify this plugin to # read SAR/psacct records if you care about that. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, diff --git a/plugins/tarsnap/README.md b/plugins/tarsnap/README.md index b66af942..f0d6de18 100644 --- a/plugins/tarsnap/README.md +++ b/plugins/tarsnap/README.md @@ -8,18 +8,18 @@ This plugin creates two graphs: * *tarsnap_total* - summarising the total amount of data the local tarsnap instance has stored on the service (total and compressed). * *tarsnap_unique* - summarising the total amount of unique (deduplicated) -data the local tarsnap instance has stored on the service (total and +data the local tarsnap instance has stored on the service (total and compressed). The compressed value here is the actual amount of data stored on the tarnap servers and what tarsnap uses for billing. Usage ----- -Add the following to your backup script (after tarsnap has run), or to a +Add the following to your backup script (after tarsnap has run), or to a cron job: /usr/local/bin/tarsnap --print-stats > /path/to/tarsnap-stats.txt - + N.B.: ensure `/path/to/munin-stats.txt` is readable by munin-node. Configuration diff --git a/plugins/tarsnap/tarsnap b/plugins/tarsnap/tarsnap index a5633cff..006d54ce 100755 --- a/plugins/tarsnap/tarsnap +++ b/plugins/tarsnap/tarsnap @@ -3,14 +3,14 @@ # Munin plugin for Tarsnap # # https://github.com/warrenguy/munin-tarsnap -# +# # USAGE: # # Add the following to your backup script (after tarsnap has run), or to a # cron job: # # /usr/local/bin/tarsnap --print-stats > /path/to/tarsnap-stats.txt -# +# # N.B.: ensure /path/to/munin-stats.txt is readable by munin-node. The # default path this script tries is /var/lib/munin/tarsnap-stats.txt # @@ -37,7 +37,7 @@ case $1 in multigraph tarsnap_total graph_title Tarsnap total data graph_vlabel bytes -graph_category backup +graph_category backup total_size.label Total size total_compressed.label Total size (compressed) diff --git a/plugins/teamspeak/teamspeak_user b/plugins/teamspeak/teamspeak_user index a2490e8d..14b1f5fd 100755 --- a/plugins/teamspeak/teamspeak_user +++ b/plugins/teamspeak/teamspeak_user @@ -26,7 +26,7 @@ use Net::Telnet; # CONFIG HERE! my $hostname = "localhost"; # serveraddress my $port = 10011; # querryport (default: 10011) -my @serverids = (1); # array of virtualserverids (1,2,3,4,...) +my @serverids = (1); # array of virtualserverids (1,2,3,4,...) my $username = ""; # only set if the default queryuser hasnt enough rights (should work without this) my $password = ""; @@ -47,11 +47,11 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") exit 0; } else -{ - my $telnet = new Net::Telnet(Timeout=>5, Errmode=>"return", Prompt=>"/\r/"); +{ + my $telnet = new Net::Telnet(Timeout=>5, Errmode=>"return", Prompt=>"/\r/"); if (!$telnet->open(Host=>$hostname, Port=>$port)) { die exit; - } + } $telnet->waitfor("/Welcome to the TeamSpeak 3 ServerQuery interface/"); foreach my $server (@serverids) { @@ -62,7 +62,7 @@ else $telnet->waitfor("/error id=0 msg=ok/"); } $telnet->cmd("serverinfo"); - + my $clients = 0; my $queryclients = 0; my $line = $telnet->getline(Timeout=>5); @@ -74,7 +74,7 @@ else } $telnet->waitfor("/error id=0 msg=ok/"); print "$server.value ".($clients - $queryclients)."\n"; - } + } $telnet->close; } exit; diff --git a/plugins/teamspeak/ts3v2_ b/plugins/teamspeak/ts3v2_ index be251358..969efb94 100755 --- a/plugins/teamspeak/ts3v2_ +++ b/plugins/teamspeak/ts3v2_ @@ -16,7 +16,7 @@ # ####################################################### # 02.10.10 - v0.3 -# -now works again with 3.0.0-beta29 +# -now works again with 3.0.0-beta29 # # 18.04.10 - v0.2 # -transfer mode added @@ -25,20 +25,20 @@ # -initial release # ###################################################### - + #%# family=auto #%# capabilities=autoconf suggest - + use strict; use Net::Telnet; - + # CONFIG HERE! my $hostname = $ENV{host} || "localhost"; # serveraddress my $port = $ENV{port} || 10011; # querryport my $user = $ENV{user} || "serveradmin"; my $password = $ENV{password} || ""; my $filename = "ts3v2_"; - + # SCRIPT START! #init telnet connection my $name = ""; my $server = ""; my @num; my $id = 0; my $i = 0; my $myserver; my $transfer; @@ -57,7 +57,7 @@ if ($password ne "") { die "ServerQuery login failed: ".$response; } } - + #get argument my $argument = ""; if ($0 =~ /$filename(\w+)$/i) { @@ -66,7 +66,7 @@ if ($0 =~ /$filename(\w+)$/i) { die "Error: We need to know what serverid you want, so link this plugin as " .$filename."1, ".$filename."2 or ".$filename."overview.\n"; } - + #check for overview, transfer or single server mode my $mode = ""; if ($argument eq "overview" or $argument eq "transfer" or (defined $ARGV[0] and ($ARGV[0] eq "suggest" or $ARGV[0] eq "autoconf"))) { @@ -92,7 +92,7 @@ if ($argument eq "overview" or $argument eq "transfer" or (defined $ARGV[0] and } else { die "ERROR: unknown plugin mode: $argument\n"; } - + #check for config mode if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { print "yes"; @@ -111,7 +111,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { $telnet->waitfor("/error id=0 msg=ok/"); $telnet->cmd("serverinfo"); my $line = $telnet->getline(Timeout=>1); - + if ($line =~ m/virtualserver_id=(\d+) virtual/) { my $id = $1; } else { @@ -124,7 +124,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { die "ERROR: server string not recognized!\n"; } $telnet->waitfor("/error id=0 msg=ok/"); - + print "graph_title Teamspeak Users ".$name."\n"; print "graph_vlabel Connected Teamspeak Users\n"; print "graph_category voip\n"; @@ -132,7 +132,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { print "users.label Users\n"; print "users.info Connected users to ".$name."\n"; print "users.type GAUGE\n"; - + exit 0; } elsif ($mode eq "o") { #overview mode @@ -140,7 +140,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { print "graph_vlabel Connected Teamspeak Users\n"; print "graph_category voip\n"; print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; - + foreach (@num) { $telnet->cmd("use sid=".$_); $telnet->waitfor("/error id=0 msg=ok/"); @@ -176,7 +176,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { $telnet->cmd("use sid=".$server); $telnet->waitfor("/error id=0 msg=ok/"); $telnet->cmd("serverinfo"); - + my $line = $telnet->getline(Timeout=>1); if ($line =~ m/virtualserver_clientsonline=(\d+) /) { print "users.value ".($1-1)."\n"; @@ -191,7 +191,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { $telnet->cmd("use sid=".$_); $telnet->waitfor("/error id=0 msg=ok/"); $telnet->cmd("serverinfo"); - + my $line = $telnet->getline(Timeout=>1); if ($line =~ m/virtualserver_clientsonline=(\d+) /) { print $_.".value ".($1-1)."\n"; @@ -211,10 +211,10 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { die "ERROR: server string not recognized!\n"; } $telnet->waitfor("/error id=0 msg=ok/"); - + } } - + #close telnet connection $telnet->close; exit; diff --git a/plugins/teamspeak/tsuser b/plugins/teamspeak/tsuser index 30ea2e07..39507773 100755 --- a/plugins/teamspeak/tsuser +++ b/plugins/teamspeak/tsuser @@ -1,5 +1,5 @@ #!/usr/bin/perl -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, @@ -68,7 +68,7 @@ elsif ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) } } } -else +else { foreach my $server (@uports) { @@ -76,7 +76,7 @@ else print $FS "si ".$server, "\n\n"; my $MASK = $string."=*"; - while(<$FS>) + while(<$FS>) { my $input_line = $_; if ( $input_line =~ m/($MASK)/ ) diff --git a/plugins/thin/thin_memory b/plugins/thin/thin_memory index 580ee91e..f4313166 100755 --- a/plugins/thin/thin_memory +++ b/plugins/thin/thin_memory @@ -4,8 +4,8 @@ # For Linux ONLY ! # DOES NOT WORK on OSX, Solaris or BSD. # only linux, because this script relies on proc filesystem -# -# Original author: +# +# Original author: # Frederico de Souza Araujo - fred.the.master@gmail.com # http://www.frederico-araujo.com # @@ -14,20 +14,20 @@ # http://www.furbism.com # # Originally based on: -# thin_process_memory - -# A munin plugin to monitor memory size of +# thin_process_memory - +# A munin plugin to monitor memory size of # each individual thin process # by Ben VandenBos and Avvo, Inc. # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -38,7 +38,7 @@ module Munin class ThinProcessMemory - + # run main method def run instances = get_pids() @@ -48,7 +48,7 @@ module Munin puts "thin_#{port}.value #{rss}" end end - + # only get the memory for each pid def pid_rss(pid) res = `grep "VmRSS" /proc/#{pid}/status`.split[1] @@ -58,7 +58,7 @@ module Munin return res end end - + # fetch all pids that match thin def get_pids pids = `pgrep -f 'thin' -l | awk -F " " '{ if (substr( $4, 10, 4)>=1) print $1"|"substr( $4, 10, 4)}' | sort -t'|' -nk 2`.split(/\r?\n/) @@ -67,7 +67,7 @@ module Munin def autoconf get_pids().length > 0 end - + end end diff --git a/plugins/thin/thin_threads b/plugins/thin/thin_threads index d559bd08..f07c280f 100755 --- a/plugins/thin/thin_threads +++ b/plugins/thin/thin_threads @@ -1,12 +1,12 @@ #!/usr/bin/env ruby -# thin_threads - +# thin_threads - # A munin plugin for Linux to monitor how many threads per thin process -# +# # For Linux ONLY ! # DOES NOT WORK on OSX, Solaris or BSD. # only linux, because this script relies on proc filesystem -# -# Original author: +# +# Original author: # Frederico de Souza Araujo - fred.the.master@gmail.com # http://www.frederico-araujo.com # @@ -15,20 +15,20 @@ # http://www.furbism.com # # Originally based on: -# thin_process_memory - -# A munin plugin to monitor memory size of +# thin_process_memory - +# A munin plugin to monitor memory size of # each individual thin process # by Ben VandenBos and Avvo, Inc. # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -39,7 +39,7 @@ module Munin class ThinThreads - + def run instances = get_pids() instances.each do |instance| @@ -48,7 +48,7 @@ module Munin puts "thin_#{port}.value #{rss}" end end - + # only get threads count for each pid # Using Proc filesystem # ONLY LINUX! because relies on proc filesystem @@ -62,7 +62,7 @@ module Munin return res end end - + # fetch all pids that match thin def get_pids pids = `pgrep -f 'thin' -l | awk -F " " '{ if (substr( $4, 10, 4)>=1) print $1"|"substr( $4, 10, 4)}' | sort -t'|' -nk 2`.split(/\r?\n/) @@ -71,7 +71,7 @@ module Munin def autoconf get_pids().length > 0 end - + end end @@ -86,7 +86,7 @@ when "config" puts "graph_scale yes" puts "graph_info Tracks how many threads per thin processes" mpm.get_pids.each do |instance| - pid, port = instance.split("|") + pid, port = instance.split("|") puts "thin_#{port}.label thin_#{port}" puts "thin_#{port}.info Threads per Thin process" puts "thin_#{port}.type GAUGE" diff --git a/plugins/thin/thins_peak_memory b/plugins/thin/thins_peak_memory index 3797cc44..762013d1 100755 --- a/plugins/thin/thins_peak_memory +++ b/plugins/thin/thins_peak_memory @@ -1,31 +1,31 @@ #!/usr/bin/env ruby -# thin_peak_memory - +# thin_peak_memory - # A munin plugin for Linux to monitor the maximum memory size # that an each individual thin process has reached -# +# # For Linux ONLY ! # DOES NOT WORK on OSX, Solaris or BSD. # only linux, because this script relies on proc filesystem # -# Author: +# Author: # Frederico de Souza Araujo - fred.the.master@gmail.com # http://www.frederico-araujo.com # # Based on: -# thin_process_memory - -# A munin plugin to monitor memory size of +# thin_process_memory - +# A munin plugin to monitor memory size of # each individual thin process # by Ben VandenBos and Avvo, Inc. # # This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 +# it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -36,7 +36,7 @@ module Munin class ThinPeakMemory - + def run instances = get_pids() instances.each do |instance| @@ -45,7 +45,7 @@ module Munin puts "thin_#{port}.value #{hwm}" end end - + # only get VmHWM count for each pid # (Virtual Memory High Water Mark) # Using Proc filesystem @@ -60,7 +60,7 @@ module Munin return res end end - + # fetch all pids that match thin def get_pids pids = `pgrep -f 'thin' -l | awk -F " " '{ if (substr( $4, 10, 4)>=1) print $1"|"substr( $4, 10, 4)}' | sort -t'|' -nk 2`.split(/\r?\n/) @@ -69,7 +69,7 @@ module Munin def autoconf get_pids().length > 0 end - + end end @@ -84,7 +84,7 @@ when "config" puts "graph_scale yes" puts "graph_info Tracks the peak memory of thin processes, aka High Water Mark." mpm.get_pids.each do |instance| - pid, port = instance.split("|") + pid, port = instance.split("|") puts "thin_#{port}.label thin_#{port}" puts "thin_#{port}.info Peak Memory" puts "thin_#{port}.type GAUGE" diff --git a/plugins/tinydns/tinydns_err b/plugins/tinydns/tinydns_err index 2f57b768..20b11a45 100755 --- a/plugins/tinydns/tinydns_err +++ b/plugins/tinydns/tinydns_err @@ -25,7 +25,7 @@ if [ "$1" = "config" ]; then cat - < {Start => \&xml_start_elem, End => \&xml_end_elem, Char => \&xml_characters} ); - + if ($varnishname) { $arg .= " -n $varnishname"; } @@ -1009,7 +1009,7 @@ sub print_dynamic } my $counter = $ASPECTS{$self}{'values'}{$name}{'counter'}; my $type = $ASPECTS{$self}{'values'}{$name}{'family'}; - + foreach my $key (keys %{$data{$type}}) { my $pname = normalize_name($type . "_" . $key . "_" . $counter); print $pname . $suffix . " "; @@ -1097,7 +1097,7 @@ sub get_config } next; } - + if (!print_if_exist(\%values,$value,'label')) { print "$value.label $data{$value}{'description'}\n"; } diff --git a/plugins/vdr/vdr_ b/plugins/vdr/vdr_ index f759a620..35febff1 100755 --- a/plugins/vdr/vdr_ +++ b/plugins/vdr/vdr_ @@ -139,7 +139,7 @@ sub ermittelnTimer(){ $anzahlSerienMinuten+=$dauer*length($anzahl); } else { #print "keine Series"; - $anzahlTimer++; + $anzahlTimer++; $anzahlMinuten+=$dauer; } diff --git a/plugins/vmware/esxcli_env_ b/plugins/vmware/esxcli_env_ index 6b6dd7e3..036d1ba5 100755 --- a/plugins/vmware/esxcli_env_ +++ b/plugins/vmware/esxcli_env_ @@ -9,7 +9,7 @@ Configuration variables (/etc/munin/plugin-conf.d/): host_name - Name of you ESXi host as defined in munin.conf timeout - Plugin specific timeout Plugin specific: - env.esxi_host - (REQUIRED) hostname/ip esxcli connect to + env.esxi_host - (REQUIRED) hostname/ip esxcli connect to env.esxi_user - (REQUIRED) ESXi username to connect env.esxi_password - (REQUIRED) password for user given above env.cache_file - path to cache file (we do not want two or more sequential reconnections to ESXi host) diff --git a/plugins/vmware/esxi b/plugins/vmware/esxi index 7aef50a3..00c5b982 100755 --- a/plugins/vmware/esxi +++ b/plugins/vmware/esxi @@ -30,7 +30,7 @@ function get_vmlist () { vmlist_linenumber="$(echo "$summary" | $GREP -n "^Vmid")" vmlist_linenumber="${vmlist_linenumber/:*/}" - + vmlist="$(echo "$summary" | $TAIL -n $(($total_linenumber - $vmlist_linenumber)) | tr -s " ")" } @@ -74,7 +74,7 @@ function VMoverallCpuUsage () { temp="${c/*overallCpuUsage = /}" vmcpu[$n]="${temp/,*/}" if [ "${vmcpu[$n]}" == "" ]; then - vmcpu[$n]="0" + vmcpu[$n]="0" fi (( n++ )) done <" ]; then - vmmem[$n]="0" + vmmem[$n]="0" fi (( n++ )) done <" ]; then - vmmem[$n]="0" + vmmem[$n]="0" fi (( n++ )) done < To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide -under a CC0 waiver. This software is distributed without any warranty. +under a CC0 waiver. This software is distributed without any warranty. http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/plugins/vmware/fusion_ b/plugins/vmware/fusion_ index 6ae2eec6..75500297 100755 --- a/plugins/vmware/fusion_ +++ b/plugins/vmware/fusion_ @@ -63,37 +63,37 @@ my @lines=split(/\n/,$output); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $lcount = 0; my $base_config = "graph_category virtualization\n"; - + if( $type eq "pcpu" ) { print $base_config; - print "graph_args --base 1000 -l 0 -u 100 -r\n"; - print "graph_scale no\n"; + print "graph_args --base 1000 -l 0 -u 100 -r\n"; + print "graph_scale no\n"; print "graph_title CPU usage in % per VM\n"; print "graph_vlabel % of CPU usage\n"; - print "graph_info The Graph shows the CPU usage in % per VM\n"; + print "graph_info The Graph shows the CPU usage in % per VM\n"; foreach my $line(@lines) { - if( $line =~ /(? 2 ){ $vm[$count]=$entry; $count++; } - } + } $vm[3] =~ s/\.vmx//; - my $cat = clean_vmname($vm[3]); + my $cat = clean_vmname($vm[3]); if( $cat =~ /(? 0 ){ print $cat,"_pcpu.draw STACK\n"; } else { print $cat,"_pcpu.draw AREA\n"; } - $lcount++; + $lcount++; print $cat,"_pcpu.label $vm[3]\n"; print $cat,"_pcpu.type GAUGE\n"; - } + } } } } @@ -104,64 +104,64 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_scale no\n"; print "graph_title Memory usage in % per VM\n"; print "graph_vlabel % of Memory usage\n"; - print "graph_info The Graph shows the Memory usage in % per VM\n"; + print "graph_info The Graph shows the Memory usage in % per VM\n"; foreach my $line(@lines) { - if( $line =~ /(? 2 ){ - $vm[$count]=$entry; - $count++; - } - } - $vm[3] =~ s/\.vmx//; - my $cat = clean_vmname($vm[3]); - if( $cat =~ /(? 0 ){ - print $cat,"_pmem.draw STACK\n"; - } else { - print $cat,"_pmem.draw AREA\n"; - } - $lcount++; - print $cat,"_pmem.label $vm[3]\n"; - print $cat,"_pmem.type GAUGE\n"; - } - } - } - } - - if( $type eq "mem" ) { - print $base_config; - print "graph_args --base 1024 -r --lower-limit 0\n"; - print "graph_title absolute Memory usage per VM\n"; - print "graph_vlabel Memory usage\n"; - print "graph_info The Graph shows the absolute Memory usage per VM\n"; - foreach my $line(@lines) { - if( $line =~ /(? 2 ){ $vm[$count]=$entry; $count++; } } - $vm[3] = clean_vmname($vm[3]); - if( $vm[3] =~ /(? 0 ){ + print $cat,"_pmem.draw STACK\n"; + } else { + print $cat,"_pmem.draw AREA\n"; + } + $lcount++; + print $cat,"_pmem.label $vm[3]\n"; + print $cat,"_pmem.type GAUGE\n"; + } + } + } + } + + if( $type eq "mem" ) { + print $base_config; + print "graph_args --base 1024 -r --lower-limit 0\n"; + print "graph_title absolute Memory usage per VM\n"; + print "graph_vlabel Memory usage\n"; + print "graph_info The Graph shows the absolute Memory usage per VM\n"; + foreach my $line(@lines) { + if( $line =~ /(? 2 ){ + $vm[$count]=$entry; + $count++; + } + } + $vm[3] = clean_vmname($vm[3]); + if( $vm[3] =~ /(? 0 ){ print "$vm[3]_mem.draw STACK\n"; } else { print "$vm[3]_mem.draw AREA\n"; } print "$vm[3]_mem.label $vm[3]\n"; - print "$vm[3]_mem.type GAUGE\n"; - $lcount++; - } + print "$vm[3]_mem.type GAUGE\n"; + $lcount++; + } } - } + } } } else { @@ -169,7 +169,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { if( $line =~ /(? 2 ){ $vm[$count]=$entry; @@ -177,7 +177,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } } $vm[3] = clean_vmname($vm[3]); - if( $vm[3] =~ /(? $_ENV['IceScret'] if (count($argv)==1) { do_count(); - } + } -switch ($argv[1]) +switch ($argv[1]) { case 'autoconf': do_autoconf(); break; - + case 'config': do_config(); break; - + default: do_count(); break; @@ -103,7 +103,7 @@ exit(1); function IceConnect() { global $ICE, $host, $port, $IceProfile, $IceSecret; - try + try { Ice_loadProfile($IceProfile); $iceproxy = $ICE->stringToProxy("Meta:tcp -h $host -p $port"); @@ -146,15 +146,15 @@ function do_config_header() function do_config_data() { global $ICE, $IceSecret; - - try + + try { $metaServer = IceConnect(); $AdefaultConf = $metaServer->getDefaultConf(); - + $AvirtualServer = $metaServer->getAllServers(); - foreach ($AvirtualServer as $numserver=>$s) + foreach ($AvirtualServer as $numserver=>$s) { $serverid = $s->ice_context($IceSecret)->id(); $servename = $s->ice_context($IceSecret)->getConf( 'registername'); @@ -189,14 +189,14 @@ function do_count() $totalMaxUsers="0"; $totalConnectedUsers="0"; - try + try { $metaServer = IceConnect(); $AdefaultConf = $metaServer->getDefaultConf(); - + $AvirtualServer = $metaServer->getAllServers(); - foreach ($AvirtualServer as $numserver=>$s) + foreach ($AvirtualServer as $numserver=>$s) { $maxusers = $s->ice_context($IceSecret)->getConf( 'users' ); if (!$maxusers) $maxusers = $AdefaultConf['users']; @@ -215,7 +215,7 @@ function do_count() fwrite(STDOUT, "vserver_".$serverid."_maxusers.value ".$maxusers."\n"); fwrite(STDOUT, "vserver_".$serverid."_online.value ".$connectedUsers."\n"); } - + fwrite(STDOUT, "total_maxusers.value ".$totalMaxUsers."\n"); fwrite(STDOUT, "total_online.value ".$totalConnectedUsers."\n"); exit(0); @@ -227,4 +227,4 @@ function do_count() } } -?> \ No newline at end of file +?> diff --git a/plugins/voip/murmur_users b/plugins/voip/murmur_users index 9da3df2a..23c4dd69 100755 --- a/plugins/voip/murmur_users +++ b/plugins/voip/murmur_users @@ -7,7 +7,7 @@ error_reporting( E_ALL &!E_NOTICE); //to avoid of the crap generation ///////////////////////////////////////////////////////////////////////////////////////////////////// Murmur users online grahpher -ver 0.2alpha 2008.12.02, 20:32 +ver 0.2alpha 2008.12.02, 20:32 author _KaszpiR_ kaszpir at gmail dot com code is under GPL @@ -18,9 +18,9 @@ Requirements: Notice: - script allows the usage of the 'config' and 'autoconf' parameters during startup, make fure you edt config section before running it -- $limit - number of lines to tail from the lgo file, better keep it below 5000 for lower cpu load, +- $limit - number of lines to tail from the lgo file, better keep it below 5000 for lower cpu load, additionally on busy servers you can keep it really low, suggested 3x maximum number of users online -- tested on +- tested on PHP 5.2.6-3 with Suhosin-Patch 0.9.6.2 (cli) (built: Aug 21 2008 17:02:32) murmur 1.1.4 precompiled binaries from sourceforge net, all running under debian etch - this is not the best way to get users connected to the murmur server, maybe in the beginningn of the 2009 gonna make another script @@ -60,7 +60,7 @@ if(isset($argv[1]) && $argv[1] == "autoconf") { fwrite(STDOUT, "Yes\n"); } - else + else { fwrite(STDOUT, "No\n"); fwrite(STDERR, "check if '$logfile' exists and it is allowed to be read by munin user group\n"); @@ -82,7 +82,7 @@ if(isset($argv[1]) && $argv[1] == "config") fwrite(STDOUT, "murmur.label Users on server\n"); fwrite(STDOUT, "murmur.type GAUGE\n"); return 0; - + }else { echo "check if '$logfile' exists and it is allowed to be read by munin user group\n"; return 1; @@ -125,15 +125,15 @@ for($i=count($fp);$i>(count($fp)-$limit);--$i) strpos($msg," Connection closed")!==FALSE || strpos($msg," Tiemout")!==FALSE ){ - $seen[$nick]['online'] = 0; + $seen[$nick]['online'] = 0; $offline+=1; } else { - $seen[$nick]['online'] = 1; + $seen[$nick]['online'] = 1; $online+=1; - + } } } @@ -147,4 +147,4 @@ return 0; ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// //end of file -?> \ No newline at end of file +?> diff --git a/plugins/voip/murmurice_host_port_id_description b/plugins/voip/murmurice_host_port_id_description index 2deeb8f1..8f350d46 100755 --- a/plugins/voip/murmurice_host_port_id_description +++ b/plugins/voip/murmurice_host_port_id_description @@ -7,7 +7,7 @@ error_reporting( E_ALL & ~E_NOTICE ); //to avoid of the crap generation ///////////////////////////////////////////////////////////////////////////////////////////////////// Murmur users online grapher PHP using ICE -ver 0.4 2011.06.10, 15:44 +ver 0.4 2011.06.10, 15:44 author _KaszpiR_ kaszpir at gmail dot com code is under GPL @@ -31,8 +31,8 @@ By default script tries to connect 127.0.0.1:6502 and query server id 1 2. ice profile configuration This is not needed with ice 3.4.1 - Scroll down in this file and change - $ice_profile = 'Murmur'; + Scroll down in this file and change + $ice_profile = 'Murmur'; to the profile that is installed on the server, this is required if you have multiple Ice profiles for various applicaitions. @@ -82,7 +82,7 @@ $ice_profile = 'Murmur'; -// Define STDIN in case if it is not already defined by PHP for some reason +// Define STDIN in case if it is not already defined by PHP for some reason if(!defined("STDIN")) { define("STDIN", fopen('php://stdin','r')); @@ -108,7 +108,7 @@ if(isset($argv[1]) && $argv[1] == "autoconf") if(FALSE) { } - else + else { fwrite(STDOUT, "No\n"); fwrite(STDERR, "symlink ".$argv[0]." to somethilg like ".$argv[0]."_127.0.0.1_6502_1_description_here \n"); @@ -123,7 +123,7 @@ if(isset($argv[1]) && $argv[1] == "config") { if(TRUE) { - // yea dirty hack + // yea dirty hack echo "graph_title Mumble Users".($desc?" on ".str_replace("_"," ",$desc):"")."\n"; echo "graph_vlabel Connected Users\n"; echo "graph_category VoIP\n"; @@ -134,7 +134,7 @@ if(isset($argv[1]) && $argv[1] == "config") echo "unregistered.label Unregistered\n"; echo "chanlinks.label Linked chans\n"; echo "error.label Server status error\n"; - + $arr=array("channels","players","registered","unregistered","chanlinks","error"); foreach($arr as $field){ echo "".$field.".draw LINE1\n"; @@ -143,7 +143,7 @@ if(isset($argv[1]) && $argv[1] == "config") return 0; - + }else { echo "RTFM\n"; return 1; @@ -174,7 +174,7 @@ try { $initData->properties = Ice_createProperties(); $initData->properties->setProperty("Ice.MessageSizeMax", "65536"); $ICE = Ice_initialize($initData); - } + } $base = $ICE->stringToProxy("Meta:tcp -h ".$ip." -p ".$port); @@ -222,4 +222,4 @@ echo "unregistered.value ".$online_noreg."\n"; echo "chanlinks.value ".$links."\n"; echo "error.value 0\n"; return 0; -?> \ No newline at end of file +?> diff --git a/plugins/voip/murmurice_host_port_id_description_avg b/plugins/voip/murmurice_host_port_id_description_avg index 600232c0..4d0ddfee 100755 --- a/plugins/voip/murmurice_host_port_id_description_avg +++ b/plugins/voip/murmurice_host_port_id_description_avg @@ -7,7 +7,7 @@ error_reporting( E_ALL & ~E_NOTICE ); //to avoid of the crap generation ///////////////////////////////////////////////////////////////////////////////////////////////////// Murmur users online grapher PHP using ICE, shows averages -ver 0.2 2011.06.10, 15:44 +ver 0.2 2011.06.10, 15:44 author _KaszpiR_ kaszpir at gmail dot com code is under GPL @@ -31,8 +31,8 @@ By default script tries to connect 127.0.0.1:6502 and query server id 1 2. ice profile configuration This is not needed with ice 3.4.1 - Scroll down in this file and change - $ice_profile = 'Murmur'; + Scroll down in this file and change + $ice_profile = 'Murmur'; to the profile that is installed on the server, this is required if you have multiple Ice profiles for various applicaitions. @@ -80,7 +80,7 @@ Todo: $ice_profile = 'Murmur'; -// Define STDIN in case if it is not already defined by PHP for some reason +// Define STDIN in case if it is not already defined by PHP for some reason if(!defined("STDIN")) { define("STDIN", fopen('php://stdin','r')); @@ -106,7 +106,7 @@ if(isset($argv[1]) && $argv[1] == "autoconf") if(FALSE) { } - else + else { fwrite(STDOUT, "No\n"); fwrite(STDERR, "symlink ".$argv[0]." to somethilg like ".$argv[0]."_127.0.0.1_6502_1_description_here \n"); @@ -121,7 +121,7 @@ if(isset($argv[1]) && $argv[1] == "config") { if(TRUE) { - // yea dirty hack + // yea dirty hack echo "graph_title Mumble Average Users per chan ".($desc?" on ".str_replace("_"," ",$desc):"")."\n"; echo "graph_vlabel Number\n"; echo "graph_category VoIP\n"; @@ -133,7 +133,7 @@ if(isset($argv[1]) && $argv[1] == "config") echo "selfdeaf.label Self Deafen\n"; echo "avgperchan.label Users per chan\n"; echo "error.label Server status error\n"; - + $arr=array("mute","deaf","suppress","selfmute","selfdeaf","avgperchan","error"); foreach($arr as $field){ echo "".$field.".draw LINE1\n"; @@ -142,7 +142,7 @@ if(isset($argv[1]) && $argv[1] == "config") return 0; - + }else { echo "RTFM\n"; return 1; @@ -177,7 +177,7 @@ try { $initData->properties = Ice_createProperties(); $initData->properties->setProperty("Ice.MessageSizeMax", "65536"); $ICE = Ice_initialize($initData); - } + } $base = $ICE->stringToProxy("Meta:tcp -h ".$ip." -p ".$port); @@ -238,4 +238,4 @@ echo "selfdeaf.value ".$selfdeaf."\n"; echo "avgperchan.value ".$avg."\n"; echo "error.value 0\n"; return 0; -?> \ No newline at end of file +?> diff --git a/plugins/vpn/openvpn_as_mtime b/plugins/vpn/openvpn_as_mtime index 7dbf90ac..eb8ce58a 100755 --- a/plugins/vpn/openvpn_as_mtime +++ b/plugins/vpn/openvpn_as_mtime @@ -36,7 +36,7 @@ GPLv2 if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -62,7 +62,7 @@ if [ "$1" = "config" ]; then echo "time.label Users" echo "time.type GAUGE" echo "time.min 0" - + exit 0 fi @@ -78,5 +78,5 @@ fi # Total is total time between number of users between 60 for give it in minutes echo "time.value $(($(($SUM / $TOTU)) / 60))" - + diff --git a/plugins/vpn/openvpn_as_traffic b/plugins/vpn/openvpn_as_traffic index d59e7fee..718ff7f1 100755 --- a/plugins/vpn/openvpn_as_traffic +++ b/plugins/vpn/openvpn_as_traffic @@ -40,7 +40,7 @@ SUM2=0 NOW=`date +%s` if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi diff --git a/plugins/vpn/openvpn_as_ttime b/plugins/vpn/openvpn_as_ttime index 06809a92..2f514cba 100755 --- a/plugins/vpn/openvpn_as_ttime +++ b/plugins/vpn/openvpn_as_ttime @@ -36,7 +36,7 @@ GPLv2 if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -73,7 +73,7 @@ if [ "$1" = "config" ]; then echo "$NAME.min 0" COUNT=$((COUNT + 1)) done - + exit 0 fi diff --git a/plugins/vpn/openvpn_as_users b/plugins/vpn/openvpn_as_users index 3d655cb6..1fe54eb5 100755 --- a/plugins/vpn/openvpn_as_users +++ b/plugins/vpn/openvpn_as_users @@ -33,7 +33,7 @@ GPLv2 if [ "$1" = "autoconf" ]; then - echo yes + echo yes exit 0 fi @@ -59,9 +59,9 @@ if [ "$1" = "config" ]; then echo "users.type GAUGE" echo "users.draw AREA" echo "users.min 0" - - echo "users.warning $((VAL2 * $WARN / 100))" - echo "users.critical $VAL2" + + echo "users.warning $((VAL2 * $WARN / 100))" + echo "users.critical $VAL2" echo "limit.label Limit" @@ -76,5 +76,5 @@ fi echo "users.value $VAL1" echo "limit.value $VAL2" - + diff --git a/plugins/vserver/vserver_jiffies b/plugins/vserver/vserver_jiffies index dfb8dd41..f57c41e4 100755 --- a/plugins/vserver/vserver_jiffies +++ b/plugins/vserver/vserver_jiffies @@ -56,7 +56,7 @@ if [[ "$1" = "config" ]]; then echo 'graph_vlabel jiffies used per ${graph_period}' echo 'graph_info Shows jiffies used on each vserver.' - for i in $XIDS[@]; do + for i in $XIDS[@]; do LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) LABEL=${LABEL%$STRIPDOMAIN} NAME=${LABEL//./_} @@ -74,7 +74,7 @@ if [[ "$1" = "config" ]]; then exit 0 fi -for i in $XIDS[@]; do +for i in $XIDS[@]; do LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) LABEL=${LABEL%$STRIPDOMAIN} NAME=${LABEL//./_} diff --git a/plugins/vserver/vserver_limit_hits b/plugins/vserver/vserver_limit_hits index d4a690cc..d896ac1e 100755 --- a/plugins/vserver/vserver_limit_hits +++ b/plugins/vserver/vserver_limit_hits @@ -23,14 +23,14 @@ # # NOTE: If no configuration variables are set, the defaults will be used -# Example /etc/munin/plugin-conf.d/munin-node +# Example /etc/munin/plugin-conf.d/munin-node # # The first group monitors the vservers named "vserver1 vserver2 # vserver3 vserver4" and looks to see if the resource limit has been # breached, if so it sends a message to nagios via send_nsca, and # sends an email to notify that this has happened. # -# The second monitors the vservers "vserver5 vserver6 vserver7" and +# The second monitors the vservers "vserver5 vserver6 vserver7" and # has no limit notifications turned on. # # The third monitors all vservers on the system, in one graph, and it has @@ -73,7 +73,7 @@ # version 0.5 - 2008 Apr 12 - Chris Wilson # - Changed to display limit hits instead of resource usage # - Adapt to latest vserver kernel (lack of some variables in /proc/virtual) -# Note that your vserver names may change if the contents of +# Note that your vserver names may change if the contents of # /etc/vservers/* do not match the nodenames. Also you must specify # the vservers variable with context IDs (XIDs) rather than names. @@ -91,7 +91,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel $resource limit hits" echo 'graph_category virtualization' echo "graph_info Shows number of hits on $resource limits by each vserver.'" - + for vserver_xid in $vservers ; do longname=`/usr/sbin/vuname --xid $vserver_xid NODENAME | cut -f2` name=`echo $longname | cut -d. -f1` diff --git a/plugins/vserver/vserver_limits b/plugins/vserver/vserver_limits index ea4317c5..f724eed0 100755 --- a/plugins/vserver/vserver_limits +++ b/plugins/vserver/vserver_limits @@ -23,7 +23,7 @@ # # NOTE: If no configuration variables are set, the defaults will be used -# Example /etc/munin/plugin-conf.d/munin-node +# Example /etc/munin/plugin-conf.d/munin-node # # [vserver_limits_RSS] # user root @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel $resource limits" echo 'graph_category virtualization' echo "graph_info Shows current $resource limits for each vserver.'" - + for vserver_xid in $vservers ; do longname=`/usr/sbin/vuname --xid $vserver_xid NODENAME | cut -f2` name=`echo $longname | cut -d. -f1` diff --git a/plugins/weather/buienradar_ b/plugins/weather/buienradar_ index 663b3cd0..2fda3f52 100755 --- a/plugins/weather/buienradar_ +++ b/plugins/weather/buienradar_ @@ -6,12 +6,12 @@ # BuienRadar: http://www.buienradar.nl # # -# 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 +# 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 +# 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. @@ -41,9 +41,9 @@ # env.rain = 'yes' # # -# +# ### Installation Example -# Below is a example to monitor station Venlo (6391). Codes for the station can be seen at the end of +# Below is a example to monitor station Venlo (6391). Codes for the station can be seen at the end of # this source or can be show with the command './buienradar_ stations' # # wget -O /usr/lib/munin/plugins/buienradar_ http://exchange.munin-monitoring.org/plugins/buienradar_/version/1/download @@ -101,7 +101,7 @@ if($stationName eq "") { # Darn, no station name found. This means we don't provided a correct id. die "Error:\n" ."You have provided an invalid station code. (".$station.") Please use a correct one.\n\n" - .print_stations(); + .print_stations(); } diff --git a/plugins/weather/novra_s300 b/plugins/weather/novra_s300 index ca20cbc7..e82fa9be 100755 --- a/plugins/weather/novra_s300 +++ b/plugins/weather/novra_s300 @@ -16,7 +16,7 @@ # @email icedown@gmail.com # # Usage: -# Copy this to your plugin folder (default: /usr/share/munin/plugins) +# Copy this to your plugin folder (default: /usr/share/munin/plugins) # Edit is file, replacing CMCS, IP, and PW with your values # Make a symlink to your active plugins folder (default: /etc/munin/plugins) # Finally run munin-node-config and restart munin-node @@ -59,15 +59,15 @@ if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) { if($status =~ m/Login unsuccessful/) { print "No (Invalid receiver details)\n"; exit(0); - } + } print "yes\n"; exit(0); } - + print "no (Cannot locate CMCS)\n"; exit(0); - - + + } require XML::Simple; diff --git a/plugins/weather/temperatures b/plugins/weather/temperatures index 52711c8b..a19cbdf4 100755 --- a/plugins/weather/temperatures +++ b/plugins/weather/temperatures @@ -65,7 +65,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { } else { print "graph_vlabel temp in C\n"; } - + for my $station (@wcode) { my $url = "$datasource$station.TXT"; my $response = $ua->request(HTTP::Request->new('GET',$url)); diff --git a/plugins/weather/wfrog b/plugins/weather/wfrog index 4fb5bd79..3c9d1243 100755 --- a/plugins/weather/wfrog +++ b/plugins/weather/wfrog @@ -19,8 +19,8 @@ use Data::Dumper; # 1. Install wfrog, get it up running with your weather station # 2. Locate your wfrog.csv file (wfrog creates after 10 mins) # 3. cd /etc/munin/plugins/ -# 4. ln -s /usr/share/munin/plugins/wfrog wfrog_temp -# 4. ln -s /usr/share/munin/plugins/wfrog wfrog_pressure +# 4. ln -s /usr/share/munin/plugins/wfrog wfrog_temp +# 4. ln -s /usr/share/munin/plugins/wfrog wfrog_pressure # 5. etc.. # 6. reload munin-node ;-) @@ -35,7 +35,7 @@ my %CONFIG = ( my $interesting; if ($0 =~ m#wfrog_(\w+)#) { - $interesting = $1; + $interesting = $1; } else { diff --git a/plugins/websphere/webspherelogin b/plugins/websphere/webspherelogin index e1744c55..b4b57cfc 100755 --- a/plugins/websphere/webspherelogin +++ b/plugins/websphere/webspherelogin @@ -7,12 +7,12 @@ SITEANALYZERLOG="/opt/WebSphere/SiteAnalyzer/sa.log" if [ "$1" = "config" ]; then - echo 'graph_title WebSphere Portal online users' - echo 'graph_category appserver' -# echo 'graph_order total actual' - echo 'graph_order actual' + echo 'graph_title WebSphere Portal online users' + echo 'graph_category appserver' +# echo 'graph_order total actual' + echo 'graph_order actual' echo 'graph_vlabel Users' - echo 'graph_scale no' + echo 'graph_scale no' # echo 'total.label Total logged' # echo 'total.draw LINE2' # echo 'total.colour DDDDDD' diff --git a/plugins/wifi/wifi_signal b/plugins/wifi/wifi_signal index 88ed22d1..a1e24306 100755 --- a/plugins/wifi/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -24,7 +24,7 @@ do_config () { echo "graph_vlabel dB" echo "graph_category wireless" echo "graph_info This graph shows the noise and singal levels of your WiFi devices" - + awk -F'[ :]*' '/:/ { print $2"_noise.label Noise "$2; print $2"_signal.label Signal "$2; diff --git a/plugins/wordpress/wordpress b/plugins/wordpress/wordpress index f2f08a27..3f2f5976 100755 --- a/plugins/wordpress/wordpress +++ b/plugins/wordpress/wordpress @@ -1,6 +1,6 @@ #!/bin/sh # wordpress-munin plugin -# +# # Author Andre Darafarin # Improvements by Chris Bair # Version 0.2 15 Feb 2011 @@ -38,7 +38,7 @@ Add file plugin-conf.d/wordpress and fill like this # Replace your_db_pass =item * env.DB_HOST host_of_your_db -# Replace with host of database server. Will be localhost for many users. +# Replace with host of database server. Will be localhost for many users. =back diff --git a/plugins/wordpress/wordpress-mu-or-network b/plugins/wordpress/wordpress-mu-or-network index a23074fe..dfc9f6a4 100755 --- a/plugins/wordpress/wordpress-mu-or-network +++ b/plugins/wordpress/wordpress-mu-or-network @@ -1,11 +1,11 @@ #!/bin/bash # wordpress-mu-munin plugin -# +# # Author Andre Darafarin # Improvements by Chris Bair # Modified for Wordpress MU (or Network) by raT rat@espiv.net @ 22-04-2011 # Version 0.2 15 Feb 2011 -# +# # # : <<=cut @@ -40,7 +40,7 @@ Add file plugin-conf.d/wordpress and fill like this # Replace your_db_pass =item * env.DB_HOST host_of_your_db -# Replace with host of database server. Will be localhost for many users. +# Replace with host of database server. Will be localhost for many users. =back diff --git a/plugins/wordpress/wordpress2 b/plugins/wordpress/wordpress2 index c075b21a..f219225d 100755 --- a/plugins/wordpress/wordpress2 +++ b/plugins/wordpress/wordpress2 @@ -8,7 +8,7 @@ error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); it's a simple plugin to monitor users, comments, pingbacks and your posts from your wordpress homepage. - Simply put your path of wp-config.php in your munin-node + Simply put your path of wp-config.php in your munin-node configuration and this plugin does the rest for you. Happy monitoring! :) @@ -19,7 +19,7 @@ error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); ---- CONFIGURATION ---- You just need to provide the path to your wp-config.php of your wordpress installation. - + The configuration for munin-node is by default at: /etc/munin/plugin-conf.d/munin-node diff --git a/plugins/wowza/wowza-media-server b/plugins/wowza/wowza-media-server index 2cbb73cf..fd04aa5e 100755 --- a/plugins/wowza/wowza-media-server +++ b/plugins/wowza/wowza-media-server @@ -141,7 +141,7 @@ try: print ("graph_vlabel minutes") print ("avg.label average listening duration") print ("mdn.label median listening duration") - + elif plugin_name == "wowza_vhost_listeners": print ("graph_title Wowza listeners count by vhosts") print ("graph_args --base 1000 -l 0") @@ -157,7 +157,7 @@ try: is_first = False else: print (vname,".draw STACK",sep='') - + elif plugin_name == "wowza_vhost_duration": print ("graph_title Wowza clients listening duration by vhosts") print ("graph_args --base 1000 -l 0") @@ -168,7 +168,7 @@ try: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") print (vname,"_avg.label average listening duration for ",vh["Name"],sep='') print (vname,"_mdn.label median listening duration for ",vh["Name"],sep='') - + elif plugin_name == "wowza_vhost_uptime": print ("graph_title Wowza vhosts uptime") print ("graph_args --base 1000 -l 0") @@ -178,7 +178,7 @@ try: for vh in vhosts: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") print (vname,".label vhost: ",vh["Name"],sep='') - + elif plugin_name == "wowza_app_listeners": print ("graph_title Wowza listeners count by apps") print ("graph_args --base 1000 -l 0") @@ -196,7 +196,7 @@ try: is_first = False else: print (vname,"_",aname,".draw STACK",sep='') - + elif plugin_name == "wowza_app_duration": print ("graph_title Wowza clients listening duration by apps") print ("graph_args --base 1000 -l 0") @@ -209,7 +209,7 @@ try: aname = app["Name"].strip("/").replace(".","_").replace("-","_") print (vname,"_",aname,"_avg.label average listening duration for ",vh["Name"],".",app["Name"],sep='') print (vname,"_",aname,"_mdn.label median listening duration for ",vh["Name"],".",app["Name"],sep='') - + elif plugin_name == "wowza_app_uptime": print ("graph_title Wowza apps uptime") print ("graph_args --base 1000 -l 0") @@ -221,7 +221,7 @@ try: for app in vh["Applications"]: aname = app["Name"].strip("/").replace(".","_").replace("-","_") print (vname,"_",aname,".label vhost.app: ",vh["Name"],".",app["Name"],sep='') - + else: # wowza_listeners print ("graph_title Wowza listeners count") print ("graph_args --base 1000 -l 0") diff --git a/plugins/wuala/wuala_stats b/plugins/wuala/wuala_stats index bab9827c..f151b737 100755 --- a/plugins/wuala/wuala_stats +++ b/plugins/wuala/wuala_stats @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# +# # Plugin to monior the Wua.la Linux client # for shared, social storage from http://wua.la. # @@ -10,14 +10,14 @@ # # Set env.wualadir , e.g. /home/fred/wuala # and user fred. Please note, that if you don't run this -# plugin as the correct user it won't work, since the +# plugin as the correct user it won't work, since the # wualacmd command will only work when running as the correct user. # # The Plugin tries to detect if wuala is running and will # report unknown (U) as values if it is not running. # # This is a dual-personality plugin. If the file- or -# softlink-name of this plugin ends in _storage it will report +# softlink-name of this plugin ends in _storage it will report # the storage values and if it ends in _uptime it will report the # uptime. # diff --git a/plugins/xastir/xastir b/plugins/xastir/xastir index 5894b181..bd575173 100755 --- a/plugins/xastir/xastir +++ b/plugins/xastir/xastir @@ -1,6 +1,6 @@ #!/bin/bash -## Copyright (C) 2012 Robert Kilian +## Copyright (C) 2012 Robert Kilian ## ## This file is part of the Xastir plugin for Munin. ## @@ -106,7 +106,7 @@ NETTX=`cat $LOGDIR/net.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l` TNC=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l` TNCTX=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l` -# Display values +# Display values echo "igatetonet.value $IGATETONET" echo "message.value $MESSAGE" echo "messagetx.value $MESSAGETX" diff --git a/plugins/xen/xen-cpu b/plugins/xen/xen-cpu index 90fabeaa..4c558134 100755 --- a/plugins/xen/xen-cpu +++ b/plugins/xen/xen-cpu @@ -3,7 +3,7 @@ # Script to minitor the cpu usage of Xen domains # # Author: Adam Crews shroom com> -# +# # License: GPL # Based on the original xen script from Matthias Pfafferodt, syntron at web.de # @@ -55,7 +55,7 @@ if (defined($ARGV[0])) { 'graph_vlabel' => 'Percent (%)', 'graph_category' => 'virtualization', 'graph_info' => 'Display the % of CPU Usage for each domain', - ); + ); my @domains = `$XM list`; shift(@domains); # we dont need the header line @@ -75,7 +75,7 @@ if (defined($ARGV[0])) { if ( "$cnt" == "0") { $cnf{$dom.'.draw'} = 'AREA'; } $cnt++; } - + foreach my $key (sort(keys(%cnf))) { print "$key $cnf{$key}\n"; } @@ -87,7 +87,7 @@ if (defined($ARGV[0])) { my @chunks; undef(@chunks); -{ +{ # run the xentop command a few times because the first reading is not always accurate local $/ = undef; @chunks = split(/^xentop - .*$/m, `$XMTOP -b -i2 -d2`); @@ -97,10 +97,10 @@ my @chunks; undef(@chunks); my @stats = split (/\n/,pop(@chunks)); # remove the first 4 items that are junk that we don't need. -shift(@stats); -shift(@stats); -shift(@stats); -shift(@stats); +shift(@stats); +shift(@stats); +shift(@stats); +shift(@stats); my %vals; undef(%vals); diff --git a/plugins/xen/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 index 63e41248..4483cf96 100755 --- a/plugins/xen/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -7,8 +7,8 @@ # Based loosely on Adam Crews' xen_cpu script # # This script tries to measure the CPU usage of the Xen guests -# accurately. -# The problem with the current monitoring script is that these +# accurately. +# The problem with the current monitoring script is that these # scripts use the CPU output of xentop or xm list, which might be # inaccurate due to the resources used up at the time of the query by # the xm or xentop command. @@ -50,7 +50,7 @@ if ( defined($ARGV[0]) ) { $arg = 'autoconf'; } - + if ( $arg eq 'autoconf') { if ( -e $XM && -e $XMTOP ) @@ -102,7 +102,7 @@ if ( defined($ARGV[0]) ) exit 0; } } - + my @xmlist = `$XM list`; shift (@xmlist); @@ -124,7 +124,7 @@ if ( -e $TEMPFILE ) $_ =~ /(\S+)\s+\S+\s+\S+\s+\d+\s+\S+\s+(\S+)/; $dom{$1}->{'oldtime'} = $2; } - + close FH; } @@ -137,14 +137,14 @@ foreach my $domain ( @xmlist ) $diff = $dom{$1}->{'newtime'} - $dom{$1}->{'oldtime'}; $diff = sprintf("%.2f", $diff); - + # Calc the diff between old and new cputime, or reset the counter if ( $diff < 0 ) { $diff = $dom{$1}->{'newtime'}; } $dom{$1}->{'diff'} = $diff; - + # Calc a sum CPU usage $cpusum = $cpusum + $diff; } @@ -168,7 +168,7 @@ while (($key, $value) = each %dom) my $tmp = 0; $tmp = ( $dom{$key}->{'diff'} / $cpusum ) * 100; $dom{$key}->{'pc_time'} = sprintf("%.2f", $tmp); - + # Calc a percentage based on the _total_ available CPU time $tmp = 0; if ($tcpuavail != 0) { @@ -177,7 +177,7 @@ while (($key, $value) = each %dom) } else { $dom{$key}->{'pc_tcpu'} = "U"; } - + if ( $debug ) { print "$key newtime: ".$dom{$key}->{'newtime'}.", oldtime: ".$dom{$key}->{'oldtime'}.", diff: ".$dom{$key}->{'diff'}.", pc_bytime ".$dom{$key}->{'pc_time'}.", pc_bytcpu ".$dom{$key}->{'pc_tcpu'}."\n"; @@ -190,4 +190,4 @@ open(FH, ">", $TEMPFILE) or die $!; print FH $curtime."\n"; print FH @xmlist; close FH; - + diff --git a/plugins/xen/xen_traffic_all b/plugins/xen/xen_traffic_all index 7c5fa3d2..b22a594f 100755 --- a/plugins/xen/xen_traffic_all +++ b/plugins/xen/xen_traffic_all @@ -33,7 +33,7 @@ if [ "$1" = "config" ]; then name=$( echo "$dom" | tr .- __ ) #echo $name"UP.label $name" #echo $name"DOWN.label $name" - + echo "${name}Down.label received" echo "${name}Down.type COUNTER" echo "${name}Down.graph no" diff --git a/plugins/xen/xen_vbd b/plugins/xen/xen_vbd index 1f74c24b..55e8c701 100755 --- a/plugins/xen/xen_vbd +++ b/plugins/xen/xen_vbd @@ -16,13 +16,13 @@ $XM = '/usr/sbin/xm'; $XMTOP = '/usr/sbin/xentop'; -# ah, parameters coming in +# ah, parameters coming in if ( defined($ARGV[0])) { if ($ARGV[0] eq 'config') { $arg = 'config'; } if ($ARGV[0] eq 'autoconf') { $arg = 'autoconf'; } - - if ( $arg eq 'autoconf' ) + + if ( $arg eq 'autoconf' ) { if ( -e $XM && -e $XMTOP ) { @@ -35,7 +35,7 @@ if ( defined($ARGV[0])) exit 0; } } - + if ( $arg eq 'config' ) { %cnf = ( @@ -45,38 +45,38 @@ if ( defined($ARGV[0])) 'graph_category' => 'virtualization', 'graph_info' => 'Display the I/O operations for each domain', ); - + @domains = `$XM list`; shift(@domains); # we don't need the header line - + foreach $domain ( @domains ) { ($dom, undef) = split(/\s/, $domain); $dom =~ s/[-.]/_/g; - + $cnf{ $dom.'RD' . '.label' } = 'read'; $cnf{ $dom.'RD' . '.type' } = 'COUNTER'; $cnf{ $dom.'RD' . '.graph' } = 'no'; $cnf{ $dom.'RD' . '.cdef' } = $dom.'RD,8,*'; - + $cnf{ $dom.'WR' . '.label' } = $dom; $cnf{ $dom.'WR' . '.type' } = 'COUNTER'; $cnf{ $dom.'WR' . '.negative' } = $dom.'RD'; $cnf{ $dom.'WR' . '.cdef' } = $dom.'WR,8,*'; - + if ( "$cnt" == "0" ) { $cnf { "$dom" . '.draw' } = 'AREA'; } $cnt++; } - + foreach $key ( sort(keys(%cnf)) ) { print "$key $cnf{$key}\n"; } exit 0; - + } } @@ -98,12 +98,12 @@ foreach $domain (@stats) { $domain =~ s/^\s+//; @tmp = split(/\s+/, $domain); - + $domname = $tmp[0]; $domname =~ s/[-.]/_/g; $vbdrd = $tmp[14]; $vbdwr = $tmp[15]; - + $vals{$domname."RD"}{'value'} = $vbdrd; $vals{$domname."WR"}{'value'} = $vbdwr; } @@ -112,4 +112,4 @@ foreach $key ( sort(keys(%vals)) ) { print "$key.value " . ($vals{$key}{'value'}) . "\n"; } - + diff --git a/plugins/yacy/yacy b/plugins/yacy/yacy index 19589f4e..8c6348c3 100755 --- a/plugins/yacy/yacy +++ b/plugins/yacy/yacy @@ -29,7 +29,7 @@ yacy - Munin plugin to monitor YaCy distributed search engine network. =head1 APPLICABLE SYSTEMS -YaCy +YaCy =head1 CONFIGURATION @@ -59,14 +59,14 @@ GPLv2 =cut BEGIN { - if(!eval "require XML::Smart;") { + if(!eval "require XML::Smart;") { die("XML::Smart not found"); } - if(!eval "require LWP;") { + if(!eval "require LWP;") { die("LWP not found"); } } - + use XML::Smart; use strict; @@ -99,7 +99,7 @@ EOF die("PUDDI PUDDI PUDDI PUDDI PUDDI") unless ($action); # Config -if ($ARGV[0] and $ARGV[0] eq "config") +if ($ARGV[0] and $ARGV[0] eq "config") { my $titles = { network_peers => 'YaCy Network Online Peers', diff --git a/plugins/yum/yum_activity b/plugins/yum/yum_activity index bad9a4dd..5b02ea9b 100755 --- a/plugins/yum/yum_activity +++ b/plugins/yum/yum_activity @@ -4,14 +4,14 @@ # # Parameters: # -# config -# autoconf +# config +# autoconf # if [ "$1" = "autoconf" ]; then if [ -r /var/log/yum.log ]; then - echo yes + echo yes exit 0 else echo "no (/var/log/yum.log not readable)" diff --git a/plugins/zfs/zfs-filesystem-graph b/plugins/zfs/zfs-filesystem-graph index a3bd62d6..1324f95d 100755 --- a/plugins/zfs/zfs-filesystem-graph +++ b/plugins/zfs/zfs-filesystem-graph @@ -2,12 +2,12 @@ # # Plugin to monitor a ZFS Filesystem # -# Wildcard-plugin to monitor a zfs filesystems. +# Wildcard-plugin to monitor a zfs filesystems. # # To monitor a filesystem, link zfs_fs__ to this file. E.g. # # ln -s /usr/share/munin/node/plugins-auto/zfs_fs_ /etc/munin/node.d/zfs_fs_tank_foo -# +# # ...will monitor tank/foo fs. # # You can monitor zpool as well by a link on zfs_fs_ @@ -16,7 +16,7 @@ # # config (required) # autoconf (optional - used by munin-config) -# +# # ** WARNING ** # For now this plugin does not allow "_" in the name of a zpool or filesystems # diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency index 178c7000..0d9905c4 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency @@ -42,8 +42,8 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the ARC Efficiency' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /Efficiency/ {print}' | \ -awk '/^\t.*%/ {sub("\t","");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' - +awk '/^\t.*%/ {sub("\t","");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' + exit 0 fi diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization index 266ddaf0..97baef57 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization @@ -42,8 +42,8 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the ARC Size utilization' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /^ARC Size/ {print}' | \ -awk '/^\t/ {sub("\t","");sub(/ \(.*\):.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' - +awk '/^\t/ {sub("\t","");sub(/ \(.*\):.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' + exit 0 fi diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list index 3e9bbb51..5c5a406c 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list @@ -42,7 +42,7 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the ARC cache hits by cache list' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /CACHE HITS BY CACHE LIST/ {print}' | \ -awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/ \(.*\):/,":");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' +awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/ \(.*\):/,":");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' exit 0 fi diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type index 030f0f42..7070b69f 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type @@ -42,10 +42,10 @@ if [ "$1" = "config" ]; then echo 'graph_info This graph shows the ARC cache hits and misses by data type' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /HITS BY DATA TYPE/ {print}' | \ -awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0" (hits)"; gsub(/[ .]/,"_",$0); print "hits_"$0".label "displayname"\nhits_"$0".min 0"}' +awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0" (hits)"; gsub(/[ .]/,"_",$0); print "hits_"$0".label "displayname"\nhits_"$0".min 0"}' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /MISSES BY DATA TYPE/ {print}' | \ -awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0" (misses)"; gsub(/[ .]/,"_",$0); print "misses_"$0".label "displayname"\nmisses_"$0".min -100"}' - +awk '/^\t.*%/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0" (misses)"; gsub(/[ .]/,"_",$0); print "misses_"$0".label "displayname"\nmisses_"$0".min -100"}' + exit 0 fi diff --git a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch index bbe8b652..db10af62 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch +++ b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch @@ -41,9 +41,9 @@ if [ "$1" = "config" ]; then echo 'graph_category fs' echo 'graph_info This graph shows the DMU prefech stats' -/usr/local/bin/zfs-stats -Z | awk '/Hit Ratio/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' -/usr/local/bin/zfs-stats -Z | awk '/Miss Ratio/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min -100"}' - +/usr/local/bin/zfs-stats -Z | awk '/Hit Ratio/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' +/usr/local/bin/zfs-stats -Z | awk '/Miss Ratio/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min -100"}' + exit 0 fi diff --git a/plugins/zfs/zfs_arcstats b/plugins/zfs/zfs_arcstats index 7b0d74eb..b2b79b3a 100755 --- a/plugins/zfs/zfs_arcstats +++ b/plugins/zfs/zfs_arcstats @@ -25,7 +25,7 @@ For FreeBSD, it should be necessary to change shebang /bin/bash -> /usr/local/bin/bash =head1 ENVIRONMENT VARIABLES - + None =head1 AUTHOR @@ -66,7 +66,7 @@ get_osname() { SunOS) case $osver in illumos*) - osname=illumos + osname=illumos ;; esac ;; @@ -84,7 +84,7 @@ preconfig() { case $func in size) global_attr=" - graph_title ZFS ARC - Size + graph_title ZFS ARC - Size graph_category fs graph_args --base 1024 --lower-limit 0 graph_vlabel Bytes @@ -150,7 +150,7 @@ preconfig() { " data_attr=" mfu_ghost_hits DERIVE LINE dummy - mfu_hits DERIVE LINE MFU + mfu_hits DERIVE LINE MFU mru_ghost_hits DERIVE LINE dummy mru_hits DERIVE LINE MRU " @@ -219,12 +219,12 @@ preconfig() { echo "Unknown function: $func" exit 1 ;; - esac + esac } do_config() { local func=$1 - local label_max_length=45 + local label_max_length=45 local field type draw label preconfig "$func" @@ -254,7 +254,7 @@ do_config() { get_stats() { local arcstats stat value - + case $osname in SunOS|illumos) arcstats=$( kstat -p 'zfs:0:arcstats' | sed -e 's/:/ /g' | awk '{ print $4,$5 }' ) @@ -286,8 +286,8 @@ get_stats() { esac while read -r stat value - do - printf -v "arcstats_${stat}" "%s" "$value" + do + printf -v "arcstats_${stat}" "%s" "$value" # printf -v means indirect variable assignment (similar to eval) done <<< "$arcstats" } @@ -303,8 +303,8 @@ do_fetch() { do [ -z "$field" ] && continue - ref="arcstats_${field}" - value=${!ref:-0} + ref="arcstats_${field}" + value=${!ref:-0} # ${!varname} means indirect evaluation (similar to eval) echo "${field}.value ${value}" diff --git a/plugins/zfs/zfs_cache_efficiency b/plugins/zfs/zfs_cache_efficiency index e793ca6e..0ed85949 100755 --- a/plugins/zfs/zfs_cache_efficiency +++ b/plugins/zfs/zfs_cache_efficiency @@ -16,7 +16,7 @@ graph_title ZFS ARC efficiency graph_vlabel % graph_scale no graph_category fs -graph_args -l 0 +graph_args -l 0 graph_info FreeBSD ZFS ARC Utilisation - Counters graph_period minute graph_order hits misses l2_hits l2_misses mfu_hits mru_hits arc_access_total mfu_hits_perc mru_hits_perc arc_misses_perc l2_efficency_tot diff --git a/plugins/zfs/zlist b/plugins/zfs/zlist index 161a23bc..7c27f986 100755 --- a/plugins/zfs/zlist +++ b/plugins/zfs/zlist @@ -29,7 +29,7 @@ fi case $i in *) name=`echo $i | awk '{ gsub("[^a-zA-Z0-9_]", "_", $1); print $1 }'` ;; esac - echo -n "$name.value " + echo -n "$name.value " echo $i|awk '{ print $4}' | awk ' /T/ {print $1*1000000}; /G/ {print $1*1000}; /M/ {print "777"}; /K/ {print "1"}' | sed 's/^\$name.value //' done diff --git a/plugins/zfs/zpool_capacity b/plugins/zfs/zpool_capacity index e8c7a3ce..921b7c20 100755 --- a/plugins/zfs/zpool_capacity +++ b/plugins/zfs/zpool_capacity @@ -26,7 +26,7 @@ user root =head1 ENVIRONMENT VARIABLES - + critical : default 90 warning : default 80 @@ -132,7 +132,7 @@ preconfig() { do_config() { local func="$1" - local label_max_length=45 + local label_max_length=45 local field type draw label preconfig "$func" diff --git a/plugins/znc/README.md b/plugins/znc/README.md index 0f7204fc..497ef8eb 100644 --- a/plugins/znc/README.md +++ b/plugins/znc/README.md @@ -6,7 +6,7 @@ ZNC-Logs 2. Enable the log-plugin in znc (if you enable it for the complete instance, there will be some issues because this plugin only uses the network-name + channel-name, if there are some networks with the same name, it will count all lines together) -3. Add this to your `/etc/munin/plugin-conf.d/munin-node` +3. Add this to your `/etc/munin/plugin-conf.d/munin-node` ``` [znc_logs] user $your_znc_user diff --git a/plugins/zope/README-zodb b/plugins/zope/README-zodb index d1889cbf..ecd70dd0 100644 --- a/plugins/zope/README-zodb +++ b/plugins/zope/README-zodb @@ -17,7 +17,7 @@ wget --delete-after -q -O - localhost:8080//munin_db_activity.py wget --delete-after -q -O - localhost:8080//munin_cache_parameters.py Edit zope_db_activity and zope_cache_parameters scripts to reflect - and your instance(s) ports. + and your instance(s) ports. Then move them into your munin plugins lib, and create a symlink under etc according to the usual munin custom: @@ -33,4 +33,4 @@ Test them with: Restart your munin-node and the graphs should appear shortly. If you make any improvements (which should be easy), please update -the munin exhange entry, or mail me at gaute(at)pht(dot)no \ No newline at end of file +the munin exhange entry, or mail me at gaute(at)pht(dot)no diff --git a/plugins/zope/zeo_monitor_ b/plugins/zope/zeo_monitor_ index 60b59f48..8ad8cac1 100755 --- a/plugins/zope/zeo_monitor_ +++ b/plugins/zope/zeo_monitor_ @@ -11,7 +11,7 @@ # Reads has logarithmic scale # Adjust some scaling factors # Support for INET connections, the new default -# +# # Invoke using symlinks to zeo_monitor_ in the form zeo_monitor_{clients,reads,writes,errors}_ # # This plugin can be configured manually or by autoconf (provided that the ZEO @@ -26,7 +26,7 @@ # ln -s /usr/share/munin/plugins/zeo_monitor_ /etc/munin/plugins/zeo_monitor_reads_temp # ln -s /usr/share/munin/plugins/zeo_monitor_ /etc/munin/plugins/zeo_monitor_writes_1 # ln -s /usr/share/munin/plugins/zeo_monitor_ /etc/munin/plugins/zeo_monitor_writes_temp -# +# # # Configuration variables: # @@ -131,7 +131,7 @@ die qq(Symlink to this script by appending a mode and storage name such as "zeo_ ########## if ( $ARGV[0] and $ARGV[0] eq "config") { - + print <<"EOF"; graph_title ZEO $mode for storage $storage_name graph_args --base 1000 --lower-limit 0 @@ -258,7 +258,7 @@ sub parse_zeo_monitor { ) \n /sx; - + my %name_var=( 'Clients' => 'clients', 'Commits' => 'commits', @@ -268,7 +268,7 @@ sub parse_zeo_monitor { 'Conflicts' => 'conflicts', 'Conflicts resolved' => 'conflictsres', ); - + my %stats=(); foreach (split /\n/, $stats) { (my $name,my $value)=split ': ',$_,2; @@ -276,7 +276,7 @@ sub parse_zeo_monitor { next unless $var; $stats{$var}=$value; } - + return %stats; } diff --git a/plugins/zope/zope_cache_parameters b/plugins/zope/zope_cache_parameters index a7b30700..5581177b 100755 --- a/plugins/zope/zope_cache_parameters +++ b/plugins/zope/zope_cache_parameters @@ -37,7 +37,7 @@ else: print 'obs_in_db%(i)s.value' % id, obs_in_db print 'obs_cached%(i)s.value'% id, obs_cached print 'obs_target%(i)s.value'% id, obs_target - + diff --git a/plugins/zope/zope_conflict_errors b/plugins/zope/zope_conflict_errors index 59ad66a6..c2e03d35 100755 --- a/plugins/zope/zope_conflict_errors +++ b/plugins/zope/zope_conflict_errors @@ -32,8 +32,8 @@ import time if len(argv) > 1: if argv[1] == 'config': - print """graph_title Zope Conflict Errors - graph_vlabel Count + print """graph_title Zope Conflict Errors + graph_vlabel Count graph_category appserver graph_info The number of conflict errors in event logs over the past 24h""".replace("\n ","\n") for i in range(0,len(logs)): @@ -52,7 +52,7 @@ else: delta = datetime.now() - logdate if delta.days >= 1: continue - error_count += 1 + error_count += 1 id = dict(i=i) print 'error_count%(i)s.value' % id, error_count - + diff --git a/plugins/zope/zope_db_activity b/plugins/zope/zope_db_activity index ddcbbef6..7991ee5a 100755 --- a/plugins/zope/zope_db_activity +++ b/plugins/zope/zope_db_activity @@ -35,7 +35,7 @@ else: print 'load_count%(i)s.value' % id, total_load_count print 'store_count%(i)s.value'% id, total_store_count print 'connections%(i)s.value'% id, total_connections - + From d4320aea829a210f5a489ac50db0caecab21d6b1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 3 Aug 2018 12:59:16 +0200 Subject: [PATCH 279/695] Plugin postfix-rbl-blocked-mails: improve autoconf output --- plugins/postfix/postfix-rbl-blocked-mails | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index 73fa2794..e7444e64 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -9,12 +9,12 @@ DATE=`date '+%b %e %H'` MAXLABEL=20 if [ "$1" = "autoconf" ]; then - if [[ -r $LOGFILE ]]; then - echo yes - else - echo no - fi - exit 0 + if [ -r "$LOGFILE" ]; then + echo yes + else + echo "no (log file not found: $LOGFILE)" + fi + exit 0 fi if [ "$1" = "config" ]; then From f3525cfa4a8e8cfbd0d3dd8d866286bc5fd46ee6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 3 Aug 2018 13:00:34 +0200 Subject: [PATCH 280/695] Plugin postfix-rbl-blocked-mails: unify whitespace --- plugins/postfix/postfix-rbl-blocked-mails | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index e7444e64..07fda13d 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -4,7 +4,8 @@ # Monitor blocked Mails during Postfix RBL Scan, included Spamhaus, Spamcop, Manitu, MSRBL, NJABL -LOGFILE=${logfile:-/var/log/mail.log} # Allow user to specify logfile through env.logfile +# Allow user to specify logfile through env.logfile +LOGFILE=${logfile:-/var/log/mail.log} DATE=`date '+%b %e %H'` MAXLABEL=20 @@ -18,17 +19,16 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - - echo 'graph_title RBL Counter' - echo 'graph_category mail' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel block during RBL' - echo 'spamhaus.label Blocked by Spamhaus.org' - echo 'spamcop.label Blocked by Spamcop' - echo 'manitu.label Blocked by manitu.net' - echo 'msrbl.label Blocked by msrbl.net' - echo 'njabl.label Blocked by njabl.org' - exit 0 + echo 'graph_title RBL Counter' + echo 'graph_category mail' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel block during RBL' + echo 'spamhaus.label Blocked by Spamhaus.org' + echo 'spamcop.label Blocked by Spamcop' + echo 'manitu.label Blocked by manitu.net' + echo 'msrbl.label Blocked by msrbl.net' + echo 'njabl.label Blocked by njabl.org' + exit 0 fi From e77b778c911a8c653c4c952d30f830d504bc9009 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 3 Aug 2018 13:05:16 +0200 Subject: [PATCH 281/695] Plugin postfix-rbl-blocked-mails: separate count function --- plugins/postfix/postfix-rbl-blocked-mails | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index 07fda13d..d9e595d6 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -9,6 +9,15 @@ LOGFILE=${logfile:-/var/log/mail.log} DATE=`date '+%b %e %H'` MAXLABEL=20 + +get_blocked_by_domain_count() { + local escaped_domain + # escape dots - for a proper regular expression + escaped_domain=$(echo "$1" | sed 's/\./\\./g') + grep -c "$DATE.*blocked using [^ ]*${escaped_domain}" "$LOGFILE" +} + + if [ "$1" = "autoconf" ]; then if [ -r "$LOGFILE" ]; then echo yes @@ -32,14 +41,8 @@ if [ "$1" = "config" ]; then fi -echo -en "spamhaus.value " -echo $(grep "blocked using sbl-xbl.spamhaus.org" $LOGFILE | grep "$DATE" | wc -l) -echo -en "spamcop.value " -echo $(grep "blocked using bl.spamcop.net" $LOGFILE | grep "$DATE" | wc -l) -echo -en "manitu.value " -echo $(grep "blocked using ix.dnsbl.manitu.net" $LOGFILE | grep "$DATE" | wc -l) -echo -en "msrbl.value " -echo $(grep "blocked using combined.rbl.msrbl.net" $LOGFILE | grep "$DATE" | wc -l) -echo -en "njabl.value " -echo $(grep "blocked using combined.njabl.org" $LOGFILE | grep "$DATE" | wc -l) - +printf 'spamhaus.value %s\n' "$(get_blocked_by_domain_count "sbl-xbl.spamhaus.org")" +printf 'spamcop.value %s\n' "$(get_blocked_by_domain_count "bl.spamcop.net")" +printf 'manitu.value %s\n' "$(get_blocked_by_domain_count "ix.dnsbl.manitu.net")" +printf 'msrbl.value %s\n' "$(get_blocked_by_domain_count "combined.rbl.msrbl.net")" +printf 'njabl.value %s\n' "$(get_blocked_by_domain_count "combined.njabl.org")" From 9e47c112b97ca6129f5510084d71e980ad7f642e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 3 Aug 2018 13:05:40 +0200 Subject: [PATCH 282/695] Plugin postfix-rbl-blocked-mails: count by top-level-domain It looks like the spamhaus domain changed from sbl-xbl.spamhaus.org to zen.spamhaus.org. The same may have happened for other RBL domains. Thus we just filter by the top level domain (instead of the specific sub-domain). --- plugins/postfix/postfix-rbl-blocked-mails | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index d9e595d6..beb78b4e 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -41,8 +41,13 @@ if [ "$1" = "config" ]; then fi -printf 'spamhaus.value %s\n' "$(get_blocked_by_domain_count "sbl-xbl.spamhaus.org")" -printf 'spamcop.value %s\n' "$(get_blocked_by_domain_count "bl.spamcop.net")" -printf 'manitu.value %s\n' "$(get_blocked_by_domain_count "ix.dnsbl.manitu.net")" -printf 'msrbl.value %s\n' "$(get_blocked_by_domain_count "combined.rbl.msrbl.net")" -printf 'njabl.value %s\n' "$(get_blocked_by_domain_count "combined.njabl.org")" +# sbl-xbl.spamhaus.org or zen.spamhaus.org +printf 'spamhaus.value %s\n' "$(get_blocked_by_domain_count "spamhaus.org")" +# bl.spamcop.net +printf 'spamcop.value %s\n' "$(get_blocked_by_domain_count "spamcop.net")" +# ix.dnsbl.manitu.net +printf 'manitu.value %s\n' "$(get_blocked_by_domain_count "manitu.net")" +# combined.rbl.msrbl.net +printf 'msrbl.value %s\n' "$(get_blocked_by_domain_count "msrbl.net")" +# combined.njabl.org +printf 'njabl.value %s\n' "$(get_blocked_by_domain_count "njabl.org")" From b115cb2b7c73403d69d88c6214e2cea2192079bc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 3 Aug 2018 13:08:18 +0200 Subject: [PATCH 283/695] Plugin postfix-rbl-blocked-mails: shellcheck cleanup; switch to /bin/sh --- plugins/postfix/postfix-rbl-blocked-mails | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index beb78b4e..cfb525b6 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Made by Stefan Bhler, Switzerland # Monitor blocked Mails during Postfix RBL Scan, included Spamhaus, Spamcop, Manitu, MSRBL, NJABL @@ -6,8 +6,7 @@ # Allow user to specify logfile through env.logfile LOGFILE=${logfile:-/var/log/mail.log} -DATE=`date '+%b %e %H'` -MAXLABEL=20 +DATE=$(date '+%b %e %H') get_blocked_by_domain_count() { From 397052a8e32000088dfdeed2409e53366662b67f Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 15:33:22 +0200 Subject: [PATCH 284/695] Misc code improvements --- plugins/git/git_commit_behind | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 5732b575..e97d4d49 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -96,8 +96,6 @@ GPLv2 =cut""" -from __future__ import print_function - import logging import os from random import randint @@ -121,8 +119,8 @@ conf = { 'git_commit_behind.state') } -repo_codes = set([re.search('repo\.([^.]+)\..*', elem).group(1) - for elem in os.environ.keys() if 'repo.' in elem]) +repo_codes = set(re.search('repo\.([^.]+)\..*', elem).group(1) + for elem in os.environ.keys() if elem.startswith('repo.')) repos_conf = {} for code in repo_codes: @@ -189,7 +187,7 @@ def get_info(): print('%s.value %d' % (repo_code, int(commits_behind))) except CalledProcessError as e: - logging.error('Error executing git command : %s' % str(e)) + logging.error('Error executing git command : %s', e) except FileNotFoundError as e: logging.error('Repo not found at path %s' % repos_conf[repo_code]['path']) @@ -225,7 +223,7 @@ def do_update_repos(): logging.info('Fetching repo %s' % repo_code) execute_git_command(repos_conf[repo_code], ['fetch']) except CalledProcessError as e: - logging.error('Error executing git command : %s' % str(e)) + logging.error('Error executing git command : %s', e) except FileNotFoundError as e: logging.error('Repo not found at path %s' % repos_conf[repo_code]['path']) @@ -252,7 +250,7 @@ if len(sys.argv) > 1: elif action == 'update': check_update_repos() elif action: - logging.warn('Unknown argument \'%s\'' % action) + logging.warn("Unknown argument '%s'" % action) sys.exit(1) else: get_info() From 7bc4d133d9b546ef93c3f44da3cb4cacb3c0d18e Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 15:37:22 +0200 Subject: [PATCH 285/695] Improved configuration documentation --- plugins/git/git_commit_behind | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index e97d4d49..96a9d5f8 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -47,6 +47,9 @@ Use your "/etc/munin/plugin-conf.d/munin-node" to configure this plugin. user root env.git_path /path/to/git +user root : required (to be able to switch to each repo user) +env.git_path : optional (default : /usr/bin/git), the path to the git binary. + Then, for each repository you want to check, you need the following configuration block under the git_commit_behind section env.repo.[repoCode].path /path/to/local/repo From e0b243ba93f94df9faa48ceeaeaa4647dc31bd27 Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 15:39:12 +0200 Subject: [PATCH 286/695] Improved how the statefile last modified date is updated --- plugins/git/git_commit_behind | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 96a9d5f8..adde3f4f 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -101,6 +101,7 @@ GPLv2 import logging import os +from pathlib import Path from random import randint import re from subprocess import check_output, call, DEVNULL, CalledProcessError @@ -231,7 +232,9 @@ def do_update_repos(): logging.error('Repo not found at path %s' % repos_conf[repo_code]['path']) logging.debug('Updating the state file') - open(conf['state_file'], 'w') + + # 'touch' the state file to update its last modified date + Path(conf['state_file']).touch() if len(sys.argv) > 1: From c169373f21c8c8a76a5a8b6d92944197983c4d79 Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 15:55:39 +0200 Subject: [PATCH 287/695] Improved shell command generation to protect the injected configuration values --- plugins/git/git_commit_behind | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index adde3f4f..6f0e3fa9 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -104,6 +104,7 @@ import os from pathlib import Path from random import randint import re +from shlex import quote from subprocess import check_output, call, DEVNULL, CalledProcessError import sys import time @@ -158,10 +159,12 @@ def print_config(): def generate_git_command(repo_conf, git_command): if not repo_conf['user'] or repo_conf['user'] == os.environ['USER']: - cmd = [conf['git_path']] + git_command + cmd = [quote(conf['git_path'])] + git_command else: shell_cmd = 'cd %s ; %s %s' % ( - repo_conf['path'], conf['git_path'], ' '.join(git_command)) + quote(repo_conf['path']), + quote(conf['git_path']), + ' '.join(git_command)) cmd = ['su', '-', repo_conf['user'], '-c', shell_cmd] return cmd From 61241b80825fff4bbb964647155a72f220ccb4d0 Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 16:12:12 +0200 Subject: [PATCH 288/695] Improved munin state file handling --- plugins/git/git_commit_behind | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 6f0e3fa9..8822e790 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -119,9 +119,7 @@ if debug: conf = { 'git_path': os.getenv('git_path', '/usr/bin/git'), - 'state_file': os.getenv('MUNIN_STATEFILE', - '/var/lib/munin-node/plugin-state/nobody/' + - 'git_commit_behind.state') + 'state_file': os.getenv('MUNIN_STATEFILE') } repo_codes = set(re.search('repo\.([^.]+)\..*', elem).group(1) @@ -201,6 +199,10 @@ def get_info(): def check_update_repos(): + if not conf['state_file']: + logging.error('Munin state file unavailable') + sys.exit(1) + if len(sys.argv) > 2: max_interval = int(sys.argv[2]) else: @@ -245,14 +247,22 @@ if len(sys.argv) > 1: if action == 'config': print_config() elif action == 'autoconf': + errors = [] + + if not conf['state_file']: + errors.append('munin state file unavailable') + if os.access(conf['git_path'], os.X_OK): test_git = call([conf['git_path'], '--version'], stdout=DEVNULL) - if test_git == 0: - print('yes') - else: - print('no (git seems to be broken ?!)') + if test_git != 0: + errors.append('git seems to be broken ?!') else: - print('no (git is missing or not executable)') + errors.append('git is missing or not executable') + + if errors: + print('no (%s)' % ', '.join(errors)) + else: + print('yes') elif action == 'version': print('Git commit behind Munin plugin, version {0}'.format( plugin_version)) From d079f0fa95748c03b2e78a1d538c72cbc690a2dd Mon Sep 17 00:00:00 2001 From: Neraud Date: Fri, 3 Aug 2018 16:38:54 +0200 Subject: [PATCH 289/695] Improved the update methods : added the munin mode --- plugins/git/git_commit_behind | 62 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 8822e790..bfbd1fbb 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -25,20 +25,17 @@ Link this plugin, as usual. For example : ln -s /path/to/git_commit_behind /etc/munin/plugins/git_commit_behind -You also need to setup a cron job to trigger the git fetches. - -The plugin can be called with an "update" mode to handle the fetches : -munin-run git_commit_behind update -It will run the fetches randomly (1 in chances), -and ensure that it is run at least every seconds. +If you wish to update the repositories via cron and not during the plugin +execution (cf CONFIGURATION section), you need a dedicated cron job. For example, you can use the following cron : -# If the git_commit_behind plugin is enabled, fetch git repositories approx. -# once an hour (12 invocations an hour, 1 in 12 chance that the update will -# happen), but ensure that there will never be more than two hours +# If the git_commit_behind plugin is enabled, fetch git repositories randomly +# according to the plugin configuration. +# By default : once an hour (12 invocations an hour, 1 in 12 chance that the +# update will happen), but ensure that there will never be more than two hours # (7200 seconds) interval between updates. -*/5 * * * * root if [ -x /etc/munin/plugins/git_commit_behind ]; then /usr/sbin/munin-run git_commit_behind update 7200 12 >/dev/null; fi +*/5 * * * * root if [ -x /etc/munin/plugins/git_commit_behind ]; then /usr/sbin/munin-run git_commit_behind update >/dev/null; fi =head1 CONFIGURATION @@ -46,9 +43,20 @@ Use your "/etc/munin/plugin-conf.d/munin-node" to configure this plugin. [git_commit_behind] user root env.git_path /path/to/git + env.update.mode [munin|cron] + env.update.probability 12 + env.update.maxinterval 7200 user root : required (to be able to switch to each repo user) env.git_path : optional (default : /usr/bin/git), the path to the git binary. +env.update.mode : optional (default : munin), the update mode. + munin : repositories are git fetched during the pugin execution + cron : a dedicated cron job needs to be used to update the repositories +env.update.probability : optional (default : 12), + runs the update randomly (1 in chances) +env.update.maxinterval : optional (default : 7200), + ensures that the update is run at least every seconds + Then, for each repository you want to check, you need the following configuration block under the git_commit_behind section @@ -118,8 +126,11 @@ if debug: format='%(asctime)s %(levelname)-7s %(message)s') conf = { - 'git_path': os.getenv('git_path', '/usr/bin/git'), - 'state_file': os.getenv('MUNIN_STATEFILE') + 'git_path': os.getenv('git_path', '/usr/bin/git'), + 'state_file': os.getenv('MUNIN_STATEFILE'), + 'update_mode': os.getenv('update.mode', 'munin'), + 'update_probability': int(os.getenv('update.probability', '12')), + 'update_maxinterval': int(os.getenv('update.maxinterval', '7200')) } repo_codes = set(re.search('repo\.([^.]+)\..*', elem).group(1) @@ -198,28 +209,23 @@ def get_info(): repos_conf[repo_code]['path']) -def check_update_repos(): +def check_update_repos(mode): if not conf['state_file']: logging.error('Munin state file unavailable') sys.exit(1) - if len(sys.argv) > 2: - max_interval = int(sys.argv[2]) - else: - max_interval = 7200 - - if len(sys.argv) > 3: - probability = int(sys.argv[3]) - else: - probability = 12 + if mode != conf['update_mode']: + logging.debug('Wrong mode, skipping') + return if not os.path.isfile(conf['state_file']): logging.debug('No state file -> updating') do_update_repos() - elif os.path.getmtime(conf['state_file']) + max_interval < time.time(): + elif (os.path.getmtime(conf['state_file']) + conf['update_maxinterval'] + < time.time()): logging.debug('State file last modified too long ago -> updating') do_update_repos() - elif randint(1, probability) == 1: + elif randint(1, conf['update_probability']) == 1: logging.debug('Recent state, but random matched -> updating') do_update_repos() else: @@ -267,11 +273,11 @@ if len(sys.argv) > 1: print('Git commit behind Munin plugin, version {0}'.format( plugin_version)) elif action == 'update': - check_update_repos() - elif action: + check_update_repos('cron') + else: logging.warn("Unknown argument '%s'" % action) sys.exit(1) - else: - get_info() else: + if conf['update_mode'] == 'munin': + check_update_repos('munin') get_info() From 81b129dd81090873b7dfbfc5f146a4c6bb1caea6 Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 17:31:20 +0200 Subject: [PATCH 290/695] Renamed get_info to print_info --- plugins/git/git_commit_behind | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index bfbd1fbb..4a837f2c 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -183,7 +183,7 @@ def execute_git_command(repo_conf, git_command): return check_output(cmd, cwd=repo_conf['path']).decode('utf-8').rstrip() -def get_info(): +def print_info(): if not os.access(conf['git_path'], os.X_OK): print('Git (%s) is missing, or not executable !' % conf['git_path'], file=sys.stderr) @@ -280,4 +280,4 @@ if len(sys.argv) > 1: else: if conf['update_mode'] == 'munin': check_update_repos('munin') - get_info() + print_info() From 1027f5e57ab9e1d386f7c621f34fe4b9c3dd752a Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 17:35:59 +0200 Subject: [PATCH 291/695] Fixed NOTES comment to better explain the fetch munin/cron modes --- plugins/git/git_commit_behind | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 4a837f2c..6929e397 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -11,8 +11,11 @@ how many commits behind their remote they are This plugin is similar to how apt_all works for apt packages. To be able to check how behind a git repository is, we need to run git fetch. -To avoid fetching all repos every 5 minutes and slowing down the munin-node, -the fetch operation is triggered via a cron job. +To avoid fetching all repos every 5 minutes (for each munin period) and thus +slowing down the data collection, the git fetch operation is only randomly +triggered (based on env.update.probability). +In case of very time-consuming update operations, you can run them in a +separate cron job. =head1 REQUIREMENTS From 0d5384516c1fb03eab411ac9bc9d42942fe304a4 Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 17:36:32 +0200 Subject: [PATCH 292/695] Improved CONFIGURATION comment to better describe the user property --- plugins/git/git_commit_behind | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 6929e397..42ae96b1 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -44,13 +44,14 @@ For example, you can use the following cron : Use your "/etc/munin/plugin-conf.d/munin-node" to configure this plugin. [git_commit_behind] - user root + user [user] env.git_path /path/to/git env.update.mode [munin|cron] env.update.probability 12 env.update.maxinterval 7200 -user root : required (to be able to switch to each repo user) +user [user] : required, the owner of the repository checkouts + in case of multiple different owners, use root env.git_path : optional (default : /usr/bin/git), the path to the git binary. env.update.mode : optional (default : munin), the update mode. munin : repositories are git fetched during the pugin execution From eb9681a7ccbb9f217af1cef1781855b100953914 Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 17:37:33 +0200 Subject: [PATCH 293/695] Changed the graph_category to file_transfer --- plugins/git/git_commit_behind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 42ae96b1..fdd07704 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -159,7 +159,7 @@ def print_config(): print('graph_scale yes') print('graph_info This graph shows the number of commits behind' + ' for each configured git repository') - print('graph_category system') + print('graph_category file_transfer') print('graph_order %s' % ' '.join(repo_codes)) From e29c89c0fa3d2f8bb2213216ba1281bc5fb28141 Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 17:56:23 +0200 Subject: [PATCH 294/695] Removed the use of the DEBUG environmant variable, kept only MUNIN_DEBUG --- plugins/git/git_commit_behind | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index fdd07704..759fd902 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -124,8 +124,7 @@ import time plugin_version = "1.0.0" -debug = int(os.getenv('MUNIN_DEBUG', os.getenv('DEBUG', 0))) > 0 -if debug: +if int(os.getenv('MUNIN_DEBUG', 0)) > 0: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-7s %(message)s') From 694226247bdc75977c4fed20cf8aa943597e0897 Mon Sep 17 00:00:00 2001 From: Neraud Date: Sat, 4 Aug 2018 18:08:34 +0200 Subject: [PATCH 295/695] Added example graphs --- .../example-graphs/git_commit_behind-day.png | Bin 0 -> 17619 bytes .../example-graphs/git_commit_behind-week.png | Bin 0 -> 18487 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/git/example-graphs/git_commit_behind-day.png create mode 100644 plugins/git/example-graphs/git_commit_behind-week.png diff --git a/plugins/git/example-graphs/git_commit_behind-day.png b/plugins/git/example-graphs/git_commit_behind-day.png new file mode 100644 index 0000000000000000000000000000000000000000..9f25de127b6872dfa6bda87692cbef7fcab55d8a GIT binary patch literal 17619 zcmb8XbzD_j*9N);MUYmc5kW;jLILS6Q96_k=|+(5Qd&d=q(QntN=l@qB_stE*uW+w zq$KXx=sEBEefRt0-gP`~e+X->Ip>JydBzxqC@I{-zd(5bfk5EjmyuLKAkJ*S|AN>U z@D+UqT2c7GGL*e1i8w+3m)4LIjX+#Q+?Nznb4^-JcGFQc+c`;P3A{mM63ytZBGx*> zZ2M4$h_&dgFRl}cyRxz>IiA0ANct9|w$|mG7)$NRlGWEE=aimE5)Hri6R27{r%7`C z(}%#o~2k`{2>);c?%_Dfs}k;I0#DaBqi7JyF# zIUf1Y83cmm{QvpEFiwE}CP^k{W|eUc??YE)98nIYuk!Z|SLsR~7hQuV;U|{@se11i z$lW}Xf3&-dBK(qIq#3;G&X_EP5hLSOPNHa60* zHWt$E$w`lqLe1Is2np;PH*P%U7QCXkktDgj-7zL_Zf?%&v@|^15$WOKQKMrkBl9T` z=TdlnE*{E`^zyL;V(IF|5K1bl)s+=NA)#BhZt3disNosN@mw%0+ncm&OuqfN89r8* zmL?`AwKX-}kX@Ip<<18W4G(X(5Q>~VfBv4NBriYz$nfyo!a|~h+hJS~fxo}RX=-@w|@3G?WVW$o_oCktyUE-t?HsLwd zC6DXoO*kk4JS>R;WlW6qg%4i8P5YAgEC#Rlh+ebZ%-{hIMWzsT0eXf{tZ z=ZcW~_2^DdFE8~%jV9CbkR+n3&AG}M6;vPJ*pfVW@L<|wU1*hiOeP(f_$%XHr10TS zi4_&k{Y~V($SV!I(;hoJJDQrBIf|(SgoGVEU0q$m2aDGNmE7GAp5c;-OGprlFZ8FD zl$3lceT*s2$jT~TCdA6h%E7^L^=eu}gYH!##z;w5AFgD{HlA|{NyGH{(VLr_ar)kx zt8A=mLY{j+r(C`{IXKk9BNauCxJ-NAI4zIx@$>6_@Z29OGg@yvIri-tb|#3D&hxUW zsE+}Qgs%xF_V)Gn_xJQ1F6*DDw6rKUK75&3INaZQ>fpej zkR+2aKqQXgXRxy}&OBSfFcol~P+C&5P_tZ`IVMHei=UmHotZiG6V4g>kM(2@=5}^2 z&d!hO=Z-pk6)sXxiJr@P02miVRK-=FYmh?u)1rllSqAG44ygMnbDXJ==>di6@P+%%6m>%I&4 zvQc~ZV~%)dXJ=e8HWnr(I#+RDZ2pA?(Ii`vF>7ZltAe^ZfgP2NDL2uZ6#S04Gj6i7 z-Nm|fBr#Ir+_fxv1Q%|H4^}^XG$tgRxily-r;sCz@aC zvQWUuNV92q%+%Gzlo@v;BclK!6`jl0*4CLSDAC?w@ywfFYIBf9!zf=Z%rcEamdp`t zR-wYq{&TpbNrG+;Gp!+JeMvYtI8C)O6!NuSzI>^vsZmT7hL9nS&BS23C`czt6fZY2 zHio^WC)Wig;`4j8S}Kgxf=BgO(CXXr%1XWM6hCrIUrbgOH&)%o!a^BO1$@}D+QZ%b z7|HGv)r};i6&7^gUK}aXuH4H&T~81koy&Aq&UCU2Dk)iqpfTT{8u=}&O~@#1&^S3l zoN}6y)eiX`Ho8mP(xyT&MS0mwR1^`*=e9Z5*%fmuWcdoeBO@2r_ODU>>hkhs6e=Vl z;>JHayw=88zda}mhU@{a;pNq!#te&THftghx8|<<@FC?B4y-snD#|apsJM6y3?)>T zkB`r=OoN5g%*;$NMUY~WoSfVhcBbm|DlBAavr`Qsq%DTGU0=Vq)NXPaJj^&v!IJEY zg1Px(%q?{`-P+-i5r&ut&%}TDVV=iYNJ+_c{MU&BuHC+^MCS;16iX5-O68)oIX>Kn zi~YkM{mwF**wpm2p0>6&Y1oa6A>hg+lTIl_S24KP+q%2MT7HC6@eusJp--`>nelyP z<#4z01l)Y(TVrEG!&G12TD)1x{^YmgY(vtE7dO$~?c{_?3J|m!VWEt0{{GP~HkOo! zho>!^GVt@8J4UafqksSYeN#SxD3_)-b@~A2)vD^FkV{5*5Xj=MO`XHYqW!6s7 zz~&=@7yDZasl4_x-RzCVx5=BDnoLYgFogoNM2N*5`KNk&ZMt4FVGepBW7OZ3mlIwQ zT%4ODp{DjcIrj3uc{Vuh=`=3>2p@+Pqr>P$gQ&y9t$sg0KdeIxS@AQga0L7Sd9)U1udgGPEwwhB} zd2;fo;y$?o!I9kaB%L^TQn;kPzP^{2*S&k^#e;iIJzQOL)mXEYl8SZobh4g@NYdYz zl`Y86-`v^JYjCgJq{sQiTG+RC(l=dJy}D4l6aP^gZZfnr*Lev5gu1$VppvSps*6kY zgNN*vy(V%8mts~SaD~+1J+9O?~}Jj)&#UV*$4dJyY4fJvKJ>{{4G>pQFm6B6&9t3k!=UPfC-M zb)P(WVquY&-QrQKKgu-l`Lh8L)n^P=_+?~n(b3meoR^o^h`wmGsU_Nc#f{umR8%}Y z4{xS=&aA8?rKA+s7nGMr_nKY{9JA(jRxQNPl$E{rAXM(U;nxpBE0v?<)pmpw9FpC$ z6&6EyhMZT62J2<7RqJ^yxA+hubyeK(goX>9>o#X!|GvU+6P$E&TWL#QWdDorDu)TJ zKdHH9IB8u;$+a6MPwxB%v;gU#s~dg6hCK{zxcUG&ba{Jw`#J-|=Dueesc>rJ;}$G} zplhUIyDQ_DE?vrSkcs|e+QS&M6GldJqH|m9(&fvSg@uI=CW%I0bw_Qrt9G35+e~Yy z8(E<$Hl{R(tJ%YgYq+o6{Y_LM+TC2Crb3Jr=l~X^uI}yaEhW``h9_+V}57 zl$6Xw*tNB_Kf@>tjcP4~Zai>F?QxsUE4FI}OQQ=UUr!wt2HP~iz!J({PTCbu|B4&g zF}vY9S#4L6m#0~2d08a^=MpLDBW>;Og)J*9t4fxyU%fZpD#V0_UI-W?8eNL&vW}B)&v~P<1&p8rx!Oj_uad9nXz7n4V^z9GrR3>-{0Em zU#pvLEZ|QhUf7tjGDc$~e_+eI9d0?<}??lRky%iEsl+Uv^X5S%1%%ggx({`R9+g97q>Df zMK-xOc?sokz7$vcw4;vHLWe9I-t1y%rAa+z6&fJVS(u+6L~VcnZV;&C?fpTH0T(x; zHTk7wxovzYV<{j zgHz-FD?du`_EojIX_Y&vFU%dz+Vg`uJ5_E=&T7p5_+h^>gGSJBg^{tlySkE3#x$i1 z#+BI*GiF+`5kEiXZmr}`uv)R^>NDXgj8&lGr@=`Tyb}@2G3cPS^VuBxXH9!Bv#BqNI#e4&K@{}1Z zu6A9cyVRuO7Y3uf9REq}1qVLL4o3t+&NpR$8$cRlgT6k65XqV4<$`+L`5eMQQ_+1# z*4-JZN3uEiJ&Na9KfirXN=%OTJUTi$C@84Q_eDi3s;#YUe0;pOw>LD;ZqGzK4->5Z z{fY`Dej6KwY}GYvIxdHyajMkb9qN3oU5MhU;?4DOJv}{u(`~gDnCkeD1)m{;N zo!^apy3K8{%|=NdY$mJTydftIGqEPb!+@Jc+&d;mF8v(9pb<%V^ISPKxo+FyWp$X) z_zQ&ZL!(??8XxVv_C5QxuQdP}AQxZ}b6*)Q={6r47fr4W=c3FnD(3jl2^32>m1?{296U&^b@>3OjYs9 zdUEG05bYidsemPiT3OulP?kQs1wIkF?&GogEUFu`=G57}wb|#qc&A**!9@@s^Y2&) z({$I0>PkykDCbY(wxZzRNJc4%lthxx>V5S(Dwq0z+g=le_JN2==R3=Gsz*Z9oscIB zDZNIn6#=GNTolDF8@DDTB$U;TRHMk(waX*V8H$>p5y2W6w7}GY8)9ZIMwUCY<+ z&V0RR>DX@D@MNQJf*XC z?csAvw>v9>!(BN;<{TQ4Bo(4z%~}&>Wn~*c-F8o}%}o`G&&0wCqpx^)dTs7u_=#R6 z$~C`#{Z&OpMSearyvLUUbpgJ-=kaPUhJ=KP&Gnx*zdpN(>wVmvd0&tE9nBd;LA&E_ z)QlbNU8GkgpnM+NuOIFDySrx{Y6b=d006MFD-QjDxNuC7vpYF{TP`DO2y4di)Dlo< zsaku#3lBmfXzLYA0W$GS#jl~;q3>_OXWxucmu!V)17lVPIM zcQE~If6x*{2wXSf=!$H{0Q0R|M@L67_*?{B;^|Lz*QOG=ZN#x@ggy5_76{n#Cj@idXwKHfaC@wz7m1|i>F9s<&y9KR4LwKW@O!* z@V+JwI3NOj!5^9~(5etEjhbY>CQX*U^hI4;@9znu)`mk=1Dgb`0WSE%-X<&MQ+AKt zwO?CXxC8{!*vN&2xR@BPy&pjVKRPLt#)?Iv;6VVlG;kc_ zn3k3XNzWPRlarHfz3UxDM)N0E-dxN_wlU^PWzZEN^?EA*E{#S&pbi*6q!zf^+oc5r z1TJHCeKDb?Cd#=qhb*x>u_&lEMq3?o!@u|s!NHLU0RIw z{b^{K(Q?PIuXQThuKPC9M@hjK1-T4i@P#~ox*IXG-Q3&^suX=70j)4K8Cj5Gk17#_ zW2RiGoNMttl7OecX+*ofn}-j94;(k0r{&`2zHrtbxR_DL3$MeCj`khX&*(*6{&uo6 zfqO0Pym%4;!WdE`8RXx&x$W;w0Sl=<-gBR#AhQN+eSH1L()c47hWN#%``AAc4XiB%UidX1V2d^Im18#CN)DmDp{by$W1{;~J~b0*B>2qcvnx zzmKL*2stjYo5mYfBb?-~$3@-!aBePs`Fn`TVIFoZJ5s5G~9E2`*zs*z-qrs1C1M zT3Fl%r9npK^AOeS8GC*)y_4NlseS^F)pLq}B5x-8J6qm5OsB3uTmuF1KMM~Yf(*r2 zoRz!*E2I$^>Fax;kOS0%kV7Fy7;R1LBHuKLzVFP3M;QxXARhIm%{u0(F_T;vx2^)T zqQnR=_>m^5gb}0?EXUKG;ZGi!RIzgVyDwhz9FHD81js}{#oaSKozjiG@J#$N=3RS! z()3v$lB`WeLU`rrW8@mONL;0u-ak*-2D^6IJtbF+^+)6@|GFe)VWFTdW+E(J4+(eV= zT-MhnDy=}U_1IZ)JKQVVWDAje=W}ulir(6{Mt%AybAC)f!z9CRhhEBQzp*VwFXSEh zDH`6|Km7FRlUb@)tOCzKEd>gNVsF?6Nn_~f_bytv0XjD3H1~-ZM8nUYmHb3MfB91B zfP4^RM5l1`)A^t^#qF7T5f_qEWF;Yv4EB+~+f6P>wMLPdbg(5-%gwFD&}Rbj1;r(QCl9r>0;tg zgRDgRS9nFHygUsP(_j$cz(=O$bi=eFwpqwaSt@ykVHVjRWU>Z-@2=yX#X#Z-r}m?z z;Gh=bur}CGWrc17fMWhy`csQPcb^=3d@{deu4#$pS*VgMA6@B~>){v?M(*O`&7g62 zMAF*5+wKmzbQ2UfJ2SIg@XqL)iqiM)-Es!lEx^kwDy-{s9Ch45?h=H*<#f0JB8J*F z#gGYyCh#X&*|5k+MH3U`d;8hfyOz`?D8~a32<77yX@xyq9UL5VbiM*E0Y$(mQw@)U zg{82nDz<-NbYz50t3qaoqLiI?qHLI&dJgRaEE2K;^ihig8TT3*8d6eHVq#*Ld8p}F z?Q2D^iemRB^7Mm2D&CYsw4%=L@0mx%l~1*krP56jBT6lSmWjA+wzjlLWVMHJP_{fl z<0I_185#22BDy|*a}!=3F2hUDV zIAzPmQQ`avGvXCH?%h^o=6iS+z z9WQ8JDpp@-r7V1U7T|*ot5CXx5&pSHGMVQ)M%B$W_3 z^>+V*Kg411QiT1%?l&J15RB2Z_cQRR{QUfBpA#=54^DG*x*J5@>!oQBh&vC&8V#~q zQGglf=;)w!0v9eROKx#7_p9^n7)})DGa+?P7EAwof}C;6dK zV;DMp3Od`K`gpG?g$&vfjn|oS4x6AT6Xj5ybramOS#M4q zRTw$7|J%ArS12gr6B26OwoC(w)_v~0I)B0T>-(rzuP$D`jFzAR4-XGFx3=o)>L8nd z1s0(2R!O?Ul$B(qjjp<-S^y>W1pzj;iB4<1?QE_N_nxP>oE+YKz<t}&@qkW{uw8%?AZBuy$q`NC3-r+>< z^YLc%*RBN-&{$gAVc|mvMjbWp+u3&Plo0+2!QePUgM)&Cg4}k~^{_e@m)(jAu4`@v zqNm|EHmNVOlTHDxv=qmtXbe*h#p-tVUe)5D47he#t8VzxKfM5&e^~+U$eR)TVUz2* zNPEY=ju#@HC!=i)pWh7&pJn|#s2>(s8(vgY1g2h6SSX1d0aaJjU>2x6(hlN2S5p;U zzR=AOaoRhmYn1v+#O8)T(H||=rJ_Y&jn1oD%!G>9b`tl z${QD5ZANd>{u>`b$O6J291(F>^T_e&4-s*ojDBHo8m&S8J39~2y7#2oKwW$9g`x=k zX&!>Vr9+?IThfS5-#-xz{Zyk`CC0BH1Z2x65{W}u?1RgCN|`PNwNjkDsFnl&P%J}r zy)#%)d=x3)Q;Noer2;76o@@^(-oJm}#>VFN&T0!OQx=0#TvS@@&@-PE8&^8-rxpK6 zIP~dkMnG}_JjAkULsb=87fyba_<$Zq(TQ{92hl1)CDuokjJ(?I*53>$?A7z;j?4PA z6n044%%z(OP%`WLPt4x>i?MG~&N_xjzJij3mR2YcC6BERFyH`8C(OqXM+RNLf1wTR zZ?*pMCuGfiy$5ez(hiSpYb(-gNa(_>#Xw6_xl%!=d+{3f;!UcpE?4GJ$`Js!of0AMN;#DAV&L7 z$>LG5PM#qe+)yf5_r0I`IXdR^XJbe7;>sX2@bZrJ^|A5tlGousU_}RhJG)3h#E5CR9Xs({gv|mK@>v-^2@y5p6Bs%d@5e*7aAApCmxy{I3`-u|l>aB&NYijZ6TP+$xrWgT?HTETtc%yReqJ+-jMm|J>4 zu5$~sz)ZK#6JDVf_S9fLvTL~MxhF@1hCQ?UQ#o6>dXXoD{|D~7?_+qK5D)tgacVk5 zV@68=n^oUr+yqllTc`j`NXuMgo-R8poeyzt{?T6nsgmirHnr|=H=Q}PPqUpe9oAB` z$}yiIrJyT)80g5HqmO?0R;ACvLKd4M(Z4a}K5zJ(-D?QG(Rcn;(7AlwXP!D}W73ApkCFwcOtdD8xO;g3cDR+vI@X zZddUHfcWSiyheSi=j$((nY({>wWl^*j^Qcfm;HUypEk+B$e1nfyutf)DyeA5Dmq4@ zIN}T6zaJYJ85t|aBS6-KM`TFT={^m)ywo( z^aY*<2DQ`B><&Z$+CQE?E!Sb|s8ZUmg7Om1ptAkykH%4j`&?ZZw-5-Tz(8IhPR^%4 znxBED81!3)^?sPN8HdFmjYwTqk*ggwS!ELz65>}IPAVPuvoGVQJ5|ErS38SbM?7rJK<%jY&xr?50s%aH-|mx#q0$Fo?r zKZ2AJqe9&O3yadf!v(8<`w4mE8Pw>JGPS$I^5;HbL(AG4#S#Z^@M_NV%cb)Qv9m{Z zBZ2llt@&DCFX&(Q8l56TwYapFf4+>0z)&?uVE}LJs^qUjefh-_70AT-HCG{>H@Sbo zZmiU=33=?awzd+DKLZ8Gp0ecFb743#9Y-DA5Ku_QqEM1s)}qHj;ZU8f;-PJ` zi+M3H?}Q0Y>R(M8Ee<5txo0`r3rI{44V}#0|Mt`D%EAfra#i}|ukrVe+m)qvg+i@^ z2v&(vc1C?#Q%0oqH|rbwXHEq{EG;goJkPmT$2S!hTb|R!CO4f|=?)p1dcN~Zr^Zk~ z_FjSpBZ<2k>dmHDD^>-1&tw&gLMa6(UlBSjE$}`-ajgbjd?vo{C?e7b5Oi&A4Sp{( z>bUqdE4-pmE+-VqVq(_lp94xb>`)pVvAsyMWL$cNMwuDC^H3@tn~S|`HNwlsx8W*S z*v{@9*HxyG#AaoYUibb@yEYms0spmBHkTU>(ql?)drfgz5gHcy>~Y>I2I8Z5yQ7zq z-oX9eGC8GcZ>H*86t5C-@$kTT{QUU@DG;#nbjhjj9S_SvML}*ZBM(n?Ru-h?8!Rl) zg0^wN=-tFaJw0#e4toClIZ%radoy9sHIgvlZ@1#>$!(8`Kgm|3qo<_w1pIt{-n2%i z*fW@-GOUZ1tEH+NH1Z3`q5FZ~wG>SFaf#N?v&DHnbNBa6AkgHHLqoJXpCdkRiXlZH^ zTO~E(Ty)OL%4z_d2Bj&$PoM{;dLQH&!l^ibh+pf-B%b@>!|}=S`bla3V5QY4P%A0= zC^?3Vp}XEF>X-ctwrNe1&;!tD-1!Pl0;F%|Z=H{&S5juwG_~Y z@Sh5BUQ?8YaXI5p;85U0Ak3knLt!owY1TuwVQfDi&ldCHpvj4J5)b0~s< z{Fln_^q|I1(vzXU56f;8dRt%6isXszhf586NgSsNlDaGm)I(;xcEiK zEVNbwws`h=`yyNm$|@@283Rzm z`{W#`rL@|3uHEq#5*`5x0Z>Pfjx<)4kc<)=)CK$`moAmNtn0<-l7=@l96?80nbsF_ z=4C2ScDv))J0SF2zy7t>Nw#cY_(ei2kQCS+=JxHmxjATd2~>g>UIzyb@!YB^?$dS! zXn0lJ*AHe1k`L(e!QCm@90v{i=+`KSoY?6g{O4q4(FG0;4Moz56yN52C4ycHP$hUP zNfywct86Ccp~FF`FP1)(`#p$DrO-w@C4BOsvwJ(!uTxxkh!C@?8Wp(QjliImLM1_A^+EuK8GB5?z z8foEZ2KnX@eqPVLQieph_h*zN^!w18ykqr+O7E|o9q612RDx2jsHmuc!FPClEiElL z2B3G(iSmnzlKGvOqdTD+^@ckL=^eKS1`Aq*wOw5qZ>{qL9O`RbdX+PsMFb zzWCs30Aj}R){Wb@Z$lgcC|ytho$t7Gtv{ZC6M_T}ib}e5r@?KR&8WdQ-&^i8!LxuO z!zq!nY6Y@BM}7Z(+j(US^E|=c@86S?lh*>@>#RYSGh57q^{+J&RgIZZ(b47LyO7W! zXc&f>g5VCZ0qg8pY9V)$-=Rioh07-wm5450=k-Lk4PM13UujARoQ4u zNCeCzn2k^Q-{j?m-pZDckdROuafqQ%qz4{^bf{tYl+%(x4igI(A)TqIInMXQM^N#+ zS`E94u@4O|cvz7g83X1ZlWCV3o*Vz5!32{E2!sZ~OGRiNf^H5e4-y@KqKxYprX{mZnh}oL!@v&J4R!lq*=@4Ol9!J0 z!8}BX5z5`u?@VMJ9NsB0s(DZkElv}y88vCsO;^HD;h0)>R#c?T;On`3xvdV&4 zvDW9Gl;;Vy!?n`1hsE8i$xY-s;<}Zt$))LFO-rBi9C!tR5IVNOlY5f*I5|00SKV&Y z6|w|umhs~zKH*K;+uIv2H`AW;>IcGU&HWqdmZqoV^Q=uUwaJ=jf9|0tX320mg5FxV94g8&cK3Vq}FxVSCc`HsZX(yPj(SpRPRN3TpUxme7~H9qt_(Ds>jFjqwP z-;XAp4#(UN@E5*jSAXfb#;H{WzncHvTRITiXF^5 z#w7MJvHcv=^ANzz`cp24NJ3)?T(m;&6;V@Mce{RMXQ$k0bY}-~0$vSD4G!+^wRidm z$uS3))s#rVLcg!KG8k@k(E8Nx&nE)nD>v=scm0`mhvs@jrpSP?f%h-H+oiuA5{rNH z+`@m<^nGor?wi+cvX)V$lNJn#KnULfMO*#RBlFgM$& z?4H!;oWNB5^2k!~2FtM!LXc!t=>T1b_FX3AMP$>-y6` zN$F)@Vs-C}%*;&a%7s~v;kGs@)wj*LC#=$%k!ncU4hT{{5I~@kxclqZW7)`A<>Dd# zDWtxJhU<^!b5SzG-@YlL=QyD9@TT`QlPW-1Q@s8n3aK_67$hiHc-H*a!5+K8TSM17 z41Tq}R7bbB`JT=jWN=>bOpXQE0Rkoi z0|QJxkjl=wy1Fu2%YCn^6mw6$(1jsnBx(y>A8K1rM1%w&Gh}XX#$dhf?WckZ=zDtF zAS$0~L0p0kFsSv}SX&d>G8n3;yi#HW&$c!<|M-Y{cXN~9Wlcv+?2I@gz;7K-+}mkXX;IHgE;Q69enk3>X{xz+E;>VCbEh8hS4i(f}31ij=*llcKj4nm4_qjU0n3*T{ocCdft)uwo+Pt2t#41*-t51 ztWi-zzpP)b0)hUf#ijBqNMox$^i$Vq$xkXn0ugNXYf)$Gr&7 za}&4|Z!{ao)YV6!bu~~)S9dB>b`k@;LW2OSX52G4^*K2UR4!63DL7efl&G)dFjz4XBQmY6#C!}Nms78 zD=PNGI7miy*WMzFxe=Vh;NalW@-pC>rKKh4sntq^P5>CNdyu7qjDTIURZ~-Y@E{ak zz#8mQx5|1P=25h?wKpys{5Px#7>9YB55GpV(LqE-We~g>Z<-$$D?h=!+0sM0#=P@8 zWpK&b+p1~Yoj~afrVEaV?2;GItB(T-80V}%U|HbZ(CK48+y28!7{*;pjg6r`3Wi_s zI^;dV)Bzx<>c!EKg*w$;M9)-);XF)uM zGAK+uD0^H)IIY`Ts%xmz|rM#MhDCag!4h#oE;b zKwyOVQt%ciBST$ zpE)i4Y?X0QQ--hiQGjXEhEPD*4a>+U9iUaSzP=uiM+D{F3zHlsT_gEoC zZZ*QpTxVC8%9YQ=;?QXp`ydN`-}(7d$>SA_az5q;4ot~y{YHxlyatkfn{vX}meGha zFE6X#9l-e)b=-zxq}=C<4wLe(R4q z(8wwwApugm=+%z|P1eR7n$RQy1Fv;;9-5l*Gf9tLR9Ejp8Y?d^x8S0TWt6}Ml?-{@ zG?^>Z)$IE(1cnwpG?7m0(vb$s74t8{A^1L(a56BH=$@bQy* z?F~IV>O`-8=E}a5o|y@OBj)vMa!)xJKB@8Ce@BYhHWL@h3RtD!Q4uBdB)GZlM_s!I z@bS6Gg@TgJ`9wSD9{l?CD}-D+jF{t}T0S5!CE9&JEiJi2@d@Te^9y@&I=HPGuIS%?&p__v!L~@KDH71^LL6%iH#W2orzxVt1@6eF|=&?wv z(y}#}7^YY5y`Z+Rw#K_~VKDQ>C?K}3o*t45&wykD1sm)2;-9s`3K*D}&{9>MMWH~t zd*H8S38GItKa~FOcf!c=m#T`Ysz+sxKY^TLdw0U580em|vCv>Hd(ANN$J|`t)Q(Kf z&}CeY{B3DiD%6%r511k5HE}~7u@kvzmf~g#g=(ORO|;JzB6y}hrU1);JPJXSlN^A( z<(H5Oe{2{x^Cc)%tdwK$fUFO258%z#<|eeF0p!#K@r5BePKL!K`7Yo6Ih4wOU7&0W>ZfNZ_*PveSowY$F48zefVDH zx)x0S!ccjz`480UYEgc^R)f0}OknBjr$$8JPcvM*2BWML;IB1azqvR1_?(tV;<}Rs z-8OyOx1ouK*5|OSprCtt`WQwJ+qm<&#FX`sy*%GqmkJE9V6qw%85u=I{YG!0V@7#c zD7B#LZAM0Hk+g!!rh-e>=ZMj^P4gZ1ERg%c*bi`X&S4)oX{>++cDU2H~Ish92u4_S6S2f!nu#?yOE~C|@fy zP&P0~duu?fM^l1Cp(v=SR~8m>xTQxVK`D`u2@VUB)zzip-n%%^tg51Nk(3nOWdZL4 zIxmP4-?h7t4v=_v0Rn-Q2E7l0Eo)7|TNF@&xxt-4g&dHDV853N>OmN1{`Igl^cHm3 zovlQ}>!X8T5SI#$oYbNt7=HOxiL;`ukC|EZDwtate;V{vbz$LQwTzGRfjTtF3$wGp zrQTIns}C)fn-9E&Qf|ClHuWeE7nk*9Rqxc)QH;8t7FM)jO<; zbZR(2sJVN$GAHLe)6wf3FKTK5XKMhYz=0AIsbUpxzI~RHnK@TfJq;ZB?AgPEN?~u`(qjeuSXfZk z(gH23J(5;&j)>J121c>z7@|>YYsG?eCFSK?fabC36crVbe9dnfpf5fs=sa4@&oh*6 z6++y;0bBn%pv~|u56|TIII)hX?(dJGtE-EPA8nJ>t)c$k5gI)_I{JHWFLw+*F%K#% zFLg{@m+4q30pc}=13Lm~P>vp1+8fi{$b=mN@f)%(K0bZ|cRm;;_+v~pGz`NWJX|1n z<~w25q4bAj5orwQN4E?NC^JMuudrnqAP7*rY%MGXYKZBE0g&qJix{?ISLT4Q&#WC9 z8VXHLGxPJ$eKgh7&i>#NHMzQtU<_(#5DIHkAt*pOy2DhcLp+If$tI~h70fm#CM481 zG(22O1QIpII=Xjv0uuuRUZS*lhMAqtk3fkUhIhNfG6vK_4S=tzJ$ghy!|zCB2K90& zY$5k;Y_w}B>F9(fHabV3L*;9At2qI64Id3NF&`X2og#oA>AlBbcr-~ z51!xo$8+y8S1~3x~vEUf)4ROQe4?7VJ*>FF10BTxQiPk+*KmxDTKcMoJKiTiheD zxW9d*KQzi8;VTvv=eu~a9i_sv)79lF(x{N+wjF20|NHlEFFG7=RSbmU%!*4!S_Jb| z-MtHYNJejS&qkNS;x#*byji2xPlzT%Qd}tm0|x)E#^&bpHaYJ6VRaNFaSt7~=h{Cz zZpdXQy%yto)l28;c}7exjSw?4V7&pOzI~qL7-KyZmR`({po1+|~8-hY#vH zIy1qXUtSH+=QizsXns>qsii$EYGWt-eAVW0t zqqlcveceJ|A044r5=-OdUen0$uo@d4-jn~LEFi{BB>CE&cNQh>R${!<$3;Doz?=O1 z`@6_~r5wAJp#r&#?SY)9F`U{K=H~7{{m2)-e_$fQ2`ElUN$Jm0U?mOW)^F)b6pZOv zMEBC*5Ovvo_4qLXB7{a*fSEZ!RZCShHahybrlz@(5uf|vZs27mldZn;%Ry9e4JPlE zT@|U@+uAmFc3kJ9R03jrur9gmeCG>XRNK|^Jo&x4YL=tCyt-Pe)94)^AOAG#@!dOj z@F}=-em41?9R6N;9y`|3H~+D;v~uJp`jr!FE0TF7abd0*Wln4Do%Q@SKBUjKZsxqrM&$HC%dTVZ2j|w$kFQUv?GL9 zfqK=Koo+$jhQU0Q)=(;J1T3iN*>RcVjXt|DweG}}^71<(A|A^FIT1_>>y3fCyjX-Z z`ND`JJD+zkF@IpIUby1-J$-dx=zey35-xHWu)i^eMf(gF@!-LOxVX66hFu6Rc-UvppBHM?T}6;wzT7?^7#NtEnmRQ#6&e~! zNlBS2Ogt&m|Crh0X8g^HYvuB|cz8zL2{{f?WOc58*6YqrkM{QVjCztzwpwZ7LN$8L zxPpS8%Zz$x_^osXbDvG(J{{+;Nr)V)lI~QGi;1Z+8NBsQHZn0W@%{VlpZ>H;4%xhP zw{Mr#)Eq`Ah&;I!1=p{sDj@4I?$6A~$Oxqoj{o`-Ato)|GS?o_PC$*Qt*xaIa++CQ z9_#J3+MH>8k}my1U46RVbGpC(Y3DtYdya^<@82JRZQ$WOJ+h^}kG)tI8`jbyEh8hd zoiWEUIriYyE9OyStr|Pz?_q668ygZ_Kih?_YWE{vN-`3X8)U)Fde*YCZ55`&0e-`U zns^s4T3exxSUyXd>V&U9Wix(cW@d`GAF^qDQvB?dnW#7+d*^|}N(w31&g$CQ=e#PN zMl>TMBXClKj>yW&N=!U5{(JX`IcbGm1}3>GmTZ(vrmwPF&L1D_P*YQ5qBVly%gf6n z1(m79%6(MX7{|mvSB52NX=$Ooo@oiCLMT3c8W9t-xUo@G6PT5?=z~p&m_ga)^XK2H zGSbp+aB_08FJ{kA^dyUV&NRH9?~Kv+FBH`5c5M`z)CrJGtEmwnCnx{Ka4=b|E{N>U zB0Q+AtlY~h`f@{?Wc2cdgRcP*5fRF_7?L_)@>FzAJ1-_)W_tYc<;#(=u{XZHuV|-- z!IRWu-fPlCno%BBk-d}sn46kTbKtCRV1S93%kl=R)!^8$h*h&Qeo*0}Hr2s_Ie;ab zp)`n|Q!)6XDPAZ`A=zWD?Ok8G46Ue#5Es|mS2y14etknNDd!E6vRqVeS3uLr? z^nQ#GS1ro{pNe-C7P&w3QF_sjG$VZGtcm#tuiu~ycJ%f)y^`hE11+zTh0=%m$H$LG zO7xLPWOH+KFd2KA*$pC`goFee8=L(6{Mx!Yg#>=<#ct|f@`SHMWY`x^8?kBK&QAW^ zPvxSc6U*wCwu}Aatd;a7}2{@9?Gc~n%_%{fUKi-VwviarVV`g?onVmjsJF!JX5W$i!+Tr-x~1FpKX0+ zrr2aKw=;$_S-?IQJF8dl`a<2O&!2;ve?dwjs$b6YcCKl(va>s`KRc=q2?^2s>~NQt zcWh)t=h^`ib?55OZ$O-+yX*5tdhFLN`ltR1ZZmk)*ksv~pWVF;Kc7GMcm!2#21)eZ9q;xlh=_=^oTB;e#&Rsy=_G~G z;UK(}a%$ZUY`|jQy`j&Ss<@oxxHXbR58$WR@cUI6nO44Ox3bUmo^+YMJ&T?5HrqTB zhY(ixyyw64|Z?&KN20SvKGl=bw_CnmgUIZKlA2{E!9 zZ(n4Idv+f#Kmzd%V#A|Hj}UYuff|~cEDY~nL}ukhU(Fi0{jU8bMEh_W;j4FE^}c72 zSGJ5*e8Kw4;$(W2I@LbwgRTEXGhz(!hYHr#C5p^;4h~pe5MhUgp36Psbog!%;*M+J z?BY@b0LRJc6THIr%ePdr9KtUY>{TYxpsNXEmD|jjtdmN}#F$iVyZ*s1rIlz-Av{b8 zH`rg207C-{aTcPnzH4l1%F5uS>FxqCPg!{Y3<|JJN{x`Hs3;v>LrV)I9UYyhsCFk! zZf>sKy|)~?=H#4LUy8m75~tl2r|sPA!=|G80QUxPsk{5}uLX)o1lez7Nl8gxUthBO zUR-**zLL`W<=lk@!*{Y6h#T($J~2m2K2aYhr7g-XTGXrucjC&e7;J2O?Nm5#-Q;^w zvt{?FKl#6IxH8sMh1!8&!JJ~AXn4^x{OoE|(;l(C-JRyvmF@3d-CzJ?V`Gq=;TRtu zXKB{*dNzx^X>M-*f!91JFz|ZJw{PF_wd&N^$mBDAtc_R6NJ=X5=Hyge6&N8y1O){N z2?^B*`EuxnzIfrFVA-6~>H z3%Z)gQ$K#ZPfXNQR^EkZ_ZBbW{d+TWbEB@|vW|^Z#w$@RNJ)<2#x^NJvt;eIvn=H{QwK{J>|)!OE&$>%^Vi<$Dnq_ZBB-cwit4 zC+D!ofq?zxVnpW`?)16i@M z#;Yf7TpZMCuIUy}-r5Zc!re}yu&}ULrG|?Fc4lB;sHmtwDDILv9UA8#w|+GTYqm@X zyN%EtaVz5=f;GMhHqO0e;+Wd9Stp*Oqkc5ExEK=|xx2c2la-a#(a{l-I~?$hoIk<& zrLV8z`8)~Moog~8v#|CGmv2z(dK)3_l91IOyrQ=69u^QT?&JI7be|p!o#Eh)iVt`< zH9tbGvLZYw#Eq;&t*f6HqXg#^c_~<_VSZG^BJc8X{gKXW8=Pggy&rk<>DGXdW?Ebl zWh_Nke;>BHOOC=u?4KVDS9f+H0@_qhczGcZZQb3^nMuXP#WU?!uu!X8B%R~?=(!08 z@qAbfH{}~{EptTZEON0YnGheJ6xRw2=b9Qr1A~fk)bY~5pt`0eUP<1O*_KjRNT|H$hLXAU8uy?NHr2l# z7|QK;Ku44l_|wH(sj+eoxuwz4)YJrf5%(}tk@-&Zt81<~1qH%(OGLUeKYtQmy}CR% zH=v0g@!Fme^>`Hulj!^N&-OhDv-9dHaW=GBa*g$$9f>KlYJpQ-rJ^$60Z8!y_OY(6 z4xlT1V}jr93X$?Srv{V7vH`m)yR)kLt1G$K@wLm3$9ipr>dMN>s!_)a#)wN)ybtZK zbSnU!3>Qr!JXC$J;Cfj#aG;>7;Q)zfnB4-7p(^EyBw1cCEd4jgSQnTMw3h<3oO9pV!WuEG#T#hTq3(oy#(hM9MDz-LQ1H z0?xoZl3z6OC3pfcF)`YOm1TlF%pE%nM3Vs5ErUz7SI{J|@ zQ6^pe+RpP)03^b~2z;)MIO%9gqdbiz_ib)+Ck{=Da%qA4ZcgV27=xV)H-eY2{_pJt zTPvOs88$zO@m$1|f7&=2agoWKOEHE!TDk}SA^sNktG8Tuw4Rc*yCpZ1!=`wW3hS8Z zXO8V4d%TWV5!1nbI`kxF!|4LqdKQ`K%L|Ou3QW?P#T@B=i+3RgMbGt{ZrkCiic*Uc z$PlR{BK@ z2b1UO8cABupEDBSyn!t-8{uMQ%_}YCk@IR1Ia#0l4B^^zC|~zr1aHUy?o&KDO*-|k zI;2B9S~-Rop{A}5u%vT#%Sk9HI9MK)FvMm@+x%K`3%DP~DEb6-FCg$nXFrJk{ zXx=;9`hd%pN@%guH-h(}BQ?v(_LHYtGJscIkUtm>w|95#`1qgv@)pbMUg+}G37dtRi)P8OV@gxp&jr@wK7d0g-O zs+>}r&UaP&1YVy#d_$?;IGGHVv&*kg@3=Ab_PRuXw%)yK-Cz9)>uPJ2(eH$1L2NV| zuQdN2e-}{Fvo$b&jGP>eT==D>|RaGJJ z*-?3u?!>R=wRXYg8!@$+nQ%If4H`RIctn3fT0mI!5T~-Uv$g9zA`zj^hk)B|Ly=7D zdCbkrTbY`wyV#v*I$XH+`t>>xA#ZB4NQ4C4!kq=YDZDDNMtE_CuC5RWn%(rcg(3ZQ z29YV8&1IFGJ~pPtW3aI0v>UgEWd!udT!XN|s=@x|&(YCQFdTSIN=j05@Sa6k_*M6@ zk2>sz@4_#J8CcIEtM4#hfLNXEafr-_(UOys3knF3m6xBjsg{ps7vi3K+f{cpDlP@1k=Fg&;c%+1W0FXY3s-3H|Gw(wpp+62?<9@mGiv2Q;fa2-CP*b?ZcQGTc* znmAe$a_HkM04ZKNxl#+_Jh~i1u-k6t`0xJjq>$i0nw~FoLvI}Fk5BM^`ZaU$gcuXv z%FIi)a8O*}ArVut4dl?O9Wm&d{OW({A85R^($C1A`>eRF6{FR}7AwPBN<>O3Wm-d; zEBz38=mkb=_dSeU=ck9&lYU}VHWv9tR#NG>?B`MIOA%S&*Y3S{x~ZLu73-e#`1L-; z9a##hRtd}T`qlS-h7a=XEFJMzZm~b&l~cyZ{^rmBnDlP z(n%f%+w;I*p*Kv`xu&P3EurtMV(i#K7Wyp`7uVZ|k0w~T52<4|g_<$#d52PT2McJ8yq9!eYew=So?Ry+YB0-K;!Z!YK~eb`hcOHDzTx0DowNUGCV- zwH+S1VEc2NUX4Xt;enjKG}k{(nNO`crysv(jMUe)r~E8AJJJb28<-VTWP*SY85y~l zQ(p>P*O?!UR{+W0r-Cj7su6#goHyBZ`%lzl&XMSEdq{r9M_0 z^X@zn*ab@7A6m;TC9{*&W{zUWl1r!kQW))0X=It77EcZ9Qw|)K77o1s9oiU%F z2Ro?0csc(*3l(WQB3XoQb8(-;wd_CD9<*kM#ckL-yENQ3bFvBw3RF~795I5gdQ)f? zkgM3|=e=wyzJzmXin?E9UZXSr!Q6n2L>H^lT0 z^8R7xP|R+-dU9f3LmJO4nNbjYe1EBr9{5C$lf#RC_aWQBU(0#L3njMoB1MBrt7BSG zj!9?0;^fzmodJd(txxOoRcG^r6}$uhF;Lu}KYe=338h|QLV_~-z#LL?I`q#yy-6o# z@Ej2lXP)8~XU>O5OONdB?F(_QT)7g7NPuFw&gX3ZYly1VAEU9ah{J=fwh7Y3Bb}yN@7%-K!tig z)v6@~s8~vNcJ_1DDHvthjl7Od8$tn-5USbD64$Wr-u*+r?{l#A`VVlPtS1aUz9j-6gj4o)Lk1s^_GwRnR^bdc7HUm%uvE6 z0jC3H`_?ft5xvm;)Xho9>1Uc#6B7@2w{N`522g7s1!;ADQ@44*FH|aKE%UKG3kMb< zVcNCuXc@u+Q+v+=1JT2lmxg1SNzlQ)jC&rgz@y$vx@R=u5k~IskZiM0P*D7&Cj)A%Icaez&dx# z+t>seXMSm$-&U`(FnIYgJ8Y))e-^iS1f4{xHkvJAjQbr|hVGU&FS3BI8O}shUvPRZ zwca`MYlrk?g+~cXSN6^xl8VnFLDciK?H&Ci-eVg`vA{i#j*OtVANbkNpFihvKYqLk z2|-IBX-XMWP`YvP*+W;l0Lu4QE?(onkmOktmr54VOjT4?Q5hZ@a&dAJf_(6;to=*N zX?*9ri;GLVjZmHE*{QX)^^+%0pc>MyG^5NW^}+vdC2q2?Yh`qz>Wq_%%xxS=wryuz zFwlGZs$=e9%PT8@F{Y=cM66~S0C6aB@T0LiyGTo8A!40`>RMH8lC<2h_c-4WTHaoJ zO=l>(C#~qvH1G;S1mGx64h{!<`-}mnCsO)Xlb|ezKuJSmQF?&HYDC-F-D4~+HF($a zv=`gGtD*JRLLEMhkgB{tffIEO^eSN&S?G@W;G#Rb$jNm8JmR&S3bCjBJrVwen|}inGIlFB8DG*As`hMy z4$h|4YcUEK{T~DN?ia}4#bG}xzf~nhNs8Ef41*@fB- z*h=)1;>OKjI@#fg{fj|8VoRQX;hdJ5+Eoe)W>l&F6FPg))Chf*Ze&vd8Qa8SKb%VA|#HSDgPFr?~t$5 zEpc`+AUNLR&98x#_l{+9d_S{pNfh0N+JVawC}*1uy18TgU{8JUv2NGCD}OPl-f>9|i6lP?dH46n!d-T!37sr_ z{{TrwAxX&E))tR|;Q7hvjfi&W2}0Kj_}LT&`RI}H@u!s)BSI&`lrx;K7mc{UG8Z0r zT-7+CiOz&5n4vO<s-qe1^RDQSISfsBR*UZaze3JsXf*U3AP z>Obp5n~>a%l_cc6JsU2XJN1f%ka8fC`h{=Y@GcE(UO;*}`Vm^Z&Zh0B z!O@fUoe%$y79fc<^J|9XPX(DcRA>@P6j({9tqjNs4tdElnk4M%?c?)|oR^OeI7S3f zpd?g>xp|}&73}_mteG5U$1R=RIpx_u4G8_^Lo0el??st7=R4S2<(T!-NUUm~oh)6Z z-{{|+^8ZXY;?!-*Lgz++>)&I_(z~nLYdN!-+nN~&^oVXf;c&(LqKKlV@|==}8K3`Pb60Z;V* zK>}R}9sZo%_6}pE?&F{MI|Ppy$tHH zxgQzwTU%SYy1J2&{(;L(N=%#_8;eg&TtgyrV8wY+d+j6Q^3Cd#Mn-9bun42runQ-n z*LDf<<;$0eiN*QbUY7d$V(3kE!l_*Q0C$S-((RWOV9QX?*4YLb6W_w`a^*g0kY6ps z!9e@bI5p)l-w_2EhE=8TqvKNUc^T&;a`PI2N)C$FLMVb2g8^d`(jXdkdPHfjULDI) zpzW`*M@P|*sh4XOR$lfk9D}P{fJ|+x-LR_JU#_Z;-tgy}4G-$#&u)fj z@@}#e*`2B?Dvr=BhUAIX0C5dWeQ{~&)y<11jaxuLEwP179F&cFnu&aBD#t;^S7=*+ z5`hN3P*c}IlZF?b@I2b)JBIzG@Cf}opXq5z+d^dmCpb3lFVuW2XntlB3M*BY#*r?5 z&WW7gd494*5~>WwNvz^)-6%ei-tBvoDV430R7l*RA&s_V!qL|pO#V5f+I7G9Uuqj^ z@sKv}u_E)xtb`EH?HFs|I{@w6ZMhzLVF8Kb-TPwe&!qJ+=Ncdk)Gcq@xw;-fmz$n? z(20oF2kYWW;%qoyE_B81fa|xkwD8`!vpP{7@#z7JVB>jxv?Q^(L)%ZEoS(1d6we@X6Mw)U1gi9) zAb{JNP(17F&rU=0Ejyc;{=pJq7%=nqjm(cr0y-SmhJ?;7YZeI#i0|#~7Z5qhE&%SJ zJ^63${IA^%@5Ib_kMaurB#wx7B~ZES>|D_K7C?Oc`t{1n)knSn0*HLO-w4OOm$4i< zDM)~A?$VGsY@xE0ltLZsFG_vCQ`g-3&uWBs&O?p*k9nAlIqpR?4NI-${#|A*b#*FQ zT3QsSr=+B%Rd`7D^7=VHf$e^$pknc+Ld#49aLxc@BjY<{UPU5I8Q zN=55}7bIRsXslsk#tjuv{k4ft^uuRv!eSKH4b*&tklS{pxcbkZ54A@y1#ay^d0>d# zyq?LVhAY@JQA) z;D1!DRc3gJYCW)TVuFn%5FvJx+(uq)whgK6iHu%)o7q&P!m$} zL16ITc8eX~o7UMXA!~zh!Y$^a9OIzynCZ{h(*KaTuU;+Tan?4>$KQ!j5!{39R|bvx z5>>V+xhl(P8i_== zt*B^51(=%Vq#dbX1*a{(atPplueWw#IYA-fc#cu++K6cenZD(M(rJ5pJ2o~JUQyzP z(TicI776%Y(Vx&P)2@EY=`VGT8(P&Gk2zAadRG4$!AA{uAyIQ8rqT#hjGjYc6I^qe z4dtse8zng+rKhAIJMRee`un!dUSJNUv2zbM?(FH;*VDU65{PW=cf~s2iF-DACM}lh z-u9Oos&vQZDJk0ewLK0DE{A9R!9VBUd_>}hDu<#geD0#ct>H4y52lZ0&ikl_g7{KG zt;%h~pg=LZGRlzwZ)C@!3m?|~pGP9(6n zF??x6(4j4teun40ag31;okC16*ZbGH|B3$jP6^L5QkMGkqxWOI3U}!Vq5p|^-eO2{?z2f_0xx+h0r7HDG%IEL0bd8n9!Fxs{b)2xQk2qL2`Mj zzx2^+*88D-KKa=ZIMqiw{pkPiCR6fy%fPe9O0TX>uhGLCH+AO4IXGhhT?-`Bi!wv=sanb+`axUKg{Wa@F{-{NOzt#)juVtrUII%G z#TlR32oW(c^uM6XqJYr-OT7r&l*vc80~TM22ZDA%7ZQ4X25(gJPC)-Gdaq}@o_ z1{e97CZx$2!NABU0?NbQ6mekEORB1d4X&K03Mq9s)wzkACAe1iKee#;T2jH_8L;)yr{m~ljfZ8Pw)ABg++4jRN2V0V*cj&(qvU9 z5WKw$fdE8-phnA4t^t}dAOJ`6IWa0k2dL$)EU%sYG`u|6@%Lg+;x28y?|HhnMo+Hh zxLm(j*hv=~@~_wc2%GB8XKOZvZ4{mTM?w(&Q$k7E@aqKw+M~xMi3y#3*6AEN zeNKlUJO$STx*cAHg@s{N8V3iT#pokR7&)1_+Wy<0>zSrIbyw7d*EI^*={!TFXc7{V zfOdBI7>m0cCRvC9w%1@{`nvvzQ?ZUVqM*QBS()_U$DOm4B{^6} z;C~@f*xT6P;^JPtXY-LIYDokW6BB@CVO}1UfZY=G_sz}E_{7^VMDE?Y*BVY+l$U2~ zYx_aaaUD=_(Yy(|v|F3#^~2QAISI=P>;woa==h?fnj5t{VawC+pSUl1N_8FfPTw_j z519Xp5O%A&MnuMR`wJu0sSg=aCumF#|1B<1&JVLs^{al^}Z^?C3B7 zkdO9S%ziNEY1W-HC#lhJG!ZfuW&Mo%28oLo4sS>`ho55LD@ve9FmrU~iSol5Ama zN<=%Uo|2wE+Y!YIt--lBMRj}eQBhYWv(nP^Ktt&!;t8T6sKo}RW!60JV7U)`F@1QD zAn5owM;XMdO=Y4ru`RH#yn8kP|3MB2UB43j*7tb9K|!Rr%?+=w$`=JT|AIy!q~6nG z=gZ6$m8ToGUZOTwXIM^&5d_bFt?R=py*)BLZM7k^gWMZ=1 z$yxu9^1{aJ@UgR3_~#BhE^|3^t1ry2OChUwbTke+K~Yh^+nJKN*0!}rGJV0NKZJ)7 z#mzvMNc4D9B2yF5o5dRPQ{T|JCd}90A6gf+r-v&KgO3R-SyICtMi$cXNCJI3=WA+e zpm7&#Ope$^;?f_e8lsBJAA;Ov(;y@eCt}w*m;2rO|9uhA-3&^?(ZerR*vJ5N9gU!a z+0MfEmEoeEjt)IFe<{$jgUuouKrsC5$htP~iIkL|%E0kZFbMy1o{0U$;nNlN|5mTLWniei)hik_>;o$Z!AY!T9v)UbyG+ zEQ~rJP_prF-y{*Ta&k91m#-GRK;6`xJ9miq!jL2&$$5G4}NXJ3ckv6vGd+~CBKnXO&I7uvpnn9({Cl|!Fu0eo@7&nR-jM%b0J;x) zQ5+l`yU&0{{hg7VOdH<%LDch0&Oj{GP@p~yxqvt<;C$fb?q01?2+eQhTX$2h>QB^r z*4NbuGcsZ!!1O_= zR#)e&_z?~BHlXCMgNy;V!?s#lA)Ol*27&>R)D)1H1k?f;FxbHl-R$&qADcyu(l`8R zK^ISLOM^g^55>#K$Ox1zmmNEy?D@UBixSj=Xv6pZb;PIYYRx*AXB;uppR9e1wOc@p z0J@DSXs}0fYCoPBl;wy)bvVYyU15l!X8KJ=>uzDayzfmeE~^Hw3y?aGw>vqhuU$jv z?5&Q1W>nj43zI=1OK0nAx?NkNT}QTnaF?%ACL`ztYp>k6_ff?9*EdMLm(e*9L}X+R zr^g4dr^?Tsy-RkEo@BEFT6+)Dpm9H=jd$;BbZm%0a34TV8hUTpACmOPiw{9~-Nx zr1Wcj{R_yLU0q!@_3TDFCK{uk;PD2lQCN~yhSU*}Yv+hO!5f!jRV4u=vIb)hPEJm+ zX6>f*pF{c#SZ%S@rP)W=Omu5s;7@1u%j?Q216xAZ1lk_}pH=4LFfx$oAPOuaI0>jq zf`fxuSy--L3-<_B^q-w&q4eMnw@{VG6r;FD+M+(m$2-r5GZmxEQqT ze-*J_*|VwgAsp&)f+IX8jfPL~X20u23MDGx`pDKnq_i|QuY+lj8`5lKFnR%5{6+uW zZy;7XJv~J=-0m1F$;siCyj4=ug&rpev_pb|;sqT!N|NX1Ucz7#&~xlpn3gapi4dSMNf7kWN(^SSPXGX#}sr?n^9Kilx6#E8FF_V+qtlWfDnIoclQeGb7)}2s`6M& z@QWmG%+5+^sY<~>R7Y#8e>SzaMlC`44_pw50(%O_=C0Zrs08)$AhJ;NTZyeDAyF3u zrIopfiP{&pYUNv?^&9-(7kr*nc*w)axdGDqTxtP?M!kn7M-1>FlYm~=9>auz%r}T} zC}zY&AB*7Hez)L;+g39<2+W{yaRC8cX&cr@F1z0NI0)46L08;u(CNa21P!lQY#4Wq znZdeFXEx>sDfSRPc@R|{>i&-O6tLKb1apU%*8?JC?(EpJXU{;x#_zgk29r-?1!Qch zR_hbhVCEX1ti=288bhbJ27(m~HnaHf2-T{7*2eP{q73wc2{@qdU;deJsmTeZ-836NdON4uW4gT*JALOCk+7*>z8a$YV zJGIN23ZBC^c;LQD>Y@rGMvxQX#LaqJA(XdU!l>ohHY}R2q{S@m96)q~xVg2vyE?QW z>!@4%7}FGG=W+hV2So+(d81=v5IJ)=O4@Ec1a3*t`j@1;J8HfOa=br2<^6E(G8azZ z6&VZHG^%Y3bahijJ_m({PJXhUg-byb-iTRP@^Z*94O!{eE!0IDzSy3(~Gouu)wnM(Js;W|D!}5Y~4WhLk(X)y6cJRwU?cfZ7Y@4F%i}?;tR+uu$?L zPz8c2jD;nqrp5(EI>Ga&0DA(r3Mm$za$sl(i|p`g&AYd984#rAI5)oW8Em`vk)}Ja z;-N!;H`#b3ZA^yPhdDw>Wo;WP-<}T*4bjlh0DQBxvEhj-3QV~J($o;(BImV(83tVc zS2}~?R9%wv0R=t=z>{Bp?yd|gJYAE<5Pv)6!!U<(aGVR>`0%EZYh4uzt%qFj6T2Gj zq;;ZU|I&{?i%eR~+XUFbATJRR(7+(l7)8RnCf?%QK*nN&fi!vD%TOf1a{-bZ9voze z>KGYOb-|>FxcC7}6L$RGc(58$-uAApS~#=1WnG0q zCs~@$Q}qiiO-rRIp>cz**RNYnAA=T!_zFJETsf?cr1E|OWd;X_#bga+ENe(>DTOui zFJ4eSnVg4eq@{noLg(4#M-WY=VwxCLinLHS%%y{vU#617q`*t>^yW2uzJZCjNzx8c zJDge&=FsjVVxbcW`-q?u6m*4oArn4`Wm|v#0Ct)E_#gtnzL(T7#!2_V>Zd zQ|gKXm2Z9T?8GA^oQC}bQn7tL_lgc1{y$Hh5_+R<_lG=yihBE%=Q1+>;D}?7V5G#& z%`KpV9k3u8!o@xY64DLBiBHx*S$YqRgh<*~x;rA%X^VHb}KRV zGSEW&QY%wJGeGGF1Gli;?(QcO)wZGTFktj{Duks)3qnauL_|7r_z7wjNl~W`T9HJZ zjT)xtkbvmyMh3IW3W9=j3CAa3_Gk%`j{Nn4sZOrnzqO##oAofx=5QAYf?SL5kCE%j z(V%96V1-J@)z!}Mn;OE=6;-QDl}81Qf1m#!bd^4b{I_u7ZRvgncIbAz&uyJ9c&sa? z@Q-bKe@`Q8`OwVlUh}s;k-d3C>xT>%m=6G7kSg+8Ovq}9Ns>A}3H{ir_{m{S&C04c zJw08KIRS=8Uu9ECj1nTn?rBI%OG`?A1ATPf5H{j}U^Ao3B}DlqX&3(s-N+@cFD^Pl z4G$B<#)4e`R3UoX5`iMU;^C9Xzzk9fu)1vytN0wY$OV^kl(s zc_syjQwjO=16`#5``jXiU9k`fVZiQ9sYa^e+a5@?2X0@^tBS$$WDfmn*RCxuFDu7n z4~6FB=KeTQMa(QN!eI77PjV594w$K%$aHIKR%xkHr$-lQ=`Tr`5RZBGsEtCilE)Fk z!9Pnzx})_?&l(6H5KRM9bLyyLG3>w2)6&|?boXvWVPRoLhF|uvR1sCWgD4D&Z|fou zCU~7W_g4?V$l=$T9UrJw2PH563rv25iqz?<3eP8pQE_=S+9D9x1E|jhfJ#viI2D*) zSNz`3^44c~_lX%hgk2ca)`6T4J&g<>A9Ms8AR@3jKv17x^3)p>?~XY?%<2}^pQ?4f z9ykOOHyH=5eiGiGfX=E6sf`L?Ps5DF7{*e5CAf?rs1sv4yKlUo{{o-u@ z?Tc@QPT=*A*c9^~A93`)EUDtPc~SA$=f8jd9w!c1}qer)~wFMO_3?!_~&D}BTzE<%^Z2d*^mBSK9Qx3nr1s)zA zo}L_GV`CFi^7wwaBK*c9KPk~q4O#|owK|~gVS1cE0oAn4d`DB$eRqebn3&d|R!~pl z&r2&orrle8!G2*EuLD(#@O9HM*j*)LH}cq!6C3P*bmMY=9clqC&k- zf2nnO^hNghcnR7K|6f~9FaiZ41&KHK)qp~w)>&CunYX!z_}0?$Ugfsef=yEHMb0ga z8}BeMFhG3w1qeJCs)yJt_Tt4T2=8+9@&Gj5p-%M3hq*ckJfQpp(*atl(Ffay;Nb3m zpc)nEwbG=5?S!KLp#+j)EWU%aC|kH69tpw~STCyCW@xy)&mh4O!*uf|e2M@cH}{hm zCp4(Ce(}H$<)=m>eq8nQ^8>iRxSVG;{Y7()+IsmE?-7R zz&7O4*U!a8|HY5Y-#)rttOX*9IjSQe;o4HS0uC?$K>SnxhcqSGx+wEHzvxDk9Lp7z zHU@$1{jFJ44ZFM42a{1hpn5}XnMtDvN-NCHzNX-L*bk)}9xg5nNH8Hb_fQ8=R)jP{ z0WflKXqOg1l%be?OY$c@At50THntZanWO!EsPht;73P+g<=C#T$oGdo79DDMjgF3n z26~%9UYQ|Dm=@5{Nr{MfDkX*4cd%^3CIExD5IW#97QVxDdr!}wy}iujoDUzy|9;wo z<7~^N$xA3+R|q-en39>v`0HX!C(HmSQ%MY{IR740fd^8KS!zO|N)W%jY$vIx4>Jks zn^&z#@B=PQhUivq1dcv@;>J0nfro&R>f8%LG(EtA-n{9nDoaXiVre-%a75%64w^g@ zEd^3y8*A$qpRBH$TQLN_Spd$WpQ*_P-E8PsV*U$T{?DIqgoS0&lztlF#wqYK^@f}>GVVN z0bt&wZ{EG8dpp*JE?)x5CJeN}a?m2fALd3s zxIUE2(N|s!X*Dy`5a{^Gl?6<+3)bzxb=;L|7$5(flM~3y_SiWPeF>-Y{Nb+=_$y(2 zZEr^ce+pG6vT>@Tv-83Xq#4A_+M4^Hb Date: Wed, 22 Aug 2018 15:37:43 +0200 Subject: [PATCH 296/695] Fixed error when using a user which doesn't have a default shell (like the default munin user on Debian/Ubuntu) --- plugins/git/git_commit_behind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index 759fd902..e77d3ed3 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -177,7 +177,7 @@ def generate_git_command(repo_conf, git_command): quote(repo_conf['path']), quote(conf['git_path']), ' '.join(git_command)) - cmd = ['su', '-', repo_conf['user'], '-c', shell_cmd] + cmd = ['su', '-', repo_conf['user'], '-s', '/bin/sh', '-c', shell_cmd] return cmd From 646a6c698595658de41cc0684eabce06eeb43657 Mon Sep 17 00:00:00 2001 From: Neraud Date: Wed, 22 Aug 2018 18:34:02 +0200 Subject: [PATCH 297/695] Fixed user detection. The USER environment variable is not populated when the plugin is executed. Switched to checking the effective user. --- plugins/git/git_commit_behind | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/git/git_commit_behind b/plugins/git/git_commit_behind index e77d3ed3..bcd07319 100755 --- a/plugins/git/git_commit_behind +++ b/plugins/git/git_commit_behind @@ -114,6 +114,7 @@ GPLv2 import logging import os from pathlib import Path +import pwd from random import randint import re from shlex import quote @@ -128,6 +129,8 @@ if int(os.getenv('MUNIN_DEBUG', 0)) > 0: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-7s %(message)s') +current_user = pwd.getpwuid(os.geteuid())[0] + conf = { 'git_path': os.getenv('git_path', '/usr/bin/git'), 'state_file': os.getenv('MUNIN_STATEFILE'), @@ -170,7 +173,7 @@ def print_config(): def generate_git_command(repo_conf, git_command): - if not repo_conf['user'] or repo_conf['user'] == os.environ['USER']: + if not repo_conf['user'] or repo_conf['user'] == current_user: cmd = [quote(conf['git_path'])] + git_command else: shell_cmd = 'cd %s ; %s %s' % ( From ab3d97fca966ecc58dc76f6cf571635555f5ed4b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 22 Aug 2018 19:43:03 +0200 Subject: [PATCH 298/695] Plugin asterisk: fix typos for "unknown" dataset The "draw" configuration as well as the "value" were ignored. --- plugins/asterisk/asterisk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 1810fbb9..7994617d 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -206,7 +206,7 @@ END print < Date: Fri, 24 Aug 2018 20:29:48 +0200 Subject: [PATCH 299/695] Plugin bitcoind_: reformat documentation header --- plugins/currency/bitcoin/bitcoind_ | 83 +++++++++++++++++------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 5b5408dd..7e2b7f0d 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -1,39 +1,52 @@ #!/usr/bin/env python -# bitcoind_ Munin plugin for Bitcoin Server Variables -# -# by Mike Koss -# Feb 14, 2012, MIT License -# -# You need to be able to authenticate to the bitcoind server to issue rpc's. -# This plugin supporst 2 ways to do that: -# -# 1) In /etc/munin/plugin-conf.d/bitcoin.conf place: -# -# [bitcoind_*] -# user your-username -# -# Then be sure your $HOME/.bitcoin/bitcoin.conf has the correct authentication info: -# rpcconnect, rpcport, rpcuser, rpcpassword -# -# 2) Place your bitcoind authentication directly in /etc/munin/plugin-conf.d/bitcoin.conf -# -# [bitcoind_*] -# env.rpcport 8332 -# env.rpcconnect 127.0.0.1 -# env.rpcuser your-username-here -# env.rpcpassword your-password-here -# -# To install all available graphs: -# -# sudo munin-node-configure --libdir=. --suggest --shell | sudo bash -# -# Leave out the "| bash" to get a list of commands you can select from to install -# individual graphs. -# -# Munin plugin tags: -# -#%# family=auto -#%# capabilities=autoconf suggest + +"""=cut +=head1 NAME + + bitcoind_ - Track Bitcoin Server Variables + +=head1 CONFIGURATION + +You need to be able to authenticate to the bitcoind server to issue rpc's. +This plugin supports two ways to do that: + +1) In /etc/munin/plugin-conf.d/bitcoin.conf place: + + [bitcoind_*] + user your-username + + Then be sure your $HOME/.bitcoin/bitcoin.conf has the correct authentication info: + rpcconnect, rpcport, rpcuser, rpcpassword + +2) Place your bitcoind authentication directly in /etc/munin/plugin-conf.d/bitcoin.conf + + [bitcoind_*] + env.rpcport 8332 + env.rpcconnect 127.0.0.1 + env.rpcuser your-username-here + env.rpcpassword your-password-here + +To install all available graphs: + + sudo munin-node-configure --libdir=. --suggest --shell | sudo bash + +Leave out the "| bash" to get a list of commands you can select from to install +individual graphs. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 LICENSE + +MIT License + +=head1 AUTHOR + +Copyright (C) 2012 Mike Koss + +=cut""" import os import sys From fffb536e26adeadc3b019035353c45fa1ce49089 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 20:31:14 +0200 Subject: [PATCH 300/695] Plugin bitcoind_: fix flake8 and python3 issues --- plugins/currency/bitcoin/bitcoind_ | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 7e2b7f0d..369fbc63 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -79,15 +79,15 @@ def main(): if command == 'suggest': for var_name in request_labels.keys(): - print var_name + print(var_name) return if command == 'config': - print 'graph_category htc' - print 'graph_title Bitcoin %s' % labels[0] - print 'graph_vlabel %s' % labels[1] + print('graph_category htc') + print('graph_title Bitcoin %s' % labels[0]) + print('graph_vlabel %s' % labels[1]) for label in line_labels: - print '%s.label %s' % (label, label) + print('%s.label %s' % (label, label)) return # Munin should send connection options via environment vars @@ -110,7 +110,7 @@ def main(): if error: if command == 'autoconf': - print 'no' + print('no') return else: # TODO: Better way to report errors to Munin-node. @@ -128,11 +128,11 @@ def main(): info['waiting'] = len(memory_pool) if command == 'autoconf': - print 'yes' + print('yes') return for label in line_labels: - print "%s.value %s" % (label, info[label]) + print("%s.value %s" % (label, info[label])) def parse_conf(filename): @@ -151,7 +151,8 @@ def parse_conf(filename): continue (var, value) = (m.group(1), m.group(2).strip()) options[var] = value - except: + except OSError: + # the config file may be missing pass return options @@ -212,9 +213,9 @@ class Proxy(object): def __call__(self, *args): if DEBUG: arg_strings = [json.dumps(arg) for arg in args] - print "Calling %s(%s) @ %s" % (self.method, + print("Calling %s(%s) @ %s" % (self.method, ', '.join(arg_strings), - self.service.url) + self.service.url)) data = { 'method': self.method, @@ -229,15 +230,15 @@ class Proxy(object): try: body = urllib2.urlopen(request).read() - except Exception, e: + except urllib2.URLError as e: return (None, e) if DEBUG: - print 'RPC Response (%s): %s' % (self.method, json.dumps(body, indent=4)) + print('RPC Response (%s): %s' % (self.method, json.dumps(body, indent=4))) try: data = json.loads(body) - except ValueError, e: + except ValueError as e: return (None, e.message) # TODO: Check that id matches? return (data['result'], data['error']) From bc20826c4e7dd9d88d8c7db85a11433d5364a0a7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 20:37:57 +0200 Subject: [PATCH 301/695] Plugin bitcoind_: switch to Python3 --- plugins/currency/bitcoin/bitcoind_ | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 369fbc63..48de665c 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """=cut =head1 NAME @@ -48,12 +48,13 @@ Copyright (C) 2012 Mike Koss =cut""" +import json import os +import re import sys import time -import re -import urllib2 -import json +import urllib.error +import urllib.request DEBUG = False @@ -222,15 +223,15 @@ class Proxy(object): 'params': args, 'id': self.id, } - request = urllib2.Request(self.service.url, json.dumps(data)) + request = urllib.request.Request(self.service.url, json.dumps(data)) if self.service.username: # Strip the newline from the b64 encoding! b64 = ('%s:%s' % (self.service.username, self.service.password)).encode('base64')[:-1] request.add_header('Authorization', 'Basic %s' % b64) try: - body = urllib2.urlopen(request).read() - except urllib2.URLError as e: + body = urllib.request.urlopen(request).read() + except urllib.error.URLError as e: return (None, e) if DEBUG: @@ -245,8 +246,8 @@ class Proxy(object): def get_json_url(url): - request = urllib2.Request(url) - body = urllib2.urlopen(request).read() + request = urllib.request.Request(url) + body = urllib.request.urlopen(request).read() data = json.loads(body) return data From 675f1f696290fc7e2199eb155f67626a2b4aa2a1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 20:45:46 +0200 Subject: [PATCH 302/695] Plugin bitcoind_: retrieve debug state from environment --- plugins/currency/bitcoin/bitcoind_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 48de665c..5d110479 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -57,7 +57,7 @@ import urllib.error import urllib.request -DEBUG = False +DEBUG = os.getenv('MUNIN_DEBUG') == '1' def main(): From 0a090e5be5daaf94a10871239679f8e1f76b0026 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 21:52:11 +0200 Subject: [PATCH 303/695] Plugin bitcoind_: improved error output --- plugins/currency/bitcoin/bitcoind_ | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 5d110479..142382ce 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -115,7 +115,8 @@ def main(): return else: # TODO: Better way to report errors to Munin-node. - raise ValueError("Could not connect to Bitcoin server.") + print("Could not connect to Bitcoin server.", file=sys.stderr) + sys.exit(1) if request_var in ('transactions', 'block_age'): (info, error) = bitcoin.getblockhash(info['blocks']) @@ -180,9 +181,9 @@ class Options(dict): if self.get(name) is None: missing.append(name) if len(missing) > 0: - raise ValueError("Missing required setting%s: %s." % - ('s' if len(missing) > 1 else '', - ', '.join(missing))) + print("Missing required setting%s: %s." + % ('s' if len(missing) > 1 else '', ', '.join(missing)), file=sys.stderr) + sys.exit(1) class ServiceProxy(object): From abdeb7ec6500b14bd833f8afbd262a9d24bc16d8 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 21:52:34 +0200 Subject: [PATCH 304/695] Plugin bitcoind_: introduce explicit configuration file path The home directory of the currently effective UID cannot be easily inferred in python via os.path.expanduser. Since there is no other simple way for reaching this goal, an explicit environemnt setting is introduced. --- plugins/currency/bitcoin/bitcoind_ | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 142382ce..293b72cc 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -14,8 +14,10 @@ This plugin supports two ways to do that: [bitcoind_*] user your-username + env.bitcoin_configfile /home/your-username/.bitcoin/bitcoin.conf - Then be sure your $HOME/.bitcoin/bitcoin.conf has the correct authentication info: + Then be sure that the file referenced above (typically: $HOME/.bitcoin/bitcoin.conf) + has the correct authentication info: rpcconnect, rpcport, rpcuser, rpcpassword 2) Place your bitcoind authentication directly in /etc/munin/plugin-conf.d/bitcoin.conf @@ -97,8 +99,16 @@ def main(): bitcoin_options.rpcport = bitcoin_options.get('rpcport', '8332') if bitcoin_options.get('rpcuser') is None: - conf_file = os.path.join(os.path.expanduser('~/.bitcoin'), 'bitcoin.conf') - bitcoin_options = parse_conf(conf_file) + conf_file = os.getenv("bitcoin_configfile") + if not conf_file: + print("Missing environment settings (rpcuser/rcpassword or bitcoin_configfile)", + file=sys.stderr) + sys.exit(1) + elif not os.path.exists(conf_file): + print("Configuration file does not exist: {}".format(conf_file), file=sys.stderr) + sys.exit(1) + else: + bitcoin_options = parse_conf(conf_file) bitcoin_options.require('rpcuser', 'rpcpassword') From 4becc789debeefbf02fef3a36d99faf6546fadae Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 24 Aug 2018 22:04:59 +0200 Subject: [PATCH 305/695] Plugin ceph_osd: improve style and documentation --- plugins/ceph/ceph_osd | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/ceph/ceph_osd b/plugins/ceph/ceph_osd index 03b4b77d..4e5404de 100755 --- a/plugins/ceph/ceph_osd +++ b/plugins/ceph/ceph_osd @@ -6,6 +6,10 @@ ceph_osd - Shows ceph OSD states (total configured, up and in) +=head1 APPLICABLE SYSTEMS + +Any host with ceph configured and "jq" being installed . + =head1 AUTHOR Mate Gabri @@ -46,7 +50,6 @@ fi CEPH_STATUS=$(ceph -s --format=json) -echo "osds.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_osds')" -echo "up.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_up_osds')" -echo "in.value $(echo $CEPH_STATUS | jq '.osdmap.osdmap.num_in_osds')" - +echo "osds.value $(echo "$CEPH_STATUS" | jq '.osdmap.osdmap.num_osds')" +echo "up.value $(echo "$CEPH_STATUS" | jq '.osdmap.osdmap.num_up_osds')" +echo "in.value $(echo "$CEPH_STATUS" | jq '.osdmap.osdmap.num_in_osds')" From 6af31ea318158bf89e0ffe5b95fd23890b6115f2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 03:09:45 +0200 Subject: [PATCH 306/695] Plugins jstat__*: simplify handling of environment settings --- plugins/jvm/jstat__gccount | 20 +++----------------- plugins/jvm/jstat__gctime | 20 +++----------------- plugins/jvm/jstat__heap | 20 +++----------------- 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 165b4118..276910ea 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -35,25 +35,11 @@ # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - Defaults to '/usr/local/java/jdk' # -DefaultPidFile="/var/run/jsvc.pid" -DefaultJavaHome="/usr/local/java/jdk" -# -# Environment Variables -# -if [ -z "${pidfilepath}" ]; then - pidfilepath="${DefaultPidFile}" -fi +pidfilepath=${pidfilepath:-/var/run/jsvc.pid} +graphtitle=${graphtitle:-$pidfilepath} +JAVA_HOME=${javahome:-/usr/local/java/jdk} -if [ -z "${graphtitle}" ]; then - graphtitle="${pidfilepath}" -fi - -if [ -z "${javahome}" ]; then - JAVA_HOME="${DefaultJavaHome}" -else - JAVA_HOME="${javahome}" -fi export JAVA_HOME # diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index de221292..aad1b702 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -35,25 +35,11 @@ # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - Defaults to '/usr/local/java/jdk' # -DefaultPidFile="/var/run/jsvc.pid" -DefaultJavaHome="/usr/local/java/jdk" -# -# Environment Variables -# -if [ -z "${pidfilepath}" ]; then - pidfilepath="${DefaultPidFile}" -fi +pidfilepath=${pidfilepath:-/var/run/jsvc.pid} +graphtitle=${graphtitle:-$pidfilepath} +JAVA_HOME=${javahome:-/usr/local/java/jdk} -if [ -z "${graphtitle}" ]; then - graphtitle="${pidfilepath}" -fi - -if [ -z "${javahome}" ]; then - JAVA_HOME="${DefaultJavaHome}" -else - JAVA_HOME="${javahome}" -fi export JAVA_HOME # diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index c4c4c33a..3eaeae73 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -35,25 +35,11 @@ # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - Defaults to '/usr/local/java/jdk' # -DefaultPidFile="/var/run/jsvc.pid" -DefaultJavaHome="/usr/local/java/jdk" -# -# Environment Variables -# -if [ -z "${pidfilepath}" ]; then - pidfilepath="${DefaultPidFile}" -fi +pidfilepath=${pidfilepath:-/var/run/jsvc.pid} +graphtitle=${graphtitle:-$pidfilepath} +JAVA_HOME=${javahome:-/usr/local/java/jdk} -if [ -z "${graphtitle}" ]; then - graphtitle="${pidfilepath}" -fi - -if [ -z "${javahome}" ]; then - JAVA_HOME="${DefaultJavaHome}" -else - JAVA_HOME="${javahome}" -fi export JAVA_HOME # From 7a52b375099af0dfeb0cf3011f6786ed258239e9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 03:12:42 +0200 Subject: [PATCH 307/695] Plugins jstat__*: improve detection of default JAVA_HOME --- plugins/jvm/jstat__gccount | 7 +++++-- plugins/jvm/jstat__gctime | 7 +++++-- plugins/jvm/jstat__heap | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 276910ea..de61712d 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -33,12 +33,15 @@ # Config variables: # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' -# javahome - Defaults to '/usr/local/java/jdk' +# javahome - override automatic detection of JRE directory # +default_java_home=/usr/lib/jvm/default-java +[ -e "$default_java_home" ] || default_java_home=/usr/local/java/jdk + pidfilepath=${pidfilepath:-/var/run/jsvc.pid} graphtitle=${graphtitle:-$pidfilepath} -JAVA_HOME=${javahome:-/usr/local/java/jdk} +JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index aad1b702..fa6cab22 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -33,12 +33,15 @@ # Config variables: # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' -# javahome - Defaults to '/usr/local/java/jdk' +# javahome - override automatic detection of JRE directory # +default_java_home=/usr/lib/jvm/default-java +[ -e "$default_java_home" ] || default_java_home=/usr/local/java/jdk + pidfilepath=${pidfilepath:-/var/run/jsvc.pid} graphtitle=${graphtitle:-$pidfilepath} -JAVA_HOME=${javahome:-/usr/local/java/jdk} +JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index 3eaeae73..d510626f 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -33,12 +33,15 @@ # Config variables: # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' -# javahome - Defaults to '/usr/local/java/jdk' +# javahome - override automatic detection of JRE directory # +default_java_home=/usr/lib/jvm/default-java +[ -e "$default_java_home" ] || default_java_home=/usr/local/java/jdk + pidfilepath=${pidfilepath:-/var/run/jsvc.pid} graphtitle=${graphtitle:-$pidfilepath} -JAVA_HOME=${javahome:-/usr/local/java/jdk} +JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME From 4c755fd4a7042b31fb9d02b109642a6aab2cde2c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 03:17:45 +0200 Subject: [PATCH 308/695] Plugins jstat__*: mention 'graphtitle' setting in documentation --- plugins/jvm/jstat__gccount | 1 + plugins/jvm/jstat__gctime | 1 + plugins/jvm/jstat__heap | 1 + 3 files changed, 3 insertions(+) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index de61712d..2228a5f2 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -34,6 +34,7 @@ # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - override automatic detection of JRE directory +# graphtitle - Title of the graph (defaults to PID file location) # default_java_home=/usr/lib/jvm/default-java diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index fa6cab22..d816971c 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -34,6 +34,7 @@ # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - override automatic detection of JRE directory +# graphtitle - Title of the graph (defaults to PID file location) # default_java_home=/usr/lib/jvm/default-java diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index d510626f..20bce370 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -34,6 +34,7 @@ # # pidfilepath - Which file path use. Defaults to '/var/run/jsvc.pid' # javahome - override automatic detection of JRE directory +# graphtitle - Title of the graph (defaults to PID file location) # default_java_home=/usr/lib/jvm/default-java From 3398ff3c0cd42c16a0719dad13580fd7592ef456 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 03:53:19 +0200 Subject: [PATCH 309/695] Plugins jstat__*: simplify handling of different JVM types --- plugins/jvm/jstat__gccount | 132 ++++++------------------- plugins/jvm/jstat__gctime | 151 +++++++++------------------- plugins/jvm/jstat__heap | 197 +++++++++++++++---------------------- 3 files changed, 157 insertions(+), 323 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 2228a5f2..8724cb70 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -69,93 +69,39 @@ chk_version() fi } -config_common() -{ - echo 'graph_title GC Count' $graphtitle - echo 'graph_args -l 0' - echo 'graph_vlabel GC Count(times)' - echo 'graph_total total' - echo 'graph_info GC Count' - echo 'graph_category virtualization' +print_config() { + echo 'graph_title GC Count' $graphtitle + echo 'graph_args -l 0' + echo 'graph_vlabel GC Count(times)' + echo 'graph_total total' + echo 'graph_info GC Count' + echo 'graph_category virtualization' + + echo 'Young_GC.label Young_GC' + echo 'Young_GC.min 0' + if [ "${JDK_TYPE}" == "bea" ]; then + echo 'Old_GC.label Old_GC' + echo 'Old_GC.min 0' + else + echo 'Full_GC.label Full_GC' + echo 'Full_GC.min 0' + fi } -config_sun_jdk() -{ - config_common - - echo 'Young_GC.label Young_GC' - echo 'Young_GC.min 0' - echo 'Full_GC.label Full_GC' - echo 'Full_GC.min 0' +print_stats() { + local pid_num="$1" + local awk_script + if [ "${JDK_TYPE}" == "bea" ]; then + # shellcheck disable=SC2016 + awk_script='{ YC = $4; OC = $5; print "Young_GC.value " YGC; print "Old_GC.value " FGC; }' + else + # shellcheck disable=SC2016 + awk_script='{ YGC = $11; FGC = $13; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' + fi + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -config_bea_jdk() -{ - config_common - - echo 'Young_GC.label Young_GC' - echo 'Young_GC.min 0' - echo 'Old_GC.label Old_GC' - echo 'Old_GC.min 0' -} - -print_sun_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - S0C = $1; \ - S1C = $2; \ - S0U = $3; \ - S1U = $4; \ - EC = $5; \ - EU = $6; \ - OC = $7; \ - OU = $8; \ - PC = $9; \ - PU = $10; \ - YGC = $11; \ - YGCT = $12; \ - FGC = $13; \ - FGCT = $14; \ - GCT = $15; \ - \ - S0F = S0C - S0U; \ - S1F = S1C - S1U; \ - EF = EC - EU; \ - OF = OC - OU; \ - PF = PC - PU; \ - \ - print "Young_GC.value " YGC; \ - print "Full_GC.value " FGC; \ -}' -} - -print_bea_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - HeapSize = $1; \ - NurserySize = $2; \ - UsedHeapSize = $3; \ - YC = $4; \ - OC = $5; \ - YCTime = $6; \ - OCTime = $7; \ - GCTime = $8; \ - YCPauseTime = $9; \ - OCPauseTime = $10; \ - PauseTime = $11; \ - Finalizers = $12; \ - \ - print "Young_GC.value " YC; \ - print "Old_GC.value " OC;\ -}' -} - -# -# common for all argument -# chk_jdk # @@ -184,25 +130,9 @@ if [ "$1" = "autoconf" ]; then fi -# -# config -# if [ "$1" = "config" ]; then - if [ "${JDK_TYPE}" == "bea" ]; then - config_bea_jdk - else - config_sun_jdk - fi - exit 0 + print_config + exit 0 fi -# -# Main -# -PidNum=`cat ${pidfilepath}` - -if [ "${JDK_TYPE}" == "bea" ]; then - print_bea_stats -else - print_sun_stats -fi +print_stats "$(cat "$pidfilepath")" diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index d816971c..e3252b7d 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -69,97 +69,55 @@ chk_version() fi } -config_common() -{ - echo 'graph_title GC Time' $graphtitle - echo 'graph_args -l 0' - echo 'graph_vlabel GC Time(sec)' - echo 'graph_total total' - echo 'graph_info GC Time' - echo 'graph_category virtualization' +print_config() { + echo 'graph_title GC Time' $graphtitle + echo 'graph_args -l 0' + echo 'graph_vlabel GC Time(sec)' + echo 'graph_total total' + echo 'graph_info GC Time' + echo 'graph_category virtualization' + + echo 'Young_GC.label Young_GC' + echo 'Young_GC.min 0' + if [ "${JDK_TYPE}" == "bea" ]; then + echo 'Old_GC.label Old_GC' + echo 'Old_GC.min 0' + echo 'Young_Pause.label Young_GC Pause' + echo 'Young_Pause.min 0' + echo 'Old_Pause.label Old_GC Pause' + echo 'Old_Pause.min 0' + else + echo 'Full_GC.label Full_GC' + echo 'Full_GC.min 0' + fi } -config_sun_jdk() -{ - config_common - - echo 'Young_GC.label Young_GC' - echo 'Young_GC.min 0' - echo 'Full_GC.label Full_GC' - echo 'Full_GC.min 0' +print_stats() { + local pid_num="$1" + local awk_script + if [ "${JDK_TYPE}" == "bea" ]; then + # shellcheck disable=SC2016 + awk_script='{ + YCTime = $6; + OCTime = $7; + YCPauseTime = $9; + OCPauseTime = $10; + print "Young_GC.value " YCTime; + print "Old_GC.value " OCTime; + print "Young_Pause.value " YCPauseTime; + print "Old_Pause.value " OCPauseTime; }' + else + # shellcheck disable=SC2016 + awk_script='{ + YGCT = $12; + FGCT = $14; + print "Young_GC.value " YGCT; + print "Full_GC.value " FGCT; }' + fi + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -config_bea_jdk() -{ - config_common - - echo 'Young_GC.label Young_GC' - echo 'Young_GC.min 0' - echo 'Old_GC.label Old_GC' - echo 'Old_GC.min 0' - echo 'Young_Pause.label Young_GC Pause' - echo 'Young_Pause.min 0' - echo 'Old_Pause.label Old_GC Pause' - echo 'Old_Pause.min 0' - -} - -print_sun_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - S0C = $1; \ - S1C = $2; \ - S0U = $3; \ - S1U = $4; \ - EC = $5; \ - EU = $6; \ - OC = $7; \ - OU = $8; \ - PC = $9; \ - PU = $10; \ - YGC = $11; \ - YGCT = $12; \ - FGC = $13; \ - FGCT = $14; \ - GCT = $15; \ - - \ - S0F = S0C - S0U; \ - S1F = S1C - S1U; \ - EF = EC - EU; \ - OF = OC - OU; \ - PF = PC - PU; \ - \ - print "Young_GC.value " YGCT; \ - print "Full_GC.value " FGCT; \ -}' -} - -print_bea_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - HeapSize = $1; \ - NurserySize = $2; \ - UsedHeapSize = $3; \ - YC = $4; \ - OC = $5; \ - YCTime = $6; \ - OCTime = $7; \ - GCTime = $8; \ - YCPauseTime = $9; \ - OCPauseTime = $10; \ - PauseTime = $11; \ - Finalizers = $12; \ - \ - print "Young_GC.value " YCTime; \ - print "Old_GC.value " OCTime; \ - print "Young_Pause.value " YCPauseTime; \ - print "Old_Pause.value " OCPauseTime -}' -} # # common for all argument @@ -192,25 +150,8 @@ if [ "$1" = "autoconf" ]; then fi -# -# config -# if [ "$1" = "config" ]; then - if [ "${JDK_TYPE}" == "bea" ]; then - config_bea_jdk - else - config_sun_jdk - fi - exit 0 + print_config fi -# -# Main -# -PidNum=`cat ${pidfilepath}` - -if [ "${JDK_TYPE}" == "bea" ]; then - print_bea_stats -else - print_sun_stats -fi +print_stats "$(cat "$pidfilepath")" diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index 20bce370..1ec26c07 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -69,109 +69,89 @@ chk_version() fi } -config_common() -{ - echo "graph_title Heap Usage" $graphtitle - echo "graph_args --base 1024 -l 0" - echo "graph_vlabel Heap Usage(Bytes)" - echo "graph_info Heap Usage" - echo "graph_category virtualization" +print_config() { + echo "graph_title Heap Usage" $graphtitle + echo "graph_args --base 1024 -l 0" + echo "graph_vlabel Heap Usage(Bytes)" + echo "graph_info Heap Usage" + echo "graph_category virtualization" + + if [ "${JDK_TYPE}" == "bea" ]; then + echo "NurserySize.label NurserySize" + echo "HeapSize.label HeapSize" + echo "UsedHeapSize.label UsedHeapSize" + echo "NurserySize.draw AREA" + echo "HeapSize.draw STACK" + echo "UsedHeapSize.draw STACK" + else + echo "Eden_Used.label Eden_Used" + echo "Eden_Free.label Eden_Free" + echo "Survivor0_Used.label Survivor0_Used" + echo "Survivor0_Free.label Survivor0_Free" + echo "Survivor1_Used.label Survivor1_Used" + echo "Survivor1_Free.label Survivor1_Free" + echo "Old_Used.label Old_Used" + echo "Old_Free.label Old_Free" + echo "Permanent_Used.label Permanent_Used" + echo "Permanent_Free.label Permanent_Free" + echo "Eden_Used.draw AREA" + echo "Eden_Free.draw STACK" + echo "Survivor0_Used.draw STACK" + echo "Survivor0_Free.draw STACK" + echo "Survivor1_Used.draw STACK" + echo "Survivor1_Free.draw STACK" + echo "Old_Used.draw STACK" + echo "Old_Free.draw STACK" + echo "Permanent_Used.draw STACK" + echo "Permanent_Free.draw STACK" + fi } -config_sun_jdk() -{ - config_common - echo "Eden_Used.label Eden_Used" - echo "Eden_Free.label Eden_Free" - echo "Survivor0_Used.label Survivor0_Used" - echo "Survivor0_Free.label Survivor0_Free" - echo "Survivor1_Used.label Survivor1_Used" - echo "Survivor1_Free.label Survivor1_Free" - echo "Old_Used.label Old_Used" - echo "Old_Free.label Old_Free" - echo "Permanent_Used.label Permanent_Used" - echo "Permanent_Free.label Permanent_Free" - echo "Eden_Used.draw AREA" - echo "Eden_Free.draw STACK" - echo "Survivor0_Used.draw STACK" - echo "Survivor0_Free.draw STACK" - echo "Survivor1_Used.draw STACK" - echo "Survivor1_Free.draw STACK" - echo "Old_Used.draw STACK" - echo "Old_Free.draw STACK" - echo "Permanent_Used.draw STACK" - echo "Permanent_Free.draw STACK" +print_stats() { + local pid_num="$1" + local awk_script + if [ "${JDK_TYPE}" == "bea" ]; then + # shellcheck disable=SC2016 + awk_script='{ + HeapSize = $1; + NurserySize = $2; + UsedHeapSize = $3; + print "NurserySize.value " NurserySize * 1024; + print "HeapSize.value " HeapSize * 1024; + print "UsedHeapSize.value " UsedHeapSize * 1024; }' + else + # shellcheck disable=SC2016 + awk_script='{ + S0C = $1; + S1C = $2; + S0U = $3; + S1U = $4; + EC = $5; + EU = $6; + OC = $7; + OU = $8; + PC = $9; + PU = $10; + S0F = S0C - S0U; + S1F = S1C - S1U; + EF = EC - EU; + OF = OC - OU; + PF = PC - PU; + print "Eden_Used.value " EU * 1024; + print "Eden_Free.value " EF * 1024; + print "Survivor0_Used.value " S0U * 1024; + print "Survivor0_Free.value " S0F * 1024; + print "Survivor1_Used.value " S1U * 1024; + print "Survivor1_Free.value " S1F * 1024; + print "Old_Used.value " OU * 1024; + print "Old_Free.value " OF * 1024; + print "Permanent_Used.value " PU * 1024; + print "Permanent_Free.value " PF * 1024; }' + fi + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -config_bea_jdk() -{ - config_common - - echo "NurserySize.label NurserySize" - echo "HeapSize.label HeapSize" - echo "UsedHeapSize.label UsedHeapSize" - echo "NurserySize.draw AREA" - echo "HeapSize.draw STACK" - echo "UsedHeapSize.draw STACK" -} - -print_sun_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - S0C = $1; \ - S1C = $2; \ - S0U = $3; \ - S1U = $4; \ - EC = $5; \ - EU = $6; \ - OC = $7; \ - OU = $8; \ - PC = $9; \ - PU = $10; \ - \ - S0F = S0C - S0U; \ - S1F = S1C - S1U; \ - EF = EC - EU; \ - OF = OC - OU; \ - PF = PC - PU; \ - \ - print "Eden_Used.value " EU * 1024; \ - print "Eden_Free.value " EF * 1024; \ - print "Survivor0_Used.value " S0U * 1024; \ - print "Survivor0_Free.value " S0F * 1024; \ - print "Survivor1_Used.value " S1U * 1024; \ - print "Survivor1_Free.value " S1F * 1024; \ - print "Old_Used.value " OU * 1024; \ - print "Old_Free.value " OF * 1024; \ - print "Permanent_Used.value " PU * 1024; \ - print "Permanent_Free.value " PF * 1024; \ -}' -} - -print_bea_stats() -{ -${JAVA_HOME}/bin/jstat -gc ${PidNum} | tail -1 | awk \ -'{\ - HeapSize = $1; \ - NurserySize = $2; \ - UsedHeapSize = $3; \ - YC = $4; \ - OC = $5; \ - YCTime = $6; \ - OCTime = $7; \ - GCTime = $8; \ - YCPauseTime = $9; \ - OCPauseTime = $10; \ - PauseTime = $11; \ - Finalizers = $12; \ - \ - print "NurserySize.value " NurserySize * 1024; \ - print "HeapSize.value " UsedHeapSize * 1024; \ - print "UsedHeapSize.value " UsedHeapSize * 1024; \ -}' -} # # common for all argument @@ -204,25 +184,8 @@ if [ "$1" = "autoconf" ]; then fi -# -# config -# if [ "$1" = "config" ]; then - if [ "${JDK_TYPE}" == "bea" ]; then - config_bea_jdk - else - config_sun_jdk - fi - exit 0 + print_config fi -# -# Main -# -PidNum=`cat ${pidfilepath}` - -if [ "${JDK_TYPE}" == "bea" ]; then - print_bea_stats -else - print_sun_stats -fi +print_stats "$(cat "$pidfilepath")" From f5e6d7ea173c2b3b66cba661e392445741c80d7a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 04:09:58 +0200 Subject: [PATCH 310/695] Plugins jstat__*: add support for Java 1.8 --- plugins/jvm/jstat__gccount | 53 +++++++++++------------- plugins/jvm/jstat__gctime | 61 +++++++++++++--------------- plugins/jvm/jstat__heap | 82 ++++++++++++++++++++++---------------- 3 files changed, 98 insertions(+), 98 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 8724cb70..64ef5475 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -22,9 +22,10 @@ # # Target: # -# Target Java Virtual Machine to monitor are: -# Sun JDK 5.0 (http://java.sun.com/javase/) (default) -# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) +# Target Java Virtual Machine to monitor are: +# Sun JDK 5.0 (http://java.sun.com/javase/) +# Sun JDK 8.0 (http://java.sun.com/javase/) +# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: # @@ -46,28 +47,21 @@ JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME -# -# Functions -# -chk_jdk() -{ - isJRockit=`${JAVA_HOME}/bin/java -version 2>&1 | egrep -i 'jrockit'` - if [ -n "${isJRockit}" ]; then - JDK_TYPE="bea" - else - JDK_TYPE="sun" + +get_jdk_type() { + local version + if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then + echo "bea" + else + version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) + if echo "$version" | grep -q '^1\.5\.'; then + echo "sun15" + else + echo "sun" + fi fi } -chk_version() -{ - Version=`${JAVA_HOME}/bin/java -version 2>&1 | egrep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1` - if [ "${Version}" != "1.5.0" ]; then - return 1 - else - return 0 - fi -} print_config() { echo 'graph_title GC Count' $graphtitle @@ -95,14 +89,16 @@ print_stats() { if [ "${JDK_TYPE}" == "bea" ]; then # shellcheck disable=SC2016 awk_script='{ YC = $4; OC = $5; print "Young_GC.value " YGC; print "Old_GC.value " FGC; }' - else + elif [ "${JDK_TYPE}" = "sun15" ]; then # shellcheck disable=SC2016 awk_script='{ YGC = $11; FGC = $13; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' + else + # shellcheck disable=SC2016 + awk_script='{ YGC = $13; FGC = $15; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' fi "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -chk_jdk # # autoconf @@ -114,12 +110,6 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - chk_version - if [ $? != 0 ]; then - echo "no (Java version is invalid)" - exit 1 - fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 @@ -130,6 +120,9 @@ if [ "$1" = "autoconf" ]; then fi +JDK_TYPE=$(get_jdk_type) + + if [ "$1" = "config" ]; then print_config exit 0 diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index e3252b7d..662322b2 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -22,9 +22,10 @@ # # Target: # -# Target Java Virtual Machine to monitor are: -# Sun JDK 5.0 (http://java.sun.com/javase/) (default) -# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) +# Target Java Virtual Machine to monitor are: +# Sun JDK 5.0 (http://java.sun.com/javase/) +# Sun JDK 8.0 (http://java.sun.com/javase/) +# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: # @@ -46,28 +47,21 @@ JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME -# -# Functions -# -chk_jdk() -{ - isJRockit=`${JAVA_HOME}/bin/java -version 2>&1 | egrep -i 'jrockit'` - if [ -n "${isJRockit}" ]; then - JDK_TYPE="bea" - else - JDK_TYPE="sun" + +get_jdk_type() { + local version + if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then + echo "bea" + else + version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) + if echo "$version" | grep -q '^1\.5\.'; then + echo "sun15" + else + echo "sun" + fi fi } -chk_version() -{ - Version=`${JAVA_HOME}/bin/java -version 2>&1 | egrep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1` - if [ "${Version}" != "1.5.0" ]; then - return 1 - else - return 0 - fi -} print_config() { echo 'graph_title GC Time' $graphtitle @@ -107,23 +101,25 @@ print_stats() { print "Old_GC.value " OCTime; print "Young_Pause.value " YCPauseTime; print "Old_Pause.value " OCPauseTime; }' - else + elif [ "${JDK_TYPE}" = "sun15" ]; then # shellcheck disable=SC2016 awk_script='{ YGCT = $12; FGCT = $14; print "Young_GC.value " YGCT; print "Full_GC.value " FGCT; }' + else + # shellcheck disable=SC2016 + awk_script='{ + YGCT = $14; + FGCT = $16; + print "Young_GC.value " YGCT; + print "Full_GC.value " FGCT; }' fi "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -# -# common for all argument -# -chk_jdk - # # autoconf # @@ -134,12 +130,6 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - chk_version - if [ $? != 0 ]; then - echo "no (Java version is invalid)" - exit 1 - fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 @@ -150,6 +140,9 @@ if [ "$1" = "autoconf" ]; then fi +JDK_TYPE=$(get_jdk_type) + + if [ "$1" = "config" ]; then print_config fi diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index 1ec26c07..fbdfdfb1 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -22,9 +22,10 @@ # # Target: # -# Target Java Virtual Machine to monitor are: -# Sun JDK 5.0 (http://java.sun.com/javase/) (default) -# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) +# Target Java Virtual Machine to monitor are: +# Sun JDK 5.0 (http://java.sun.com/javase/) +# Sun JDK 8.0 (http://java.sun.com/javase/) +# BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: # @@ -46,28 +47,21 @@ JAVA_HOME=${javahome:-$default_java_home} export JAVA_HOME -# -# Functions -# -chk_jdk() -{ - isJRockit=`${JAVA_HOME}/bin/java -version 2>&1 | egrep -i 'jrockit'` - if [ -n "${isJRockit}" ]; then - JDK_TYPE="bea" - else - JDK_TYPE="sun" + +get_jdk_type() { + local version + if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then + echo "bea" + else + version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) + if echo "$version" | grep -q '^1\.5\.'; then + echo "sun15" + else + echo "sun" + fi fi } -chk_version() -{ - Version=`${JAVA_HOME}/bin/java -version 2>&1 | egrep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1` - if [ "${Version}" != "1.5.0" ]; then - return 1 - else - return 0 - fi -} print_config() { echo "graph_title Heap Usage" $graphtitle @@ -120,7 +114,7 @@ print_stats() { print "NurserySize.value " NurserySize * 1024; print "HeapSize.value " HeapSize * 1024; print "UsedHeapSize.value " UsedHeapSize * 1024; }' - else + elif [ "${JDK_TYPE}" = "sun15" ]; then # shellcheck disable=SC2016 awk_script='{ S0C = $1; @@ -148,16 +142,39 @@ print_stats() { print "Old_Free.value " OF * 1024; print "Permanent_Used.value " PU * 1024; print "Permanent_Free.value " PF * 1024; }' + else + # shellcheck disable=SC2016 + awk_script='{ + S0C = $1; + S1C = $2; + S0U = $3; + S1U = $4; + EC = $5; + EU = $6; + OC = $7; + OU = $8; + MC = $9; + MU = $10; + S0F = S0C - S0U; + S1F = S1C - S1U; + EF = EC - EU; + OF = OC - OU; + MF = MC - MU; + print "Eden_Used.value " EU * 1024; + print "Eden_Free.value " EF * 1024; + print "Survivor0_Used.value " S0U * 1024; + print "Survivor0_Free.value " S0F * 1024; + print "Survivor1_Used.value " S1U * 1024; + print "Survivor1_Free.value " S1F * 1024; + print "Old_Used.value " OU * 1024; + print "Old_Free.value " OF * 1024; + print "Permanent_Used.value " PU * 1024; + print "Permanent_Free.value " MF * 1024; }' fi "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" } -# -# common for all argument -# -chk_jdk - # # autoconf # @@ -168,12 +185,6 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - chk_version - if [ $? != 0 ]; then - echo "no (Java version is invalid)" - exit 1 - fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 @@ -184,6 +195,9 @@ if [ "$1" = "autoconf" ]; then fi +JDK_TYPE=$(get_jdk_type) + + if [ "$1" = "config" ]; then print_config fi From 67f978378072f823726a0b24d448c4de937cf835 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 04:14:33 +0200 Subject: [PATCH 311/695] Plugins jstat__*: fix shellcheck issues; switch from bash to sh --- plugins/jvm/jstat__gccount | 10 +++++----- plugins/jvm/jstat__gctime | 10 +++++----- plugins/jvm/jstat__heap | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 64ef5475..934713ec 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Plugin for monitor JVM activity - GC Count - # @@ -64,7 +64,7 @@ get_jdk_type() { print_config() { - echo 'graph_title GC Count' $graphtitle + echo "graph_title GC Count $graphtitle" echo 'graph_args -l 0' echo 'graph_vlabel GC Count(times)' echo 'graph_total total' @@ -73,7 +73,7 @@ print_config() { echo 'Young_GC.label Young_GC' echo 'Young_GC.min 0' - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then echo 'Old_GC.label Old_GC' echo 'Old_GC.min 0' else @@ -86,7 +86,7 @@ print_config() { print_stats() { local pid_num="$1" local awk_script - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then # shellcheck disable=SC2016 awk_script='{ YC = $4; OC = $5; print "Young_GC.value " YGC; print "Old_GC.value " FGC; }' elif [ "${JDK_TYPE}" = "sun15" ]; then @@ -110,7 +110,7 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then + if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 fi diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index 662322b2..3cd69f17 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Plugin for monitor JVM activity - GC Time - # @@ -64,7 +64,7 @@ get_jdk_type() { print_config() { - echo 'graph_title GC Time' $graphtitle + echo "graph_title GC Time $graphtitle" echo 'graph_args -l 0' echo 'graph_vlabel GC Time(sec)' echo 'graph_total total' @@ -73,7 +73,7 @@ print_config() { echo 'Young_GC.label Young_GC' echo 'Young_GC.min 0' - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then echo 'Old_GC.label Old_GC' echo 'Old_GC.min 0' echo 'Young_Pause.label Young_GC Pause' @@ -90,7 +90,7 @@ print_config() { print_stats() { local pid_num="$1" local awk_script - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then # shellcheck disable=SC2016 awk_script='{ YCTime = $6; @@ -130,7 +130,7 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then + if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 fi diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index fbdfdfb1..5a2084e2 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Plugin for monitor JVM activity - Heap Usage - # @@ -64,13 +64,13 @@ get_jdk_type() { print_config() { - echo "graph_title Heap Usage" $graphtitle + echo "graph_title Heap Usage $graphtitle" echo "graph_args --base 1024 -l 0" echo "graph_vlabel Heap Usage(Bytes)" echo "graph_info Heap Usage" echo "graph_category virtualization" - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then echo "NurserySize.label NurserySize" echo "HeapSize.label HeapSize" echo "UsedHeapSize.label UsedHeapSize" @@ -105,7 +105,7 @@ print_config() { print_stats() { local pid_num="$1" local awk_script - if [ "${JDK_TYPE}" == "bea" ]; then + if [ "${JDK_TYPE}" = "bea" ]; then # shellcheck disable=SC2016 awk_script='{ HeapSize = $1; @@ -185,7 +185,7 @@ if [ "$1" = "autoconf" ]; then exit 1 fi - if [ ! -f "${pidfilepath}" -o ! -r "${pidfilepath}" ]; then + if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" exit 1 fi From 0ddc7e2fec48ed30ed0e1eb9de8e4af3fb7641c4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 4 Jul 2018 20:29:52 +0200 Subject: [PATCH 312/695] Plugins jstat__*: add format examples --- plugins/jvm/jstat__gccount | 3 +++ plugins/jvm/jstat__gctime | 3 +++ plugins/jvm/jstat__heap | 3 +++ 3 files changed, 9 insertions(+) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index 934713ec..a04eb638 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -93,6 +93,9 @@ print_stats() { # shellcheck disable=SC2016 awk_script='{ YGC = $11; FGC = $13; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' else + # example output of jstat for Java 1.8: + # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT + # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 # shellcheck disable=SC2016 awk_script='{ YGC = $13; FGC = $15; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' fi diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index 3cd69f17..7326b46b 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -109,6 +109,9 @@ print_stats() { print "Young_GC.value " YGCT; print "Full_GC.value " FGCT; }' else + # example output of jstat for Java 1.8: + # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT + # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 # shellcheck disable=SC2016 awk_script='{ YGCT = $14; diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index 5a2084e2..edf6f411 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -143,6 +143,9 @@ print_stats() { print "Permanent_Used.value " PU * 1024; print "Permanent_Free.value " PF * 1024; }' else + # example output of jstat for Java 1.8: + # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT + # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 # shellcheck disable=SC2016 awk_script='{ S0C = $1; From 0a7b1c8186cc27e6dc8b8c79d647a9b85345bddf Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 30 Aug 2018 23:34:23 +0200 Subject: [PATCH 313/695] bbox: upgrade bbox version. The UI isn't compatible anymore. --- plugins/router/bbox | 140 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 plugins/router/bbox diff --git a/plugins/router/bbox b/plugins/router/bbox new file mode 100644 index 00000000..523ced40 --- /dev/null +++ b/plugins/router/bbox @@ -0,0 +1,140 @@ +#! /bin/sh +# Parse bbox information +# (c) 2018 - GPLv2 - Steve Schnepp +# +# Configuration is done via ENV vars, here is the default : +# +# [bbox] +# env.IP 192.168.1.254 + + +# fail on error +set -e + +IP=${IP:-"192.168.1.254"} + +if [ "$1" = "config" ] +then + echo multigraph bbox_adsl_bw + echo graph_title BBox Adsl Bandwidth + echo graph_category network + echo up.label UpStream + echo down.label DownStream + + echo multigraph bbox_adsl_att + echo graph_title BBox Adsl Attenuation + echo graph_category network + echo up.label UpStream + echo down.label DownStream + + echo multigraph bbox_adsl_snr + echo graph_title BBox Adsl SignalNoise Ratio + echo graph_category network + echo up.label UpStream + echo down.label DownStream + + echo multigraph bbox_adsl_pkt + echo graph_title BBox Adsl Packets + echo graph_category network + echo graph_order up down up_f down_f up_c down_c up_u down_u + echo graph_vlabel packets/\${graph_period} + echo graph_args --base 1000 --logarithmic --lower-limit 0.001 + echo up.label UpStream + echo down.label DownStream + echo up_f.label UpStream "(FEC)" + echo down_f.label DownStream "(FEC)" + echo up_c.label UpStream "(CRC)" + echo down_c.label DownStream "(CEC)" + echo up_u.label UpStream "(HEC)" + echo down_u.label DownStream "(HEC)" + echo up.type DERIVE + echo down.type DERIVE + echo up_f.type DERIVE + echo down_f.type DERIVE + echo up_c.type DERIVE + echo down_c.type DERIVE + echo up_u.type DERIVE + echo down_u.type DERIVE + echo up.min 0 + echo down.min 0 + echo up_f.min 0 + echo down_f.min 0 + echo up_c.min 0 + echo down_c.min 0 + echo up_u.min 0 + echo down_u.min 0 + + echo multigraph bbox_adsl_bytes + echo graph_title BBox Adsl Usage + echo graph_category network + echo graph_order up down + echo graph_vlabel bytes/\${graph_period} + echo graph_args --base 1024 --lower-limit 0 + echo up.label UpStream + echo up.type DERIVE + echo down.label DownStream + echo down.type DERIVE + echo up.min 0 + echo down.min 0 + + echo multigraph bbox_adsl_uptime + echo graph_title BBox Adsl Uptime + echo graph_category network + echo graph_vlabel days + echo uptime.label Uptime + echo uptime.cdef uptime,3600,/,24/ + + exit 0 +fi + +TMPFILE=$(mktemp -d) +trap 'rm -Rf $TMPFILE' EXIT + +cd $TMPFILE + +curl -s http://$IP/api/v1/wan/ip > "ip.json" & +curl -s http://$IP/api/v1/wan/ip/stats > "ip-stats.json" & +curl -s http://$IP/api/v1/wan/xdsl > "xdsl.json" & +curl -s http://$IP/api/v1/wan/xdsl/stats > "xdsl-stats.json" & + +wait + +if [ "$MUNIN_DEBUG" = 1 ] +then + for i in * + do + echo "========= $i ========" + cat "$i" + done +fi + +echo multigraph bbox_adsl_bw +echo up.value $(jq ".[].wan.xdsl.up.bitrates" < xdsl.json) +echo down.value $(jq ".[].wan.xdsl.down.bitrates" < xdsl.json) + +echo multigraph bbox_adsl_att +echo up.value $(jq ".[].wan.xdsl.up.attenuation" < xdsl.json) +echo down.value $(jq ".[].wan.xdsl.down.attenuation" < xdsl.json) + +echo multigraph bbox_adsl_snr +echo up.value $(jq ".[].wan.xdsl.up.noise" < xdsl.json) +echo down.value $(jq ".[].wan.xdsl.down.noise" < xdsl.json) + +echo multigraph bbox_adsl_pkt +echo up.value $(jq ".[].wan.ip.stats.tx.packets" < ip-stats.json) +echo down.value $(jq ".[].wan.ip.stats.rx.packets" < ip-stats.json) +echo up_f.value $(jq ".[].wan.xdsl.stats.remote_fec" < xdsl-stats.json) +echo down_f.value $(jq ".[].wan.xdsl.stats.local_fec" < xdsl-stats.json) +echo up_c.value $(jq ".[].wan.xdsl.stats.remote_hec" < xdsl-stats.json) +echo down_c.value $(jq ".[].wan.xdsl.stats.local_hec" < xdsl-stats.json) +echo up_u.value $(jq ".[].wan.xdsl.stats.remote_crc" < xdsl-stats.json) +echo down_u.value $(jq ".[].wan.xdsl.stats.local_crc" < xdsl-stats.json) + +echo multigraph bbox_adsl_bytes +echo up.value $(jq ".[].wan.ip.stats.tx.bytes" < ip-stats.json | tr -d '"') +echo down.value $(jq ".[].wan.ip.stats.rx.bytes" < ip-stats.json | tr -d '"') + +echo multigraph bbox_adsl_uptime +echo uptime.value $(jq ".[].wan.xdsl.showtime" < xdsl.json) + +exit 0 From f3347821e5b3c8d1ac3f4ee51625204234ced407 Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Mon, 10 Sep 2018 20:03:22 +0200 Subject: [PATCH 314/695] network.tc*: add the ability to detect systemd based network device names --- plugins/network/tc_ | 2 +- plugins/network/tc_drops_ | 4 +--- plugins/network/tc_packets_ | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 9933edd2..d08e24b0 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -61,7 +61,7 @@ case "$1" in suggest) if [ -r /proc/net/dev ]; then awk ' - /^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:/ { + /^ *(eth|tap|bond|wlan|ath|ra|sw|eno|ens|enp|wlp|wl)[0-9]*/ { split($0, a, /: */); gsub(/^ +/,"",a[1]); if (($2 > 0) || ($10 > 0)) print a[1]; }' /proc/net/dev diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index 8906080c..9f9925a3 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -32,12 +32,10 @@ case $1 in suggest) if [ -r /proc/net/dev ]; then awk ' - /^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:/ { + /^ *(eth|tap|bond|wlan|ath|ra|sw|eno|ens|enp|wlp|wl)[0-9]*/ { 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 ;; diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 0c5cadc2..b272abdb 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -32,12 +32,10 @@ case $1 in suggest) if [ -r /proc/net/dev ]; then awk ' - /^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:/ { + /^ *(eth|tap|bond|wlan|ath|ra|sw|eno|ens|enp|wlp|wl)[0-9]*/ { 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 ;; From e4cd049b01376089f1f3d06994c14bb2e9130ae8 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 16 Sep 2018 04:01:57 +0200 Subject: [PATCH 315/695] Fix trivial instances of wrong autoconf exitcodes --- plugins/accounting/accounting_ | 12 +++++------- plugins/amavis/amavis-debian | 3 +-- plugins/amavis/amavis_ | 3 +-- plugins/amavis/amavis_awk | 3 +-- plugins/amr/amr | 3 +-- plugins/apache/apache_activity | 4 ++-- plugins/apache/apache_users | 5 +---- plugins/apache/apache_watch_ | 4 ++-- plugins/asterisk/asterisk_inuse | 4 +--- plugins/battery/acpi-battery | 3 +-- plugins/bigbrother/b3error_ | 3 +-- plugins/bind/bind_ | 4 ++-- plugins/bird/bird | 3 +-- plugins/boinc/boinc_processes | 2 +- plugins/condor/condor_activity_ | 2 +- plugins/condor/condor_ops_ | 2 +- plugins/condor/condor_queue_ | 2 +- plugins/condor/condor_states_ | 2 +- plugins/disk/du | 3 +-- plugins/disk/e2 | 3 +-- plugins/disk/hpasmcli2_ | 4 +--- plugins/disk/linux_diskstat_ | 3 +-- plugins/disk/md_iostat_ | 6 +++--- plugins/disk/megaraid-controller-information | 3 +-- plugins/disk/raid-mismatch-count | 4 +--- plugins/dkim/dkimproxy_mails | 3 +-- plugins/dovecot/dovecot1 | 6 +++--- plugins/fan/ibmfan | 3 +-- plugins/fax/faxstat | 3 +-- plugins/ftp/pure-ftpd-bw | 4 ++-- plugins/ftp/pure-ftpd-logs | 4 ++-- plugins/ftp/vsftpd | 2 +- plugins/ftp/vsftpd-rel | 2 +- plugins/http/mongrel_memory | 2 +- plugins/http/mongrel_process_memory | 2 +- plugins/icecast/icecast2 | 6 +++--- plugins/ip6/ip6_ | 4 +--- plugins/memory/proc_memory_status | 3 +-- plugins/mogilefs/mogilefsd_activity | 4 ++-- plugins/mogilefs/mogilefsd_queries | 4 ++-- plugins/munin/healthcheck_log | 4 ++-- plugins/munin/healthcheck_process | 4 ++-- plugins/munin/healthcheck_url | 4 ++-- plugins/munin/update | 8 ++++---- plugins/mysql/mysql_size_ondisk | 3 +-- plugins/nagios/nagiosstatus | 3 +-- plugins/network/denyhosts | 3 +-- plugins/network/dns/dnsresponse_ | 3 +-- plugins/network/ethtool_ | 3 +-- plugins/network/fms | 3 +-- plugins/network/fms_apps | 3 +-- plugins/network/fms_apps_rate | 3 +-- plugins/network/fwbuilder_ | 4 +--- plugins/network/hostsdeny | 3 +-- plugins/network/if_uptime | 2 -- plugins/network/ip_forward_ | 4 +--- plugins/network/ipfwnat_ | 4 ++-- plugins/network/ipt_accounting_ | 4 +--- plugins/network/ipt_basic_ | 4 +--- plugins/network/ldap_connections | 7 +++---- plugins/network/mtr100_ | 4 +--- plugins/network/proc_netstat | 3 +-- plugins/network/qos_ | 4 ++-- plugins/network/smtp_hello_ | 3 +-- plugins/network/tc_ | 3 +-- plugins/network/tc_drops_ | 3 +-- plugins/network/tc_packets_ | 3 +-- plugins/network/zenus_ | 4 ++-- plugins/nfs/nfsv4 | 3 +-- plugins/nginx/nginx-combined | 4 ++-- plugins/nginx/nginx_connection_request | 4 ++-- plugins/ntp/ntp_peers | 4 +--- plugins/ntp/ntp_peers_ipv6 | 4 +--- plugins/ntp/ntp_queries | 4 +--- plugins/openvz/vpsmem | 3 +-- plugins/oracle/oracle__connections | 4 ++-- plugins/oracle/oracle__database_hitratio | 4 ++-- plugins/oracle/oracle__locks | 4 ++-- plugins/oracle/oracle__tablespace_usage | 4 ++-- plugins/other/earthquakes | 3 +-- plugins/other/opentracker_ | 3 +-- plugins/pf/pf | 10 +++++----- plugins/pf/pf_bytes | 4 ++-- plugins/pf/pf_openbsd | 8 ++++---- plugins/pf/pf_packets | 8 ++++---- plugins/pf/pf_states | 8 ++++---- plugins/php/php_sessions | 3 +-- plugins/php/php_time_execution | 3 +-- plugins/ping/multiping | 3 +-- plugins/ping/pinger | 3 +-- plugins/postfix/postfix-policyd | 3 +-- plugins/postfix/postfix_filtered | 3 +-- plugins/postfix/postfix_filtered_awk | 3 +-- plugins/postfix/postfix_mailfiltered | 3 +-- plugins/postfix/postfix_mailstats | 2 +- plugins/postfix/postfix_mailvolume_multi | 1 - plugins/postfix/postgrey | 3 +-- plugins/postfix/postgrey-new | 3 +-- plugins/postgresql/pgbouncer_ | 3 +-- plugins/postgresql/postgres_block_read_ | 4 ++-- plugins/postgresql/postgres_space_ | 4 ++-- plugins/power/apcupsd_ww | 3 +-- plugins/power/nut | 3 +-- plugins/qmail/qmailsend | 3 +-- plugins/qmail/qmailsend_plesk | 3 +-- plugins/qmail/spamdyke | 3 +-- plugins/radiator/radiator_acct_lag | 3 +-- plugins/radiator/radiator_acct_ppm | 3 +-- plugins/radiator/radiator_auth_lag | 3 +-- plugins/radiator/radiator_auth_ppm | 3 +-- plugins/router/freeboxuptime | 4 +--- plugins/router/tg585v7__ | 4 ++-- plugins/rsync/rsyncd_bytes | 3 +-- plugins/rsync/rsyncd_count | 3 +-- plugins/s3/s3_items | 7 +++---- plugins/s3/s3_storage | 10 +++------- plugins/sar/iostat-cputps-average | 4 +--- plugins/security/forefront_ | 6 +++--- plugins/sensors/mbmon | 3 +-- plugins/sensors/nvclock | 3 +-- plugins/sge/sge_queue_ | 3 +-- plugins/sge/sge_queue_xml_ | 3 +-- plugins/smstools/smstools_ | 3 +-- plugins/snmp/snmp_room_alert_ | 2 +- plugins/solaris/solaris-memstat | 3 +-- plugins/sourceds/srcds_cpu | 6 +++--- plugins/sourceds/srcds_fps | 6 +++--- plugins/sourceds/srcds_inout | 6 +++--- plugins/sourceds/srcds_players | 6 +++--- plugins/sourceds/srcds_uptime | 6 +++--- plugins/squid/squid_times | 3 +-- plugins/ssh/openssh-denyhosts | 3 +-- plugins/ssh/sshd_invalid_countries_ruby | 2 +- plugins/system/blockhosts | 3 +-- plugins/tcp/tcp-states | 4 +--- plugins/thin/thin_memory | 2 +- plugins/thin/thin_threads | 2 +- plugins/thin/thins_peak_memory | 2 +- plugins/tor/tor-bandwidth-usage | 4 ++-- plugins/tor/tor_routers | 4 ++-- plugins/ultramonkey/ultramonkey-l7 | 5 ++--- plugins/uucp/uustat | 3 +-- plugins/vpn/openvpn_multi | 3 +-- plugins/weather/humidity-sensor | 3 +-- plugins/weather/temperature_ | 3 +-- plugins/weather/temperatures | 3 +-- plugins/xbnbt/xbnbt_peers | 4 +--- plugins/xen/xen | 6 +++--- plugins/xen/xen-cpu | 3 +-- plugins/xen/xen_cpu_v2 | 3 +-- plugins/yum/yum_activity | 3 +-- plugins/zfs/zfsarcstats-counters | 8 ++++---- 152 files changed, 225 insertions(+), 344 deletions(-) diff --git a/plugins/accounting/accounting_ b/plugins/accounting/accounting_ index 7628d3a2..526b69f9 100755 --- a/plugins/accounting/accounting_ +++ b/plugins/accounting/accounting_ @@ -116,17 +116,15 @@ fi if [ "$1" == "autoconf" ]; then if [ -r /proc/net/dev ]; then $IPTABLES -L INPUT -v -n -x -w >/dev/null 2>/dev/null - if [ $? -gt 0 ]; then - echo "no (could not run iptables as user `whoami`)" - exit 1 - else - echo yes - exit 0 + if [ $? -gt 0 ]; then + echo "no (could not run iptables as user `whoami`)" + else + echo yes fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/amavis/amavis-debian b/plugins/amavis/amavis-debian index b80e527c..47d3c7b1 100755 --- a/plugins/amavis/amavis-debian +++ b/plugins/amavis/amavis-debian @@ -37,11 +37,10 @@ mktempfile () { if [ "$1" = "autoconf" ]; then if [ -f "${AMAVIS_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" -a -n "${BC}" -a -x "${BC}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/amavis/amavis_ b/plugins/amavis/amavis_ index c27d9cb2..2287f4a4 100755 --- a/plugins/amavis/amavis_ +++ b/plugins/amavis/amavis_ @@ -45,11 +45,10 @@ my($db_home) = # DB databases directory if ($ARGV[0] and $ARGV[0] eq "autoconf") { if (-x "/usr/sbin/amavisd-agent") { print "yes\n"; - exit 0; } else { print "no (/usr/sbin/amavisd-agent not found or not executable)\n"; - exit 1; } + exit 0; } elsif ($ARGV[0] and $ARGV[0] eq "suggest") { print "time\n"; print "cache\n"; diff --git a/plugins/amavis/amavis_awk b/plugins/amavis/amavis_awk index 332a2b60..04b6e279 100755 --- a/plugins/amavis/amavis_awk +++ b/plugins/amavis/amavis_awk @@ -23,11 +23,10 @@ STATEFILE=$MUNIN_PLUGSTATE/amavis.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/amr/amr b/plugins/amr/amr index 75b981b4..e6ae91e9 100755 --- a/plugins/amr/amr +++ b/plugins/amr/amr @@ -54,14 +54,13 @@ sub autoconf if (-d $LOGDIR) { if (-f $logfile) { print "yes\n"; - exit 0; } else { print "no (logfile not found)\n"; } } else { print "no (could not find logdir)\n"; } - exit 1; + exit 0; } sub config diff --git a/plugins/apache/apache_activity b/plugins/apache/apache_activity index 777125fb..a1dfc3fa 100755 --- a/plugins/apache/apache_activity +++ b/plugins/apache/apache_activity @@ -42,7 +42,7 @@ my %chars = ( if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { if ($ret) { print "no ($ret)\n"; - exit 1; + exit 0; } my $ua = LWP::UserAgent->new(timeout => 30); my @badports; @@ -55,7 +55,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { if (@badports) { print "no (no apache server-status on ports @badports)\n"; - exit 1; + exit 0; } else { print "yes\n"; exit 0; diff --git a/plugins/apache/apache_users b/plugins/apache/apache_users index bc178f41..257e8fa1 100755 --- a/plugins/apache/apache_users +++ b/plugins/apache/apache_users @@ -23,16 +23,13 @@ then if ! ls $ACCESSLOG > /dev/null then echo "no (could not find apache access log \"$ACCESSLOG\")" - exit 1 elif ! ls $DIRECTORY > /dev/null then echo "no (could not find munin plugins directory \"$DIRECTORY\")" - exit 2 - else echo "yes" - exit 0 fi + exit 0 fi # ######################################################################################### INIT diff --git a/plugins/apache/apache_watch_ b/plugins/apache/apache_watch_ index 88c2fd3d..ae74ca55 100755 --- a/plugins/apache/apache_watch_ +++ b/plugins/apache/apache_watch_ @@ -39,7 +39,7 @@ my $type = "throughput"; if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { if ($ret) { print "no ($ret)\n"; - exit 1; + exit 0; } my $ua = LWP::UserAgent->new (timeout => 30); my @badports; @@ -52,7 +52,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { if (@badports) { print "no (no mod_watch exists on ports @badports)\n"; - exit 1; + exit 0; } else { print "yes\n"; exit 0; diff --git a/plugins/asterisk/asterisk_inuse b/plugins/asterisk/asterisk_inuse index 837890e1..aaacbb20 100755 --- a/plugins/asterisk/asterisk_inuse +++ b/plugins/asterisk/asterisk_inuse @@ -11,15 +11,13 @@ if [ "$1" = "autoconf" ]; then asterisk -rx 'sip show inuse' > /dev/null if [ $? = "0" ]; then echo yes - exit 0 else echo no - exit 1 fi else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/battery/acpi-battery b/plugins/battery/acpi-battery index 147ee3fd..4ea36752 100755 --- a/plugins/battery/acpi-battery +++ b/plugins/battery/acpi-battery @@ -32,11 +32,10 @@ if [ "$1" = "autoconf" ]; then if grep -q 'present.*yes' /proc/acpi/battery/*/info > /dev/null 2>&1; then echo yes - exit 0 else echo "no (battery not detected)" - exit 1 fi + exit 0 fi cd /proc/acpi/battery diff --git a/plugins/bigbrother/b3error_ b/plugins/bigbrother/b3error_ index 853bb544..23b5dab1 100755 --- a/plugins/bigbrother/b3error_ +++ b/plugins/bigbrother/b3error_ @@ -29,7 +29,6 @@ mktempfile () { if [ "$1" = "autoconf" ]; then if [ -n "${B3NAME}" -a -f "${logfile}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ]; then echo yes - exit 0 else echo -n "no" if [ ! -n "${B3NAME}" ]; then @@ -43,8 +42,8 @@ if [ "$1" = "autoconf" ]; then elif [ ! -x "${LOGTAIL}" ]; then echo " (cannot execute ${LOGTAIL})" fi - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/bind/bind_ b/plugins/bind/bind_ index 92fd8759..6f7f5098 100755 --- a/plugins/bind/bind_ +++ b/plugins/bind/bind_ @@ -98,11 +98,11 @@ if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { if ( defined($ARGV[0]) && $ARGV[0] eq "autoconf" ) { if (! -f $stat_file) { printf "Unable to file bind stat file on %s",$stat_file; - exit 1; + exit 0; } if (! -f $rndc) { printf "Unable to file rndc tool (configured : %s)",$rndc; - exit 1; + exit 0; } exit 0; } diff --git a/plugins/bird/bird b/plugins/bird/bird index b9c37926..b6aa4e15 100755 --- a/plugins/bird/bird +++ b/plugins/bird/bird @@ -111,11 +111,10 @@ sub get_stats { sub autoconf { if (-S $socket) { say 'yes'; - exit 0; } else { say 'no'; - exit 1; } + exit 0; } sub config { diff --git a/plugins/boinc/boinc_processes b/plugins/boinc/boinc_processes index d0e05f74..0d06d1b7 100755 --- a/plugins/boinc/boinc_processes +++ b/plugins/boinc/boinc_processes @@ -63,7 +63,7 @@ sub autoconf { } } print "no\n"; - exit 1; + exit 0; } sub config { diff --git a/plugins/condor/condor_activity_ b/plugins/condor/condor_activity_ index adc6a579..0f05bc49 100755 --- a/plugins/condor/condor_activity_ +++ b/plugins/condor/condor_activity_ @@ -52,7 +52,7 @@ fi if [ "$1" = "autoconf" ]; then echo "no" - exit 1 + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/condor/condor_ops_ b/plugins/condor/condor_ops_ index 7321c4c5..3af67087 100755 --- a/plugins/condor/condor_ops_ +++ b/plugins/condor/condor_ops_ @@ -53,7 +53,7 @@ fi if [ "$1" = "autoconf" ]; then echo "no" - exit 1 + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/condor/condor_queue_ b/plugins/condor/condor_queue_ index 952953e1..53717bed 100755 --- a/plugins/condor/condor_queue_ +++ b/plugins/condor/condor_queue_ @@ -55,7 +55,7 @@ fi if [ "$1" = "autoconf" ]; then echo "no" - exit 1 + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/condor/condor_states_ b/plugins/condor/condor_states_ index faebdaee..98392dff 100755 --- a/plugins/condor/condor_states_ +++ b/plugins/condor/condor_states_ @@ -52,7 +52,7 @@ fi if [ "$1" = "autoconf" ]; then echo "no" - exit 1 + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/disk/du b/plugins/disk/du index 4f125077..0d886146 100755 --- a/plugins/disk/du +++ b/plugins/disk/du @@ -58,11 +58,10 @@ ID=1; if [ "$1" = "autoconf" ]; then if [ -d $DIR ]; then echo "yes" - exit 0 else echo "no (check your path)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/disk/e2 b/plugins/disk/e2 index ea6a0d3c..a9080055 100755 --- a/plugins/disk/e2 +++ b/plugins/disk/e2 @@ -57,11 +57,10 @@ lse2() { if [ "$1" = "autoconf" ]; then if which dumpe2fs >/dev/null 2>&1; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then lse2 | while read name rest; do diff --git a/plugins/disk/hpasmcli2_ b/plugins/disk/hpasmcli2_ index 069caffe..f0d6bb0d 100755 --- a/plugins/disk/hpasmcli2_ +++ b/plugins/disk/hpasmcli2_ @@ -60,7 +60,6 @@ if (defined($ARGV[0])) { my @chk_result = `$hpasmcli -s \"help\"`; if ($? eq "0") { print "yes\n"; - exit 0; } else { my $reason = 'Unknown error'; foreach my $line (@chk_result) { @@ -71,12 +70,11 @@ if (defined($ARGV[0])) { } } print "no ($reason)\n"; - exit 1; } } else { print "no (hpasmcli not found)\n"; - exit 1; } + exit 0; } elsif ($ARGV[0] eq 'suggest') { print "temp\nfans\n"; exit 0; diff --git a/plugins/disk/linux_diskstat_ b/plugins/disk/linux_diskstat_ index 6628307b..9a6131b3 100755 --- a/plugins/disk/linux_diskstat_ +++ b/plugins/disk/linux_diskstat_ @@ -147,12 +147,11 @@ if ( defined $ARGV[0] && $ARGV[0] eq 'autoconf' ) { if ( !$@ && keys %stats ) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1; } + exit 0; } diff --git a/plugins/disk/md_iostat_ b/plugins/disk/md_iostat_ index 33d5a472..cc7f8928 100755 --- a/plugins/disk/md_iostat_ +++ b/plugins/disk/md_iostat_ @@ -96,10 +96,10 @@ $mdstat_present = -f '/proc/mdstat'; if ( defined($ARGV[0]) and $ARGV[0] eq "autoconf") { if ($mdstat_present and ($detailed_present or $stat_present)) { print "yes\n"; - exit 0; + } else { + print "no\n"; } - print "no\n"; - exit 1; + exit 0; } my %devs; diff --git a/plugins/disk/megaraid-controller-information b/plugins/disk/megaraid-controller-information index 6b18b4d5..7737fa6e 100755 --- a/plugins/disk/megaraid-controller-information +++ b/plugins/disk/megaraid-controller-information @@ -126,11 +126,10 @@ my %config = ( if ($ARGV[0] and $ARGV[0] eq "autoconf" ) { if (-e $Command ) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1 } + exit 0; } #Read Output of MegaRaid command diff --git a/plugins/disk/raid-mismatch-count b/plugins/disk/raid-mismatch-count index 5979b53f..d51c8901 100755 --- a/plugins/disk/raid-mismatch-count +++ b/plugins/disk/raid-mismatch-count @@ -30,14 +30,12 @@ returnval=$? if [ "x$1" = "xautoconf" ]; then if [ -z "$targets" ]; then echo "no (no md devices found under /sys/devices/virtual/block/*/md/mismatch_cnt)" - exit 1; elif [ "x$returnval" != "x0" ]; then echo "no (discovery of md devices failed strangely)" - exit 1; else echo "yes" - exit 0 fi + exit 0 fi if [ "x$1" = "xconfig" ]; then diff --git a/plugins/dkim/dkimproxy_mails b/plugins/dkim/dkimproxy_mails index b68e4890..ef40dfd5 100755 --- a/plugins/dkim/dkimproxy_mails +++ b/plugins/dkim/dkimproxy_mails @@ -37,11 +37,10 @@ STATEFILE=$MUNIN_PLUGSTATE/dkimproxy_mails.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/dovecot/dovecot1 b/plugins/dovecot/dovecot1 index af73b414..62dee459 100755 --- a/plugins/dovecot/dovecot1 +++ b/plugins/dovecot/dovecot1 @@ -36,12 +36,12 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { if (! -x $DOVEADM) { print "no (no doveadm)\n"; - exit(1); + exit(0); } if (! -f $logfile) { print "no (logfile $logfile does not exist)\n"; - exit(1); + exit(0); } if (-r "$logfile") { @@ -50,7 +50,7 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { } else { print "no (logfile not readable)\n"; } - exit 1; + exit 0; } if (-f "$logfile.0") { diff --git a/plugins/fan/ibmfan b/plugins/fan/ibmfan index bba537b5..cdc338e0 100755 --- a/plugins/fan/ibmfan +++ b/plugins/fan/ibmfan @@ -19,11 +19,10 @@ if [ "$1" = "autoconf" ]; then if [ -r /proc/acpi/ibm/fan ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi diff --git a/plugins/fax/faxstat b/plugins/fax/faxstat index 75f99366..61962dec 100755 --- a/plugins/fax/faxstat +++ b/plugins/fax/faxstat @@ -20,11 +20,10 @@ FAXQ=${faxq:=/usr/sbin/faxqclean} if [ "$1" = "autoconf" ]; then if [ -x ${FAXSTAT} ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi JOBTYPES="Running Waiting Done" diff --git a/plugins/ftp/pure-ftpd-bw b/plugins/ftp/pure-ftpd-bw index 10ef8e73..c0c18110 100755 --- a/plugins/ftp/pure-ftpd-bw +++ b/plugins/ftp/pure-ftpd-bw @@ -42,11 +42,11 @@ if [ "$1" = "autoconf" ]; then exit 0 else echo "no (logtail not found)" - exit 1 + exit 0 fi else echo "no (logfile $LOGFILE does not exist)" - exit 1 + exit 0 fi fi diff --git a/plugins/ftp/pure-ftpd-logs b/plugins/ftp/pure-ftpd-logs index 2a0add13..1ebc8668 100755 --- a/plugins/ftp/pure-ftpd-logs +++ b/plugins/ftp/pure-ftpd-logs @@ -32,11 +32,11 @@ if [ "$1" = "autoconf" ]; then exit 0 else echo "no (logtail not found)" - exit 1 + exit 0 fi else echo "no (logfile ${LOGFILE} does not exist)" - exit 1 + exit 0 fi fi diff --git a/plugins/ftp/vsftpd b/plugins/ftp/vsftpd index d329552f..583d3d0f 100755 --- a/plugins/ftp/vsftpd +++ b/plugins/ftp/vsftpd @@ -8,7 +8,7 @@ if [ "$1" = "autoconf" ]; then exit 0 else echo no - exit 1 + exit 0 fi fi diff --git a/plugins/ftp/vsftpd-rel b/plugins/ftp/vsftpd-rel index 25ee99de..27b4dc96 100755 --- a/plugins/ftp/vsftpd-rel +++ b/plugins/ftp/vsftpd-rel @@ -56,7 +56,7 @@ then exit 0 else echo no - exit 1 + exit 0 fi fi diff --git a/plugins/http/mongrel_memory b/plugins/http/mongrel_memory index 8448d5cd..aa107675 100755 --- a/plugins/http/mongrel_memory +++ b/plugins/http/mongrel_memory @@ -83,7 +83,7 @@ when "autoconf" exit 0 end puts "no" - exit 1 + exit 0 else mpm.run end diff --git a/plugins/http/mongrel_process_memory b/plugins/http/mongrel_process_memory index 0febcfe0..958679ea 100755 --- a/plugins/http/mongrel_process_memory +++ b/plugins/http/mongrel_process_memory @@ -94,7 +94,7 @@ when "autoconf" exit 0 end puts "no" - exit 1 + exit 0 else mpm.run end diff --git a/plugins/icecast/icecast2 b/plugins/icecast/icecast2 index 2287302d..de3742aa 100755 --- a/plugins/icecast/icecast2 +++ b/plugins/icecast/icecast2 @@ -26,11 +26,11 @@ case $1 in autoconf|detect) if [ -d $ICEDIR/ ] ; then echo yes - exit 0 else echo "no (icedir not found)" - exit 1 - fi;; + fi + exit 0 + ;; config) cat <<'EOF' graph_title Icecast2 Stream Listeners diff --git a/plugins/ip6/ip6_ b/plugins/ip6/ip6_ index 54a5f1e3..5c7114ff 100755 --- a/plugins/ip6/ip6_ +++ b/plugins/ip6/ip6_ @@ -31,15 +31,13 @@ if [ "$1" = "autoconf" ]; then ip6tables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run ip6tables as user `whoami`)" - exit 1 else echo yes - exit 0 fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/memory/proc_memory_status b/plugins/memory/proc_memory_status index e5e1aa65..6f2f07fc 100755 --- a/plugins/memory/proc_memory_status +++ b/plugins/memory/proc_memory_status @@ -14,11 +14,10 @@ pid=`pgrep -o -x "$process"` if [ "$1" = "autoconf" ]; then if [ -r /proc/$pid/status ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/mogilefs/mogilefsd_activity b/plugins/mogilefs/mogilefsd_activity index 11333d16..5a0cf760 100755 --- a/plugins/mogilefs/mogilefsd_activity +++ b/plugins/mogilefs/mogilefsd_activity @@ -61,7 +61,7 @@ sub autoconf { if ($ret) { print "no ($ret)\n"; - exit 1; + exit 0; } my $conn = IO::Socket::INET->new(PeerAddr => $host, @@ -72,7 +72,7 @@ sub autoconf { if (!$conn) { print "no (could not connect: $!)\n"; - exit 1; + exit 0; } my $request = "!stats\n"; diff --git a/plugins/mogilefs/mogilefsd_queries b/plugins/mogilefs/mogilefsd_queries index 5d788f1e..16a79dd5 100755 --- a/plugins/mogilefs/mogilefsd_queries +++ b/plugins/mogilefs/mogilefsd_queries @@ -61,7 +61,7 @@ sub autoconf { if ($ret) { print "no ($ret)\n"; - exit 1; + exit 0; } my $conn = IO::Socket::INET->new(PeerAddr => $host, @@ -72,7 +72,7 @@ sub autoconf { if (!$conn) { print "no (could not connect: $!)\n"; - exit 1; + exit 0; } my $request = "!stats\n"; diff --git a/plugins/munin/healthcheck_log b/plugins/munin/healthcheck_log index 1d36c384..a47f5f4f 100755 --- a/plugins/munin/healthcheck_log +++ b/plugins/munin/healthcheck_log @@ -78,9 +78,9 @@ MINUTE_BY_GREP_RANGE=10 if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no - exit 1 + else + echo yes fi - echo yes exit 0 fi diff --git a/plugins/munin/healthcheck_process b/plugins/munin/healthcheck_process index 2eaf40ec..5c3a160d 100755 --- a/plugins/munin/healthcheck_process +++ b/plugins/munin/healthcheck_process @@ -40,9 +40,9 @@ let CHECKMAX="$CHECKMAX + 1" if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no - exit 1 + else + echo yes fi - echo yes exit 0 fi diff --git a/plugins/munin/healthcheck_url b/plugins/munin/healthcheck_url index 66d38700..e7faedf3 100755 --- a/plugins/munin/healthcheck_url +++ b/plugins/munin/healthcheck_url @@ -90,9 +90,9 @@ CURL=/usr/bin/curl if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no - exit 1 + else + echo yes fi - echo yes exit 0 fi diff --git a/plugins/munin/update b/plugins/munin/update index c86bce9c..e59ae59f 100755 --- a/plugins/munin/update +++ b/plugins/munin/update @@ -40,11 +40,11 @@ fi if [ "$1" = "autoconf" ]; then if [ -f $UPDATE_STATSFILE ]; then - echo "yes" - exit 0 + echo "yes" + else + echo "no (logfile not readable)" fi - echo "no (logfile not readable)" - exit 1 + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/mysql/mysql_size_ondisk b/plugins/mysql/mysql_size_ondisk index 1045b04a..432a7114 100755 --- a/plugins/mysql/mysql_size_ondisk +++ b/plugins/mysql/mysql_size_ondisk @@ -42,11 +42,10 @@ then if du -sb $DIR &> /dev/null then echo "yes" - exit 0 else echo "no" - exit 1 fi + exit 0 elif [ "${1:-}" = "config" ] then echo "graph_title MySQL on-disk database size" diff --git a/plugins/nagios/nagiosstatus b/plugins/nagios/nagiosstatus index 051a1fcd..aa5fc49e 100755 --- a/plugins/nagios/nagiosstatus +++ b/plugins/nagios/nagiosstatus @@ -46,11 +46,10 @@ if ($ARGV[0]) { if ($ARGV[0] eq 'autoconf') { if (-r $NAGIOSSTAT) { print "yes"; - exit 0; } else { print "no (Nagios status file not found)"; - exit 1; } + exit 0; } elsif ($ARGV[0] eq "config") { print "graph_args --base 1000 -l 0 --vertical-label Checks\n"; print "graph_title Nagios status\n"; diff --git a/plugins/network/denyhosts b/plugins/network/denyhosts index 8617caed..8eda6f17 100755 --- a/plugins/network/denyhosts +++ b/plugins/network/denyhosts @@ -19,11 +19,10 @@ LOG=/etc/hosts.deny if [ "$1" = "autoconf" ]; then if [ -r "$LOG" ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/network/dns/dnsresponse_ b/plugins/network/dns/dnsresponse_ index 684d92bd..b3821f57 100755 --- a/plugins/network/dns/dnsresponse_ +++ b/plugins/network/dns/dnsresponse_ @@ -93,11 +93,10 @@ if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { if (! -s $resconf) { $ret .= "$resconf not found. "; } if ($ret) { print "no ($ret)\n"; - exit 1; } else { print "yes\n"; - exit 0; } + exit 0; } if ( defined $ARGV[0] and $ARGV[0] eq "suggest" ) { diff --git a/plugins/network/ethtool_ b/plugins/network/ethtool_ index be9ea456..8d477e73 100755 --- a/plugins/network/ethtool_ +++ b/plugins/network/ethtool_ @@ -42,11 +42,10 @@ if [ "$1" = "autoconf" ]; then $ETHTOOL 2>/dev/null >/dev/null if [ $? -ne 0 ]; then echo no - exit 1 else echo yes - exit 0 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/network/fms b/plugins/network/fms index 2aaf70ee..c1502ebd 100755 --- a/plugins/network/fms +++ b/plugins/network/fms @@ -272,11 +272,10 @@ if ( !( ( $usenetstat eq "yes" and defined($fmshost) and defined($fmsport) ) or if ( defined($ARGV[0]) and $ARGV[0] eq "autoconf" ) { if ( ( $usenetstat eq "yes" and defined($fmshost) and defined($fmsport) ) or ( $usenetstat eq "no" and defined($adminhost) and defined($adminport) and defined($adminusername) and defined($adminpassword) ) ) { print("yes\n"); - exit 0; } else { print("no\n"); - exit 1; } + exit 0; } if ( $usenetstat eq "yes" and defined($fmshost) and defined($fmsport) ) { diff --git a/plugins/network/fms_apps b/plugins/network/fms_apps index 241958a4..d0bd5cd9 100755 --- a/plugins/network/fms_apps +++ b/plugins/network/fms_apps @@ -200,11 +200,10 @@ if ( defined($ARGV[0]) ) { if ( $ARGV[0] eq "autoconf" ) { if ( defined($host) and defined($port) and defined($username) and defined($password) ) { print("yes\n"); - exit 0; } else { print("no\n"); - exit 1; } + exit 0; } elsif ( $ARGV[0] eq "config" ) { print <<'END_GRAPH_CONFIG'; graph_title Flash Media Server application connections diff --git a/plugins/network/fms_apps_rate b/plugins/network/fms_apps_rate index d84fec3b..41470e05 100755 --- a/plugins/network/fms_apps_rate +++ b/plugins/network/fms_apps_rate @@ -201,11 +201,10 @@ if ( defined($ARGV[0]) ) { if ( $ARGV[0] eq "autoconf" ) { if ( defined($host) and defined($port) and defined($username) and defined($password) ) { print("yes\n"); - exit 0; } else { print("no\n"); - exit 1; } + exit 0; } elsif ( $ARGV[0] eq "config" ) { print <<'END_GRAPH_CONFIG'; graph_title Flash Media Server application connection rates diff --git a/plugins/network/fwbuilder_ b/plugins/network/fwbuilder_ index d8ccfb4a..106f8d42 100755 --- a/plugins/network/fwbuilder_ +++ b/plugins/network/fwbuilder_ @@ -64,15 +64,13 @@ if [ "$1" = "autoconf" ]; then iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" - exit 1 else echo yes - exit 0 fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/network/hostsdeny b/plugins/network/hostsdeny index 3977be38..2d4e7af3 100755 --- a/plugins/network/hostsdeny +++ b/plugins/network/hostsdeny @@ -24,11 +24,10 @@ if [ "$1" = "autoconf" ]; then if [ -r "$LOG" ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/network/if_uptime b/plugins/network/if_uptime index 4dce3e7d..9d42565d 100755 --- a/plugins/network/if_uptime +++ b/plugins/network/if_uptime @@ -122,10 +122,8 @@ EOF autoconf) if [[ $(ifconfig &> /dev/null; echo "$?") == 0 ]]; then echo yes - exit 0 else echo "no (ifconfig doesn't work out)" - exit 1 fi exit 0 ;; diff --git a/plugins/network/ip_forward_ b/plugins/network/ip_forward_ index 374092f2..b13cf960 100755 --- a/plugins/network/ip_forward_ +++ b/plugins/network/ip_forward_ @@ -30,15 +30,13 @@ if [ "$1" = "autoconf" ]; then iptables-save -c >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables-save as user `whoami`)" - exit 1 else echo yes - exit 0 fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/network/ipfwnat_ b/plugins/network/ipfwnat_ index 1ebba2de..79056c9d 100755 --- a/plugins/network/ipfwnat_ +++ b/plugins/network/ipfwnat_ @@ -34,13 +34,13 @@ ipfw="/sbin/ipfw" if [ "$1" = "autoconf" ]; then if [ ! -x $ipfw ]; then echo "no ($ipfw not found)" - exit 1 + exit 0 fi err=$($ipfw nat show config 2>&1) if [ $? -ne 0 ]; then echo "no ($err)" - exit 1 + exit 0 fi echo "yes" diff --git a/plugins/network/ipt_accounting_ b/plugins/network/ipt_accounting_ index 3050c368..a22a23c2 100755 --- a/plugins/network/ipt_accounting_ +++ b/plugins/network/ipt_accounting_ @@ -45,15 +45,13 @@ if [ "$1" = "autoconf" ]; then iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" - exit 1 else echo yes - exit 0 fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/network/ipt_basic_ b/plugins/network/ipt_basic_ index dd692fcd..7d3019ab 100755 --- a/plugins/network/ipt_basic_ +++ b/plugins/network/ipt_basic_ @@ -48,15 +48,13 @@ if [ "$1" = "autoconf" ]; then RES=`$iptables -L $TNAME -nvx -w 2>&1 >/dev/null` if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`; $RES)" - exit 1 else echo yes - exit 0 fi else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/network/ldap_connections b/plugins/network/ldap_connections index 49dd1cc6..8f80d437 100755 --- a/plugins/network/ldap_connections +++ b/plugins/network/ldap_connections @@ -99,12 +99,11 @@ if [ "$1" = "autoconf" ]; then done if [ -n "$ONE_LISTENING" ]; then - echo yes - exit 0 + echo "yes" else - echo no '(no slapd listening on '$PORTS')' - exit 1 + echo "no (no slapd listening on $PORTS)" fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/network/mtr100_ b/plugins/network/mtr100_ index 44ed34f3..b84d85f9 100755 --- a/plugins/network/mtr100_ +++ b/plugins/network/mtr100_ @@ -34,17 +34,15 @@ totrace=`basename $0 | sed 's/^mtr100_//g'` if [ "$1" = "autoconf" ]; then if ( mtr -nrc 1 localhost 2>/dev/null >/dev/null ); then echo yes - exit 0 else if [ $? -eq 127 ] then echo "no (mtr program not found - install the mtr(-tiny) package)" - exit 1 else echo no - exit 1 fi fi + exit 0 exit 0 fi diff --git a/plugins/network/proc_netstat b/plugins/network/proc_netstat index 3562201e..86c7f3e2 100755 --- a/plugins/network/proc_netstat +++ b/plugins/network/proc_netstat @@ -32,11 +32,10 @@ STRIP_OUTPUT="/\($TO_REMOVE\)/d" if [ "$1" = "autoconf" ]; then if [ -r $NETSTATS ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/network/qos_ b/plugins/network/qos_ index 973f5204..99743cbc 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -46,9 +46,9 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'autoconf' ) { } else { print "no (program $TC died)\n"; } - exit 1; + } else { + print "yes\n"; } - print "yes\n"; exit 0; } diff --git a/plugins/network/smtp_hello_ b/plugins/network/smtp_hello_ index 5c293beb..b8300d03 100755 --- a/plugins/network/smtp_hello_ +++ b/plugins/network/smtp_hello_ @@ -43,11 +43,10 @@ elif [ "$1" == "autoconf" ]; then if [ -x /usr/bin/time ] && [ -x /usr/bin/nc ]; then echo "yes" - exit 0 else echo "no (/usr/bin/time or /usr/bin/nc missing)" - exit 1 fi + exit 0 else diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 9933edd2..c1d4ca2a 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -52,11 +52,10 @@ case "$1" in autoconf) if [ -r /proc/net/dev ]; then echo yes - exit 0 else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 ;; suggest) if [ -r /proc/net/dev ]; then diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index 8906080c..ed5c667f 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -23,11 +23,10 @@ case $1 in autoconf) if [ -r /proc/net/dev ]; then echo yes - exit 0 else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 ;; suggest) if [ -r /proc/net/dev ]; then diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 0c5cadc2..2690e539 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -23,11 +23,10 @@ case $1 in autoconf) if [ -r /proc/net/dev ]; then echo yes - exit 0 else echo "no (/proc/net/dev not found)" - exit 1 fi + exit 0 ;; suggest) if [ -r /proc/net/dev ]; then diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 65d4bf04..9d8cb76f 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -233,9 +233,9 @@ sub load_data { if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ($ret) { print "no ($ret)\n"; - exit 1; + } else { + print "yes\n"; } - print "yes\n"; exit 0; } diff --git a/plugins/nfs/nfsv4 b/plugins/nfs/nfsv4 index 1de3e7e0..2c0392fb 100755 --- a/plugins/nfs/nfsv4 +++ b/plugins/nfs/nfsv4 @@ -48,11 +48,10 @@ proc="access close commit create delegpurge delegreturn getattr getfh link lock if [ "$1" = "autoconf" ]; then if [ -f "$NFSD" ]; then echo yes - exit 0 else echo "no (no $NFSD)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/nginx/nginx-combined b/plugins/nginx/nginx-combined index e1006b01..e5ba25b1 100755 --- a/plugins/nginx/nginx-combined +++ b/plugins/nginx/nginx-combined @@ -48,7 +48,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ($ret){ print "no ($ret)\n"; - exit 1; + exit 0; } my $ua = LWP::UserAgent->new(timeout => 30); @@ -57,7 +57,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) unless ($response->is_success and $response->content =~ /server/im) { print "no (no nginx status on $URL)\n"; - exit 1; + exit 0; } else { diff --git a/plugins/nginx/nginx_connection_request b/plugins/nginx/nginx_connection_request index 989afa64..a12171fa 100755 --- a/plugins/nginx/nginx_connection_request +++ b/plugins/nginx/nginx_connection_request @@ -112,7 +112,7 @@ my $UA = exists $ENV{'ua'} ? $ENV{'ua'} : 'nginx-status-verifier/0.1'; if (exists $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ($ret) { print "no ($ret)\n"; - exit 1; + exit 0; } my $ua = LWP::UserAgent->new(timeout => 30); # Set the UA to something different from the libwww-perl. @@ -122,7 +122,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf" ) { unless ($response->is_success and $response->content =~ /server/im) { print "no (no nginx status on $URL)\n"; - exit 1; + exit 0; } else { print "yes\n"; exit 0; diff --git a/plugins/ntp/ntp_peers b/plugins/ntp/ntp_peers index 3cd798bb..9f1a4ab0 100755 --- a/plugins/ntp/ntp_peers +++ b/plugins/ntp/ntp_peers @@ -69,15 +69,13 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if ($? eq "0") { if (`$NTPQ -np | wc -l` > 0) { print "yes\n"; - exit 0; } else { print "no (unable to list peers)\n"; - exit 1; } } else { print "no (ntpq not found)\n"; - exit 1; } + exit 0; } my %peers; diff --git a/plugins/ntp/ntp_peers_ipv6 b/plugins/ntp/ntp_peers_ipv6 index e422f012..584d45b3 100755 --- a/plugins/ntp/ntp_peers_ipv6 +++ b/plugins/ntp/ntp_peers_ipv6 @@ -55,15 +55,13 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if ($? eq "0") { if (`$NTPQ -np | wc -l` > 0) { print "yes\n"; - exit 0; } else { print "no (unable to list peers)\n"; - exit 1; } } else { print "no (ntpq not found)\n"; - exit 1; } + exit 0; } my %peers; diff --git a/plugins/ntp/ntp_queries b/plugins/ntp/ntp_queries index 68c155f8..f0690e39 100755 --- a/plugins/ntp/ntp_queries +++ b/plugins/ntp/ntp_queries @@ -51,15 +51,13 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if ($? eq "0") { if (`$NTPDC -c sysstats | wc -l` > 0) { print "yes\n"; - exit 0; } else { print "no (unable to list system stats)\n"; - exit 1; } } else { print "no (ntpdc not found)\n"; - exit 1; } + exit 0; } my $queries = 0; diff --git a/plugins/openvz/vpsmem b/plugins/openvz/vpsmem index dc3aad03..adc9442c 100755 --- a/plugins/openvz/vpsmem +++ b/plugins/openvz/vpsmem @@ -12,11 +12,10 @@ BEANCOUNTERS=/proc/user_beancounters if [ "$1" == "autoconf" ]; then if [ -e $BEANCOUNTERS ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ ! -r $BEANCOUNTERS ]; then diff --git a/plugins/oracle/oracle__connections b/plugins/oracle/oracle__connections index a96b3b23..9c2d362c 100755 --- a/plugins/oracle/oracle__connections +++ b/plugins/oracle/oracle__connections @@ -66,14 +66,14 @@ if (exists $ARGV[0]) { # Check for DBD::Oracle if (! eval "require DBD::Oracle;") { print "no (DBD::Oracle not found)"; - exit 1; + exit 0; } if ($dbh) { print "yes\n"; exit 0; } else { print "no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr; - exit 1; + exit 0; } } diff --git a/plugins/oracle/oracle__database_hitratio b/plugins/oracle/oracle__database_hitratio index e07dba87..fae7045a 100755 --- a/plugins/oracle/oracle__database_hitratio +++ b/plugins/oracle/oracle__database_hitratio @@ -63,14 +63,14 @@ if (exists $ARGV[0]) { # Check for DBD::Oracle if (! eval "require DBD::Oracle;") { print "no (DBD::Oracle not found)"; - exit 1; + exit 0; } if ($dbh) { print "yes\n"; exit 0; } else { print "no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr; - exit 1; + exit 0; } } diff --git a/plugins/oracle/oracle__locks b/plugins/oracle/oracle__locks index d8140fbc..c515dbce 100755 --- a/plugins/oracle/oracle__locks +++ b/plugins/oracle/oracle__locks @@ -64,14 +64,14 @@ if (exists $ARGV[0]) { # Check for DBD::Oracle if (! eval "require DBD::Oracle;") { print "no (DBD::Oracle not found)"; - exit 1; + exit 0; } if ($dbh) { print "yes\n"; exit 0; } else { print "no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr; - exit 1; + exit 0; } } diff --git a/plugins/oracle/oracle__tablespace_usage b/plugins/oracle/oracle__tablespace_usage index c35235a0..637a77c9 100755 --- a/plugins/oracle/oracle__tablespace_usage +++ b/plugins/oracle/oracle__tablespace_usage @@ -65,14 +65,14 @@ if (exists $ARGV[0]) { # Check for DBD::Oracle if (! eval "require DBD::Oracle;") { print "no (DBD::Oracle not found)"; - exit 1; + exit 0; } if ($dbh) { print "yes\n"; exit 0; } else { print "no Unable to access Database $dbname on host $dbhost as user $dbuser.\nError returned was: ". $DBI::errstr; - exit 1; + exit 0; } } diff --git a/plugins/other/earthquakes b/plugins/other/earthquakes index 095851b1..74645584 100755 --- a/plugins/other/earthquakes +++ b/plugins/other/earthquakes @@ -46,11 +46,10 @@ if (defined $ARGV[0] and $ARGV[0] eq "autoconf") if (defined $ret) { print "no ($ret)\n"; - exit 1; } else { print "yes\n"; - exit 0; } + exit 0; } if (defined $ARGV[0] and $ARGV[0] eq "config") diff --git a/plugins/other/opentracker_ b/plugins/other/opentracker_ index 98a9be44..2be0dc6d 100755 --- a/plugins/other/opentracker_ +++ b/plugins/other/opentracker_ @@ -228,11 +228,10 @@ if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { my $response = $ua->get($url); if ($response->is_success) { print "yes\n"; - exit 0; } else { print "no: unable to connect to url: $url\n"; - exit 1; } + exit 0; } =head2 Suggest Check diff --git a/plugins/pf/pf b/plugins/pf/pf index 5f1366ab..afc46618 100755 --- a/plugins/pf/pf +++ b/plugins/pf/pf @@ -81,31 +81,31 @@ case $1 in # enabled? if [ `${pfctl} -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8))" - exit 1 + exit 0 fi # FreeBSD elif [ ${ostype} = "FreeBSD" ]; then # enabled? if [ `${pfctl} -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8))" - exit 1 + exit 0 fi # OpenBSD elif [ ${ostype} = "OpenBSD" ]; then # pf(4) module loaded? if [ `kldstat -v | grep pf | wc -l` -eq 0 ]; then echo "no (pf(4) is not loaded)" - exit 1 + exit 0 fi # enabled? if [ `${pfctl} -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8))" - exit 1 + exit 0 fi # Other OSes else echo "no (this plugin is not supported on your OS)" - exit 1 + exit 0 fi echo "yes" exit 0 diff --git a/plugins/pf/pf_bytes b/plugins/pf/pf_bytes index a2c840e8..55114231 100755 --- a/plugins/pf/pf_bytes +++ b/plugins/pf/pf_bytes @@ -51,12 +51,12 @@ END { # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # Other OSes else echo "no (this plugin is not supported on your OS)" - exit 1 + exit 0 fi echo "yes" exit 0 diff --git a/plugins/pf/pf_openbsd b/plugins/pf/pf_openbsd index 0d7c1680..65fd05d1 100755 --- a/plugins/pf/pf_openbsd +++ b/plugins/pf/pf_openbsd @@ -48,24 +48,24 @@ EOF # pf(4) module loaded? if [ `kldstat -v | grep pf | wc -l` -eq 0 ]; then echo "no (pf(4) is not loaded)" - exit 1 + exit 0 fi # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # OpenBSD elif [ ${ostype} = "OpenBSD" ]; then # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # Other OSes else echo "no (this plugin is not supported on your OS)" - exit 1 + exit 0 fi echo "yes" exit 0 diff --git a/plugins/pf/pf_packets b/plugins/pf/pf_packets index 117fe0a5..59b30c5d 100755 --- a/plugins/pf/pf_packets +++ b/plugins/pf/pf_packets @@ -50,24 +50,24 @@ END { # pf(4) module loaded? if [ `kldstat -v | grep pf | wc -l` -eq 0 ]; then echo "no (pf(4) is not loaded)" - exit 1 + exit 0 fi # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # OpenBSD elif [ ${ostype} = "OpenBSD" ]; then # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # Other OSes else echo "no (this plugin is not supported on your OS)" - exit 1 + exit 0 fi echo "yes" exit 0 diff --git a/plugins/pf/pf_states b/plugins/pf/pf_states index 762b2c03..4d84ee0a 100755 --- a/plugins/pf/pf_states +++ b/plugins/pf/pf_states @@ -44,24 +44,24 @@ ${pfctl} -sm 2> /dev/null | awk ' # pf(4) module loaded? if [ `kldstat -v | grep pf | wc -l` -eq 0 ]; then echo "no (pf(4) is not loaded)" - exit 1 + exit 0 fi # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # OpenBSD elif [ ${ostype} = "OpenBSD" ]; then # enabled? if [ `pfctl -si 2>/dev/null | awk '/^Status:/{print $2}'` != "Enabled" ]; then echo "no (pf(4) is not enabled, consult pfctl(8)" - exit 1 + exit 0 fi # Other OSes else echo "no (this plugin is not supported on your OS)" - exit 1 + exit 0 fi echo "yes" exit 0 diff --git a/plugins/php/php_sessions b/plugins/php/php_sessions index eb947f42..2950b9d3 100755 --- a/plugins/php/php_sessions +++ b/plugins/php/php_sessions @@ -43,11 +43,10 @@ if [ "$1" = "autoconf" ]; then test -d "$SESSDIR" > /dev/null 2>&1 if [ $? ]; then echo yes - exit 0 else echo "no (session directory not found)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/php/php_time_execution b/plugins/php/php_time_execution index 431754c6..8a1ce651 100755 --- a/plugins/php/php_time_execution +++ b/plugins/php/php_time_execution @@ -40,11 +40,10 @@ NBRPAGE=${nbrpage} if [ "$1" = "autoconf" ]; then if [ -r "$LOG" ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/ping/multiping b/plugins/ping/multiping index a6f9bfa3..38d3b1fc 100755 --- a/plugins/ping/multiping +++ b/plugins/ping/multiping @@ -68,11 +68,10 @@ if ((exists $ARGV[0]) && ($ARGV[0] eq "autoconf")) { my $ping = join(" ", @ping); if ($ping =~ m@min/avg/max@) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1; } + exit 0; } if ((exists $ARGV[0]) && ($ARGV[0] eq "config")) { diff --git a/plugins/ping/pinger b/plugins/ping/pinger index 16db1279..8a4fab3c 100755 --- a/plugins/ping/pinger +++ b/plugins/ping/pinger @@ -67,11 +67,10 @@ case $1 in which ping if [[ "$?" = "0" ]]; then echo yes - exit 0 else echo "no (ping not present)" - exit 1 fi + exit 0 ;; config) cat << EOM diff --git a/plugins/postfix/postfix-policyd b/plugins/postfix/postfix-policyd index fb094740..aa6c793c 100755 --- a/plugins/postfix/postfix-policyd +++ b/plugins/postfix/postfix-policyd @@ -15,11 +15,10 @@ MYSQL_DB="postfixpolicyd" if [ "$1" = "autoconf" ]; then if [ -n "${MYSQL_PASS}" ] ; then echo yes - exit 0 else echo "no (set mysql pass)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/postfix/postfix_filtered b/plugins/postfix/postfix_filtered index c9eda5f4..22eb0200 100755 --- a/plugins/postfix/postfix_filtered +++ b/plugins/postfix/postfix_filtered @@ -44,11 +44,10 @@ STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/postfix/postfix_filtered_awk b/plugins/postfix/postfix_filtered_awk index b054c111..54dd7b38 100755 --- a/plugins/postfix/postfix_filtered_awk +++ b/plugins/postfix/postfix_filtered_awk @@ -40,11 +40,10 @@ STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered_test.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/postfix/postfix_mailfiltered b/plugins/postfix/postfix_mailfiltered index 8689bdd7..c0ef6d6e 100755 --- a/plugins/postfix/postfix_mailfiltered +++ b/plugins/postfix/postfix_mailfiltered @@ -20,11 +20,10 @@ STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/postfix/postfix_mailstats b/plugins/postfix/postfix_mailstats index c21e2df2..7d2c518e 100755 --- a/plugins/postfix/postfix_mailstats +++ b/plugins/postfix/postfix_mailstats @@ -62,7 +62,7 @@ sub autoconf } else { print "no (could not find logdir)\n"; } - exit 1; + exit 0; } sub config diff --git a/plugins/postfix/postfix_mailvolume_multi b/plugins/postfix/postfix_mailvolume_multi index 5ef5c201..03192515 100755 --- a/plugins/postfix/postfix_mailvolume_multi +++ b/plugins/postfix/postfix_mailvolume_multi @@ -133,7 +133,6 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") if (-r "$logfile") { print "yes\n"; - exit 0; } else { diff --git a/plugins/postfix/postgrey b/plugins/postfix/postgrey index 0fc3bbef..be90e875 100755 --- a/plugins/postfix/postgrey +++ b/plugins/postfix/postgrey @@ -20,11 +20,10 @@ LOGTAIL=${logtail:-`which logtail`} if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/postfix/postgrey-new b/plugins/postfix/postgrey-new index a56e0f12..37a1dd40 100755 --- a/plugins/postfix/postgrey-new +++ b/plugins/postfix/postgrey-new @@ -47,12 +47,11 @@ my $passes_white_new=0; if(defined $ARGV[0] and $ARGV[0] eq "autoconf") { if ( -f $maillog) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1 } + exit 0; } diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 69dacafd..9257886f 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -44,14 +44,13 @@ if (defined($ARGV[0])) if (!$dbh) { print "no\n"; - exit 1; } else { print "yes\n"; - exit 0; } $dbh->disconnect(); + exit 0; } if ($ARGV[0] eq 'config') diff --git a/plugins/postgresql/postgres_block_read_ b/plugins/postgresql/postgres_block_read_ index 2ea8da8a..f8afa224 100755 --- a/plugins/postgresql/postgres_block_read_ +++ b/plugins/postgresql/postgres_block_read_ @@ -76,7 +76,7 @@ if (exists $ARGV[0]) { # Check for DBD::Pg if (! eval "require DBD::Pg;") { print "no (DBD::Pg not found)"; - exit 1; + exit 0; } # Then we try to detect Postgres presence by connecting to # 'template1'. @@ -88,7 +88,7 @@ if (exists $ARGV[0]) { exit 0; } else { print "no (Can't connect to given host, please check environment settings)\n"; - exit 1; + exit 0; } } elsif ($ARGV[0] eq 'debug') { # Set debug flag diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index 1b6d88da..7085c14f 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -31,7 +31,7 @@ if (exists $ARGV[0]) { # Check for DBD::Pg if (! eval "require DBD::Pg;") { print "no (DBD::Pg not found)"; - exit 1; + exit 0; } # Then we try to detect Postgres presence by connecting to # 'template1'. @@ -43,7 +43,7 @@ if (exists $ARGV[0]) { exit 0; } else { print "no (Can't connect to given host, please check environment settings)\n"; - exit 1; + exit 0; } } elsif ($ARGV[0] and $ARGV[0] eq 'debug') { # Set config flag diff --git a/plugins/power/apcupsd_ww b/plugins/power/apcupsd_ww index 4a657f28..0b7c1a6c 100755 --- a/plugins/power/apcupsd_ww +++ b/plugins/power/apcupsd_ww @@ -178,11 +178,10 @@ if (-f $config) { if (defined $ARGV[0] && $ARGV[0] =~ /autoconf|detect/) { if (-x $apcaccess) { print "yes\n"; - exit 0; } else { print "no (apcaccess not found)\n"; - exit 1; } + exit 0; } # Read info from apcupsd using apcaccess diff --git a/plugins/power/nut b/plugins/power/nut index 8c3f158d..31e34002 100755 --- a/plugins/power/nut +++ b/plugins/power/nut @@ -47,11 +47,10 @@ case "$1" in grep ^MONITOR < /etc/nut/upsmon.conf &> /dev/null if [[ "$?" = "0" ]]; then echo yes - exit 0 else echo "no (NUT not installed or no UPS info available in /etc/nut/upsmon.conf)" - exit 1 fi + exit 0 ;; config) FETCH_DATA diff --git a/plugins/qmail/qmailsend b/plugins/qmail/qmailsend index b0b750f3..107f7f7f 100755 --- a/plugins/qmail/qmailsend +++ b/plugins/qmail/qmailsend @@ -18,11 +18,10 @@ if (exists $ARGV[0]) { if ($ARGV[0] eq "autoconf") { if (-f "${logpath}current") { print "yes\n"; - exit 0; } else { print STDERR "no (Cannot find ${logpath}current. Please specify env.logpath)\n"; - exit 1; } + exit 0; } } diff --git a/plugins/qmail/qmailsend_plesk b/plugins/qmail/qmailsend_plesk index 2e9e767f..585c020b 100755 --- a/plugins/qmail/qmailsend_plesk +++ b/plugins/qmail/qmailsend_plesk @@ -18,11 +18,10 @@ if (exists $ARGV[0]) { if ($ARGV[0] eq "autoconf") { if (-f "${logpath}maillog") { print "yes\n"; - exit 0; } else { print STDERR "no (Cannot find ${logpath}maillog. Please specify env.logpath)\n"; - exit 1; } + exit 0; } } diff --git a/plugins/qmail/spamdyke b/plugins/qmail/spamdyke index 04458acf..b6e58284 100755 --- a/plugins/qmail/spamdyke +++ b/plugins/qmail/spamdyke @@ -34,11 +34,10 @@ STATEFILE=$MUNIN_PLUGSTATE/spamdyke.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/radiator/radiator_acct_lag b/plugins/radiator/radiator_acct_lag index be9ab4e8..362b53a0 100755 --- a/plugins/radiator/radiator_acct_lag +++ b/plugins/radiator/radiator_acct_lag @@ -23,11 +23,10 @@ if [ "$statisticsdir" ]; then STATISTICSDIR=$statisticsdir ; fi if [ "$1" = "autoconf" ]; then if [ -d ${STATISTICSDIR} ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi diff --git a/plugins/radiator/radiator_acct_ppm b/plugins/radiator/radiator_acct_ppm index 04ef8687..af8d74f6 100755 --- a/plugins/radiator/radiator_acct_ppm +++ b/plugins/radiator/radiator_acct_ppm @@ -23,11 +23,10 @@ if [ "$statisticsdir" ]; then STATISTICSDIR=$statisticsdir ; fi if [ "$1" = "autoconf" ]; then if [ -d ${STATISTICSDIR} ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi diff --git a/plugins/radiator/radiator_auth_lag b/plugins/radiator/radiator_auth_lag index 9b33b0d1..4a14e06b 100755 --- a/plugins/radiator/radiator_auth_lag +++ b/plugins/radiator/radiator_auth_lag @@ -23,11 +23,10 @@ if [ "$statisticsdir" ]; then STATISTICSDIR=$statisticsdir ; fi if [ "$1" = "autoconf" ]; then if [ -d ${STATISTICSDIR} ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi diff --git a/plugins/radiator/radiator_auth_ppm b/plugins/radiator/radiator_auth_ppm index 0e4c5d14..e4d2cd84 100755 --- a/plugins/radiator/radiator_auth_ppm +++ b/plugins/radiator/radiator_auth_ppm @@ -23,11 +23,10 @@ if [ "$statisticsdir" ]; then STATISTICSDIR=$statisticsdir ; fi if [ "$1" = "autoconf" ]; then if [ -d ${STATISTICSDIR} ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi diff --git a/plugins/router/freeboxuptime b/plugins/router/freeboxuptime index 7202f82d..1ced6e0a 100755 --- a/plugins/router/freeboxuptime +++ b/plugins/router/freeboxuptime @@ -65,16 +65,14 @@ fi if [ "$1" = "autoconf" ]; then if [ -z "$NMAP" ]; then echo "no (nmap not installed)" - exit 1 else if [ $PING -eq 0 ]; then echo "no (Freebox not reachable)" - exit 2 else echo yes - exit 0 fi fi + exit 0 fi diff --git a/plugins/router/tg585v7__ b/plugins/router/tg585v7__ index 1bf4e576..db438baf 100755 --- a/plugins/router/tg585v7__ +++ b/plugins/router/tg585v7__ @@ -112,9 +112,9 @@ print "# Access Mode is: $ACCESS_MODE\n" if $MUNIN_DEBUG; if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ($ret) { print "no ($ret)\n"; - exit 1; + } else { + print "yes\n"; } - print "yes\n"; exit 0; } diff --git a/plugins/rsync/rsyncd_bytes b/plugins/rsync/rsyncd_bytes index 985086e7..750e6abb 100755 --- a/plugins/rsync/rsyncd_bytes +++ b/plugins/rsync/rsyncd_bytes @@ -21,11 +21,10 @@ STATEFILE=$MUNIN_PLUGSTATE/rsync-bytes.offset if [ "$1" = "autoconf" ]; then if [ -f "${RSYNCD_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/rsync/rsyncd_count b/plugins/rsync/rsyncd_count index ec04c3f4..ebb10764 100755 --- a/plugins/rsync/rsyncd_count +++ b/plugins/rsync/rsyncd_count @@ -21,11 +21,10 @@ STATEFILE=$MUNIN_PLUGSTATE/rsync-count.offset if [ "$1" = "autoconf" ]; then if [ -f "${RSYNCD_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/s3/s3_items b/plugins/s3/s3_items index d31c149f..1f716c65 100755 --- a/plugins/s3/s3_items +++ b/plugins/s3/s3_items @@ -10,12 +10,11 @@ my $s3cmd = 's3curl.pl --id ' . $s3_id . ' http://s3.amazonaws.com/'; if ( $ARGV[0] eq "autoconf" ) { if (`/usr/bin/perl $0` eq "" ) { - print "no\n"; - exit 1; + print "no\n"; } else { - print "yes\n"; - exit 0; + print "yes\n"; } + exit 0; } diff --git a/plugins/s3/s3_storage b/plugins/s3/s3_storage index 3fda1d7c..314590f1 100755 --- a/plugins/s3/s3_storage +++ b/plugins/s3/s3_storage @@ -28,16 +28,12 @@ if (not @bucket_list) if ($ARGV[0] and $ARGV[0] eq "autoconf") { - if (@bucket_list) - { + if (@bucket_list) { print "yes\n"; - exit 0; - } - else - { + } else { print "no\n"; - exit 1; } + exit 0; } sub get_bucket_stats diff --git a/plugins/sar/iostat-cputps-average b/plugins/sar/iostat-cputps-average index bcbde46e..4f5e20b1 100755 --- a/plugins/sar/iostat-cputps-average +++ b/plugins/sar/iostat-cputps-average @@ -17,16 +17,14 @@ export LANG=en_US.UTF-8 if [ "$1" == "autoconf" ]; then if ( sar 1 1 >/dev/null 2>&1 ); then echo yes - exit 0 else if [ $? -eq 127 ]; then echo "no (could not run \"sar\")" - exit 1 else echo no - exit 1 fi fi + exit 0 fi ARRAY=( `sar -p -d -s ${tenMago} -e ${current} | grep -v nodev | grep "Average" | awk '{ print $2 , $3 , $10 }'` ) diff --git a/plugins/security/forefront_ b/plugins/security/forefront_ index eb9b61d4..e1400eb5 100755 --- a/plugins/security/forefront_ +++ b/plugins/security/forefront_ @@ -101,10 +101,10 @@ if(!eval "require MIME::Base64;") { if($ARGV[0] and $ARGV[0] eq "autoconf") { if($ret) { - print "no ($ret)\n"; - exit 1; + print "no ($ret)\n"; + } else { + print "yes\n"; } - print "yes\n"; exit 0; } diff --git a/plugins/sensors/mbmon b/plugins/sensors/mbmon index 0044bafe..345dd98c 100755 --- a/plugins/sensors/mbmon +++ b/plugins/sensors/mbmon @@ -19,11 +19,10 @@ my $mbmon = "/usr/local/bin/mbmon"; if ($ARGV[0] eq 'autoconf') { if (-x $mbmon) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1; } + exit 0; } elsif ($ARGV[0] eq 'config') { print < /dev/null; then echo "yes" - exit 0 else echo "no" - exit 1 fi + exit 0 fi # env.options diff --git a/plugins/sge/sge_queue_xml_ b/plugins/sge/sge_queue_xml_ index 6adc3e2c..6fe02f3b 100755 --- a/plugins/sge/sge_queue_xml_ +++ b/plugins/sge/sge_queue_xml_ @@ -91,11 +91,10 @@ XMLSTARLET=$( which xmlstarlet ) if [ "$1" = "autoconf" ]; then if [ -n "$QSTAT" -a -n "$XMLSTARLET" ]; then echo "yes" - exit 0 else echo "no" - exit 1 fi + exit 0 fi # check requirements diff --git a/plugins/smstools/smstools_ b/plugins/smstools/smstools_ index e5e4644d..2f41f5e6 100755 --- a/plugins/smstools/smstools_ +++ b/plugins/smstools/smstools_ @@ -58,11 +58,10 @@ MODEM=`basename $0 | sed 's/^smstools_//g'` if [ "$1" = "autoconf" ]; then if [ -d $STATSDIR ]; then echo yes - exit 0 else echo "no ($STATSDIR not found)" - exit 1 fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/snmp/snmp_room_alert_ b/plugins/snmp/snmp_room_alert_ index 4114aaf9..8c4ec86e 100755 --- a/plugins/snmp/snmp_room_alert_ +++ b/plugins/snmp/snmp_room_alert_ @@ -73,7 +73,7 @@ community = ENV['community'] || "public" case ARGV[0] when "autoconf" puts "no" - exit 1 + exit 0 when "config" puts "graph_title Room Alert 11E units (#{name} probes)" puts "graph_vlabel #{label}" diff --git a/plugins/solaris/solaris-memstat b/plugins/solaris/solaris-memstat index d6c58931..64e3e39c 100755 --- a/plugins/solaris/solaris-memstat +++ b/plugins/solaris/solaris-memstat @@ -41,11 +41,10 @@ if [ "$1" = "autoconf" ]; then if [ -e /usr/bin/mdb ]; then echo yes - exit 0 else echo /usr/bin/mdb not found - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/sourceds/srcds_cpu b/plugins/sourceds/srcds_cpu index 9a1b5099..ae56702b 100755 --- a/plugins/sourceds/srcds_cpu +++ b/plugins/sourceds/srcds_cpu @@ -130,15 +130,15 @@ sub test_service { my $sock = Rcon::sock_connect($HOST, $PORT); if (!$sock) { print "no (could not open socket to $HOST:$PORT)\n"; - exit 1; + exit 0; } if (!Rcon::rcon_auth($sock, $PASS)) { print "no (could not authenticate)\n"; - exit 1; + exit 0; } if (!defined(Rcon::rcon_command($sock, "stats"))) { print "no (did not receive reply from server)\n"; - exit 1; + exit 0; } print "yes\n"; diff --git a/plugins/sourceds/srcds_fps b/plugins/sourceds/srcds_fps index c2625b7e..813ad5c2 100755 --- a/plugins/sourceds/srcds_fps +++ b/plugins/sourceds/srcds_fps @@ -132,15 +132,15 @@ sub test_service { my $sock = Rcon::sock_connect($HOST, $PORT); if (!$sock) { print "no (could not open socket to $HOST:$PORT)\n"; - exit 1; + exit 0; } if (!Rcon::rcon_auth($sock, $PASS)) { print "no (could not authenticate)\n"; - exit 1; + exit 0; } if (!defined(Rcon::rcon_command($sock, "stats"))) { print "no (did not receive reply from server)\n"; - exit 1; + exit 0; } print "yes\n"; diff --git a/plugins/sourceds/srcds_inout b/plugins/sourceds/srcds_inout index 2ae34792..aab351f1 100755 --- a/plugins/sourceds/srcds_inout +++ b/plugins/sourceds/srcds_inout @@ -143,15 +143,15 @@ sub test_service { my $sock = Rcon::sock_connect($HOST, $PORT); if (!$sock) { print "no (could not open socket to $HOST:$PORT)\n"; - exit 1; + exit 0; } if (!Rcon::rcon_auth($sock, $PASS)) { print "no (could not authenticate)\n"; - exit 1; + exit 0; } if (!defined(Rcon::rcon_command($sock, "stats"))) { print "no (did not receive reply from server)\n"; - exit 1; + exit 0; } print "yes\n"; diff --git a/plugins/sourceds/srcds_players b/plugins/sourceds/srcds_players index ce2c1621..22bb4f5a 100755 --- a/plugins/sourceds/srcds_players +++ b/plugins/sourceds/srcds_players @@ -106,15 +106,15 @@ sub test_service { my $sock = Rcon::sock_connect($HOST, $PORT); if (!$sock) { print "no (could not open socket to $HOST:$PORT)\n"; - exit 1; + exit 0; } if (!Rcon::rcon_auth($sock, $PASS)) { print "no (could not authenticate)\n"; - exit 1; + exit 0; } if (!defined(Rcon::rcon_command($sock, "stats"))) { print "no (did not receive reply from server)\n"; - exit 1; + exit 0; } print "yes\n"; diff --git a/plugins/sourceds/srcds_uptime b/plugins/sourceds/srcds_uptime index 0e4011e2..0aee1ea2 100755 --- a/plugins/sourceds/srcds_uptime +++ b/plugins/sourceds/srcds_uptime @@ -104,15 +104,15 @@ sub test_service { my $sock = Rcon::sock_connect($HOST, $PORT); if (!$sock) { print "no (could not open socket to $HOST:$PORT)\n"; - exit 1; + exit 0; } if (!Rcon::rcon_auth($sock, $PASS)) { print "no (could not authenticate)\n"; - exit 1; + exit 0; } if (!defined(Rcon::rcon_command($sock, "stats"))) { print "no (did not receive reply from server)\n"; - exit 1; + exit 0; } print "yes\n"; diff --git a/plugins/squid/squid_times b/plugins/squid/squid_times index 3e5311fc..4817e260 100755 --- a/plugins/squid/squid_times +++ b/plugins/squid/squid_times @@ -20,11 +20,10 @@ if [ "$1" = "autoconf" ]; then SQUID_STATS=`printf "GET cache_object://$host/info HTTP/1.0\n\n" | netcat $host $port` if [ -n "${SQUID_STATS}" ]; then echo yes - exit 0 else echo "no (HTTP GET failed)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/ssh/openssh-denyhosts b/plugins/ssh/openssh-denyhosts index 9990975c..f94478bd 100755 --- a/plugins/ssh/openssh-denyhosts +++ b/plugins/ssh/openssh-denyhosts @@ -21,11 +21,10 @@ LOGTAIL=${logtail:-`which logtail`} if [ "$1" = "autoconf" ]; then if [ -f "${AUTH_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/ssh/sshd_invalid_countries_ruby b/plugins/ssh/sshd_invalid_countries_ruby index 45cc9148..fe0448f4 100755 --- a/plugins/ssh/sshd_invalid_countries_ruby +++ b/plugins/ssh/sshd_invalid_countries_ruby @@ -62,7 +62,7 @@ when 'autoconf' fh = open(SYSLOG, 'r') rescue puts 'no' - exit 1 + exit 0 else puts 'yes' exit 0 diff --git a/plugins/system/blockhosts b/plugins/system/blockhosts index 400d7404..11945533 100755 --- a/plugins/system/blockhosts +++ b/plugins/system/blockhosts @@ -17,11 +17,10 @@ LOG=/etc/hosts.allow if [ "$1" = "autoconf" ]; then if [ -r "$LOG" ]; then echo yes - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/tcp/tcp-states b/plugins/tcp/tcp-states index eb093263..b83dd257 100755 --- a/plugins/tcp/tcp-states +++ b/plugins/tcp/tcp-states @@ -21,17 +21,15 @@ if [ "$1" = "autoconf" ]; then if ( netstat -nt 2>/dev/null >/dev/null ); then echo yes - exit 0 else if [ $? -eq 127 ] then echo "no (netstat program not found)" - exit 1 else echo no - exit 1 fi fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/thin/thin_memory b/plugins/thin/thin_memory index f4313166..0c5aa89d 100755 --- a/plugins/thin/thin_memory +++ b/plugins/thin/thin_memory @@ -94,7 +94,7 @@ when "autoconf" exit 0 end puts "no" - exit 1 + exit 0 else mpm.run end diff --git a/plugins/thin/thin_threads b/plugins/thin/thin_threads index f07c280f..055fc657 100755 --- a/plugins/thin/thin_threads +++ b/plugins/thin/thin_threads @@ -98,7 +98,7 @@ when "autoconf" exit 0 end puts "no" - exit 1 + exit 0 else mpm.run end diff --git a/plugins/thin/thins_peak_memory b/plugins/thin/thins_peak_memory index 762013d1..bdc0a40f 100755 --- a/plugins/thin/thins_peak_memory +++ b/plugins/thin/thins_peak_memory @@ -96,7 +96,7 @@ when "autoconf" exit 0 end puts "no" - exit 1 + exit 0 else mpm.run end diff --git a/plugins/tor/tor-bandwidth-usage b/plugins/tor/tor-bandwidth-usage index 4c78378a..5c95acf3 100755 --- a/plugins/tor/tor-bandwidth-usage +++ b/plugins/tor/tor-bandwidth-usage @@ -87,7 +87,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if ($failed) { say "no (failed to connect to $address port $port)"; - exit 1; + exit 0; } my $msg = Authenticate($socket); @@ -95,7 +95,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { say $socket "QUIT"; close($socket); say "no ($msg)"; - exit 1; + exit 0; } say $socket "QUIT"; diff --git a/plugins/tor/tor_routers b/plugins/tor/tor_routers index 4709bfd4..96c83908 100755 --- a/plugins/tor/tor_routers +++ b/plugins/tor/tor_routers @@ -78,7 +78,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if ($failed) { print "no (failed to connect to $address port $port)\n"; - exit 1; + exit 0; } my $msg = Authenticate($socket); @@ -86,7 +86,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { print $socket "QUIT\r\n"; close($socket); print "no ($msg)\n"; - exit 1; + exit 0; } print $socket "QUIT\r\n"; diff --git a/plugins/ultramonkey/ultramonkey-l7 b/plugins/ultramonkey/ultramonkey-l7 index 22878f9e..8778bf0f 100755 --- a/plugins/ultramonkey/ultramonkey-l7 +++ b/plugins/ultramonkey/ultramonkey-l7 @@ -24,10 +24,9 @@ sub get_autoconf `$L7VSADM`; if ( $? ) { print qq{no ($L7VSADM something wrong ...)\n}; - exit 1; + } else { + print qq{yes\n}; } - - print qq{yes\n}; } diff --git a/plugins/uucp/uustat b/plugins/uucp/uustat index 76bd6de7..638a3611 100755 --- a/plugins/uucp/uustat +++ b/plugins/uucp/uustat @@ -79,11 +79,10 @@ sub host_excluded if (@ARGV > 0 && $ARGV[0] eq 'autoconf') { if (-x $uustat) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1; } + exit 0; } if (@ARGV > 0 && $ARGV[0] eq 'config') { diff --git a/plugins/vpn/openvpn_multi b/plugins/vpn/openvpn_multi index bafcc230..2ddbcc82 100755 --- a/plugins/vpn/openvpn_multi +++ b/plugins/vpn/openvpn_multi @@ -83,11 +83,10 @@ sub config { sub autoconf { if (-e $statusfile) { print "yes\n"; - exit 0; } else { print "no\n"; - exit 1; } + exit 0; } sub report { diff --git a/plugins/weather/humidity-sensor b/plugins/weather/humidity-sensor index 3c96b8f7..40d6b8e5 100755 --- a/plugins/weather/humidity-sensor +++ b/plugins/weather/humidity-sensor @@ -29,11 +29,10 @@ if (! eval "require LWP::UserAgent;") if (defined $ARGV[0] and $ARGV[0] eq "autoconf") { if (defined $ret) { print "no ($ret)\n"; - exit 1; } else { print "yes\n"; - exit 0; } + exit 0; } my $datasource = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"; diff --git a/plugins/weather/temperature_ b/plugins/weather/temperature_ index e81e6740..174ef6a0 100755 --- a/plugins/weather/temperature_ +++ b/plugins/weather/temperature_ @@ -28,11 +28,10 @@ if (! eval "require LWP::UserAgent;") { if (defined $ARGV[0] and $ARGV[0] eq "autoconf") { if (defined $ret) { print "no ($ret)\n"; - exit 1; } else { print "yes\n"; - exit 0; } + exit 0; } # Extract weather-code from filename. Example: weather_CODE diff --git a/plugins/weather/temperatures b/plugins/weather/temperatures index a19cbdf4..ae550bd0 100755 --- a/plugins/weather/temperatures +++ b/plugins/weather/temperatures @@ -36,11 +36,10 @@ if (! eval "require LWP::UserAgent;") if (defined $ARGV[0] and $ARGV[0] eq "autoconf") { if (defined $ret) { print "no ($ret)\n"; - exit 1; } else { print "yes\n"; - exit 0; } + exit 0; } my $datasource = "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/"; diff --git a/plugins/xbnbt/xbnbt_peers b/plugins/xbnbt/xbnbt_peers index 34d559ac..346cb2ec 100755 --- a/plugins/xbnbt/xbnbt_peers +++ b/plugins/xbnbt/xbnbt_peers @@ -75,14 +75,12 @@ if ($response->content =~ /xpeerstats.*?odd">(\d*).*?even">(\d*).*?odd">(\d*).*? if ( defined $ARGV[0] and $ARGV[0] eq "autoconf") { if (! $response->is_success) { print 'no (Error: ', $response->header('WWW-Authenticate') || ' Error accessing', $response->status_line, ' at http://'.$host.':'.$port.'/xstats.html Aborting)'; - exit 1; } elsif (! defined $peers ) { print "no (Unable to find peer values within the page from the given URL.)"; - exit 1; } else { print "yes"; - exit 0; } + exit 0; } diff --git a/plugins/xen/xen b/plugins/xen/xen index e4f9abec..cad9b7a9 100755 --- a/plugins/xen/xen +++ b/plugins/xen/xen @@ -20,10 +20,10 @@ statefile="$MUNIN_PLUGSTATE/munin-plugin-xen.state" if [ "$1" = "autoconf" ]; then if which xm > /dev/null ; then echo yes - exit 0 + else + echo "no (xm not found)" fi - echo "no (xm not found)" - exit 1 + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/xen/xen-cpu b/plugins/xen/xen-cpu index 4c558134..76cdca29 100755 --- a/plugins/xen/xen-cpu +++ b/plugins/xen/xen-cpu @@ -40,11 +40,10 @@ if (defined($ARGV[0])) { if ( "$arg" eq 'autoconf') { if ( -e $XM && -e $XMTOP ) { print "yes\n"; - exit 0; } else { print "no ($XM and/or $XMTOP not found\n"; - exit 1; } + exit 0; } if ( "$arg" eq 'config') { diff --git a/plugins/xen/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 index 4483cf96..06a4a2fc 100755 --- a/plugins/xen/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -56,13 +56,12 @@ if ( defined($ARGV[0]) ) if ( -e $XM && -e $XMTOP ) { print "yes\n"; - exit 0; } else { print "no ($XM and/or $XMTOP not found\n"; - exit 1; } + exit 0; } if ( $arg eq 'config' ) diff --git a/plugins/yum/yum_activity b/plugins/yum/yum_activity index 5b02ea9b..e41500f9 100755 --- a/plugins/yum/yum_activity +++ b/plugins/yum/yum_activity @@ -12,11 +12,10 @@ if [ "$1" = "autoconf" ]; then if [ -r /var/log/yum.log ]; then echo yes - exit 0 else echo "no (/var/log/yum.log not readable)" - exit 1 fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/zfs/zfsarcstats-counters b/plugins/zfs/zfsarcstats-counters index 594b3d31..04106875 100755 --- a/plugins/zfs/zfsarcstats-counters +++ b/plugins/zfs/zfsarcstats-counters @@ -23,15 +23,15 @@ $sysctl kstat.zfs.misc.arcstats.hits kstat.zfs.misc.arcstats | awk '/hits/ || /m autoconf) if [ ! -x ${sysctl} ]; then echo "no (${sysctl} is not executable)" - exit 1 + exit 0 fi ostype=`uname -s` if [ ${ostype} = "FreeBSD" ]; then echo "yes" - exit 0 + else + echo "no (Your OS is not supported by this plugin)" fi - echo "no (You're OS is not supported by this plugin)" - exit 1 + exit 0 ;; suggest) exit 0 From 2ec4e4c1f7050ba03dfe5e9edc150f43863e1dbc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 16 Sep 2018 04:09:21 +0200 Subject: [PATCH 316/695] Improve incorrect or unspecific output for "autoconf" Some plugins emit wrongly formatted "no" messages or lack the "yes" message on success. --- plugins/amule/amule_queue | 7 +++---- plugins/amule/amule_shares | 7 +++---- plugins/amule/amule_transfers | 7 +++---- plugins/amule/amule_uptime | 7 +++---- plugins/bind/bind95_ | 8 ++++---- plugins/disk/lvm_snap_used | 6 +++--- plugins/disk/raid | 5 ++--- plugins/disk/xfs | 5 ++--- plugins/http/wget_page | 32 ++++++++++-------------------- plugins/mysql/mysql_report | 5 ++--- plugins/mythtv/mythtv_programs | 6 ++---- plugins/other/listeners | 2 +- plugins/php/php_errors_ | 6 +++--- plugins/punbb/punbb_users | 7 ++++--- plugins/snmp/snmp__fn | 26 ++++++++---------------- plugins/solaris/zones_cpu | 5 ++--- plugins/solaris/zones_mem | 5 ++--- plugins/vbulletin/vbulletin4_users | 8 ++++---- plugins/vbulletin/vbulletin_users | 8 ++++---- plugins/vmware/esxi | 5 ++--- 20 files changed, 68 insertions(+), 99 deletions(-) diff --git a/plugins/amule/amule_queue b/plugins/amule/amule_queue index 53f5afd0..e73d37b0 100755 --- a/plugins/amule/amule_queue +++ b/plugins/amule/amule_queue @@ -28,16 +28,15 @@ if [ "$1" = "autoconf" ]; then if [ -z "$(which amule)" ]; then - echo "$0: error: amule not installed" - exit 1 + echo "no (amule not installed)" else if [ ! -e /tmp/amulesig.dat ]; then - echo "$0: error: amulesig.dat not found" + echo "no (amulesig.dat not found)" else echo yes - exit 0 fi fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/amule/amule_shares b/plugins/amule/amule_shares index 33789a36..c0129e35 100755 --- a/plugins/amule/amule_shares +++ b/plugins/amule/amule_shares @@ -28,16 +28,15 @@ if [ "$1" = "autoconf" ]; then if [ -z "$(which amule)" ]; then - echo "$0: error: amule not installed" - exit 1 + echo "no (amule not installed)" else if [ ! -e /tmp/amulesig.dat ]; then - echo "$0: error: amulesig.dat not found" + echo "no (amulesig.dat not found)" else echo yes - exit 0 fi fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/amule/amule_transfers b/plugins/amule/amule_transfers index 2591c344..79623d66 100755 --- a/plugins/amule/amule_transfers +++ b/plugins/amule/amule_transfers @@ -28,16 +28,15 @@ if [ "$1" = "autoconf" ]; then if [ -z "$(which amule)" ]; then - echo "$0: error: amule not installed" - exit 1 + echo "no (amule not installed)" else if [ ! -e /tmp/amulesig.dat ]; then - echo "$0: error: amulesig.dat not found" + echo "no (amulesig.dat not found)" else echo yes - exit 0 fi fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/amule/amule_uptime b/plugins/amule/amule_uptime index fbf34e39..c58114d8 100755 --- a/plugins/amule/amule_uptime +++ b/plugins/amule/amule_uptime @@ -28,16 +28,15 @@ if [ "$1" = "autoconf" ]; then if [ -z "$(which amule)" ]; then - echo "$0: error: amule not installed" - exit 1 + echo "no (amule not installed)" else if [ ! -e /tmp/amulesig.dat ]; then - echo "$0: error: amulesig.dat not found" + echo "no (amulesig.dat not found)" else echo yes - exit 0 fi fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/bind/bind95_ b/plugins/bind/bind95_ index bf42fe81..edad0815 100755 --- a/plugins/bind/bind95_ +++ b/plugins/bind/bind95_ @@ -122,12 +122,12 @@ if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { if ( defined($ARGV[0]) && $ARGV[0] eq "autoconf" ) { if (! -f $stat_file) { - printf "Unable to file bind stat file on %s",$stat_file; - exit 1; + printf "no (Unable to file bind stat file on %s)",$stat_file; + exit 0; } if (! -f $rndc) { - printf "Unable to file rndc tool (configured : %s)",$rndc; - exit 1; + printf "no (Unable to file rndc tool (configured : %s))",$rndc; + exit 0; } exit 0; } diff --git a/plugins/disk/lvm_snap_used b/plugins/disk/lvm_snap_used index aabbe460..4be838a7 100755 --- a/plugins/disk/lvm_snap_used +++ b/plugins/disk/lvm_snap_used @@ -26,10 +26,10 @@ lvdisplay=$(which lvdisplay) if [ "$1" = "autoconf" ]; then if test -n "${lvdisplay}"; then echo yes - exit 0 + else + echo "no (lvdisplay not found)" fi - echo "no lvdisplay found" - exit 1 + exit 0 fi diff --git a/plugins/disk/raid b/plugins/disk/raid index bbfc0e8a..c02a1689 100755 --- a/plugins/disk/raid +++ b/plugins/disk/raid @@ -13,11 +13,10 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") { if (-r "/proc/mdstat" and `grep md /proc/mdstat`) { print "yes\n"; - exit 0; } else { - print "no RAID devices\n"; - exit 1; + print "no (no RAID devices found)\n"; } + exit 0; } if ( $ARGV[0] and $ARGV[0] eq "config" ) { diff --git a/plugins/disk/xfs b/plugins/disk/xfs index 8a06a555..55919038 100755 --- a/plugins/disk/xfs +++ b/plugins/disk/xfs @@ -20,13 +20,12 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") if (-r "/proc/fs/xfs/stat") { print "yes\n"; - exit 0; } else { - print "/proc/fs/xfs/stat not found\n"; - exit 1; + print "no (/proc/fs/xfs/stat not found)\n"; } + exit 0; } my %runtime_stats = ( diff --git a/plugins/http/wget_page b/plugins/http/wget_page index 00081281..c137ff96 100755 --- a/plugins/http/wget_page +++ b/plugins/http/wget_page @@ -97,30 +97,20 @@ default_timeout=20 default_join_lines=true if [ "${1}" = "autoconf" ]; then - result=0 - if [ -z "${wget_bin}" -o ! -f "${wget_bin}" -o ! -x "${wget_bin}" ]; then - result=1 + if [ -z "$wget_bin" ] || [ ! -f "$wget_bin" ] || [ ! -x "$wget_bin" ]; then + echo "no (missing 'wget' executable)" + elif [ -z "$time_bin" ] || [ ! -f "$time_bin" ] || [ ! -x "$time_bin" ]; then + echo "no (missing 'time' executable)" + elif [ -z "$mktemp_bin" ] || [ ! -f "$mktemp_bin" ] || [ ! -x "$mktemp_bin" ]; then + echo "no (missing 'mktemp' executable)" + elif [ -z "$grep_bin" ] || [ ! -f "$grep_bin" ] || [ ! -x "$grep_bin" ]; then + echo "no (missing 'grep' executable)" + elif [ -z "$tail_bin" ] || [ ! -f "$tail_bin" ] || [ ! -x "$tail_bin" ]; then + echo "no (missing 'tail' executable)" else - if [ -z "${time_bin}" -o ! -f "${time_bin}" -o ! -x "${time_bin}" ]; then - result=2 - else - if [ -z "${mktemp_bin}" -o ! -f "${mktemp_bin}" -o ! -x "${mktemp_bin}" ]; then - result=3 - else - if [ -z "${grep_bin}" -o ! -f "${grep_bin}" -o ! -x "${grep_bin}" ]; then - result=4 - else - [ -z "${tail_bin}" -o ! -f "${tail_bin}" -o ! -x "${tail_bin}" ] && result=5 - fi - fi - fi - fi - if [ ${result} -eq 0 ]; then echo "yes" - else - echo "no" fi - exit $result + exit 0 fi if [ -z "${names}" ]; then diff --git a/plugins/mysql/mysql_report b/plugins/mysql/mysql_report index 06df41f6..a680df38 100755 --- a/plugins/mysql/mysql_report +++ b/plugins/mysql/mysql_report @@ -60,13 +60,12 @@ default_args="--base 1000 -l 0" default_scale="no" if [ "${1}" = "autoconf" ]; then - result=0 if [ -z "${mysqlbin}" ]; then - echo "no" + echo "no (missing mysql executable)" else echo "yes" fi - exit $result + exit 0 fi if [ -z "${names}" ]; then diff --git a/plugins/mythtv/mythtv_programs b/plugins/mythtv/mythtv_programs index e205329b..4abcf559 100755 --- a/plugins/mythtv/mythtv_programs +++ b/plugins/mythtv/mythtv_programs @@ -42,12 +42,10 @@ my $Channel=""; if ($ARGV[0] and $ARGV[0] eq "autoconf" ) { if ( $SQLDBName ne "" ) { print "yes\n"; - exit 0; } else { - print "no\n"; - print "cannot find MythTV configuration file my.txt\n"; - exit 1; + print "no (cannot find MythTV configuration file my.txt)\n"; } + exit 0; } #Config Options diff --git a/plugins/other/listeners b/plugins/other/listeners index 8f426d36..235bcb08 100755 --- a/plugins/other/listeners +++ b/plugins/other/listeners @@ -71,7 +71,7 @@ LASTSTREAM=${#STREAMS[*]} # --- check whether any stream found --- run_autoconf() { if (( $LASTSTREAM )) ; then echo yes ; exit 0 ; fi # found streams - echo U ; exit 1 ; } # no radio or streams + echo no ; exit 0 ; } # no radio or streams diff --git a/plugins/php/php_errors_ b/plugins/php/php_errors_ index fd4f43fe..d02f8064 100755 --- a/plugins/php/php_errors_ +++ b/plugins/php/php_errors_ @@ -34,9 +34,9 @@ LOGS=${logfile:-/var/log/apache2/error.log} if [[ $1 == autoconf ]]; then for LOG in $LOGS; do - if [[ ! -r $LOGS ]]; then - echo no - exit 1 + if [[ ! -r $LOG ]]; then + echo "no (cannot read '$LOG')" + exit 0 fi done diff --git a/plugins/punbb/punbb_users b/plugins/punbb/punbb_users index 453fc923..954233d9 100755 --- a/plugins/punbb/punbb_users +++ b/plugins/punbb/punbb_users @@ -42,11 +42,12 @@ my $type = undef; if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { - if ($ret) - { + if ($ret) { print "no ($ret)\n"; - exit 1; + } else { + print "yes\n"; } + exit 0; } diff --git a/plugins/snmp/snmp__fn b/plugins/snmp/snmp__fn index 521b19a9..051408b1 100755 --- a/plugins/snmp/snmp__fn +++ b/plugins/snmp/snmp__fn @@ -87,26 +87,16 @@ ATUN=`$SNMPGET $fnVPNSslStatsActiveTunnels | cut -d ":" -f4 | cut -d " " -f2` autoconf() { - if [ $SCPU ]; then - echo yes, OID $FGTcpu can be readed. + if [ -z "$SCPU" ]; then + echo "no (one or multiple OID can not be read)" + elif [ -z "$SMEM" ]; then + echo "no (one or multiple OID can not be read)" + elif [ -z "$SCNT" ]; then + echo "no (one or multiple OID can not be read)" else - echo no, one or multiple OID can not be readed. - exit 1 + echo "yes" fi - - if [ $SMEM ]; then - echo yes, OID $fnSysMemUsage can be readed. - else - echo no, one or multiple OID can not be readed. - exit 1 - fi - if [ $SCNT ]; then - echo yes, OID $fnSysSesCount can be readed. - else - echo no, one or multiple OID can not be read. - exit 1 - fi -exit 0 + exit 0 } config() diff --git a/plugins/solaris/zones_cpu b/plugins/solaris/zones_cpu index 003d6d1a..5fb58f45 100755 --- a/plugins/solaris/zones_cpu +++ b/plugins/solaris/zones_cpu @@ -12,13 +12,12 @@ if [ "$1" = 'autoconf' ]; then if [ $zones -gt 1 ]; then echo yes else - echo yes + echo no fi - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = 'config' ]; then diff --git a/plugins/solaris/zones_mem b/plugins/solaris/zones_mem index d492af05..a25cb830 100755 --- a/plugins/solaris/zones_mem +++ b/plugins/solaris/zones_mem @@ -14,13 +14,12 @@ if [ "$1" = 'autoconf' ]; then if [ $zones -gt 1 ]; then echo yes else - echo yes + echo no fi - exit 0 else echo no - exit 1 fi + exit 0 fi if [ "$1" = 'config' ]; then diff --git a/plugins/vbulletin/vbulletin4_users b/plugins/vbulletin/vbulletin4_users index ead4ebb8..b8ea1c12 100755 --- a/plugins/vbulletin/vbulletin4_users +++ b/plugins/vbulletin/vbulletin4_users @@ -47,12 +47,12 @@ my $timeout = 30; if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { - if ($ret) - { + if ($ret) { print "no ($ret)\n"; - exit 1; + } else { + print "yes\n"; } - + exit 0; } if ( defined $ARGV[0] and $ARGV[0] eq "config" ) diff --git a/plugins/vbulletin/vbulletin_users b/plugins/vbulletin/vbulletin_users index 663eb3d0..27dd837d 100755 --- a/plugins/vbulletin/vbulletin_users +++ b/plugins/vbulletin/vbulletin_users @@ -47,12 +47,12 @@ my $timeout = 30; if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { - if ($ret) - { + if ($ret) { print "no ($ret)\n"; - exit 1; + } else { + print "yes\n"; } - + exit 0; } if ( defined $ARGV[0] and $ARGV[0] eq "config" ) diff --git a/plugins/vmware/esxi b/plugins/vmware/esxi index 00c5b982..0c2894a6 100755 --- a/plugins/vmware/esxi +++ b/plugins/vmware/esxi @@ -119,11 +119,10 @@ if [ "$1" = "autoconf" ]; then get_infos if [ -n "$summary" ]; then echo yes - exit 0 else - echo "Cannot connect to ESX server $HOST" - exit 1 + echo "no (cannot connect to ESX server $HOST)" fi + exit 0 fi if [ "$1" = "config" ]; then From 46e2de55def5c1a660f7ebbec9f22bbb5a8f8cf2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 16 Sep 2018 04:23:14 +0200 Subject: [PATCH 317/695] Remove unused "autoconf" handling from plugins without this capability Some plugins contained code for handling "autoconf" (always returning "no") but did not announce the respective capability via the magic marker. --- plugins/router/snmp__linksys_poe | 3 --- plugins/rtorrent/rtom_allsessions_mem | 4 ---- plugins/rtorrent/rtom_allsessions_peers | 4 ---- plugins/rtorrent/rtom_allsessions_spdd | 4 ---- plugins/rtorrent/rtom_allsessions_vol | 4 ---- plugins/rtorrent/rtom_mem | 4 ---- plugins/rtorrent/rtom_peers | 4 ---- plugins/rtorrent/rtom_spdd | 4 ---- plugins/rtorrent/rtom_vol | 4 ---- plugins/wuala/wuala_stats | 7 ++----- 10 files changed, 2 insertions(+), 40 deletions(-) diff --git a/plugins/router/snmp__linksys_poe b/plugins/router/snmp__linksys_poe index 0e5c9a4d..a79a27ea 100755 --- a/plugins/router/snmp__linksys_poe +++ b/plugins/router/snmp__linksys_poe @@ -73,9 +73,6 @@ community = ENV['community'] || "public" version = ENV['version'] == '1' ? :SNMPv1 : :SNMPv2c case ARGV[0] -when "autoconf" - puts "no" - exit 1 when "snmpconf" puts "require 1.3.6.1.4.1.3955.89.108.1.1.2.1. [0-9]" puts "require 1.3.6.1.4.1.3955.89.108.1.1.5.1. [0-9]" diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 565eea90..82c6dcd4 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -46,10 +46,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_title rTorrent memory usage\n"; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index f452677c..93a06a38 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -44,10 +44,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_title rTorrent peer statistics\n"; diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index a9ce779b..cf893cb5 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -56,10 +56,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $diff = $ENV{"diff"} || ""; my $category = $ENV{"category"} || ""; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 97818291..ed483e66 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -46,10 +46,6 @@ my @views = ( "default", "started", "stopped", "complete", "incomplete" ); -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_args --base 1000 -r --lower-limit 0\n"; diff --git a/plugins/rtorrent/rtom_mem b/plugins/rtorrent/rtom_mem index afb00cef..f691aab1 100755 --- a/plugins/rtorrent/rtom_mem +++ b/plugins/rtorrent/rtom_mem @@ -44,10 +44,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_title rTorrent memory usage\n"; diff --git a/plugins/rtorrent/rtom_peers b/plugins/rtorrent/rtom_peers index dace07bb..ae414542 100755 --- a/plugins/rtorrent/rtom_peers +++ b/plugins/rtorrent/rtom_peers @@ -44,10 +44,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_title rTorrent peer statistics\n"; diff --git a/plugins/rtorrent/rtom_spdd b/plugins/rtorrent/rtom_spdd index cbb06c14..e2b08835 100755 --- a/plugins/rtorrent/rtom_spdd +++ b/plugins/rtorrent/rtom_spdd @@ -57,10 +57,6 @@ #%# family=auto -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $diff = $ENV{"diff"} || ""; my $category = $ENV{"category"} || ""; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index f8e08e33..ea88fe13 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -45,10 +45,6 @@ my @views = ( "default", "started", "stopped", "complete", "incomplete" ); -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { - exit 1; -} - if ( $ARGV[0] and $ARGV[0] eq "config" ) { my $category = $ENV{"category"} || ""; print "graph_args --base 1000 -r --lower-limit 0\n"; diff --git a/plugins/wuala/wuala_stats b/plugins/wuala/wuala_stats index f151b737..57ec557b 100755 --- a/plugins/wuala/wuala_stats +++ b/plugins/wuala/wuala_stats @@ -33,11 +33,8 @@ # my $wualadir = $ENV{wualadir}; -if ($ARGV[0] and $ARGV[0] eq "autoconf") -{ - print "no\n"; - exit 1; -} elsif ($ARGV[0] and $ARGV[0] eq "config") + +if ($ARGV[0] and $ARGV[0] eq "config") { if($0 =~ /.*_uptime/) { print "graph_args -l0 --vertical-label %\n"; From 9cef55a3edb9c8129752117ba46cd95d8d90118f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 16 Sep 2018 05:03:47 +0200 Subject: [PATCH 318/695] Restructure incomplete or broken "autoconf" implementations Some plugins with non-zero autoconf exitcodes (it must be zero instead) deserved a bit of code cleanup for improved readability. --- plugins/arp/arp | 9 +++++---- plugins/arp/arp_ | 10 ++++------ plugins/backuppc/backuppc | 9 ++++++--- plugins/google/google-rank | 12 +++++------- plugins/jvm/jstat__gccount | 14 ++++++-------- plugins/jvm/jstat__gctime | 14 ++++++-------- plugins/jvm/jstat__heap | 14 ++++++-------- plugins/memory/kmemsum | 17 +++++++---------- plugins/mpd/mpdstats_ | 10 +++------- plugins/mythtv/dvb-signal | 14 +++++--------- plugins/network/brc_rssi | 8 ++++---- plugins/php/php_apc_ | 8 ++++++-- plugins/php/php_opcache | 8 ++++++-- plugins/wifi/wifi_signal | 25 +++++++++---------------- plugins/zfs/zfs_cache_efficiency | 18 +++++++----------- 15 files changed, 85 insertions(+), 105 deletions(-) diff --git a/plugins/arp/arp b/plugins/arp/arp index 896b38bb..0dee771d 100755 --- a/plugins/arp/arp +++ b/plugins/arp/arp @@ -15,10 +15,11 @@ if [ "$1" = "autoconf" ]; then # Search for arp - which arp >/dev/null 2>/dev/null || (echo "no (can't find arp binary)" && exit 1) - - # ...or success - echo yes + if which arp >/dev/null; then + echo yes + else + echo "no (missing 'arp' executable)" + fi exit 0 fi diff --git a/plugins/arp/arp_ b/plugins/arp/arp_ index f85865b4..979d9a57 100755 --- a/plugins/arp/arp_ +++ b/plugins/arp/arp_ @@ -19,13 +19,11 @@ case "$1" in autoconf) # Search for ip - which ip >/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo "no (can't find ip binary)" - exit 1 + if which ip >/dev/null; then + echo 'yes' + else + echo "no (missing 'ip' executable)" fi - # ...or success - echo 'yes' exit 0 ;; suggest) diff --git a/plugins/backuppc/backuppc b/plugins/backuppc/backuppc index ea5f3cf8..db74d222 100755 --- a/plugins/backuppc/backuppc +++ b/plugins/backuppc/backuppc @@ -18,9 +18,12 @@ HOSTS=$(cd ${PCDIR} 2>/dev/null && ls -1) . $MUNIN_LIBDIR/plugins/plugin.sh if [ "$1" = "autoconf" ]; then - [ ! -z "${HOSTS}" ] && echo "yes" && exit 0 - echo "no" - exit 1 + if [ -n "$HOSTS" ]; then + echo "yes" + else + echo "no" + fi + exit 0 fi if [ "$1" = "config" ]; then diff --git a/plugins/google/google-rank b/plugins/google/google-rank index 9c6cba45..e941940f 100755 --- a/plugins/google/google-rank +++ b/plugins/google/google-rank @@ -40,14 +40,12 @@ # Auto Configure, Check it word 1 is defined if [ "$1" = "autoconf" ]; then - if [ "$URL1" != "" ]; then - if [ "$WORD1" != "" ]; then - echo yes - exit 0 - fi + if [ -n "$URL1" ] && [ -n "$WORD1" ]; then + echo yes + else + echo no fi - echo no - exit 1 + exit 0 fi #Configure, loop through each variable defined WORDx URLx dumping it to munin diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index a04eb638..a6eae247 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -110,15 +110,13 @@ if [ "$1" = "autoconf" ]; then if [ ! -x "${JAVA_HOME}/bin/jstat" ]; then echo "no (No jstat found in ${JAVA_HOME}/bin)" - exit 1 + elif [ ! -f "$pidfilepath" ]; then + echo "no (missing file $pidfilepath)" + elif [ ! -r "$pidfilepath" ]; then + echo "no (cannot read $pidfilepath)" + else + echo "yes" fi - - if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then - echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" - exit 1 - fi - - echo "yes" exit 0 fi diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index 7326b46b..21cdec82 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -130,15 +130,13 @@ if [ "$1" = "autoconf" ]; then if [ ! -x "${JAVA_HOME}/bin/jstat" ]; then echo "no (No jstat found in ${JAVA_HOME}/bin)" - exit 1 + elif [ ! -f "$pidfilepath" ]; then + echo "no (missing file $pidfilepath)" + elif [ ! -r "$pidfilepath" ]; then + echo "no (cannot read $pidfilepath)" + else + echo "yes" fi - - if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then - echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" - exit 1 - fi - - echo "yes" exit 0 fi diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index edf6f411..d6739662 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -185,15 +185,13 @@ if [ "$1" = "autoconf" ]; then if [ ! -x "${JAVA_HOME}/bin/jstat" ]; then echo "no (No jstat found in ${JAVA_HOME}/bin)" - exit 1 + elif [ ! -f "$pidfilepath" ]; then + echo "no (missing file $pidfilepath)" + elif [ ! -r "$pidfilepath" ]; then + echo "no (cannot read $pidfilepath)" + else + echo "yes" fi - - if [ ! -f "${pidfilepath}" ] || [ ! -r "${pidfilepath}" ]; then - echo "no (No such file ${pidfilepath} or cannot read ${pidfilepath}" - exit 1 - fi - - echo "yes" exit 0 fi diff --git a/plugins/memory/kmemsum b/plugins/memory/kmemsum index 6101d1c0..a09a0fa8 100755 --- a/plugins/memory/kmemsum +++ b/plugins/memory/kmemsum @@ -16,18 +16,15 @@ if [ "$1" = "autoconf" ]; then if [ -x /sbin/sysctl ]; then - /sbin/sysctl vm.kmem_size_max > /dev/null - if [ $? = "0" ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi + if /sbin/sysctl vm.kmem_size_max >/dev/null 2>&1; then + echo "yes" + else + echo "no (missing sysctl variable 'vm.kmem_size_max')" + fi else - echo no - exit 1 + echo "no (missing 'sysctl' executable)" fi + exit 0 fi TEXT=`kldstat | tr a-f A-F | awk 'BEGIN {print "ibase=16"}; NR > 1 {print $4}' | bc | awk '{a+=$1}; END {print a}'` diff --git a/plugins/mpd/mpdstats_ b/plugins/mpd/mpdstats_ index f9d0360a..a3b336bc 100755 --- a/plugins/mpd/mpdstats_ +++ b/plugins/mpd/mpdstats_ @@ -83,15 +83,11 @@ ACTION="$(basename "$0" | sed 's/^.*_//')" do_autoconf () { if [ -z "$NCBIN" ] ; then echo "no (missing netcat program ('nc'))" - exit 1 - fi - - if ! echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" >/dev/null 2>&1; then + elif ! echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" >/dev/null 2>&1; then echo "no (connection failed)" - exit 1 + else + echo "yes" fi - - echo "yes" exit 0 } diff --git a/plugins/mythtv/dvb-signal b/plugins/mythtv/dvb-signal index aeaa2ebb..f3e70e43 100755 --- a/plugins/mythtv/dvb-signal +++ b/plugins/mythtv/dvb-signal @@ -18,19 +18,15 @@ Tuner=0 case $1 in autoconf|detect) - REQ=`which femon` - if [ "$REQ" = "" ]; then + if ! which femon >/dev/null; then echo "no (femon not installed)" - exit 1 - fi - - if [ -e /dev/dvb/adapter$Tuner/ ]; then + elif [ -e "/dev/dvb/adapter$Tuner/" ]; then echo yes - exit 0 else echo "no (dvb tuner $Tuner not found)" - exit 1 - fi;; + fi + exit 0 + ;; config) echo "graph_title DVB signal strength" diff --git a/plugins/network/brc_rssi b/plugins/network/brc_rssi index 01c76325..146bdc87 100755 --- a/plugins/network/brc_rssi +++ b/plugins/network/brc_rssi @@ -77,11 +77,11 @@ EOF do_autoconf () { case $WLERR in - 0) echo yes; exit 0;; - 127) echo "no ($AL)"; exit 1;; - *) echo "no (wl error: $AL)"; exit 1;; - *) echo "no (no wl executable, or error)"; exit 1;; + 0) echo yes;; + 127) echo "no ($AL)";; + *) echo "no (wl error: $AL)";; esac + exit 0 } case $1 in diff --git a/plugins/php/php_apc_ b/plugins/php/php_apc_ index 82c00177..bbf152e3 100755 --- a/plugins/php/php_apc_ +++ b/plugins/php/php_apc_ @@ -39,8 +39,12 @@ WGET_FLAGS="-Yoff"; # refer to wget manual, you may set extra parameters like di act=`basename $0 | sed 's/^php_apc_//g'` if [ "$1" = "autoconf" ]; then - [ -z "$URL" ] && echo "no (edit URL config in header file !)" && exit 1 - [ -n "$URL" ] && echo "yes" && exit 0 + if [ -z "$URL" ]; then + echo "no (missing URL config in header file)" + else + echo "yes" + fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/php/php_opcache b/plugins/php/php_opcache index 4055d72f..89d18b4a 100755 --- a/plugins/php/php_opcache +++ b/plugins/php/php_opcache @@ -21,8 +21,12 @@ WGET_FLAGS="-Yoff"; # refer to wget manual, you may set extra parameters like di act=memory if [ "$1" = "autoconf" ]; then - [ -z "$URL" ] && echo "no (edit URL config in header file !)" && exit 1 - [ -n "$URL" ] && echo "yes" && exit 0 + if [ -z "$URL" ]; then + echo "no (missing URL config in header file)" + else + echo "yes" + fi + exit 0 fi if [ "$1" = "suggest" ]; then diff --git a/plugins/wifi/wifi_signal b/plugins/wifi/wifi_signal index a1e24306..0126320f 100755 --- a/plugins/wifi/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -32,23 +32,16 @@ do_config () { } do_autoconf () { - if [ ! -f $PNWL ] ; then - echo "no (no $PNWL)" - exit 1 + if [ ! -f "$PNWL" ]; then + echo "no (missing file '$PNWL')" + elif [ ! -r "$PNWL" ]; then + echo "no (cannot read file '$PNWL')" + elif grep -qs : "$PNWL"; then + echo yes + else + echo "no (no devices in $PNWL)" fi - - if [ ! -r $PNWL ] ; then - echo "no (could not read $PNWL)" - exit 1 - fi - - if grep -qs : $PNWL ; then - echo yes - exit 0 - fi - - echo "no (no devices in $PNWL)" - exit 1 + exit 0 } case $1 in diff --git a/plugins/zfs/zfs_cache_efficiency b/plugins/zfs/zfs_cache_efficiency index 0ed85949..c65c7f77 100755 --- a/plugins/zfs/zfs_cache_efficiency +++ b/plugins/zfs/zfs_cache_efficiency @@ -216,22 +216,18 @@ EOF autoconf) if [ ! -x ${sysctl} ]; then echo "no (${sysctl} is not executable)" - exit 1 - fi - if [ ${ostype} = "FreeBSD" ]; then + elif [ "${ostype}" = "FreeBSD" ]; then echo "yes" - exit 0 - fi - if [ ${ostype} = "Linux" ]; then + elif [ "${ostype}" = "Linux" ]; then if [ -f ${procfile} ]; then echo "yes" - exit 0 + else + echo "no (The statsfile does not exist: ${procfile})" fi - echo "no (The statsfile does not exist: ${procfile})" - exit 1 + else + echo "no (Your OS is not supported by this plugin)" fi - echo "no (You're OS is not supported by this plugin)" - exit 1 + exit 0 ;; suggest) exit 0 From 6995742e591fcd11088801dced1d08ef83c8da21 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 18 Sep 2018 03:07:05 +0200 Subject: [PATCH 319/695] Plugins tc_*: check existence of "tc" during autoconf --- plugins/network/tc_ | 8 +++++--- plugins/network/tc_drops_ | 8 +++++--- plugins/network/tc_packets_ | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 6b60fb93..1b774e46 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -50,10 +50,12 @@ mytc() { case "$1" in autoconf) - if [ -r /proc/net/dev ]; then - echo yes - else + if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev not found)" + elif [ ! -x /sbin/tc ]; then + echo "no (missing 'tc' executable)" + else + echo yes fi exit 0 ;; diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index 6169bfd4..ab049c64 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -21,10 +21,12 @@ mytc() { case $1 in autoconf) - if [ -r /proc/net/dev ]; then - echo yes - else + if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev not found)" + elif ! which tc >/dev/null; then + echo "no (missing 'tc' executable)" + else + echo yes fi exit 0 ;; diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 42a5d9c5..0a280c2e 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -21,10 +21,12 @@ mytc() { case $1 in autoconf) - if [ -r /proc/net/dev ]; then - echo yes - else + if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev not found)" + elif ! which tc >/dev/null; then + echo "no (missing 'tc' executable)" + else + echo yes fi exit 0 ;; From 725d9041313d82f2ec75c8977ba33e49dc494719 Mon Sep 17 00:00:00 2001 From: Christopher Desjardins Date: Thu, 20 Sep 2018 12:05:01 +0200 Subject: [PATCH 320/695] Add clean_path back, it got dropped when clean_fieldname was added This allows you to tidy up your munin dir stats --- plugins/disk/du-2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index 6b8dfddc..de739d51 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -91,6 +91,7 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { while(defined (my $bar = )) { if ($bar =~ m/(\d+)\s+(.+)/) { my $dir = $2; + clean_path(\$dir); my $clean_dir = clean_fieldname($dir); print "$clean_dir.label $dir\n"; if ($foo++) { @@ -110,6 +111,7 @@ open (FILE, "<", $CACHEFILE) or munin_exit_fail(); while(defined (my $foo = )) { if ($foo =~ m/(\d+)\s+(.+)/) { my ($field, $value) = ($2, $1); + clean_path(\$field); print clean_fieldname($field), ".value ", $value, "\n"; } } From a5ed119b6ed2d83607bcd32884f942116d9cc2b5 Mon Sep 17 00:00:00 2001 From: Christopher Desjardins Date: Thu, 20 Sep 2018 12:06:05 +0200 Subject: [PATCH 321/695] Add sort to the du command to keep things in order of size --- plugins/disk/du-2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index de739d51..3dc5e610 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -134,7 +134,7 @@ sub daemonize { ## In the child, let's get the du infos if necessary if (cache_is_too_old() && du_not_running()) { my $dirs = $ENV{dirs}; - system("touch $LOCKFILE; du -sb $dirs > $TEMPFILE; cat $TEMPFILE > $CACHEFILE; rm $LOCKFILE; date +%s > $TIMEFILE;"); + system("touch $LOCKFILE; du -sb $dirs | sort -n -r > $TEMPFILE; cat $TEMPFILE > $CACHEFILE; rm $LOCKFILE; date +%s > $TIMEFILE;"); } exit; } ## daemonize From cca33b39842bf76037a2938c42475b5da93c5311 Mon Sep 17 00:00:00 2001 From: Kjetil Torgrim Homme Date: Wed, 26 Sep 2018 19:57:13 +0200 Subject: [PATCH 322/695] nvme - Munin plugin to monitor the use of NVMe devices This is a multigraph plugin which makes three graphs, nvme_usage, nvme_bytes and nvme_writecycles --- plugins/disk/nvme | 244 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100755 plugins/disk/nvme diff --git a/plugins/disk/nvme b/plugins/disk/nvme new file mode 100755 index 00000000..efb43f46 --- /dev/null +++ b/plugins/disk/nvme @@ -0,0 +1,244 @@ +#! /usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +nvme - Munin plugin to monitor the use of NVMe devices + +=head1 CONFIGURATION + +The plugin uses nvme(1) from the nvme-cli project to read status from +the NVMe devices. This requires root access. + + [nvme] + user root + +The plugin does not support alerting. + +=head1 INTERPRETATION + +This is a multigraph plugin which makes three graphs + +=head2 nvme_usage + +This reports how much of capacity is allocated in each NVMe +"namespace". The report is in percent. This number may not have much +relation to actual use, e.g., if deleted data areas have not been +trimmed/discarded. + +=head2 nvme_bytes + +This reports read and write activity on each NVMe device, in bytes per +second. Ideally there should be much more read than write. If they +are symmetrical, you are using your NVMe as a very expensive FIFO, and +if you write more than you read, you should probably look for archival +storage instead. + +It is a good idea to compare these numbers to I/O counters from +diskstats. If they are much higher, look into if the write +amplification can be due to suboptimal I/O request sizes. + +=head2 nvme_writecycles + +This graphs is intended to give an indication of how much life there +is left in your NVMe. It calculates the number of bytes written +during each device's lifetime against the capacity of the device, +thereby getting an average number of write cycle each cell has +experienced. + +A prosumer NVMe will handle a few thousand writes to each cell before +the error rate gets out of hand. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 BUGS + +None known. + +=head1 VERSION + + 1.0 + +=head1 AUTHOR + +Kjetil Torgrim Homme + +=head1 LICENSE + +GPLv2 + +=cut + +use strict; +use Munin::Plugin; + +# Check that multigraph is supported +need_multigraph(); + +sub run_nvme { + my (@cmd) = @_; + my @lines; + $ENV{'LC_ALL'} = 'C'; + if (open(my $nvme, '-|', 'nvme', @cmd)) { + @lines = <$nvme>; + close($nvme); + } else { + # Perl printed a warning about failed exec already. Ignore + # error and return nothing. + } + @lines; +} + +sub human_to_bytes { + my ($str) = @_; + my %units = ( + kB => 1000, + MB => 1000_000, + GB => 1000_000_000, + TB => 1000_000_000_000, + PB => 1000_000_000_000_000, # I wish I had need for this + ); + $str =~ /(\d+(\.\d+)?)\s+(.B)/; + int($1 * $units{$3}); +} + +sub nvme_list { + # Node SN Model Namespace Usage Format FW Rev + # ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- -------- + # /dev/nvme1n1 S464NB0K601188N Samsung SSD 970 EVO 2TB 1 695.50 GB / 2.00 TB 512 B + 0 B 1B2QEXE7 + my %devices; + for (run_nvme('list')) { + if (m:^(/\S+)\s+(\S+)\s+(\S.*\S)\s{3,}(\d+)\s+(\S+\s+.B)\s+/\s+(\S+\s+.B):) { + $devices{$2} = { + device => $1, + sn => $2, + model => $3, + namespace => $4, + usage => human_to_bytes($5), + capacity => human_to_bytes($6), + }; + } + } + \%devices; +} + +sub smart_log { + my ($dev) = @_; + my %info; + for (run_nvme('smart-log', $dev)) { + next if /^Smart Log/; + if (/(.*?)\s+:\s+(.*)/) { + my ($var, $value) = ($1, $2); + $var =~ s/\s/_/g; + if ($value =~ /^\d+(,\d\d\d)+$/) { + $value =~ s/,//g; + } + $info{lc $var} = $value; + } + } + return \%info; +} + +use Data::Dumper; + +my $mode = ($ARGV[0] or "print"); + +my $list = nvme_list(); +if ($mode eq 'autoconf') { + if (keys %{$list}) { + print "yes\n"; + } else { + print "no (no devices to monitor)\n"; + } + exit 0; +} + +my @sn = sort keys %{$list}; + +if ($mode eq 'config') { + my $sn_list = join(' ', @sn); + + print <<'EOF'; +multigraph nvme_usage +graph_title NVME Namespace Usage +graph_order $sn_list +graph_vlabel Percent used +graph_scale no +graph_category disk +graph_info How much space is used +EOF + for (@sn) { + print <<"EOF"; +$_.label $list->{$_}->{device} used +$_.type GAUGE +$_.max 100 +$_.min 0 +EOF + } + print <<'EOF'; +multigraph nvme_bytes +graph_title NVME Bytes Read / Written +graph_order $sn_list +graph_vlabel bytes read (-) / written (+) per ${graph_period}' +graph_category disk +graph_info How much data is read and written +graph_period second +EOF + for (@sn) { + print <<"EOF"; +${_}_r.label $list->{$_}->{device} +${_}_r.type DERIVE +${_}_r.min 0 +${_}_r.graph no +${_}_w.label $list->{$_}->{device} +${_}_w.type DERIVE +${_}_w.min 0 +${_}_w.negative ${_}_r +EOF + } + print <<'EOF'; +multigraph nvme_writecycles +graph_title NVME Write Cycles +graph_order $sn_list +graph_vlabel Cycles +graph_args --logarithmic +graph_category disk +graph_info How much data has been written in lifetime divided by capacity +EOF + for (@sn) { + print <<"EOF"; +$_.label $list->{$_}->{device} write cycles +$_.type GAUGE +$_.min 0 +EOF + } +} else { + for (@sn) { + $list->{$_}->{smart} = smart_log($list->{$_}->{device}); + } + print "multigraph nvme_usage\n"; + for (@sn) { + my $info = $list->{$_}; + my $used = 100 * $info->{usage} / $info->{capacity}; + print "$_.value $used\n"; + } + print "multigraph nvme_bytes\n"; + for (@sn) { + my $info = $list->{$_}; + my $rbytes = $info->{smart}->{data_units_read}; + my $wbytes = $info->{smart}->{data_units_written}; + print "${_}_r.value $rbytes\n"; + print "${_}_w.value $wbytes\n"; + } + print "multigraph nvme_writecycles\n"; + for (@sn) { + my $info = $list->{$_}; + + # The unit size reported is 1000 blocks. + my $cycles = $info->{smart}->{data_units_read} * 512_000 / $info->{capacity}; + print "$_.value $cycles\n"; + } +} From a2267c05edbd006a76332fe871756b4278bb8585 Mon Sep 17 00:00:00 2001 From: Kjetil Torgrim Homme Date: Tue, 2 Oct 2018 10:29:23 +0200 Subject: [PATCH 323/695] more sanity checking. give appropriate feedback to autoconf. --- plugins/disk/nvme | 53 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/plugins/disk/nvme b/plugins/disk/nvme index efb43f46..2bb585bc 100755 --- a/plugins/disk/nvme +++ b/plugins/disk/nvme @@ -1,10 +1,15 @@ -#! /usr/bin/perl -w -# -*- perl -*- +#! /usr/bin/perl +# -*- mode: perl; perl-indent-level: 4 -*- =head1 NAME nvme - Munin plugin to monitor the use of NVMe devices +=head1 APPLICABLE SYSTEMS + +Linux systems with NVMe (Non-Volatile Memory storage attached via PCIe +bus). + =head1 CONFIGURATION The plugin uses nvme(1) from the nvme-cli project to read status from @@ -17,7 +22,7 @@ The plugin does not support alerting. =head1 INTERPRETATION -This is a multigraph plugin which makes three graphs +This is a multigraph plugin which makes three graphs. =head2 nvme_usage @@ -35,7 +40,7 @@ if you write more than you read, you should probably look for archival storage instead. It is a good idea to compare these numbers to I/O counters from -diskstats. If they are much higher, look into if the write +diskstats. If they are much higher, look into whether the write amplification can be due to suboptimal I/O request sizes. =head2 nvme_writecycles @@ -74,20 +79,30 @@ GPLv2 use strict; use Munin::Plugin; +use IPC::Cmd qw(can_run); # Check that multigraph is supported need_multigraph(); +# Return undef if no problem, otherwise explanation +sub autoconf_problem { + return if can_run('nvme'); + if (open(my $mods, '/proc/modules')) { + while (<$mods>) { + return "missing nvme(1)" if /^nvme[^a-z]/; + } + close($mods); + } + return "missing nvme"; # vague message for non-Linux +} + sub run_nvme { my (@cmd) = @_; my @lines; - $ENV{'LC_ALL'} = 'C'; - if (open(my $nvme, '-|', 'nvme', @cmd)) { + if (can_run('nvme') && open(my $nvme, '-|', 'nvme', @cmd)) { @lines = <$nvme>; close($nvme); - } else { - # Perl printed a warning about failed exec already. Ignore - # error and return nothing. + warn "nvme: probably needs to run as user root\n" if $? && $> != 0; } @lines; } @@ -110,8 +125,14 @@ sub nvme_list { # ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- -------- # /dev/nvme1n1 S464NB0K601188N Samsung SSD 970 EVO 2TB 1 695.50 GB / 2.00 TB 512 B + 0 B 1B2QEXE7 my %devices; + + my $recognised_output; + my $lineno = 0; for (run_nvme('list')) { - if (m:^(/\S+)\s+(\S+)\s+(\S.*\S)\s{3,}(\d+)\s+(\S+\s+.B)\s+/\s+(\S+\s+.B):) { + ++$lineno; + if (m:^Node\s+SN\s+Model\s+Namespace Usage:) { + ++$recognised_output; + } elsif (m:^(/\S+)\s+(\S+)\s+(\S.*\S)\s{3,}(\d+)\s+(\S+\s+.B)\s+/\s+(\S+\s+.B):) { $devices{$2} = { device => $1, sn => $2, @@ -120,8 +141,14 @@ sub nvme_list { usage => human_to_bytes($5), capacity => human_to_bytes($6), }; + } elsif ($lineno > 2) { + # could not parse device information + $recognised_output = 0; } } + if ($lineno && !$recognised_output) { + warn "Could not recognise output from 'nvme list', please report\n"; + } \%devices; } @@ -146,12 +173,14 @@ use Data::Dumper; my $mode = ($ARGV[0] or "print"); -my $list = nvme_list(); +my $problem = autoconf_problem(); +my $list = nvme_list(); + if ($mode eq 'autoconf') { if (keys %{$list}) { print "yes\n"; } else { - print "no (no devices to monitor)\n"; + printf("no (%s)\n", $problem || "no devices to monitor"); } exit 0; } From 21c28638e314ac3bf24e0b586f53fd917060e348 Mon Sep 17 00:00:00 2001 From: Mikkel Kirkgaard Nielsen Date: Thu, 4 Oct 2018 19:53:31 +0200 Subject: [PATCH 324/695] Generate cache file if missing Don't exit with error if cache file is missing, instead refrain from sending values and daemonize() in an attempt to generate one. Invocation using 'config' before generation is successful will fail, as mentioned in #914, but configuration will get picked up as soon as it is present. --- plugins/disk/du-2 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index 3dc5e610..b38f5550 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -107,15 +107,16 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { } ##### fetch -open (FILE, "<", $CACHEFILE) or munin_exit_fail(); -while(defined (my $foo = )) { - if ($foo =~ m/(\d+)\s+(.+)/) { - my ($field, $value) = ($2, $1); - clean_path(\$field); - print clean_fieldname($field), ".value ", $value, "\n"; +if (open (FILE, "<", $CACHEFILE)) { + while(defined (my $foo = )) { + if ($foo =~ m/(\d+)\s+(.+)/) { + my ($field, $value) = ($2, $1); + clean_path(\$field); + print clean_fieldname($field), ".value ", $value, "\n"; + } } + close(FILE); } -close(FILE); daemonize(); # From 556c536c26520d7f7ce7b4c383f5f00586b7f350 Mon Sep 17 00:00:00 2001 From: JTSage Date: Sun, 7 Oct 2018 17:07:33 -0400 Subject: [PATCH 325/695] Add a plugin for unifi (Ubiquity) gear Plugins available: * unifi_api - Providing the following graphs: * unifi_clients_by_device - Count the number of clients connected on a per-device basis * unifi_clients_by_network - Count the number of clients connected to each named network * unifi_device_stats - Get basic stats from each unifi device - cpu, memory, load average, and uptime * unifi_xfer_by_device - Transfer stats on a per-device basis * unifi_xfer_by_network - Transfer stats on a per-network basis * unifi_xfer_by_port - Transfer stats per switch port * unifi_xfer_by_uplink - Overall transfer with speedtest stats --- .../unifi_api-clients_by_device-detail.png | Bin 0 -> 20064 bytes .../unifi_api-clients_by_device.png | Bin 0 -> 14774 bytes .../unifi_api-clients_by_type-detail.png | Bin 0 -> 16910 bytes .../unifi_api-clients_by_type.png | Bin 0 -> 15023 bytes .../example-graphs/unifi_api-device_cpu.png | Bin 0 -> 14120 bytes .../example-graphs/unifi_api-device_load.png | Bin 0 -> 30221 bytes .../example-graphs/unifi_api-device_mem.png | Bin 0 -> 9708 bytes .../unifi_api-device_uptime.png | Bin 0 -> 8186 bytes .../unifi_api-xfer_by_device-detail.png | Bin 0 -> 20516 bytes .../unifi_api-xfer_by_device.png | Bin 0 -> 20617 bytes .../unifi_api-xfer_by_network-detail.png | Bin 0 -> 21722 bytes .../unifi_api-xfer_by_network.png | Bin 0 -> 13808 bytes .../unifi_api-xfer_by_port-detail.png | Bin 0 -> 17337 bytes .../example-graphs/unifi_api-xfer_by_port.png | Bin 0 -> 32029 bytes .../unifi_api-xfer_by_radio-detail.png | Bin 0 -> 27335 bytes .../unifi_api-xfer_by_radio.png | Bin 0 -> 14718 bytes .../unifi_api-xfer_by_uplink.png | Bin 0 -> 12183 bytes plugins/ubiquiti/unifi_api | 1212 +++++++++++++++++ 18 files changed, 1212 insertions(+) create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-clients_by_device-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-clients_by_device.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-clients_by_type-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-clients_by_type.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-device_cpu.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-device_load.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-device_mem.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-device_uptime.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_port-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_port.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_radio-detail.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_radio.png create mode 100644 plugins/ubiquiti/example-graphs/unifi_api-xfer_by_uplink.png create mode 100755 plugins/ubiquiti/unifi_api diff --git a/plugins/ubiquiti/example-graphs/unifi_api-clients_by_device-detail.png b/plugins/ubiquiti/example-graphs/unifi_api-clients_by_device-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3d44a5f460245e4ced11056a275b2225438bbd GIT binary patch literal 20064 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-clients_by_device.png b/plugins/ubiquiti/example-graphs/unifi_api-clients_by_device.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac539f7c788164b96e6b48ffc6ac0a1b17252b7 GIT binary patch literal 14774 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 Y8g1uu8y1=kgaJMz#AHQFKk54XUv_Yn;s5{u literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-clients_by_type.png b/plugins/ubiquiti/example-graphs/unifi_api-clients_by_type.png new file mode 100644 index 0000000000000000000000000000000000000000..8d74aaf019677a27732088107f9470826d398536 GIT binary patch literal 15023 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 M8D;4zNu$931x?mK5&!@I literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-device_cpu.png b/plugins/ubiquiti/example-graphs/unifi_api-device_cpu.png new file mode 100644 index 0000000000000000000000000000000000000000..0e5e7ef215be35af3268ad526aa8be9dfd5597c5 GIT binary patch literal 14120 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-device_load.png b/plugins/ubiquiti/example-graphs/unifi_api-device_load.png new file mode 100644 index 0000000000000000000000000000000000000000..ebecac8cc84d6a528267eb210381bd55886fd01f GIT binary patch literal 30221 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 J6(UAI{}1yp_BsFn literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-device_mem.png b/plugins/ubiquiti/example-graphs/unifi_api-device_mem.png new file mode 100644 index 0000000000000000000000000000000000000000..c6fc0073c1603f2ae404119b9e82fd0d0d5315e1 GIT binary patch literal 9708 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

    _vS%_o4m9DjR{`6{Bb__Yry}8R(RGM(GX78-e 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 T`g!%b7J;IynhZ?ZDB%AAGkfSU literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-device_uptime.png b/plugins/ubiquiti/example-graphs/unifi_api-device_uptime.png new file mode 100644 index 0000000000000000000000000000000000000000..92e22c9ab9c34dfbc5aec16314da752b15a3fbeb GIT binary patch literal 8186 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device-detail.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..2ffb1594bc30b04e5028d4e3e1a41f75f2369292 GIT binary patch literal 20516 zcmZU(WmH>H*DXv*2^4}A3vMk^G`N#e+=@eScPs8RNRi@JpcHp^DHbU1?k>fh;&RjH zeco}uJHGtMIQ#6q_FQYtwdPLF2~$#(!bB%QM?ylvl#v!!K|%t+kdR*7q5%*iXsLtV zh_9Cx?-kx7Ayq|VJQ$%Mu3tE-NQohpkC5#lW+N%dt4W-mo~ERv%=fP-k&z({AuX-_ zH8(eZetuqAS-EI$M=a^>?VXyMnxCKd_4PeDIr;PF&(YD*{{DVXPmjO9|JvGGKtRCP zuV161qsPa`9UUEmf`T?SHe6g>Ha9o7x3_n9ce}g0)6>(Bk587CmJSaOv$M0kyu9}I z_GV^gA|fK<Ax%cz$)tA+4YipgIovqfb{@ncmkAo+#Cd0$S z^N#XRhEc4otv_#mwy?0MKCM1`I5S=|#vj8^+E1dNqHk?&{rmWrb%s@MRj;F?qpPbc zdN2Ch?l;#h*RrxQ-d0|Y84jLVp2fw*rn9E@^Y+HZ#=6rwre91{lT^nK$FD|S6&)AN zUe7)}J!Bka_-^}>O^_ufCdTc>NiRx|U5(v7-P&!~efjg{0Slk&hnft8h&$;rue zb#?NK@_mCdJyt zI9fQWs;V@8Yb>uUTl88yJwN@4iIMM+_Z##J9S+sZq!Dftj*E+1xLN35=#L$ZonM}B zXlRTaiL}n;IjF14$jETb6KiQ{sjT!Jo4N5TkeykcIbK=$TByp}%$ikUw^&e+l$2zc z&775$HPSYD*w&a(VpLe+I?|YG!^<00@~NvnI#R*y!H&-I@}eueaumL>;(a7Ykry?tMXghYiTBmQ2^W8qNm$c9vkgz|%Z znV}4mT=mLZ!I^Jn^WiLmZUh-`77gROB5|m>u*OmIz@2ZYVPF!zP)tIybv}9}+1-Me zk5(svVcxH(85jB%dT6c9_q1p|3XMA9Qtlz4BYwyrm$7;i=$lqgxQN|9GOge4s}w_%Gbv5rRj=s z`~h^>a7{2~ZAqF-4BbbPy3W|XF*R3ol$`(FFo%rYOGh`lM3d`G82-5SLi(KiR^n*+ z$$@EM>_^kUij9HT-wC^JroB5lSkg(h!NlT)88%aCJa&N&dlC69r^ywSo|3 z&WR?!JfqH!1Jn;U;(CWqTVzhXK8%Mx(e96(HSDzStr7uyND|-rNV-@tt6JBl-u$pd zdz)r+Gs$G3Li!?&oMJ@r1cm@XqLmW%(F^UdQQ$*laDQX>uWT|DzMD28rg<+Rdqz9f z4o*i|-};!I>&Ni=7AHsHwIT*H%qH&*2f9vEAkXr}`j*)u=lJAP@ut3zLlX2Tdcx^( zG^OMXn^&@N@sJZGVRcUQ=S@|V24c_CwfGNLTGqc!USl@Zr187E*@36cUkZIXcz0Z4 z-1BaBQb7W5HF~pZ%Di1qB;@ze3vK{EZQGmiT&{5SUlav?F=>(z!~{p`W#08Niyy zQt7St{YegE93tHn{Yu8~y0nfX8IP3ry&t9>-rH`e3}l;U3Pdsq2xhaeHHOCoe~KF< z*%rO9rsbpK{y;R!AV!h+VlDx%$E!)RRxfG*jcTlwO zQeoQ>q`P-Us~?Bd?$2gJRq~Xn+E=kr-A03O;(BDPwLp|CO7A7Y#o7-d1z3QlxZIj= z!hNFbkpmAm?-~ot3{$Jd)YJ#zPN;V?n^cyW?m23M&?5B~gkv$@^rlYg2Q-u{ZLbJE z8k%>|CcB&7+r{2~>vWxl*K3mL1_NbeX)O961SZTz-a6iD5caiCgvP{Il$*C>87wag z?rN>TlWhzt-z`j9ZlXISno4!e=qQ|P!npvNb(4FTL$K1k5Jl5lTrpk(s8WbsD1n8}j5C2Osbvq-rS)lRo;<5!qU$x9&m zS(xLNTs(2;55vxh$lfg^hjg|LtY2|>^BmkUXWtWm{j*NSUtg12mxmIsxX=~u*$^AXs}vC>m8<0 z2D}1^GpHw#=IWhFCnhErC*}${jFXl8fxR%-BSB~HnR?Vpbn|&HqRw1k%`?q(c@QTH z@W#iq{k4;m472qG9xpg=mIa^dHS?7lrl?ed8w>PSO6k3Vv z6tB&tRDf+FY{?Q%p6`-G{G8~_V|QS*#dq1khXUbdnrg>I;5nAkU=_8zvac3YtJt9= zp;Fgbc0a7%b7LoH=NVH`xEzcCq}1>Vt${!xE1lKtjoc-wkIbe2%DyK#V>_27HK&*| zBm{Vrzj@(A>p4BgvxwSc!|zJ6uPRWerpVt&p;Ez+uOW8T6cN)an4gNuv+P_;(xqx$ zs+0EpU(a3oVw0u&-BZBev=<*J>41%&*4rgQKq>e35i1h-E8fOa>}#zUgV|b2sydD4 zs@mABgJ$(UrxMSnWJ6^lXZ>ycqZ1gu`Z!07Q8?ZQk11Yt^cjbPvfyw~nvAJb2FIBf z2y}Eqr{QHo9Quopr=}b?@BQx_;&!?ZQAJ>r+mOWopRLaU&tu)_!MR zvfo>BVKj{0fwHd2{ZX26Fm6u%SJ%l`cX{ittm!5s1WWoIa{Zg;9`>bmW6K@<$nNrv zdJcHTOTSuwLk7#fibY^Eu_kRC%xL0l5}H$eD=31)KVU4Lo3j%JgB$Emq&LGdg(H!W z;4s5}n;K7A98{#WFF0D1X|Fzj5sI1y5<^2mGNeKp^XqfAqyYHey%Ix5B1;24onv6E zzeFPG+B7G)JUEzb7vp>J1vo}De-JrOU2v&+!wqCEMMk>Z?H?W5^4>i- zbT1QFKz$ZrxE*vf;1I(=q4Zg)UPxHh6@V{ht zvm1EYgPg12Ea6j*fs)_G@i81IH^e4BH1&~5Ajl7_)r5??h z=u6uLR8`P=jn9(Ga?NNQI`x&r<1bm+wzp#q1JnXj+^!H7Ju;r|x5tc_kXcS*Ssyf4 zKMB8|BH{BR=&}3KMq+sa`Jr&dGYMCnBJm*j2khFDinO}+J1|{H!p8GhBw`T>FbsIr z-uFYgUEVF$uS`hRPKG%??M^3dJEy|Z=GSFtz5L`#i$g2N^~n!jrIcKQt;NqQmyjgH3GT9^ZvTlJ|_kTc1~DdM4r6h1{pk zCZLKgy=R?onK0lO{A&i$0r%okjh|Ay}<%C=G2A|dTm$9I$=!;zB7sP&r{Vkav(Gw=i8IP^83p*#Rc z0tGCdd1e)`jcKnm+y4jtDj%t!^z~Gvdp*^fXI3^)KXV2?*#p)OYku-?alprXv#cuR zqw8rS!ZrmFTr|J#dN})_J%~{d38`PRHY0ZQi6 zGOBzdW{f7rm9=Ic3t#6ZDDFA$1A#a|*m9_E5~bK(i(kGK$eRuVGePVoWtHgSwJfsj zCi3HX;-8e)m-6>c?eq$yyhhwl=6TbTe~Vn(He`jW-Tf*V-`Y$7)W$(;9_0jxFPgQ-oM0jsT%$vG6 zDNY5>u^17@qtx6)QBc@2;Yp-cnE^5J+yb@lgBa;6KrD*>^Os;)AF_de@4&0N8j&$l z+{8-V$=9>Hn#;&JQNg&7O7>3+H97GncEUlI8s0K-+7Q5FwHi}3H0KUp@$wP=(^q}6 zlXrKPA45K3YLmD@lJaHq)BWFcCXXi@NaN03P9wd~M;~7B-!z#dzX{WVOW{>cUa$BQ zPk**X??)4+UaPfu$#$$`{oAf7@6-o7?aXW4OUuhFE8ooP{R)v$_-^u>ccPUNL+~xJ z*Fc)qX`MGRWn09;=ERorlwdEHMBekA^iSCAI_)D6b}pKT0qny#5fQDvbz^Av=3hSwKDO>JZm-4f#&t1`yRP=Dt|QpGEi~z&FcdvwV7+5$nbKKb+*CNH zmt}T}WLIRAh>}CWU>C@i+K8$eejp_czR|=V4$9NE>w|y$?|ligiF7>_vIBNC@5(oI z8zg^Lg$E%XA2uYVC@qs=C}g0Z@ZK;lLSVM#pIhwGG^Jbbr-{(UadeDYlflB+D`qAd zb1FF%rgEbZmHd&IN`N(OZDOh$6&$R^=-W~i8U&L*f+)*%!lRW%UNEa#d;oSjZJ)Hi zvsmL4ZVJvdcT>-)wHUUF(jKalc3xVQRFNT>VSnDEjv9aV5^QXKv^Y-{6Lu>kAlf2ay_#tL6$f%Ye}cqM2pN%4z$M zKV_$U4I)=mj8Fzf;m5P%Y^Sc;8W~ZA=UvX~@MoQ02W<~jv@lz&iD_C_o5%^W9;K)Z zRpET7Lq+WF&V`3AsAjfL2HY>(@i1hs{1OCHWk{Vju=%$oA`=%UBu0j6Ex)1ag6Tc~ zR!I@dAVRo0ppuP>AviKXTXZ+X3Nj^ z8@~pwNC-An3c<*ULnj8*UkQh|jGYnzM!g*@>Eo)3$Rhk-ydgk+BhRvd1UGCan}q%+gF`6{@DdOK1BCE{5z(DgKv=3uh<%6X zk4OMz?WT8GsGlxq(;QaA<-?Il8&F5zNiD$rzZw8;L(r$%$cKE3)Fd zO_^$SL0?Bc%$e=c>$UY{#h!fN2VQf?aGg_Tr=nzVf41BbRRFq^ejMaJf6)&YfB?Mt z2{)({Y(`!WbqO|kOS?JgD5;dlCT-lnyhuy%?qRKDepEv)k00p5C~v5%;qk*7iiIyk zaXw|fOWs4wDPc{#DlPe=70MZlQvH%s?`sFC!2#4zllD&1Xk;K6axr^lzVUNOE$*jo@>DUpd1?c^ zuHtB^{=?)yWpTYP|5ybT+!hJGtxbi>Cey|KG| zAZ|J>yvB8YW~PQkbIm1GMhGwdnNLMwMleh~`rX^>c^`~OG&!wut1z3(5__0(Z)Y-C zi^Z4BD}u)qF}rnkuoG$uQ%W*MG`iQqzu#i)qPEqm54IIst&?6Jq@mDB?z^(uM~a}i z%|E*~YcAC!tgqr7{)$IEo>~6hW!A?>UXz@+`T$M$@_6g9cbU?Xm*+Rst0%}hEX2Wv^qHAIW;z*@14)+6 z13&YSa^Ulk`$?H&qhemm4{du z+44jJ{_Zz%gA^k9Gz4z+E{GDR^}h!--jAYVMLrmiDA@%=q-dXW=|1uc$#+6`x&Fr4 zZXr;ABf`i_DIPNE#h&^hbVOl2YPXBp8Tnh#QzL2;{-aZ2u}bA1Ki=!U`y085N@8Yc zUi>Y4`tNIpEn^gP#Nu|kA}(%(OCbEgiYDS@`)xi2&dQO0h|$8(*RTM8>DY4lr`ED9 z*$3$nB+f&)nr21-CkNhv8CZX#d`xSgMVwy5`e7`@p=f?AqyOKPqDn6`*sF{JKZ!7t zHZAEh%kL_`STf37`Pv=LVId-W$B@=Rfz{wf%DD6g3{5)e@5Sf_A45K<%UDA)UM!P0S#oOGwwY7z+z2 zB2H#nHmRisWDOROv_!z<+`77s76^!HOv2jlVCN>DnKy%9F(vsv@iJcq@!vg4*ntcDF7LypQ#>bFYQIG?N@cLM;&Phyx&`=Xo-yCMM^Vq!# zW)Mv$Abe3p3kV72b*j<`4qjt_G-H^))GzGKxE9-%N|e&B8J5}#srY{v12g$rf`sYJaxmeY+UNj%%H08HDU(k-n} zqoDZiux`&^%r8T#@u%0wMU@;Pi6?s3*ByB-0Y*X2birNNX{@pK9~Q^3gQ^qo81t{i z+Sf)wrTsBiL8!S4l^tmGn1FRFZ|0nkSHi^|f(vMrfNx!((se^Bp$4D)AHgsMAiFUy zQ0&t{m^~*5^y8Yy-^B1!Y^{YdF~SwvG-gF+I=(?3E<`L8T8EGgLYG{|H=7z6&r$r% z)(m0EFth)3zlOLVSiBX&lpxStwq3CQ5gsI=<94!A&CucS2royr8by8$>_nsghBv6- zm^_wqcl_oWFkAkZ(E^S0STiiSjMqlx-+-}q@~eJ$jh!Z*^IZFtmf~g}r_tMMtSIF% zH))CHXg=V7ao|APS>_-Vt(4sl_!khyrqk=5 zqQomoAfc=`VH`V-B%eA41hwOnu48gs$zAJbgmPg7`}M+U954KcUI84k|K`*UA}Z#S zM@8LlA%O5`ShLsnSV(1#>OxITpb7O?Ttz~9LcEsXpf&clnt#1%>)|Y^7IK%QyYzyA z<`>idR~mFUDpiNzSXc<3cOFd%frP}4dA7Y!{2`%f`#9M9g$CM;Z!l4>rz(^l`)}8h zr85-~AX|s{D_fhL|IPYK3u0mU-J)UMet1RM-M-v{eI5!O^K5Mw_nmqJywJo*9CYXq zFXHzuBM4^c?p_=i3_}6xGB!hS@@YF(Nohe3!{t9^&|wp$DCP#!wWpYOt|Q2Jw65B{+-^1@woA=;bGi|bCw6jrs4%5@}Y z43zABqkIc{tn=%I);0@Hk95Kb0p$rt;7~C}hhxrGqF+;+XACT3oChz=4`%s+UnPSe zx3K$zJmG_DXFgi?&LSttv~kfvpsG{!A?F^dd9RPjW>4fU{a|1~fc}KoM)quz?<~Kl z<5{=|9dPCwabrdx%+cNbpLck{|M9C5m*bQr9u)EF>>v&NY(f>`IKhq+m2S3g#C|cR z9}jvU@}8wsop$Zbh=orv99Gf1jg?F>o|-AD@T9{`wPX?Xncj#OA)C%B8AlD2d8cM$ zKF{^q22Mj|trw;aovriDOfXggkLP~s+;~m;oq2u+SQ9r)XOqf2_>`i)GtoL)lW}mp z;(XGyav~R+p5}afoqj!Xn|5-YcCsV*`($4b0;scfZp;Yry+VoG$ZIhm0MeWsTl{eC zfa4ur{SE^+V&e0BM}wTs8{92N!1C&BhsE!2J;KxiI+cEA^Dd}VK*HB9KIzqlgrs~R z7X!(+(H?ih#Yg}@=0r@4l(h6qcek_LuWMBF*elbQ{rGSI>#n(f3UL5>6r|&7eKV!j z#y0h{UpfPP1R*h~jz3VW4GP1qP6bpxsD;Al%&XjtBYZSz?{j-t>qpJiF1`UJAm-GI$%W?{nP~8BMbwvxb-7OP?IHgK;sLkrH`rTsE?`n9b59(#U~99`7r}Af zVeHmcWNgk0n;>v<%J(!8-zu9Uc=@|}{!6P4k#z#BmA!X9o;R7Ja$Hq_3JR_@MVC}^ zv_J(xj3BqQ_@_GlXa#2$pxfA3LSF#^8#@@Agm0jw`Bx^|Ogg2PHwuM$eQ;iz(rQH8 zA-&!=nD7TKpj)M{*jH&09OZCWU&Ezm2rF2t533aqa>{T(%o_kJ@$nQ*zT%uUHmv0}uRBP)%up0ndJ{%K7FhKcGS~?W=bnmZgg?c{AeStBT%AWXc2r?ok z8t#F9S+k<2?@51`k4Ij_uOJ&s+aM}qiApEx^3Jot$T0cn0`5f!_7u+I9{;w%;uYgy zrAq{AiK_kRlKY1XbQl2Ru{=7=vM^LsbW&6tiS|Fz27wx?xNn2OD)cAw)Dw6jeObroac-cNCmXqeo0IySvu_|E3d$biAcxjzsR0JYs#q5_TZdMwz*TVJew{WEbm zLl?BR@Qr|qGZ|2WcsIU1<>>eVN&&!IwhR3}+W_m-)F;^c6dFKkNcG~Sq@L1C8w0Yb z54TvfXKeb%x!z!{_a=s4j_|5AGCV)z(&u6Xx}Rm439^2`{Q+_0DntdI6P31rZ|`3h z8rqy=oDsSRpPm9^)7f2-xBfWMgo|g=H>57dLpR7#(f#}Ry1&IfpA-o7h*;HrKXOLu zUVb*olOBx^QCc~l-i;Jc&Umk5vZcdDHS>i>=|W(ltyCo%?3s?*F-Yrg5YT-opvcxW zM&@DEApH^S@Zus#5Ld#DO3Lxo`Etm3dM;~j6|H6{>>E+;Wu$89|Go<#@&Jm+87g&4 zC!ZrV`Vp;G?JMT+CIdAox`D4Dw$9c;ZNVBR}_;ZLRb6TY4(T z;v3M`W7K{whp`B-^BO;YE%=PE*{v$6j03;u1EGNmQY1$NA9;w8fr*p4%SagJdVqiwBnpy7Ph@UveE53mevDWHfv?FyuMVCM20GgI3 zq_)3ilM6XNsfrj-F-`bu`Dop?6#^ z!GVW(Us2btlhKU^U#rp4LGaxsaVT9|leO#b;e&tf#O81vBJR=H4 z{7Cayinf|~3KUuCTW(3vIxdYADfD=mAlW5BNn)~iB4F2cwVqr!dTtD;o5#DID%w-c z&rc16Slh<=bo-m})z}D+8jF*p z_Qn;L;q2sa7H)*^B(BE&bhQD)NnhR(Jfd~py}IFWnB4?ePk~p)ehLhoBC>WExrTYw zf0UMZg&4lB@$K~HX*q9SrM?#l0-)m^q>HKUnz+#Ja7~B+u_-R>y5MPz{?g`|?4B@= z9tGEGdtn_sCzE>c4CF{qIm$b02a!eJB(l1so<9Tylj7#%@d~{|2GEi9MKLCd;j>yZw5*L+0*F}7Zj+cf=tXe4)x@l$ zKBEC}e?lCCuq^Ib&`GxDA$1+lzI#q-lSl9)={Ol+P^53@A#*5!*aPPb9cqmQ_Epzr z({VWmxb=`hO#`Wy*&SEV-Q?vE#ShCm@JZ&6h~4q0O;-NuCxB&G)kBo3r;%hA#di5- zV+J-w#*R$NtSI*krQArt61 z9F#ProFM=Up&=n2xMLWatcrM{>95L6 zMD4r3yt|xm8una?&A&m1$zyQ=rf(BgxM;)`!S>2Gf(CTeL}Ah=2at)BYwff*Wr$FBHvIygp4m z>MG~LBa$-e~YCaJ3>)WH)jpigP?%bsf~ zfW!E;lo?LOaKx)EB7@3we=NkpFAuFG^L`5-#+Tz{-jS#Xym`Pf$gMx9WxX`s5o%;we{RkS*PO2>a-=5afQ<{Ks$cQ^i`qIH*Or zNP3i&5Ab6z;bL|iL{?Vo3}yLcDnAM*_M0@%#M|C(m`nY|+yw>%hNtU?m+$4`^Mo1x zv$wZfj|>>y8Fu9a){iwENpW{TcVn7y)pB=`TJ~17WKF12VO5zw0oH~9!!4I&QS27l z@@}}cz)gu(d@xvBrTc}%$6j}TYC+@^IraS3x003k0KE~Sg{2OjXcVp<_}Ld4;Zd>F z&?FYbk-O%@i8!dV8W}MoWq5U_aLknYYm%CT*7#&}*5V4IRCJhpQGF%FhgG#*qs(>1 zTb=u=N(%5?p!u4IR%|+vV&=rmrftD#ww=Y?$O{6Jj(Vd^y0rPnZJEVQk?lfrohAz1 zp>ZRn1!9fMOMqUT4qZ|TW3BUYTLZjw(mx~2X+>sXG#fYxN&`F;F}cw1b6k$$rtPvO zZl~zD^g#POQHYG`jj5`0?Tb3$9~PDN`hRi(X)J)0+D2?ZF{xAOJRE?+&{8%waK@?y z=X)k`A1XbX8Lz1WOx~eIP&wsFgD(JYIj@?*S?=n3=?knC*NxhZ3fTFfNQXQ8GN?t6 z@!owBtW^R5>O(o%$Ac;GI2uZ2)7|b*?wdY+Q$2ZnOd>9fgccL%5gJ>{aElWNlV4P{ zUhynaoHx9i)Mw}&SC-FqHs$1K@Oi!hr9EE89Y&dQVx7eE z`!$emYqTBqa@aDt7w8e)1wVdQEE+Z$dUE&p6vSD1SUvh(FnG&hZHC9pqWUPrT2w^DaXRzv9l7+WrJo_& z31Zt8+#h|=a5#4Q_}0$*Lu5xfssET8_3(VJXOC8FuoVYO`g{5tHyjG7Poi;5k0FI{ zD3MAuVuyH{ofFpp zeD^Bib|Ch6v-kp@NDQX5`BZiGGy!8;#bK7b8gpJ69OU-(Cg|3#%+>vS(O4#qFpf#e zvB+OR2CE9GsmAKPi+~lVJ9N=;AkaQ;r#^^x6WL-iBCsp9RyOs5Y#rBp-Z;_TF#`-l zS#BgFNH3=`+Lfk#dktWHLjlQ75p{^ z3{FkkH~`-am0Fhy8$hM`c6)BF8`-{F-Vk}t?@qLmZ_V6kqZ+r3UjErC^7Au^{l3y% ztG$r)GywVzI^6W#N`WP8{c>)=0jj*zU(M+s9pNAm)+!!?>!N}oQ3xUkUN@NpI714xT^OkO`s{or_T4&2 zHN4yhDfk^%tFV$E*p-SjnNUJ42ov zOWH;HPK!EBIU#n<+C}>%xMA*#24dw{2zKGq0{5ZvD$Q*l<1<*~EHC>=rsLBK(A3zP zj>@ovr12CL2v}VWZDrxrJ-6Rr$>yvQ5t;ui10$eI%mH8jN*?7VSS5Y<#_Cq5GVPv+ zi6(B%Ed~NWy0La@q6V@7hsICKFyD#?i(mE$vXQWJfR z5#i;ryF3s0B_GvQfJ_e}Bc+^65Lz+-bp_~2h=D^}#wBA1(5Lbf%fWs0+@sdT35iwl zO=GY?RP4)nU55O*^~gE#e>o~2+oIQho1W1fuWzk{QUU|n=Am`X8-)nF;GLW_`)-}N z`mja(ndv|Eqbsv=C>pyw3J$S!(Tm^T`Cc1yJ>iU4gcV=oSNVm_RH-0%REuVS)KS{FJN9hbx%$?obmtJMyN{=EuZ(GH~D2Y3Y8f$uu+ePut53^8x=928@Sc$w!m(-%U*de7g~d<7p(7WUO+~cnje{a^_SL znO+$B4lEV&>SVy5WqtIZ3@~LFfhUCjJY%QRauAo8|2#Q*rA|CP<1eb^CA4*)*VVmq zcNm4kY0Dw0!&0hbk?gj436C}f))=WA4u1bOFOp`dl<3=NmQku_4i0c*f>IOL9S#*e z4(s?ARl0qsS5$L7G(KYvlJqU61}m4#`7h7q%05*0?zbYzxG)6KvFBuPX$x~}m4lk| zE0&7yJWH7uG?+Fgax&hkvl8$23PY0hEB#?0pfUY*xwe8|TWgYJ6*X=8rEJ?f9Yitc znKh>qUt4~Z-;Zg7|6iB;(wevg+@tn)s!%JIXJjWq{xojFoJ%KRGTv@i2}Mb+E-vR? zL4v&%VNib1kVb1q%0E3Qmsjw6dQE%Uq*o4}V+?}vsTU#!-y)MsP@%=8-m`)@mX9Dk)<{q5Zff`v7d5YXM(d<5kaS^f@{E6G9|n*H;zbq}Xw@^49_VSf_@(~|8gcSs=8 zE8S+VR8WrqBT7NrvkERJN}hI!d}Pvd$AqZk!9Am83Rhr$9?HbTga+9a5#VfG*Z>Wn zEF+VJX7PG1Z!X1LLalrbVafQVx#>#|4vvQ;QrM&>gCv4Z!o7N>XA>XkL2sn0unNLJ zxjjHP-<*UDv5?zJMFceI4m2i4YJ4N9K{->t=%c#BJISKat|hYvnUUCDzcq4tv#q`c z6~(J=61GQ|Qw5^EcmB$(v{Fx#zU&Ka-DXh_hhdO569jt@hTatwzh(sG(nrQQ5s#R3 zoc@tToU@isk5uD*G?*|O&ewy|U;;Q2lfh2Z0yy+`pS@*!F0y;D{eO~Rw6DPQ*Mg?EvuDB+58CzR=NFH6S%_&PKL9IC_gIci)SI1`Fs z+**IkKP5Fj`$P}&ad2yYk(787gqRby@V|z3loh1EUd(X;e#Gz@t$lT@R*t9ugI^<* zrlAGreBvW2efJwIMo?Ei@m%Pj5w_myaPWq(Cb}ehEEHXW50FP?@IR}Si#geSeQY}& zD2Tbo3=UuhL6$$uKJ<94r3A8jy?tXUT=}i#nb`m_(Ehz+_v~l+4B)=IH@g4MYNq+g zm})~29l)W`axARf%g~EzT2|p|){9D3b1L3TY!&{*#X?_+(sJmV`Yx9Kgusz{pSe)fX|;B zp1M%7L0b}H5YW)Yzx3x8muewTiv#WHf%eYb@GZeVY`~2xB1S~hQ>PP`K_O*ChG=#} zfA=W6wc@pEMdJL1E@=c9cR_*1dXvX0h~zt}*q)ne+7EF7k2BGHY|@#4ch15%4H~i? zePaEx;eu-OiKLvvimC_oM;F@Lu9iGL2Qu?hQz`y$!3@7 zQelxC;Lu^b=vLfmSQKXbRIYqqR$MVbc5cA@)Wb`gWKv6K2IQkVR4G?O2hfl*0YV8Y zgK17xa=!Zs7$JJo)fsyXY))vr+}|+))~cnSVi;kFC)nIM+(TTiU&+*Dm1=Y$-u%>| z47@s5{7(e_qyO5Qc^O!WUWqiP()ItS_T;CSfY|?^_Xyg*6-&V9#PKE}wPZtN>o0Z` zN9A|-jr!OBZ4xSh_&}BWEH`RU7qHXqF)#;o*zKqjh9AKW2xcfUd@GTuH*Yn0*e_+| zcpgsyQF4lU7}f?0Ptedx`N;`JxLIKI$F}RD=422J_Qe|fLYG|i=`|a+73HWb(_ohr zXDTvebS7_vx{8fJ$wP;SMu{-SVc&~hXzAsF$Zt@ssUE)n zRVLq^`wxUr4V9&U3NbXmtm?}V14^^Z&G?NtZSN`^pa!C{5N2&!4M_l^xri3mpiP`C zkjuu7(~v0L5o-Cio8^Y*5BizkxvxQ8e}^~X&j!4>Rk_NjD8=-f_RU#w!vvVX)0x60 znS0xWoaj$>M@mSgG!Q`kbJF)^eq`DceAIZdKwehCi{0mt6V&yVn-ScfSehFUj1M~q z5AYqOImb^;<$9>fO5(ONy#YtT*MDquaXQxQ2c;qU*iK_jlm6Y_TJCHS%4$|817Ms3 zSyV>LYoHn!{fg3?#&Mz2G3?%%-u@CB-IpLEWV|Mkqve^+dxvtd%d_Wrn|SlYxbUlZ8G7LN&~$q&oN;uTJ9kFv6Mb@D=fdA!MJYP^Gvj{g zqV44N_~fm&LHnrb;o#&H#M?uOunbC;ybMG^55VQr7K24P^JIhtUS3rRXSj7%vA1R4PD^w`Hffqcx^Od?`h z!%jXIB|A_WuiS zg1WakZb8ayO->394S_gHAbha}dxUZVDvkkwpWRR==}0-o?&dh{N5XNwnXq<6lnkgOs1;jAqwx-z|(I#g>6! znA%%cXKp;;(*=Gv{J=1?W*=HD7%LaDG}3^Gutfu2WI=fjmJ1kA1bTy-XY1kN`=dD5 z{+Ri{Ik~XtfU?J6O7^XEVG5?$-u+es+KXZU1-`*d-8WLE<-yYGD!din|HbIZm$w`K z8?ghp@lGlN;`E5WBfI}H5!U?i3k=JIYC7dTmh5~iY|3+KxCY^i7SPWJ8RS9;=s)hl zlxKHJa(!FpL@?fH{T9I;o2}<&n5FPDPqfXJLmIud1G%|V;Ck!-15vS$3Gs?s_4+8%f`9_YB74(4V06eB5VY5XB{B(_c z*i#ojxSJGep{%X?UR?*}m2CT+CPzmP+6fh@W zHWDUKBPW^~))r`Ru^yt`XT%Ry8&Q3djU{*D5o+-h``H_LxYG&MIUmRe$&;0q5Dcn- zbtz)yQedt!*zUmUo<6@`H5Ek*LwE694))Iqp;pe@6MgV)nDyqWdxej%tL*XStZKf< zlOwL3>Qm}9lgtm7gQ*sS+kUk1yY@$kc^8E*8MVXQ;s??)k3L@B>9ND%C*?kZPlpE+ zT(dF%r;ICshPv(I-bM(aCYmuwgpeg8KI2*=?n*kUY@*fMn})8(wzwzS<&HqatC?5f*sy`zvcsMH~GiFH#ly>wZu=7qHr_HVzUH4&YMbeuTC?OV~=H`Qm_dZ$W@Q&`;jUL z$HmB`ys20zdAAbLlH%1jK_GfNU*|fCI4%Wa=C>Tr1!cL|pTm!*|3hEQ*{5lE(vwNt zL^l)QD2uor2{LAJF#d*rz5f>uYCU?!evDZvWB?DMR6W1*`q>C`YUTruIe0owf*2Us z3y=E({K=%l&!Xud2x4J!4E}w(Alz?Bi|a^rj7wwN0h~w1zl8-27A^k2^dJ^Z8VHnr zRAeR^gjHr|cPFd<69`&d2|cZPr*)hh%9>qmM$c@)#F8BZJ%ex|vMar5^e2&b9bD$0 zS(;1!xfX8I$zE*W->SjV8H{`UYi#%M?iumr_&a3U=Jg4)huN=ID`>^5e0 zwH=~OnP&EDiL25lsDIv1^lr^F!5ez_K`VBDW;5HzzyJ;MJ-PI#t(VRYvV&e}A<>v* zyqu20q4csvTolmpoI<-75Q2V56zbCo85tO!`p4@?r_7i)O}f|YI`$LQSF2uroUQe8 z436%%v@yIDS)+h94*%Dc1ri3VkBq+niPy2=YY0CVC(Wo?&jeY51TSCMbT=c4SF{X2 zNmUsvBf5TyY+`HBF4xsllCVe^O^D}1oXqerby`fyxap`E<;$#GtrHP!NI`%bXWpew zF6EGj=T*qar{#L5H;b+=2(Wsesy^o>-=1ej!!)0xu0O$nZbFUDo-XKb>Y z3+115<$aulDsZ3lu4G?~4p#<9Wryw!f#5&ee2uI6`S3nEFW^cYnzk!S3fx2#330CB zE;<(WB?Db>`ddYT?2&olq*oH|y7kS&L0}?#)k{|IDueu~L?_eI{JW74oU@NPS&;-! ziHLDo^5Deas9l!AZ&E3IVGzNV-}SB2piv8Nc2od|Wu6%+K{CQ~A)asWWCqgIZ7~n7 zQx4IEe21(5z?U48eVqJl7UOOjmLRYmnWo&;%fDcOM8{%A-KB=-FYC=ju;OPgF`Ipu zBO^coL_?8sJfteOu%+yd*GdvRQRUm3DSU;CQS*O>(F^X^`qiQMrG;e;m3=O4u&xou z#asC`msIRMat_Ojg{b;)vq%1qFkwAHmb`@pr*tgC1mpDU`BkzOhqxwYJw&hw7mU+z zJVn{QhJVmPQgX3o9RL8}Sg2edJDT6O-oBqRw)U1%%T+Di|8<10Q@w#Lthg=EIN)M^ z2jx75Qo;Tht1CGZJ4oKtXkLtA^}TqdZnP49X_oB_n5(~cworB?P1cZYGaP()zj~rg z`uSNmYkOZN%ye(}2DN9eya?C#Jbb~FU3f1f$CtAE!d3oE?4!)G7HPhk80Jkg~cYrN3I$Je{u`iJ$LU73KgIGQJ1_2CE%MuKN za)@+mt!#Rbzq|K;Q|97^B(t93gpQwTR!d8r)#o=(1!H1vnlTe=dh~~IY%WifO~L<3 zR>zhlyvy_Hf)~Ai$V9A!zA|6|h%XC6+LD@`V_XYZ6`eqzJ;e~BpDHP;jvchmnwW`f zlMduY2uBawU`%Igv3fCi5rUWAQi}tsNf?yNemchH5W-x15yOQ_S1b4m?? z35rEfZy)o~0I^%EE`DTbAiaDykrbWMg4p=efS$~*AJctz|f3HR! zeeybCVCko9rRWTIW?L!`*SGXxX^?&v(9i)q8JJU_zs8S^PmMS*AW1>KuCOJU;Z}qXTskD#S0qhJoW}WXjT% zVqXfdc7R%$=#;rJyr%R6dwEX7uc7I);VWuXmWb}91zIv*sj0i_@_pEnXO(0ct~uma zo?K4({hEKUCo`dDW3MKxF3BHKx-l}iGyT9R^D6zz8?8Diw6w%` z@gLW&Dv3xd0z&fnlgh5n(K@!9jVKw)8oW!L=uEwCcWOGgi!bkK785C*b)bYkTK*=% z;{D!S+eWjJw`o36xe-jARUA!2i?7)GOD3oA?2_7cGxksIl|t9GbLq_fa$UC~Pi(_o zfF+c#;M!ut@Y%c2OviD>rBoOro(RYB@xy0j_shS8%{*M-Z5g@dtf98a?^T zFRgoDYwbEGdfav})`{do1yVjmuMZ<~AFD(UbwJ2lE>m}}QZu09W) z>br1*(4H($eNkZ82}xd>n&ew8-lA{yI=zd!#QLd?sO1B(=LO=cmcYDcRi_-o!(WGA z)J*jhimE{V5qag+8iQ4zww#CE-ERy^Y?2gD=c}Bu-vje{bIC&};46x-(kHrp6{X$m zonbPywy++1Eeyr8u$9Xkx!ic8ssi3-*xq}TeK7$R+1M(hbT`^qcRk$7Crsvpt_(a5 zH+LcIdVNTrSOWCvoW%vbH}bLMnaFenOew#NXPG<(4e!0LI3zsAPtUQ+zbB}F+0dst=d=E$udbI7@9*wTKjS(&Opw>$>rJieDin=*J$%iK8_1YQ1p zreyp_4ZVMXR}GtZ+Ytx&-O)n z%knE%+inaEz1X;lzd8Zips~Nvp?&SH-;~<@^!P(Gv9%|-)RuYS)C?yK3^))y+koje zB`()@5Q~O&eRzn}JUO-|$wN=3h(jZh1ng3iqTH1Bow6-+b;n{+O^V;|Y(utK zTozQs&Xy1t+hp2%&$nP1SG|1i=&8(D5oLO=+)-j&?z{B#lsPmcaJ||{EsHi4Z;y&V z0LyJ#=EZ+JuIAkDnZELwK+Q|VL0u1*pC?@ATotB;al`)ZnncU6LS>(3cr8vLH8^|! zip3EUcGn%2J>xPG0=3O+ir!k=FNyP7k|c6yZv?kyJn9xc#LEFzjO5;viu4qLw45d3 ziVEAN;Q0bIGXQ0c8oLIS+~T$^da{z+&Cngl6tVH7*1A@{0lj@YphlJN-GB$5T2_0v zd=lBjq${7nPt4EGV8vB_$i8BdCkJHwJcp${bf(EQ+jd4OZfly?80^NI1Vk{I z6P^#eiH;0RD@_#97Z-;ztf%25)YR!e*GxqL7i4_X$$zYDY=jCZE@*cmoZm7iVIVtB zPa6`>d-tZsF8LhpxElsde?sA14WN$pSoT}`DSy0PXGecUn$kV>(wicuO)+GTIYK{Z z{-#MvF@qAw_w5YMo=7n8TNT|Z_OzILr3M370gCRlMdPf>%jT`yf@PeT9MYj*qQwjm z{*AVlBH}a4dZ+_Y!+_jGN>nKz?MBM+hpFNyKJ~Niz)#L5G-yg$%WvibRbi#(!@D7{E6iw_$E($D$r}fG{DTOf z6VSl@kv(6)CgGz{3wTJ2>BLGDyG={J7#Nf+)Ty!m@sx$*srzvTo3BD!OxkQh&f76D zJwU8cA@>9sw)p@9XqQPxQ0S7i;0R*TL+uLrBr-=1SuZ49eL{tef2d6T!V1KqYd#n9 fchLW9p)1i(bk0NKgsryfj@9})x3x>P9D@D}sg^}> literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_device.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b1cd4d90d1ff0c8505902c4a4e00c80aabafea GIT binary patch literal 20617 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network-detail.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..33f77e6e8c18805a0fc6b21cb79abcb666bce313 GIT binary patch literal 21722 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_network.png new file mode 100644 index 0000000000000000000000000000000000000000..af71a0dedd3cf2303bbd5fe80cd5e0777f87ef0f GIT binary patch literal 13808 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 literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_port.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_port.png new file mode 100644 index 0000000000000000000000000000000000000000..570f625c6dffb7c3a239b513f0933a658da5229b GIT binary patch literal 32029 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νKN-BdNwEBy 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 B7E=HK literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_radio.png b/plugins/ubiquiti/example-graphs/unifi_api-xfer_by_radio.png new file mode 100644 index 0000000000000000000000000000000000000000..f60ae9b9ba1a210b8ab2825c56c0e425dd5f1aa5 GIT binary patch literal 14718 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 G{l5T5yoz!F literal 0 HcmV?d00001 diff --git a/plugins/ubiquiti/unifi_api b/plugins/ubiquiti/unifi_api new file mode 100755 index 00000000..19dc59b9 --- /dev/null +++ b/plugins/ubiquiti/unifi_api @@ -0,0 +1,1212 @@ +#!/usr/bin/perl +# -*- perl -*- + +=encoding utf8 + +=head1 NAME + +unifi_api - Munin plugin to display device and network information from the + Ubiquiti unifi API + +=head1 APPLICABLE SYSTEMS + +Unifi controllors with direct API access + +Controller version 5+ required (tested with 5.8.x) + +WebRTC is not supported at this time + +=head1 CONFIGURATION + +This script uses the multigraph functionality to generate many graphs. As such, there +are a significant amount of available configuration options + +=head2 API Details + +You will need to supply your API login details: + + [unifi_api] + # User name to login to unifi controller API. Default is "ubnt". Ideally, this should + # point to a read-only account. + env.user Controller_Username + + # Password to login to unifi controller API. Default is "ubnt" + env.pass Controller_Password + + # URL of the API, with port if needed. No trailing slash. + # Default is https://localhost:8443 + env.api_url https://unifi.fqdn.com:8443 + + # Verify SSL certificate name against host. + # Note: if using a default cloudkey certificate, this will fail unless you manually add it + # to the local keystore. + # Default is "yes" + env.ssl_verify_host no + + # Verify Peer's SSL vertiicate. + # Note: if using a default cloudkey certificate, this will fail + # Default is "yes" + env.ssl_verify_peer no + + # The human readable name of the unifi site - used for graph titles + env.name Site Name + + # "Site" string - the internal unifi API site identifier. + # default is "default" - found when you connect to the web interface + # it's the term in the URL - /manage/site/site_string/dashboard + env.site site_string + + +=head2 Graph Categories / Host Managment + +Sometimes, you need more control over where the unifi graphs appear. + + env.force_category 0 + # By default, Use standard munin well know categories - + # system: cpu, mem, load, & uptime + # network: clients, transfer statistics. + # + +To use this feature, set "force_category" to a text string (i.e. "unifi"). + +This is very helpful if your graphs are going to appear inside another host - for instance +if your munin graphs for that host are monitoring the host the controller is running on, and +the unifi API instance. + +Sometimes however, you want to monitor either an offsite API, or a cloudkey which, at least by +default, does not run munin-node. In that case, you can actually create a "virtual" munin host to +display only these graphs (or any combination you like). This is documented in the main munin docs, +but in a nutshell: + +In your munin-node plugin configuration: (Something like: /etc/munin/plugin-conf.d/munin-node) + + [unifi_api] + host_name hostname.whatever.youlike + env.force_category unifi + +And, in your munin master configuration: (Something like: /etc/munin/munin.conf) + + [hostname.whatever.youlike] + address ip.of.munin.node + +Make sure you do *not* set "use_node_name" on this new host. It may be nessesary to define "host_name" +in your munin-node configuration as well, if you have not already (Likely, on a multi-homed host, this +has been done to keep munin-node from advertising itself as localhost) + +More information: + + * L + + +=head2 Toggling of graphs / Individual options + +You can turn off individual graphs. A few graphs have extra configuration +options. + +By default, everything is enabled. Set to "no" to disable + + [unifi_api] + # Show device CPU utilization + env.enable_device_cpu yes + + # Show device memory usage + env.enable_device_mem yes + + # Show device load average (switches and APs only) + env.enable_device_load yes + + # Show device uptime + env.enable_device_uptime yes + + # Show number of clients connected to each device + env.enable_clients_device yes + # Show detailed graphs for each device (per device graphs) + env.enable_detail_clients_device yes + + # Show number of clients connected to each network type + env.enable_clients_type yes + # Show detailed graphs for each client type (per type graphs) + env.enable_detail_clients_type yes + # Show unauthorized / authorized client list + # if you are not using the guest portal, this is useless + env.show_authorized_clients_type yes + + # Show transfer statistics on switch ports + env.enable_xfer_port yes + # Show detailed graphs per switch port + env.enable_detail_xfer_port yes + # Hide ports that have no link (When set to no, unplugged ports will transfer 0, not be undefined) + env.hide_empty_xfer_port yes + + # Show transfer statistics per device + env.enable_xfer_device yes + # Show detailed graphs for each device + env.enable_detail_xfer_device yes + + # Show transfer statistics per named network + env.enable_xfer_network yes + # Show detailed graphs for each named network + env.enable_detail_xfer_network yes + + # Show transfer statistics per radio + env.enable_xfer_radio yes + # Show detailed graphs for each radio + env.enable_detail_xfer_radio yes + + +=head1 CAPABILITIES + +This plugin supports L + +=head1 DEPENDENCIES + +This plugin requires munin-multiugraph. + +=over + +=item WWW::Curl::Easy + +Perl extension interface for libcurl + +=item JSON + + JSON (JavaScript Object Notation) encoder/decoder + +=back + +=head1 PERFORMANCE CONCERNS + +The main performance concern on this is the huge number of graphs that may be +generated. Using the cron version of munin-graph may hurt a lot. + +A bit of a case study: + + | My Site | UBNT Demo +--------------------------------------- +Devices | 8 | 126 +AP's | 4 | 118 +24xSwitch | 1 | 5 +8xSwitch | 2 | 2 +Output Bytes | 64,262 | 431,434 +Output Lines | 1,761 | 14,586 +Output Graphs | 77 | 530 + +So, just note that the growth in the amount of graphed date can be extreme. + + +=head1 LICENSE + +Copyright (C) 2018 J.T.Sage (jtsage@gmail.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +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 L. + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities= + +=cut + +use warnings; +use strict; +use utf8; +use Munin::Plugin; + +# Check dependancies +my @errorCode; +my $me = (split '/', $0)[-1]; + +if (! eval {require JSON; JSON->import(); 1; } ) { + push @errorCode, "JSON module not found"; +} +if (! eval {require WWW::Curl::Easy; 1;} ) { + push @errorCode, "WWW::Curl::Easy module not found"; +} + +# Fail on not found dependancies +if ( @errorCode != 0 ) { + die "FATAL:$me: Perl dependancies not installed (", join(", " => @errorCode), ")\n"; +} + +# Multigraph cabability is required for this plugin +need_multigraph(); + +# Somewhat (in)sane defaults for host, pass, etc +my %APIconfig = ( + 'user' => env_default_text('user' , 'ubnt'), + 'pass' => env_default_text('pass' , 'ubnt'), + 'api_url' => env_default_text('api_url' , 'https://localhost:8443'), + 'site' => env_default_text('site' , 'default'), + 'ssl_verify_host' => env_default_text('ssl_verify_host', 'yes'), + 'ssl_verify_peer' => env_default_text('ssl_verify_peer', 'yes'), + 'name' => env_default_text('name' , 'Unnamed Site'), +); + +# The big table of plugin options - see POD documentation for what these do. +my %PluginConfig = ( + 'enable_device_cpu' => env_default_bool_true('enable_device_cpu'), + 'enable_device_mem' => env_default_bool_true('enable_device_mem'), + 'enable_device_load' => env_default_bool_true('enable_device_load'), + 'enable_device_uptime' => env_default_bool_true('enable_device_uptime'), + 'enable_clients_device' => env_default_bool_true('enable_clients_device'), + 'enable_clients_type' => env_default_bool_true('enable_clients_network'), + 'enable_xfer_port' => env_default_bool_true('enable_xfer_port'), + 'enable_xfer_device' => env_default_bool_true('enable_xfer_device'), + 'enable_xfer_network' => env_default_bool_true('enable_xfer_network'), + 'enable_xfer_radio' => env_default_bool_true('enable_xfer_radio'), + 'enable_detail_xfer_port' => env_default_bool_true('enable_detail_xfer_port'), + 'enable_detail_xfer_device' => env_default_bool_true('enable_detail_xfer_device'), + 'enable_detail_xfer_network' => env_default_bool_true('enable_detail_xfer_network'), + 'enable_detail_xfer_radio' => env_default_bool_true('enable_detail_xfer_radio'), + 'enable_detail_clients_device' => env_default_bool_true('enable_detail_clients_device'), + 'enable_detail_clients_type' => env_default_bool_true('enable_detail_clients_network'), + 'hide_empty_xfer_port' => env_default_bool_true('hide_empty_xfer_port'), + 'show_authorized_clients_type' => env_default_bool_true('show_authorized_clients_type'), + 'force_category' => env_default_text('force_category', 0), +); + +# Set up needed API endpoints +my %APIPoint = ( + 'login' => $APIconfig{"api_url"} . "/api/login", + 'device' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/stat/device", + 'wlan' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/rest/wlanconf", + 'sta' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/stat/sta", +); + +my %APIResponse; +my %APIJsonResponse; +my %Data; +my $retcode; + +# Init curl and JSON +my $curl = WWW::Curl::Easy->new() or die "FATAL:$me: WWW::Curl::Easy init failed!\n"; +my $jsonOBJ = JSON->new() or die "FATAL:$me: JSON init failed!\n"; + + +## Fetch the data from the API + +# The rest is a way to use local files from the local disk. Undocumented and not really supported. + +if ( !env_default_bool_true('USE_API') ) { + if (! eval {require File::Slurp; File::Slurp->import(); 1; } ) { + die "Local debug unavailable, File::Slurp CPAN moduel required\n"; + } + foreach ( "./demo-test-files/device", "./demo-test-files/sta", "./demo-test-files/wlanconf" ) { + if ( ! -f $_ ) { die "File not found: $_\n"; } + } + $APIJsonResponse{'device'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/device')); + $APIJsonResponse{'sta'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/sta')); + $APIJsonResponse{'wlan'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/wlanconf')); +} else { + fetch_data(); +} + + +## Process the data + +make_data(); + +if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { + # Do the config step for each set of graphs + do_config_mem(); + do_config_cpu(); + do_config_load(); + do_config_uptime(); + do_config_xfer_by_device(); + do_config_xfer_by_uplink(); + do_config_xfer_by_port(); + do_config_xfer_by_network(); + do_config_xfer_by_radio(); + do_config_clients_by_device(); + do_config_clients_by_type(); + + # If dirtyconfig is not supported, or turned off, exit here. Otherwise, continue to fetch section + if ( !defined($ENV{'MUNIN_CAP_DIRTYCONFIG'}) || !$ENV{'MUNIN_CAP_DIRTYCONFIG'} ) { exit 0; } +} + +# Do the fetch step for each set of graphs +do_values_cpu(); +do_values_mem(); +do_values_load(); +do_values_uptime(); +do_values_xfer_by_device(); +do_values_xfer_by_uplink(); +do_values_xfer_by_port(); +do_values_xfer_by_network(); +do_values_xfer_by_radio(); +do_values_clients_by_device(); +do_values_clients_by_type(); + + + + + + + +####################### +# SUBROUTINES CONFIG # +####################### + +sub do_config_clients_by_type { + # Provide client count by type - CONFIG + if ( !$PluginConfig{'enable_clients_type'} ) { return 0; } + + graph_prologue( + 'unifi_clients_per_network', + 'Clients Connected / Network', + '-l 0 --base 1000', + 'clients', + 'network', + 'Clients connected per type - manually summing these numbers may be meaningful, as clients are often of multiple types' + ); + + foreach ( @{$Data{'typesOrder'}} ) { + print $_ , ".label " , $Data{'types'}{$_}[0] , "\n"; + } + + if ( ! $PluginConfig{'enable_detail_clients_type'} ) { return 1; } + + foreach ( @{$Data{'typesOrder'}} ) { + if ( $Data{'types'}{$_}[1] == 1 ) { + graph_prologue( + 'unifi_clients_per_network.' . $_, + 'Clients Connected : ' . $Data{'types'}{$_}[0], + '-l 0 --base 1000', + 'clients', + 'network', + 'Clients connected via that are of type: ' . $Data{'types'}{$_}[0] + ); + print "users.label Users\n"; + print "guests.label Guests\n"; + } + } + return 1; +} + +sub do_config_clients_by_device { + # Provide client count by device - CONFIG + if ( !$PluginConfig{'enable_clients_device'} ) { return 0; } + + graph_prologue( + 'unifi_clients_per_device', + 'Clients Connected / Device', + '-l 0 --base 1000', + 'clients', + 'network', + 'Clients connected to each unifi device' + ); + + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".label " , $Data{'device'}{$_}->{'label'} , "\n"; + } + + if ( ! $PluginConfig{'enable_detail_clients_device'} ) { return 1; } + + foreach ( sort keys %{$Data{'device'}} ) { + graph_prologue( + 'unifi_clients_per_device.' . $_, + 'Clients / Device : ' . $Data{'device'}{$_}->{'label'}, + '-l 0 --base 1000', + 'clients', + 'network', + 'Clients connected to the ' . $Data{'device'}{$_}->{'label'} . " unifi device" + ); + print "users.label Users\n"; + print "guests.label Guests\n"; + } + return 1; +} + +sub do_config_xfer_by_radio { + # Provide transfer for radios - CONFIG + if ( !$PluginConfig{'enable_xfer_radio'} ) { return 0; } + + graph_prologue( + 'unifi_xfer_per_radio', + 'Transfer / radio', + '--base 1000', + 'Packets/${graph_period}', + 'network', + 'Number of packets transfered per individual radio band' + ); + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; } + + foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) { + print $thisDevice , "_" , $_->{"name"} , "_pack.label " , $_->{"label"} , "\n"; + print $thisDevice , "_" , $_->{"name"} , "_pack.type DERIVE\n"; + print $thisDevice , "_" , $_->{"name"} , "_pack.min 0\n"; + } + } + + if ( ! $PluginConfig{'enable_detail_xfer_radio'} ) { return 1; } + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; } + + graph_prologue( + 'unifi_xfer_per_radio.' . $thisDevice, + 'Transfer / radio : ' . $Data{'device'}{$thisDevice}->{'name'}, + '--base 1000', + 'Packets/${graph_period}', + 'network', + 'Transfered Packets, Dropped / Retried Packets, and Error Packets for the WLAN device: ' . $Data{'device'}{$thisDevice}->{'name'} + ); + + foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) { + print $_->{"name"} , "_pkt.label " , $_->{"type"} , " Packets\n"; + print $_->{"name"} , "_pkt.type DERIVE\n"; + print $_->{"name"} , "_pkt.min 0\n"; + print $_->{"name"} , "_dret.label " , $_->{"type"} , " Dropped / Retries\n"; + print $_->{"name"} , "_dret.type DERIVE\n"; + print $_->{"name"} , "_dret.min 0\n"; + print $_->{"name"} , "_err.label " , $_->{"type"} , " Errors\n"; + print $_->{"name"} , "_err.type DERIVE\n"; + print $_->{"name"} , "_err.min 0\n"; + } + } + return 1; +} + +sub do_config_xfer_by_network { + # Provide transfer for named networks - CONFIG + if ( !$PluginConfig{'enable_xfer_network'} ) { return 0; } + + graph_prologue( + 'unifi_xfer_per_network', + 'Transfer / named network', + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received per each named network' + ); + + foreach my $thisNet ( sort keys %{$Data{'networks'}} ) { + foreach ( "_rxbytes", "_txbytes" ) { + print $thisNet , $_ , ".label " , $Data{'networks'}{$thisNet}->{"label"} . "\n"; + print $thisNet , $_ , ".type DERIVE\n"; + print $thisNet , $_ , ".min 0\n"; + } + print $thisNet , "_rxbytes.graph no\n"; + print $thisNet , "_txbytes.negative " , $thisNet , "_rxbytes\n"; + } + + if ( ! $PluginConfig{'enable_detail_xfer_network'} ) { return 1; } + + foreach my $thisNet ( sort keys %{$Data{'networks'}} ) { + graph_prologue( + 'unifi_xfer_per_network.' . $thisNet, + 'Transfer / named network : ' . $Data{'networks'}{$thisNet}->{'label'}, + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received for the network named: ' . $Data{'networks'}{$thisNet}->{'label'} + ); + foreach ( "rxbyte", "txbyte" ) { + print $_ , ".label Bytes\n"; + print $_ , ".type DERIVE\n"; + print $_ , ".min 0\n"; + } + print "rxbyte.graph no\n"; + print "txbyte.negative rxbyte\n"; + } + return 1; +} + +sub do_config_xfer_by_port { + # Provide transfer for switch ports - CONFIG + if ( !$PluginConfig{'enable_xfer_port'} ) { return 0; } + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; } + graph_prologue( + 'unifi_xfer_per_port_' . $thisDevice, + 'Transfer / port : ' . $Data{'device'}{$thisDevice}->{'label'}, + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received per port on the switch named: ' . $Data{'device'}{$thisDevice}->{'label'} + ); + foreach my $thisPort ( @{$Data{'device'}{$thisDevice}{'ports'}} ) { + foreach ( "_rxbytes", "_txbytes" ) { + print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".label " , $thisPort->{"label"} . "\n"; + print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".type DERIVE\n"; + print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".min 0\n"; + } + print $thisDevice , "_" , $thisPort->{"name"} , "_rxbytes.graph no\n"; + print $thisDevice , "_" , $thisPort->{"name"} , "_txbytes.negative " , $thisDevice , "_" , $thisPort->{"name"} , "_rxbytes\n"; + } + } + + if ( ! $PluginConfig{'enable_detail_xfer_port'} ) { return 1; } + + # Extended graphs + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; } + foreach my $thisPort ( @{$Data{'device'}{$thisDevice}{'ports'}} ) { + graph_prologue( + 'unifi_xfer_per_port_' . $thisDevice . "." . $thisPort->{'name'}, + 'Transfer / port : ' . $Data{'device'}{$thisDevice}->{'label'} . " : " . $thisPort->{'label'}, + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received on port "' . $thisPort->{'label'} . '" of the switch "' . $Data{'device'}{$thisDevice}->{'label'} . '"' + ); + foreach ( "rxbyte", "txbyte" ) { + print $_ . ".label Bytes\n"; + print $_ . ".type DERIVE\n"; + print $_ . ".min 0\n"; + } + print "rxbyte.graph no\n"; + print "txbyte.negative rxbyte\n"; + } + } + return 1; +} + +sub do_config_xfer_by_uplink { + # Provide transfer for unifi uplink - CONFIG + if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; } + + graph_prologue( + 'unifi_xfer_by_uplink', + 'Transfer on uplink : ' . $Data{'uplink'}{'devName'}, + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received on the WAN port of the USG, and the speedtest result of the same port' + ); + + foreach ( "rx", "tx" ) { + print $_ , "_speed.label Speedtest\n"; + print $_ , "_bytes.label Transfered\n"; + print $_ , "_speed.type GAUGE\n"; + print $_ , "_bytes.type DERIVE\n"; + print $_ , "_speed.min 0\n"; + print $_ , "_bytes.min 0\n"; + } + + print "rx_speed.graph no\n"; + print "rx_bytes.graph no\n"; + print "tx_speed.negative rx_speed\n"; + print "tx_bytes.negative rx_bytes\n"; + + return 1; +} + +sub do_config_xfer_by_device { + # Provide transfer for each unifi device - CONFIG + if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; } + + graph_prologue( + 'unifi_xfer_per_device', + 'Transfer / device', + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received per unifi device' + ); + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + foreach ( "_rxbytes", "_txbytes" ) { + print $thisDevice , $_ , ".label " , $Data{'device'}{$thisDevice}->{'label'} , "\n"; + print $thisDevice , $_ , ".type DERIVE\n"; + print $thisDevice , $_ , ".min 0\n"; + } + print $thisDevice , "_rxbytes.graph no\n"; + print $thisDevice , "_txbytes.negative " , $thisDevice , "_rxbytes\n"; + } + + if ( $PluginConfig{'enable_detail_xfer_device'} ) { + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + graph_prologue( + 'unifi_xfer_per_device.' . $thisDevice, + 'Transfer / device : ' . $Data{'device'}{$thisDevice}->{'label'}, + '--base 1000', + 'Bytes/${graph_period} rcvd (-) / trans (+)', + 'network', + 'Bytes sent and received on the unifi device named: ' . $Data{'device'}{$thisDevice}->{'label'} + ); + foreach ( "rxbyte", "txbyte" ) { + print $_ , ".label Bytes\n"; + print $_ , ".type DERIVE\n"; + print $_ , ".min 0\n"; + } + print "rxbyte.graph no\n"; + print "txbyte.negative rxbyte\n"; + } + } + return 1; +} + +sub do_config_uptime { + # Provide device uptime for each unifi device - CONFIG + if ( !$PluginConfig{'enable_device_uptime'} ) { return 0; } + graph_prologue( + 'unifi_device_uptime', + 'Uptime', + '--base 1000 -r --lower-limit 0', + 'days', + 'system', + 'Uptime in days for each unifi device' + ); + + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n"; + } + return 1; +} + +sub do_config_cpu { + # Provide device CPU usage for each unifi device - CONFIG + if ( !$PluginConfig{'enable_device_cpu'} ) { return 0; } + graph_prologue( + 'unifi_device_cpu', + 'CPU Usage', + '--base 1000 -r --lower-limit 0 --upper-limit 100', + '%', + 'system', + 'CPU usage as a percentage for each unifi device' + ); + + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n"; + } + return 1; +} + +sub do_config_load { + # Provide device load average for each unifi device - CONFIG + if ( !$PluginConfig{'enable_device_load'} ) { return 0; } + graph_prologue( + 'unifi_device_load', + 'Load Average', + '-l 0 --base 1000', + 'load', + 'system', + 'Load average for each unifi Access Point or Switch' + ); + + foreach ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$_}->{'type'} eq 'ugw' ) { next; } + print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n"; + } + return 1; +} + +sub do_config_mem { + # Provice device memory usage for each unifi device - CONFIG + if ( !$PluginConfig{'enable_device_mem'} ) { return 0; } + graph_prologue( + 'unifi_device_mem', + 'Memory Usage', + '--base 1000 -r --lower-limit 0 --upper-limit 100', + '%', + 'system', + 'Memory usage as a percentage for each unifi device' + ); + + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n"; + } + return 1; +} + + + + + + + + + + +######################### +# SUBROUTINES VALUES # +######################### + +sub do_values_clients_by_type { + # Provide client count by type - VALUES + if ( !$PluginConfig{'enable_clients_type'} ) { return 0; } + + print "multigraph unifi_clients_per_network\n"; + + foreach ( @{$Data{'typesOrder'}} ) { + print $_ , ".value " , ( $Data{'types'}{$_}[2] + $Data{'types'}{$_}[3] ) , "\n"; + } + + if ( ! $PluginConfig{'enable_detail_clients_type'} ) { return 1; } + + foreach ( @{$Data{'typesOrder'}} ) { + if ( $Data{'types'}{$_}[1] == 1 ) { + print "multigraph unifi_clients_per_network.$_\n"; + print "users.value " , $Data{'types'}{$_}[2] , "\n"; + print "guests.value " , $Data{'types'}{$_}[3] , "\n"; + } + } + return 1; +} + +sub do_values_clients_by_device { + # Provide client count by device - VALUES + if ( !$PluginConfig{'enable_clients_device'} ) { return 0; } + + print "multigraph unifi_clients_per_device\n"; + + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".value " , $Data{'device'}{$_}->{'clients'} , "\n"; + } + + if ( ! $PluginConfig{'enable_detail_clients_device'} ) { return 1; } + + foreach ( sort keys %{$Data{'device'}} ) { + print "multigraph unifi_clients_per_device.$_\n"; + print "users.value " , $Data{'device'}{$_}->{'users'} , "\n"; + print "guests.value " , $Data{'device'}{$_}->{'guests'} , "\n"; + } + return 1; +} + +sub do_values_xfer_by_radio { + # Provide transfer for radios - VALUES + if ( !$PluginConfig{'enable_xfer_radio'} ) { return 0; } + + print "multigraph unifi_xfer_per_radio\n"; + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; } + + foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) { + print $thisDevice , "_" , $_->{"name"} , "_pack.value " , $_->{"pckt"} , "\n";; + } + } + + if ( ! $PluginConfig{'enable_detail_xfer_radio'} ) { return 1; } + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; } + + print "multigraph unifi_xfer_per_radio.$thisDevice\n"; + + foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) { + print $_->{"name"} , "_pkt.value " , $_->{"pckt"} , "\n"; + print $_->{"name"} , "_dret.value " , $_->{"dret"} , "\n"; + print $_->{"name"} , "_err.value " , $_->{"err"} , "\n"; + } + } + return 1; +} + +sub do_values_xfer_by_network { + # Provide transfer for named networks - CONFIG + if ( !$PluginConfig{'enable_xfer_network'} ) { return 0; } + + print "multigraph unifi_xfer_per_network\n"; + + foreach my $thisNet ( sort keys %{$Data{'networks'}} ) { + print $thisNet , "_rxbytes.value " , $Data{'networks'}{$thisNet}->{"rx"} , "\n"; + print $thisNet , "_txbytes.value " , $Data{'networks'}{$thisNet}->{"tx"} , "\n"; + } + + if ( ! $PluginConfig{'enable_detail_xfer_network'} ) { return 1; } + + foreach my $thisNet ( sort keys %{$Data{'networks'}} ) { + print "multigraph unifi_xfer_per_network.$thisNet\n"; + print "rxbyte.value " , $Data{'networks'}{$thisNet}->{"rx"} , "\n"; + print "txbyte.value " , $Data{'networks'}{$thisNet}->{"tx"} , "\n"; + } + return 1; +} + +sub do_values_xfer_by_port { + # Provide transfer for switch ports - VALUES + if ( !$PluginConfig{'enable_xfer_port'} ) { return 0; } + + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; } + print "multigraph unifi_xfer_per_port_$thisDevice\n"; + + foreach ( @{$Data{'device'}{$thisDevice}{'ports'}} ) { + print $thisDevice , "_" , $_->{"name"} , "_rxbytes.value " , $_->{"rx"} , "\n"; + print $thisDevice , "_" , $_->{"name"} , "_txbytes.value " , $_->{"tx"} , "\n"; + } + } + + if ( ! $PluginConfig{'enable_detail_xfer_port'} ) { return 1; } + + # Extended graphs + foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; } + foreach ( @{$Data{'device'}{$thisDevice}{'ports'}} ) { + print 'multigraph unifi_xfer_per_port_' . $thisDevice . "." . $_->{'name'} . "\n"; + print "rxbyte.value " , $_->{"rx"} , "\n"; + print "txbyte.value " , $_->{"tx"} , "\n"; + } + } + return 1; +} + +sub do_values_xfer_by_uplink { + # Provide transfer for unifi uplink - CONFIG + if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; } + + print "multigraph unifi_xfer_by_uplink\n"; + print "rx_speed.value " . $Data{'uplink'}{"rx_speed"} . "\n"; + print "tx_speed.value " . $Data{'uplink'}{"tx_speed"} . "\n"; + print "rx_bytes.value " . $Data{'uplink'}{"rx_bytes"} . "\n"; + print "tx_bytes.value " . $Data{'uplink'}{"tx_bytes"} . "\n"; + return 1; +} + +sub do_values_xfer_by_device { + # Provide transfer for each unifi device - CONFIG + if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; } + + print "multigraph unifi_xfer_per_device\n"; + foreach ( sort keys %{$Data{'device'}} ) { + print $_ . "_rxbytes.value " . $Data{'device'}{$_}->{"rx"} , "\n"; + print $_ . "_txbytes.value " . $Data{'device'}{$_}->{"tx"} , "\n"; + } + if ( $PluginConfig{'enable_detail_xfer_device'} ) { + foreach ( sort keys %{$Data{'device'}} ) { + print "multigraph unifi_xfer_per_device." , $_ , "\n"; + print "rxbyte.value " , $Data{'device'}{$_}->{"rx"} , "\n"; + print "txbyte.value " , $Data{'device'}{$_}->{"tx"} , "\n"; + } + } + return 1; +} + +sub do_values_cpu { + # Provide device CPU usage for each unifi device - VALUES + if ( !$PluginConfig{'enable_device_cpu'} ) { return 0; } + + print "multigraph unifi_device_cpu\n"; + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".value " , ( $Data{'device'}{$_}->{"cpu"} ) , "\n"; + } + return 1; +} + +sub do_values_mem { + # Provice device memory usage for each unifi device - VALUES + if ( !$PluginConfig{'enable_device_mem'} ) { return 0; } + + print "multigraph unifi_device_mem\n"; + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".value " , ( $Data{'device'}{$_}->{"mem"} ) , "\n"; + } + return 1; +} + +sub do_values_load { + # Provide device load average for each unifi device - VALUES + if ( !$PluginConfig{'enable_device_load'} ) { return 0; } + + print "multigraph unifi_device_load\n"; + foreach ( sort keys %{$Data{'device'}} ) { + if ( $Data{'device'}{$_}->{'type'} eq 'ugw' ) { next; } + print $_ , ".value " , ( $Data{'device'}{$_}->{"load"} ) , "\n"; + } + return 1; +} + +sub do_values_uptime { + # Provide device uptime for each unifi device - VALUES + if ( !$PluginConfig{'enable_device_uptime'} ) { return 0; } + + print "multigraph unifi_device_uptime\n"; + foreach ( sort keys %{$Data{'device'}} ) { + print $_ , ".value " , ( $Data{'device'}{$_}->{"uptime"} / 86400 ) , "\n"; + } + return 1; +} + + + + + + + +######################### +# SUBROUTINES GENERAL # +######################### + +sub graph_prologue { + # Generate graph prologues - slightly less copy-pasta, and less chance for things to go wrong + my ( $id, $title, $args, $vlabel, $category, $info ) = (@_); + + print "multigraph $id\n"; + print 'graph_title ' , $title , ' : ' , $APIconfig{"name"} , "\n"; + print "graph_args $args\n"; + print "graph_vlabel $vlabel\n"; + if ( $PluginConfig{'force_category'} ) { + print "graph_category ", $PluginConfig{'force_category'}, "\n"; + } else { + print "graph_category $category\n"; + } + if ( $info ) { + print 'graph_info For the unifi site named "' , $APIconfig{"name"} , "\", $info\n"; + } + return 1; +} + +# Collate all collected data into something we can use. +sub make_data { + foreach my $thisDevice ( @{$APIJsonResponse{'device'}->{'data'}} ) { + # Grab everything we care to know about each device. + $Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) } = { + 'label' => $thisDevice->{'name'}, + 'users' => ($thisDevice->{'user-num_sta'} || 0), + 'guests' => ($thisDevice->{'guest-num_sta'} || 0), + 'clients' => ($thisDevice->{'user-num_sta'} + $thisDevice->{'guest-num_sta'} || 0), + 'tx' => $thisDevice->{'rx_bytes'}, + 'rx' => $thisDevice->{'tx_bytes'}, + 'name' => $thisDevice->{'name'}, + 'uptime' => $thisDevice->{'uptime'}, + 'cpu' => $thisDevice->{'system-stats'}->{'cpu'}, + 'mem' => $thisDevice->{'system-stats'}->{'mem'}, + 'load' => ( $thisDevice->{'type'} eq 'ugw' ? 'U' : $thisDevice->{'sys_stats'}->{'loadavg_1'} ), + 'type' => $thisDevice->{'type'} + }; + + if ( $thisDevice->{'type'} eq 'ugw' ) { # Handle firewall specially, record uplink and networks + foreach my $thisNet ( @{$thisDevice->{'network_table'}} ) { + $Data{'networks'}{ make_safe($thisNet->{'name'}, $thisNet->{'_id'} ) } = { + 'label' => $thisNet->{'name'}, + 'tx' => $thisNet->{'tx_bytes'}, + 'rx' => $thisNet->{'rx_bytes'} + } + } + + $Data{'uplink'}{'devName'} = $thisDevice->{'name'}; + $Data{'uplink'}{'rx_speed'} = $thisDevice->{'speedtest-status'}->{'xput_download'} * 1000000; + $Data{'uplink'}{'tx_speed'} = $thisDevice->{'speedtest-status'}->{'xput_upload'} * 1000000; + + foreach ( @{$thisDevice->{"port_table"}} ) { + if ( $_->{name} eq "wan" ) { + $Data{'uplink'}{'rx_bytes'} = $_->{'rx_bytes'}; + $Data{'uplink'}{'tx_bytes'} = $_->{'tx_bytes'}; + } + } + } + + if ( $thisDevice->{'type'} eq 'usw' ) { # Handle swiches specially - record port stats + my @port_list; + + foreach my $port ( @{$thisDevice->{'port_table'}} ) { + if ( !$PluginConfig{'hide_empty_xfer_port'} || $port->{'up'} ) { + push @port_list , { + 'name' => 'port_' . zPad($port->{'port_idx'}), + 'label' => zPad($port->{'port_idx'}) . '-' . $port->{'name'}, + 'rx' => $port->{'rx_bytes'}, + 'tx' => $port->{'tx_bytes'} + }; + } + } + $Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) }{'ports'} = \@port_list; + } + + if ( $thisDevice->{'type'} eq 'uap' ) { # Handle APS specially - record radio stats + my @theseRadios; + + foreach my $thisRadio ( @{$thisDevice->{'radio_table_stats'}} ) { + my $name = make_safe( $thisRadio->{'name'}, "" ); + my $label = ( $thisRadio->{'channel'} < 12 ) ? '2.4Ghz' : '5Ghz'; + + $_ = $thisDevice->{'stat'}; + + push @theseRadios, { + 'name' => $name, + 'label' => $label . '-' . $thisDevice->{'name'}, + 'pckt' => $_->{$name . '-rx_packets'} + $_->{$name . '-tx_packets'}, + 'dret' => $_->{$name . '-rx_dropped'} + $_->{$name . '-tx_retries'} + $_->{$name . '-tx_dropped'}, + 'err' => $_->{$name . '-rx_errors'} + $_->{$name . '-tx_errors'}, + 'type' => $label + }; + } + $Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) }{'radio'} = \@theseRadios; + } + } # END PROCESSING OF DEVICE DATA + + + # PROCESS NETWORK TYPE DATA + + # -> UNLESS, type graph is disabled. + # + # WHY: if the client list is large (huge. 10,000+), this is CPU intensive + if ( !$PluginConfig{'enable_clients_type'} ) { return 1; } + + $Data{'types'} = { + "wired" => ["Wired Connection", 1, 0, 0], + "wifi" => ["Wireless Connection", 1, 0, 0], + "tuser" => ["Total Users", 0, 0, 0], + "tguest" => ["Total Guests", 0, 0, 0], + "authed" => ["Authorized Guests", 0, 0, 0], + "unauth" => ["Unauthorized Guests", 0, 0, 0], + }; + + $Data{'typesOrder'} = ( $PluginConfig{'show_authorized_clients_type'} ) ? + [ "wired", "wifi", "tuser", "tguest", "authed", "unauth"] : + [ "wired", "wifi", "tuser", "tguest" ]; + + + my @wlans; + + foreach my $thisNet ( @{$APIJsonResponse{'wlan'}->{'data'}} ) { + $Data{'types'}{ make_safe($thisNet->{'name'}, "") } = [ $thisNet->{'name'}, 1, 0, 0 ]; + push @wlans, make_safe($thisNet->{'name'}, ""); + } + + foreach ( sort @wlans ) { + push @{$Data{'typesOrder'}}, $_; + } + + foreach my $client ( @{$APIJsonResponse{'sta'}->{'data'}} ) { + if ( $client->{"is_wired"} ) { + if ( $client->{"is_guest"} ) { + $Data{'types'}->{'wired'}[3]++; + $Data{'types'}->{'guest'}[3]++; + } else { + $Data{'types'}->{'wired'}[2]++; + $Data{'types'}->{'user'}[2]++; + } + } else { + if ( $client->{"is_guest"} ) { + $Data{'types'}->{make_safe($client->{"essid"}, "")}[3]++; + $Data{'types'}->{'wifi'}[3]++; + $Data{'types'}->{'guest'}[3]++; + if ( $client->{"authorized"} ) { + $Data{'types'}->{'authed'}[3]++; + } else { + $Data{'types'}->{'unauth'}[3]++; + } + } else { + $Data{'types'}->{make_safe($client->{"essid"}, "")}[2]++; + $Data{'types'}->{'wifi'}[2]++; + $Data{'types'}->{'user'}[2]++; + } + } + } + + return 1; +} + + +sub fetch_data { + # Set up curl, and login to API + $curl->setopt($curl->CURLOPT_POST,1); + $curl->setopt($curl->CURLOPT_COOKIEFILE,""); # Session only cookie + $curl->setopt($curl->CURLOPT_SSL_VERIFYPEER, (( $APIconfig{"ssl_verify_peer"} =~ m/no/i ) ? 0 : 1) ); + $curl->setopt($curl->CURLOPT_SSL_VERIFYHOST, (( $APIconfig{"ssl_verify_host"} =~ m/no/i ) ? 0 : 2) ); + $curl->setopt($curl->CURL_SSLVERSION_TLSv1, 1); + $curl->setopt($curl->CURLOPT_URL, $APIPoint{'login'}); + $curl->setopt($curl->CURLOPT_POSTFIELDS, q[{'username':'] . $APIconfig{"user"} . q[', 'password':'] . $APIconfig{"pass"} . q['}] ); + $curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'login'}); + $retcode = $curl->perform; + + if ( $retcode != 0 ) { + die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n"; + } + + $APIJsonResponse{'login'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'login'}); + + if ( $APIJsonResponse{'login'}->{'meta'}->{'rc'} ne 'ok' ) { + die "FATAL:$me: Unable to login to API - it said: " , $APIJsonResponse{'login'}->{'meta'}->{'msg'} , "\n"; + } + + # Change method to GET + $curl->setopt($curl->CURLOPT_HTTPGET,1); + + + # Get some API data. + + # Device data + $curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'device'}); + $curl->setopt($curl->CURLOPT_URL, $APIPoint{'device'}); + $retcode = $curl->perform; + + if ( $retcode != 0 ) { + die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n"; + } + + $APIJsonResponse{'device'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'device'}); + + if ( $APIJsonResponse{'device'}->{'meta'}->{'rc'} ne 'ok' ) { + die "FATAL:$me: Unable get device data from API - it said: " , $APIJsonResponse{'device'}->{'meta'}->{'msg'} , "\n"; + } + + # STA (client) data + $curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'sta'}); + $curl->setopt($curl->CURLOPT_URL, $APIPoint{'sta'}); + $retcode = $curl->perform; + + if ( $retcode != 0 ) { + die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n"; + } + + $APIJsonResponse{'sta'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'sta'}); + + if ( $APIJsonResponse{'sta'}->{'meta'}->{'rc'} ne 'ok' ) { + die "FATAL:$me: Unable get sta data from API - it said: " , $APIJsonResponse{'sta'}->{'meta'}->{'msg'} , "\n"; + } + + # WLAN data + $curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'wlan'}); + $curl->setopt($curl->CURLOPT_URL, $APIPoint{'wlan'}); + $retcode = $curl->perform; + + if ( $retcode != 0 ) { + die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n"; + } + + $APIJsonResponse{'wlan'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'wlan'}); + + if ( $APIJsonResponse{'wlan'}->{'meta'}->{'rc'} ne 'ok' ) { + die "FATAL:$me: Unable get wlan data from API - it said: " , $APIJsonResponse{'wlan'}->{'meta'}->{'msg'} , "\n"; + } +} + +# Make field names safe, and lowercase. +# +# Typically, $extraName should be the MAC address of the unique ID identifier as the unifi +# controller software does not enforce that device names or network names are unique. +sub make_safe { + my ( $name, $extraName ) = ( @_ ); + if ( $extraName ne "" ) { + return clean_fieldname(lc($name) . "_" . $extraName); + } else { + return lc(clean_fieldname($name)); + } +} + +# Get a default from an enviromental variable - return text +# +# env_default(, ) +sub env_default_text { + my ( $env_var, $default ) = (@_); + return ( ( defined $ENV{$env_var} ) ? $ENV{$env_var} : $default ), +} + +# Get a default from an enviromental variable - boolean true +# +# env_default_bool_true (, ) +sub env_default_bool_true { + my $env_var = $_[0]; + return ( ( defined $ENV{$env_var} && $ENV{$env_var} =~ m/no/i ) ? 0 : 1 ); +} + +# Quick 2 digit zero pad +sub zPad { return sprintf("%02d", $_[0]); } \ No newline at end of file From 1eb83ab6e6211e95117c68326d24c26595e31038 Mon Sep 17 00:00:00 2001 From: Neraud Date: Tue, 16 Oct 2018 12:38:42 +0200 Subject: [PATCH 326/695] Fixed CONFIGURATION documentation --- plugins/debian/debsecan_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/debian/debsecan_ b/plugins/debian/debsecan_ index a3a381b4..c78a797d 100755 --- a/plugins/debian/debsecan_ +++ b/plugins/debian/debsecan_ @@ -24,9 +24,9 @@ The default configuration is as follows. [debsecan] env.suite jessie - env.fixed_warn 1 + env.fixed_warning 1 env.fixed_critical 1000 - env.remote_warn 1 + env.remote_warning 1 env.remote_critical 10 The name of the group needs to match the name of the symlink to be applied. From 01e7e90dc54977f39f33bae8f87b6e8db277b402 Mon Sep 17 00:00:00 2001 From: Neraud Date: Tue, 16 Oct 2018 12:39:21 +0200 Subject: [PATCH 327/695] Added example graphs for debsecan_pkg --- .../debian/example-graphs/debsecan_pkg-day.png | Bin 0 -> 17391 bytes .../example-graphs/debsecan_pkg-month.png | Bin 0 -> 22360 bytes .../debian/example-graphs/debsecan_pkg-week.png | Bin 0 -> 19486 bytes .../debian/example-graphs/debsecan_pkg-year.png | Bin 0 -> 32601 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/debian/example-graphs/debsecan_pkg-day.png create mode 100644 plugins/debian/example-graphs/debsecan_pkg-month.png create mode 100644 plugins/debian/example-graphs/debsecan_pkg-week.png create mode 100644 plugins/debian/example-graphs/debsecan_pkg-year.png diff --git a/plugins/debian/example-graphs/debsecan_pkg-day.png b/plugins/debian/example-graphs/debsecan_pkg-day.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4461d8ed650dc18544f769404d4552ca3e6d90 GIT binary patch literal 17391 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 literal 0 HcmV?d00001 diff --git a/plugins/debian/example-graphs/debsecan_pkg-month.png b/plugins/debian/example-graphs/debsecan_pkg-month.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1dab7c98c9a399c111b6d6abe1ba1384ead45a GIT binary patch literal 22360 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 literal 0 HcmV?d00001 diff --git a/plugins/debian/example-graphs/debsecan_pkg-week.png b/plugins/debian/example-graphs/debsecan_pkg-week.png new file mode 100644 index 0000000000000000000000000000000000000000..ecdc7fa6039174d9138b5f2cadfe5fd006dba171 GIT binary patch literal 19486 zcmb`vWk8f|*ETvJND3$lDBY+?iFD}DAs_;xNP{3CUD7BB3J6F`iHLMccZWepcT0Eo zw+6lAdER$_`^VmMdov2mT<3M3D~@%nW6f(N1*vnmS8x#s#5oyhab*MoZ4Lfc!A6Jg zlzF~$fTy#1a#G@m6V!hnDw0DG2pWWp_#G9;n57Em$HWfXCnp46rZhvLWuzG5pELpr zdz`!8Xb=%)OPdZ4EG^ljRxM|n&*Q48%$t-B+by{{JuHf!PkywVIi#BZp+~JeS4E

    0^)lwc#G0d!v$Hchdv@ymWHznx zzJ`Vd4%4HfqokxHUh|=#Z`1bn_IK{w!6RkE$HC!DHSoCGe3NwqmXt4g%Z<(a*|S)l zXYYe0v5Car+g<2s|h#DHUS{3aln2y@DhY_r`sw*|aN%Mn|d7pFdAc zZDC;%xW#_`x}f`U`J>D@ew!!q^7z}sWpEiEYB%4KpA8L$jsBN!KKWj~zrdBGmfjY_ zZ?iSu?dj=Bs_kO6Hqn5c&=g1=FZ6tUdAQ7^K!Ab4U}>NL-4j8}5G>u3@$mkmM+3vd zOR#eDVF7At>hB_k42;3j9}^Q>1nl~gFTU*T>av|`4rY`KXMJ3(6oDBg8x|4q;NioT znGMn#k5p7tyi$Ko%6KVRSgbTryR!yM$Hc^}hKnA}DOQ~vtvD>QpLv?aD}UPl6{_5#F?WSIf=%-yMGQBs;NeD_ zXgsgQ$oHt5xdhfFhZgP!gN4rqbF#^hv(=twvMaW)6egPXX8nj0Ca3M{>9OCOz4u!D zYJFOif3fSA@NkBqb%EK7DFnz<^|7U|?WoHmZ4n2?ybYEfU8U3cWV6KczOmKJilNBzJl4ffZ5geqHmhQ(ojGV{Dm}!mu*3z=qc5ZbWgB5x&uA4Wdgk12AjApct<{wN?Ps54T zJo@_Wn^9}bYsIzwgw5A+!Xp*VJHrm64HKloS)XlV*>ax7a@|RglW}s|Pg6~=t*a|5 zE0dFz#kLWs86y-uZ13qQv|CU!6MKf*@N@klNzzL{t|q-J`gu!1QjiZPh_ceGKkvnh z7hr*_jzV5aGBPc--gs*`l(ZK~NbFZegsG|H;ZTX06@)gv-y|R)a9A0M6|hUAYFnTV z@JcWbs4k62wzRWbT3TutiHL~E)~Xoj?Cfl5c{=?y49>8=4E6|oCP9gel+@v9-!5Y< z?19#0QMbdLQTHl1#G`{9QWBEwot*_pHf4^di5O3s%rb#6^v zy}dr(-gEpE!ag!o^F7VJ3tEk8Fhc`9qWmcsA(eow78e)09&FF?PXGG#D!P{6?amh)xel8A(agz)h2^78U;5$xIO zx#*@bycSyUIt`6z*(9gk)p3ZyYsZ+Fn7An4o0^$vdw&bh^l-wD{o3{GVY1V!t2y#a z*V)(zaB+WH%!4f)KnybEdZ?|ft))fP!^g)rJ2s|cVZk2p?aP-hbF;k#SKccpvmJ!$ z5h2aZ%~#4dTBpB7xbKc*9g&FBov&_O{MJ{{RUsZ2+e!g3G;3xhOqLj{S zQemG&BeNi%(pxVwoufE(UnsG}@I1RhqW`pRjZc#<7{Z*OpddhwoSdAU5obc}_)47` zbgICeAH1W@26oUpUf|gPx7m?D z)qTBOM@!39NLbVq6pt%i_}mhCv&k4{hf3aoprD@(SyY=m6ak$?lifU&AHB#m6KydwunG#Q4vngdsSZxYAWn107+$3Mk4? zoK8UWF2etOZ-u3h0+zg-mevTYQ1zOso12@sIL4qg;yO48Y}G@iknx@82=0!!`j-1b z-{-1XRyQ|=Y-ih7S1rZGy|VnV5bfhH2}M>{R~@&yA4rvtAdKr~HdL=&vfo2b!9Ug@FEHqE!qahH*Z@Qc2(sZ>R>{Pw|DED+> zUg&#Kcg}Sj1fp6V|J=EPf`YBhO&Tu@bkD#*!URdq1FKTWM1J>Um*>x)OG)`iCI-tU z$tH0L3s)p0=uXtX2Gg;%vtuk8i!N$ebUrw#aB6DN?dvu54VZ+ba|jYr30YcOudlBo zu?vcdc6N4%)0}dv!T~e-0eWDsI$S?KI>`N~0;#mJvQkDy2EM}ILvQej#J#+8e3aE% z2VdcjPgJ_VzPQw+VSS$z*T&qYsW$h7drmRTi=&t7vMzZh;T;`4 zc=PB8!cL#FX7=ZK`baMCTvtyjSY>fSQ{hJ>2X~jv6n1QNynj{INpU&hHCRwS9N^``ameg_Z584Y5=Vefu3o(=AtBLc0ARF!=6s&8Q|z2pbNt*x7S zO|@U}i#PxN#aA^Y5$DGTPTgvcOei_TZ^8PIU#d@${K>xMtx+s6qgl>)s>!HhZZAbz zSS~{6ivEP{x=_}+`ngv-<0cene}2VAf6GQ7!x;^=0`*`jgP~H=qdNikFttZfHGr1N&wMbAITScV0XEow?cfgSO26vgqUeM zJ$1jnHv$~AXRA3{s=gvVA9xApi zee(I>m95(`zpv*1DJiL8WJClCGX{qG*4C_~B=7bbx(QnVZ&ZQ2hF?Qf)71d?CVl>F zK3uBGa`D>5H)1{njLgg-8rYIF3PuRHrD_H;;^YSp9{9X^r6+N@+r;kKvuEn+k@i^I zEA$Pu8_Ubf;A{T={zW5B=|x2kXoyhJ`VOEVr0>v+#PF8=_4VV@Vzj$^H_6wVh{{c! zMB{3M@DVxoA{hrqg5GEZHZ$wVcgdFu-@bhdSXxU<3p-(Z+g?ga3Kj5d@kvP|E?rk! z94;fJomyNR>g=Syi0ka+6cZbJRm0LuNEj1p+mn_hk%~hc!Jkd|U|SwCeP5{TDxT=X zEU&Dbtd7@}mI`0q1G1Yk<32qE@2^tz2c0zl5yUk4N*3M46po95*S8C{u z(wxS__qXOfT?D|!UiEWc5ELxKA!hROMm=<=yKpF3>Eho)ZnKRMdT#CFQVx}da;m(X zT%B480~3?pf)@4-4Ei9&Kie0%C87*B65`P|cV10B`NyM8h}QvWG_*Ni_nZcXwv&7Wu_8*@z2r~_khBy%+uLRt)*$05KrV-G*Lt*ty0qc7c+3QZ` zoeu{ZeI#g>SM+~v3OX#y#|d4=OLG(&f!YvfL5r0!-kd{e#al*LKVd{p>7g)^g$_A$ zeYioh&MMVV;B{;*ZUO1oTO12|njG?i9 zj$ZGo>E*lCXT5?{fAwY{19v)avNzbgCk?+tEYHuG-+NQT96IcIK{l58{kd<7}iMuFnKcr`WGk1y_B9!p}CoddYKA9+` zaA{KZnff`zZDQZft+lRF(uHyY^Gi0M8WM}_@=j5$zUG?q@GiJB?-NQHlCC*1#5Tv6sd!umP5 z6S5~w(mDHcb*B$NgG{}O$-bS87jU*?a1YOoS74%B37i4{LNL`dyX@Eb5avpq;TIG< z*x$Fl5l^t771?!WK?@bYe-dz+5Bt$>bwUJZfw*}aJ1Il4zrc-m@LER9)|HKJ#pJ9V zWR!A|C3h5X2(IqpL5U;I`trTWWg_j2DX*hkbF-LdPae=st*?lDa$=7>x!kFL8?|Aq zcLLXl=97swy%l6G)o`j(l}nJ?cKxM+G>7rn-Vdr`#2`+B#P|f z-X@BjVpfQ8JlxIIEi;JNKgMp{x9!DxODR0jK9s!Q4q4*!;qQR<>y@4Yz-@Qn0{5y; z4yvTyQq;sLJZmx5rf+cD)iLkhFi8_(yK~on$aqye&#OzKGHjl zn)D4Ht&wI~0jO}Kk z`t^9{qm3(|!ql|1{rU84g?Nz)r>*&ukC|LE|01T7Q zQeJQIYvwD9fQefEd$7Vb3fcxiXCoUOfv za(7C#?sj7d>zQk_F5t*o&0f83^DysIS;lw6mUnA}R3AQkFwYEUKBi7Q-R{EJ>eq6E z&Tpw5A_XHvpA8r@%ZtTvx&o<+!gn*gz_Y=%joRXb2ZlRQ6-K-9upL{N?EU-qi_C|w zUBtbF+mImU^DZQ$1w!AFwcrRB8ZzYuOKq~7*8+f`jqDRz$?b+xsuPCX$pypR_$fQ~ zofk4C5x--=?7$u4ubLs#TFpw|=YS=3oDATFI(;=9azb|H{?{Qp^woDU$ zQdl5I=3@rLv~j6YPl;H&1>AIU$`%Q`ogAV_egAYPvM47bMO^oRcmnkE%*>2QAx642 z`F{Oeyvw@GWTx(!bJE5vdVqzl_;a0pMN# z1LaL^`e{I?k%Y~tRLWtkKy7zqn|}7-Y2AN>*0TegH&H*o@y0XO%gU`^P< z#qjg>DZGsIf_jsQXKd4*OSvnT4ZjN4+q{W%*d}Saite1f(5>S7`LTtbnRdp)Tx7w; zIn5oe##2D0DY)bRXh&J%MZ0IDv&`MQu29KB;M%RhqSkSk*T7t|E*on>hma{Q-kup8 z_f+qr$grm;+g*C$9k1(70umv@Sto)#$&Rh(12dgWA1GI{*P8n2(r;Cg?IlX%x`Uu|)^1xdTc2*RfON;3_ zch=Ljk4)ec!A@^u%fvV8V9F~~-=QZpL#=~*{;G{c;L%v8>ui@IwcM#meWs)j{x%IH zFeL>AJ~no-`$=VTGIPQihn|YtG3NHtqQi0M6SjyCug_cUzr-SRadE-ErIp`NvMj+C zK`rcL+nc3XX1^rhw3$*Mf7IhRQYb4-lnm%zKZue(iu>uiI2_8kC?JqKU0qxXEk-MW z%!4xqP!fG74yQe9=Y{5n>Y=n5*zm{i-;)hSB+YhctYD&IeVeaqkkyu!q`1*CBR~Ih z;NF0(#!i4b6)1CvcJIYJWjyTIfv^Da$@_5^wucAaA z|Jdo6KeKmgHF^5xI>UIk$xmTt&V^)+tN(ese+nL56~fc1r-@I{H`9*22Q$N27y0K~ z#)8Mn%!-sooP1GxWxs&Y)Vmr&jV%S&1zCsdT87kR; z{2-h3E>D6K3bCWeG{OfPuKD|L6ThP|d90O_iOIb4O2rHyZF5YBMc36bfW&W*MKVFx z%Ps!=`BfgK)QylW3jUAb-*UegAK|foU^Y@g74eulNpgl~#_!zC}jd$jV!9>&mbgbg-q1ADl+zmTo?p8ILj zTS^G#P|#6QQH_+?%z&NzN6J!v-I>Jj{S#ua7k)A^IeC+diwFs|y0EY?c0ybnm64GV zs>0BB`*CgSpMBZxt(cmgh79+;qhsyYk7#hKzpMPyP+fyvKPQhWsvT~xFR%>MJIPlr(HNAo;GVoOL#6%`hO0Tvb(N=iuFE1a!`;G&lL zC%Ax_*f!3&A8*7z$a=ij{XknL=_fQ?fTwT)zr^=T1T6RWC2gbRNhm1%D~bvVpyS6I zf=*A{b+-CAA$g^qvHU%hsya0K2YDBT6|bS`7#DfC19$@)&s8NKTga|*+wM1leSo}% zhl^`wX2xSaWK4j`gf?NzbXuaJU$@#XwOfEj0>qM&!x_=RzP|c~hA0`&r!@*Mzi*D4 zR0gV1H~;Zgo4Vst(h%5--&TP(pWNB>Kkthx3dl{ugZZYB8$;wXg(b7THUS83xUr5?`>o<}< z+t0~c&lmgn?fIq*!*YKpQvd8gh?PJI1^pSj%XfWII9mA+jy7l-s@}Kt;Q6!YcsDqi zK)^$Q9fE|w`~5L`Dmc=xekYw6%KDzt2uRXUk4lJ(_w}jQxXPfg6#h*2=wDiZQ(Of` z3WX?iMKbh|y=VU_upj*?ubHs7EH>NdeAjF%xd{bLMZYf!F2$&3dz zNlxQ`ATIFn{WxJ~Xb|6oMt-_<)xS3#GH?|5@3i7<&a(GC;hbem2n>^pG=&hvMJQ=d zx&PF_-=Q8Cnv)kiq459Ly3QT!1Rq0t2xu<=77#0;D-2!w|BbsO^8fq7W7{@w-S(y< z?nd)0K^t>_ejj!YS5RQ1Zi3Dwww6xryD{r^Zqf0D+WZ~xC zgq{%yA0TZ-3)o#V^kd85ta=jW^qXvfefZJ5XG;}0Gd&G`W?tw%*s#zrECXbPt`-tz?k>YF z!UvPszMXeYImR#aMb=b+Fra(+t(W{3_OPF?$Ag=dCftx4JNHa*aK|rcWsVm>cOC!} z^p<`_UeI+d9d|8u(xm$Bxgx^YHNsNd|76=C8*yq=sOgfZ{*HP;i2X_Fw__BjO@%Bp zlBj;Kp!sb{?mj=|JSq~*pTbXw1Zowe3+NCW68;2-d`d3>1?OnuTQN{9W(bsj`+qZ* z|I}{3&QAOlZany6MgHG_19neEJKF!{D%DFa0wp44%BfH>u0q?4kXj@|<=RyZThF8X zj=)qDQ!ABDICZ*_sM`od^~uKCa$3KCx3#u@nG)|D{=Z|8dDEPM ziHS+QUT$JyqB?6cksO{>sn?xON z8>5{7gcoa4aM1aXR#7q2e7-wYu$dxqa$M%@YLAA+_)5{U!JhVZnuu@YSFV6OM6w_G zVc9M4EbU>3(*8+QS|ArkP_FAkFRB|kQa!?$yT5Il8~Z8um5m=s;gO1M&CQnf_C2j} zKoCZYiNxZ7uOXN`2ls4UI`EE}1O!S83ZR+=ka6qUwKo8!dLp&I$O`{eU;k=Up0K6& z7UmtMJrdw*+=0N=n&JN)BJ%cXnx zOMhV~nqo;=tiKZ@QF15N|B_vRf&cqG1za=M%AY~FQTRkJP=}lN|LM&+ zjAB}OLo^%7C@9QKO&7?mMg1x%-jX$5d1ld1Aceo+bW1h9I?Ly-dFA&-;Ad)mKL^aF zChO98{yde49*`t3AnJJzN7s3@#4ZlbZF?*!h*)*#~I1V zx3sW|WdG#Wmy5=ogBG1#PuL%(dvRnAxYWW(okM86%ji%Cwf7lY>Hq|FTe3k5YmVTLx%x!gSWT(QYBO=3u z;>_xC){g$FD59NicG!=25`$-CqEngK!Q z)^R2j3!+b&u)UP@^(Vb>h&MHEs>XZzky4Vd>O9c~F42R^e756rCFWXC1XlCFpvx z>QnA*p3j|0Z!zsl2c6=ZHwIi$$;n2xwwowo`53rPX2rOSl$3U3Zpgn*b4d}kr?dIUoYBk>3|9b9z<9ADMbx}@n;i4 zKXn+l#*2cEVx*%Zp~E;oAX@RA-Bjn?ZLK;`cU&SS{sQDv*!2g{(>HZsyK&>$)2Gj& zp$O#DKv$QFnp&er-1qO_f$V{|h1%wVTK~-tQrE&Rob`!QJ;|7*Ey_usfoh{a|7j=a z*#I%1Ky%L)9KcKf??@lKJE)o}kG3^>wrIS73kL~VOKks7dRO!fCl*G{a>v~A^26x} z?U5>X(Q*0>nF;Bbx2U?UAMEiokc-&P5+S8oE>6UoR7qiLR=FMVR_z5uN0TF2L`06E z!wb?rI%G;_<_+j3l$1P7Qq~pDwaG>^vdE(f=-FQeZjvLz=PlBHYu59UFjPUcBN$j% z!2f*7Vy2};1j{5OfH?I!cM@oUd+e}O4!U{f@g;aAYJ#lw$CU^Jx} zoDP7?=`O4lXLCIG*{0c2YNDBx~b9bPJ$X>pR z{_AeY0uCV|w9%+tH#ExWv&;-njN7MF0nQ$ei2edR8IOkLe^ zu&&EJV)tqNVB6lrf#?9(;|z@=bEq)FKQM!wrmT#dwtjLA;=!YmZ*StxU=fU6HbAnr zOzeWUT)%PS95!|ihVXzV|H|CNaxG5PNTLngP9avHdvp|~^|hY<3Odp8 z_`Hu&2lOxQkepP3Obo&Y1vPc{(fvp?;m~)bWo1w^LpMoSNN8eW0x2eTIjBAsrI$vG zDJ)~c(;xA4HL3?*O{s(GagPyjpt{1ASSTtNQGDjVHP;+!G(*3z5HT3PY`+;gI_?5N z>wY*%4fXBQ&u`jd#OZ<`7h9sy+8!T17jBb5U;}JZq6^M1D9F80|GxcJpE~M=N%{Ht zB_(^nQuXKRV})9hT>^Dnk1;oVeWKxe>z=0OkI6MC=KD$X4Alp8Uh)rHm$)4{nVDty z`1k$Xe*l zo(z!3D`0?kjdxz=cTo^^T-AB@>;^KWuyA{<<|TFlFk?V}FflShm10ULT8N>6?N#$C zAYb%woY4JfK^r);Kq3LF32`SUsCww>C+lAmJM!L~^MuvS&Cjo|u72q31oEYhP_%x* z5BW~__;4>-F+Pshf@%c0{W>7vuC=u_5{bNbFE~p?-v|5?U6@-km+pR}Tc#qwT6<0Y{J z-(v-V9A{!>^^dx+O42%9=75ET1#WX5X=i5#N}+;kUQ$|yi@2b>fjz(`+)i?t;QK&*wo+3~94>2Xo3C;$;LRI|>vyoBs|OnOt!Rr% z96UT5*g@>Q{e7)6`@0aiRjbfsadB~hB!fb!%a+2M0~{_44nzWs;O*g`Epw z!@qpFpV%Rtq-;%{N9m0W1`OEO#|ILU`g(ED4OruCNEwMVi-05T!<4|3bxC}@wwc-D zR15Tq3IT#RRe9cUIXq|5d*D|fB?{4gLFL_h~ks@H_M2m&VaiZAEG>uvSPUV6o6X1StH`Z2W>jd^z+# z`=o$ufK&>gZi-}G^U;imh`ydU~>)m|w@3 zLmc_#%LaJLYa(Tqi@+0rz!4UxSzvO>UJg5fFzOV9qoj@&BjqJ12FY7rY5xD%Ny9E<3BY*T8F`3|(Z5We&Cxj)&7 z@u*o|qoL8Uv?QY4oJAo~udsL3%P*P5p8Cj1C;bGaF9-qQ0#zr+2MP)b@Ne+Et*tF^ zJ^)$_JGA6hr?|d9&(IH<0442bvvD$mcy3-^_l+OCFj?XVAUH>4iiFEcWC&%SJajOc z0uStj`T2Qx3z{s!a}Vo&FOB6Q5fKfEAHXt@I%N-7c`7@LwTAN7JCOW2CEgfJ>7C6SJ6Chgqk+Oq1>Gd?*%V)(%K+N*<^8$wXB6b+J^8NcEAt5+7pmfBj z3ND&oS(!;2oyF&QA;zH{9NRd#2B1xXu48@r+n&p@I=LCiE~EE>^l5g-`tlQCfIi88 zB>S38o6@XDEBmI^Vur5pXou`4b%UoLN*B#Kzz^F8GZ>1*vU0M zsvzYm#Wgo}e&^3NIzReGzgp)ezTZ0elosoIFc+9!Ie)SN0TiMaY6#>7Cf-|0LCF#s zNQDrDl$GDX*L(NQ8{!qEW@HTgt_>RWIF}h;Ft;e}+t+6nW8m}D<#1O;87iDW=jgt& z^7dlC4QeFAdzGU3-re2RzjqhV& zVR)YRVEOvj>kzcCY0JmX_JtKeCfstv!$K>7gWxZZR%&;k5zud7xb?EiW%#Dl8*8 zShQI*f#B%FuR}k263*`mZ{#8(%E~IAprmAP{Op)#Me7-B^gqnT;*d7#9CcVkx z*tftJULq`>-Y}8rii;};_P3SaAKz? z>0960f($dT*#89uN`H=;WLS+=qb;6u)8~vWWz^3c0UNG4m&e=^wj=tAOV>|Z%(Bc)Bm4#?T3ZUEPRI^__uoC!yXFYtOM@~i^Q#m!%Wz-tdBT(Z|1?40? z2_;i1et9zpi@ZO;An(hU2iaP&04*KYCTeSuUrEgMJ++m;bPXJVNIfqvPh@W@#NQv^ zO9^DVQ0_*>#2mIT#|t^G0?GdK=ZkLVj$>`+E-%_DW{qDwQ>Ba<5$|%8A8Tsn4$?q( z%T{=H3)s zNb*N8)iqc2SuNe};V&WOR9j=?S>UN5#;;VJh#+C2XAg2x6sLY8j6sQ);Aru|SO<}; z%-9Y6fX&U#`uci^wjd;i4f`H~!S=ZLLx(ZM!{DA9pXr^V<$|yVeZ@~7G+uz-p+_v% z|7UUhlqYLR9mYgRSfgGpA2)YbYb(LR*yQB>d-rHByxt7$hEfoCP)NmaUfqUKgFy@7 zIa;!{vaq-sfJc3{4i5LXIWsJ=jA#gX-h+-BVhPL*1WSKP_pVbPi?F?DQ(BY$n8&Oy zC^|YiJe-Gv!-MFZY!YgO@9S5n2I^t6Awj+8_zfvx%8`rfXU@?h!9S;p%d&tsJu?IJ zm6M}mhISRziHduvg}kZ-Hg)V=Lv?(AI^F#8mv}H!zeWosF&J0_e=jpk> zcsFfJ$irmqAYuaL1j#QzQfy8@I7<=*t&=}};$Vuq#JVc?b>Jo^LV)NMX6$_?H9kE6 zB2Zd^*c+nX6v@V$C*&CzK2UUnfDB?cwAaO(EFjXpAH|+Fmz9%40gC4vPeesU7b}Ul zN*@QHDkZPPM0(=V{>Gn`pX8@Hfr^4krx*+n$}EVibY921O`<3@caS{OAn-Y7u4JIt zC@?`vVW<3bzugdNQl*uKoS%$vq0rIhX>$-@kdTuvtgK|4BBe?-hkJV;+`GpKQwU#7 z3W>m6zlz7ig@k;8nV4W{TbRg$aZnhOVdvv3E-d5$bpVWNLD_|GoFkb->;^-#Fn}g0 z*{ESrAz^gA+_E0p<~T_VPzAk|kb(NiqH^zZjYh!Ls_|*_pPik#B;H7<4P0Sj6L2SV zNawC8$cGdFI zQUDPy4h{_0MZpB`vu8_v8Ml$~AT0sptl#i$*5lIK>cXFFj0wZF&p=)_ZOtFpM6Nn_ zVO>rCs|9Fls_{PSXW#!UUz2`qPPR(vv;_rF8s5Mky9k?g9%94*0&r0abkSB;n$Ak; z>+35hw8FiYS5&}q1$ApNyxL(r_YgWClp-#Uprm6Wtv;7y*-}$O%fbS*+(>6C< z9)=GxK^o+{>>u!8QW>uftCj;c0GjXWIBy}mfxcb9jo#u`&*3Z2GSKAcU*{Ye8bXc1 z4t?`g5W|LfBX?rIhN}JU>^=buCWx0;yPP4#j1m0hP`_uz|Ii(MQJo2n;6@(&WOpkf zIziiceRhQR*2GOigr;%C7|i1RcMVh`+T~o+zD-`jf)$$yd+YY=`{k0y%QG{i{0#|6JzF-5ROqJVM`D%ls~KTdYlZSD)*T2mg} zC1V^ zml8}72)r`X=K?_Rf4=bx#4R6~XE+bp3f!0a8kzX{FALtGMa=}ye2)r&E<7=DqAPtK zu$=%JrBBY@VDpR^tJVP66cQU-0g)6YaWoyIC%ow~(9uq@&9i2=5ZNRETBvC=D58MI zGB)?_=(bbP3v>c3niwt*}yYbE) zSwe> z26tU&S}DI~0=~obesPNLcnqp^m(=gQ1u*+_H0?hUTz#ku4{fn={hx)wkUGFm7;%Q) zWz_vPssT6sqdz-Icj5KwJR3HxNcL59I?c9?kA+*5?4DF@@J$S{4VS5 zAMZ21awd93Ql|a$-uF4~2PGMrWe^W%4pyp~(#muK{77!>PlYH#Q^8I%Z6HTi3~Cmb z&4-%hh1BTW?bE?S2$%5D1-5g_<@W|PVTcN1Cse5oY#;6y`rpd;vmSd8FZtLH{MDOi zA3#u!D3C}{d;iD?z17V2QRwhI*5KE$>!6Mcq!j!G6Rz$@{P?HmlrVz&cn1fMKPAb3 z5RN+G{73My2OztnyJ*Mxxe>3+2ORoC9ZOGzK{2sKk95%e(P}4{7AP8Z8-$9y?0k5h zKM6ou0SVdMIA>b_ip=$@<)3&`;J_!#D@VLtfEeFMaOSMXrJ=sR%am7fp|8P@);Bilma7=Wmc@*&v)w7yzf2nn#tW1fWUK~1 zQkVl*How()^rTNG{of@_;iZ0pB3f#8O2SCHoG3R1U@Wf$yiywWUY!9wYEH7hk%0kf zjQC!+Gi)e7aFS3BWwF!F(2@>)xPK9SzCsDz&U#b7Pse#W>;Xs;iI6bqQ&Z#llOWNj zhHq-}R))e`iC*V~jAf*6G?bO0vWDj7S;z!mKR+>*EiFK*Fu|SUykpMJ&K}~IPnVBp z-hLGqZA+^;N9#r1BeEqOF>D!zca(zmungpZLKLSm1D20Vd17XWe#e&9td71u@uf?4 zv+W6)8bx{%=*?SNR3a|E-2qX;&f5;7u1#+gdT1~HS!5_L6LC!}abt@cA3EF1YS+j=c;2Lsr-+USQ8Z$=m$<2UCu`Y>e zngn4VYbnFvIM38%(j?j!0-i5jXK60-(qxE-$|k`t0XqYIJlJ^`5qAIwpa_C79N;G$ zD?89yDSLX`h7C|JGqztp%yT21y9wh;3%N-RrdLbdNJcYLpvB>{#DGJ5N z<6(ZKKs#IAx^IifQI~W%4sPK!Fx8&IM84_=))}A|kf^x$`111dwl+4M1%gLqG^F$Q zQcj0tL3sQum;Rp`rqP9U)<1*x}1m zR9Q`S11K_5|Ki~j3v2I~d-v4Up~GDcgc_)C;ch0k#zMoxN6Meupt!1?mENSF^02*Y zSxVcmjVM{?76obSBY{wfH;|ck7W;{biC+p!1u;Qep&DDK`_1vHkLcl6wtIy;)CRLK zUke2Tl**2@^oOd7wO&eSILVcUt$F)hVPiVBJyl+}@tY zKCn{e!ky4?oXzd+3EpCThma>vp0vKdwE)8ujWI9rw*rv~DOp(}AS+H!Ux3Ep>IANCucK?&k7!=vYguDAYzqaRuYUruA<%L;AHBG@l$QOfLGXCI7U&K-S0Fem_I^cg z5>mit;S%ku7VUFKP4#yO7F->vI`eb`uyPFJI{HNpy>nag3s!Bp!J;b6pn8%WLHUIdqY3R z$H(+^e3g>l8Y8-M&&BPuq2|LTh)l@&{F#Y`1v{|jJQAkB>4H-}eyklFBp1GF!vMLH zOKPejWarYqNI!pm16%r}oS1C$s00q?|0x(d`Ig<$ z3eXT^SDVxCv;si@y)LhIuw=hCZ*sWNktjZ@u1+87IZ(n1AybNrcVXK>+fiCM2!QxH zE2~O+H%#6_sEMS^Rd)dx53L$tv_&{Lf=DbpU&~ z(8DEERl_nfpBWn)!_>^9%+EF@RcUFmibcS=coD_7QH#2jZzD@#R0k&8y_9a>_Sn9n zn^6Ul+`zyR(b<%exF%wu@mg3u~Bk~zalg9 zM^h7)Gx5cX;6JJsYPFeuv1MC41Y8ya7 zYX)F&UkLy{)c)Jp*azTL{ruoBG)efi&w?69FX15Y;e(@etbj#H={5o8SJ>4HISxG# zL#}?`0PF-P#i3M$&wh}-zZ?6855gnVtYp0A6nkxOlh|!&a%Yi*)e|tg4m01q1nGb( z_YV#VzJ1b(Hz}GmfrwycVG++~MK``|4?|&#;ik||d*J~pbGSL!71sWbN6ZZEXn0t^ zY^x{6)*QX)$lxa+$OnAITm8t?^g0q%=V2$nOMtD?-^hbmUAKUXPc@AjvQsBKOSa?r z9BZ+jJg@%f;rteWQ!z@8_^)&N-##7dKO4L!g!-9CJ-_fPx$ts?jD&)C7E literal 0 HcmV?d00001 diff --git a/plugins/debian/example-graphs/debsecan_pkg-year.png b/plugins/debian/example-graphs/debsecan_pkg-year.png new file mode 100644 index 0000000000000000000000000000000000000000..1d08855ca9bb5a01eb9d67e767b6123a77aa81d5 GIT binary patch literal 32601 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 Date: Tue, 16 Oct 2018 22:27:51 +0200 Subject: [PATCH 328/695] Fixed example graphs file names Renamed from debsecan_pkg-FOO.png to debsecan_-FOO.png --- .../{debsecan_pkg-day.png => debsecan_-day.png} | Bin .../{debsecan_pkg-month.png => debsecan_-month.png} | Bin .../{debsecan_pkg-week.png => debsecan_-week.png} | Bin .../{debsecan_pkg-year.png => debsecan_-year.png} | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename plugins/debian/example-graphs/{debsecan_pkg-day.png => debsecan_-day.png} (100%) rename plugins/debian/example-graphs/{debsecan_pkg-month.png => debsecan_-month.png} (100%) rename plugins/debian/example-graphs/{debsecan_pkg-week.png => debsecan_-week.png} (100%) rename plugins/debian/example-graphs/{debsecan_pkg-year.png => debsecan_-year.png} (100%) diff --git a/plugins/debian/example-graphs/debsecan_pkg-day.png b/plugins/debian/example-graphs/debsecan_-day.png similarity index 100% rename from plugins/debian/example-graphs/debsecan_pkg-day.png rename to plugins/debian/example-graphs/debsecan_-day.png diff --git a/plugins/debian/example-graphs/debsecan_pkg-month.png b/plugins/debian/example-graphs/debsecan_-month.png similarity index 100% rename from plugins/debian/example-graphs/debsecan_pkg-month.png rename to plugins/debian/example-graphs/debsecan_-month.png diff --git a/plugins/debian/example-graphs/debsecan_pkg-week.png b/plugins/debian/example-graphs/debsecan_-week.png similarity index 100% rename from plugins/debian/example-graphs/debsecan_pkg-week.png rename to plugins/debian/example-graphs/debsecan_-week.png diff --git a/plugins/debian/example-graphs/debsecan_pkg-year.png b/plugins/debian/example-graphs/debsecan_-year.png similarity index 100% rename from plugins/debian/example-graphs/debsecan_pkg-year.png rename to plugins/debian/example-graphs/debsecan_-year.png From 39d6b73c5f77174be2820ba566390ff81e8c4e0b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 17 Oct 2018 03:08:25 +0200 Subject: [PATCH 329/695] doc: change link for plugin example graphs to the guide --- plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/README.md b/plugins/README.md index 1bee0c21..7ddc08fd 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -29,7 +29,7 @@ Please note, that this repository is not supposed to be a dumping site for rando 6. pick a suitable [name and location](#Plugin_name_and_location) 7. bonus: * use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins - * add [example graphs](http://munin-monitoring.org/wiki/PluginGallery#Rulesforplugincontributors) for the [gallery](http://gallery.munin-monitoring.org/) + * add [example graphs](http://guide.munin-monitoring.org/en/latest/develop/plugins/plugin-gallery.html#rules-for-plugin-contributors) for the [gallery](http://gallery.munin-monitoring.org/) * support [dirtyconfig](http://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html#plugin-protocol-dirtyconfig) if it is suitable 8. open a [pull request](https://github.com/munin-monitoring/contrib/pull/) with your new plugin or send it attached to an email to the [mailing list](https://lists.sourceforge.net/lists/listinfo/munin-users) From caf3b6c90fc9b0bf675f03bef28fcae48dd6bcba Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Wed, 17 Oct 2018 08:44:02 +0200 Subject: [PATCH 330/695] Nginx error: log files may be empty --- plugins/nginx/nginx_error | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 6372867a..a0ff11b5 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -122,9 +122,13 @@ http_codes[503]='Service Unavailable' do_fetch () { local count status_code declare -A line_counts - while read -r count status_code; do + values="$(awk '{print $9}' "$log" | sort | uniq -c)" + # Log files may be empty due to logrotation + if [ -n "$values" ]; then + while read -r count status_code; do line_counts[$status_code]=$count - done <<< "$(awk '{print $9}' "$log" | sort | uniq -c)" + done <<< "$values" + fi for status_code in "${!http_codes[@]}"; do echo "error${status_code}.value ${line_counts[$status_code]:-0}" From 161f2bb534a69be6be336c30d88c1aa0bcccad26 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Wed, 17 Oct 2018 17:10:21 -0500 Subject: [PATCH 331/695] Added print_{warning,critical} for greater flexibility Updated to allow admin to set custom values for warning and critical levels for all `$state` fields. --- plugins/systemd/systemd_units | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index 33e59115..a8bd3409 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -13,7 +13,14 @@ Linux systems with systemd installed. =head1 CONFIGURATION -None needed. +None needed. You may optionally pass warning and critical values for any of the possible states (active, +reloading, inactive, failed, activating, deactivating) like so: + + [systemd_units] + env.failed_warning 0 + env.failed_critical 5 + env.inactive_warning 10 + env.inactive_critical 20 =head1 AUTHOR @@ -30,6 +37,8 @@ GPLv2 =cut +. $MUNIN_LIBDIR/plugins/plugin.sh + states="active \ reloading \ inactive \ @@ -52,9 +61,13 @@ EOF for state in $states; do echo "$state.label $state" echo "$state.draw AREASTACK" + # Set default alert levels for failed units if [ "$state" = "failed" ]; then - echo "$state.warning 0" - echo "$state.critical 10" + failed_warning="${failed_warning:-0}" print_warning $state + failed_critical="${failed_critical:-10}" print_critical $state + else + print_warning $state + print_critical $state fi done } From e4e5d3631c06801dcd0127e9e7e926906f9f5849 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 17 Oct 2018 20:28:04 -0500 Subject: [PATCH 332/695] Accepted suggestion from maintainer --- plugins/systemd/systemd_units | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index a8bd3409..5d5f15f4 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -37,7 +37,7 @@ GPLv2 =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" states="active \ reloading \ From eda5c9b4b4a83c47d83b3e66e6592ab809df6b96 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Wed, 17 Oct 2018 20:32:44 -0500 Subject: [PATCH 333/695] Implemented minor changes as suggested by maintainer --- plugins/systemd/systemd_units | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index 5d5f15f4..dc12d463 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -21,6 +21,9 @@ reloading, inactive, failed, activating, deactivating) like so: env.failed_critical 5 env.inactive_warning 10 env.inactive_critical 20 + +Note that for failed units, default warning and critical values are set to 0 and 10, respectively. No other +states have default levels set. =head1 AUTHOR @@ -39,6 +42,9 @@ GPLv2 . "$MUNIN_LIBDIR/plugins/plugin.sh" +failed_warning="${failed_warning:-0}" +failed_critical="${failed_critical:-10}" + states="active \ reloading \ inactive \ @@ -61,14 +67,8 @@ EOF for state in $states; do echo "$state.label $state" echo "$state.draw AREASTACK" - # Set default alert levels for failed units - if [ "$state" = "failed" ]; then - failed_warning="${failed_warning:-0}" print_warning $state - failed_critical="${failed_critical:-10}" print_critical $state - else - print_warning $state - print_critical $state - fi + print_warning $state + print_critical $state done } From 0d4ad57fc9970f2fdb8cee1d1dc4f04c45d8aa4b Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Fri, 19 Oct 2018 13:22:20 -0500 Subject: [PATCH 334/695] Added loggrepx_ and service_events --- plugins/logevents/loggrepx_ | 162 ++++++++++++++ plugins/logevents/service_events | 361 +++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 plugins/logevents/loggrepx_ create mode 100755 plugins/logevents/service_events diff --git a/plugins/logevents/loggrepx_ b/plugins/logevents/loggrepx_ new file mode 100755 index 00000000..b75a4417 --- /dev/null +++ b/plugins/logevents/loggrepx_ @@ -0,0 +1,162 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + +loggrepx - Counts the number of matching log lines by log file + +=head1 DESCRIPTION + +This plugin is somewhat of a bash port of the original loggrep plugin, +except that it adds a breakdown of matches per file, rather than aggregating +matches across all files. + +=head1 CONFIGURATION + +The plugin can be included multiple times to create graphs for various +differing kinds of information in your log files. For example, you may +want to monitor the occurrence of 5xx errors in your webserver logs, but +also the occurrence of alert|critical|emergency entries in your syslog. + +You can accomplish this by linking the plugin twice and providing +different criteria for each instance. + +Note that most instances will probably work best when run as root, since +log files are usually (or at least should be) controlled with strict +permissions. + +Available config options include the following: + + env.logfiles - Files to grep (shellglob) (required) + env.regex - Regex to look for (required) + env.title - Graph title + env.warning - Default warning level + env.critical - Default critical level + env.[field]_label - Label to use for a specific logfile + env.[field]_warning - Warning level for specific logfile + env.[field]_critical - Critical level for specific logfile + +NOTE: for any variable with [field] in it, [field] is derived from the +full logfile path by simply replacing all non-alphanumerics with +underscores. For example, the "warning" field for the logfile +\`/var/log/nginx/errors.log\` would be \`var_log_nginx_errors_log_warning\` + +One good way to get these names is to run \`munin-run [plugin-name]\` +after you've configured the required variables and then just copy/pasting +the names from the output. + +=head1 AUTHOR + +Kael Shipman + +=head1 LICENSE + +Copyright 2018 Kael Shipman + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + + + +. "$MUNIN_LIBDIR/plugins/plugin.sh" +LOGFILES="$(IFS= ; for f in $logfiles; do echo "$f"; done)" +title="${title:-Log Matches}" + + + + +function config() { + echo "graph_title ${title}" + echo "graph_args --base 1000 -l 0" + echo "graph_vlabel ${title}" + echo "graph_category logevents" + echo "graph_info Lists number of times the given regex is matched in the given log files per period" + + local nm logfile lbl + while read -u 3 -r logfile; do + nm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + lbl="$(eval "echo \$${nm}_label")" + echo "$nm.label $([ -n "$lbl" ] && echo "$lbl" || echo "$logfile")" + print_warning "$nm" + print_critical "$nm" + echo "$nm.info Lines that match '${regex}' in log file '$logfile'" + done 3< <(echo "$LOGFILES") +} + + + + +function fetch() { + # Load state + touch "$MUNIN_STATEFILE" + . "$MUNIN_STATEFILE" + + local nm logfile prvlines curlines matches + while read -u 3 -r logfile; do + nm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + + # Get running number of lines to determine whether or not the file may have been rotated + prvlines="$(eval "echo \$${nm}_lines")" + if [ -z "$prvlines" ]; then + prvlines=0 + fi + + # Get the current number of lines in the file + curlines="$(wc -l "$logfile" | egrep -o '^[0-9]+')" + if ! [ "$curlines" -eq "$curlines" ] &>/dev/null; then + curlines=0 + fi + + # If the current line count is less than the previous line count, we've probably rotated. + # Reset to 0. + if [ "$curlines" -lt "$prvlines" ]; then + prvlines=0 + else + prvlines=$((prvlines + 1)) + fi + + # Get current number of incidents + matches="$(tail -n +"$prvlines" "$logfile" | egrep -c "${regex}" || true)" + + # Echo the value + echo "$nm.value $matches" + + # Update the statefile + sed -i "/^$nm.*/d" "$MUNIN_STATEFILE" + echo "${nm}_lines=$curlines" >> "$MUNIN_STATEFILE" + done 3< <(echo "$LOGFILES") + return 0 +} + + + + + +case "$1" in + autoconf) echo no; exit 0 ;; + config) config ;; + *) fetch ;; +esac + diff --git a/plugins/logevents/service_events b/plugins/logevents/service_events new file mode 100755 index 00000000..c3fc0ace --- /dev/null +++ b/plugins/logevents/service_events @@ -0,0 +1,361 @@ +#!/bin/bash + +set -e + +: << =cut + +=head1 DESCRIPTION + +service_events - Tracks the number of significant event occurrences per service + +This plugin is a riff on the loggrep family (\`loggrep\` and my own \`loggrepx_\`). +However, rather than focusing on single log files, it focuses on providing +insight into all "significant events" happening for a given service, which +may be found across several log files. + +The idea is that any given service may produce events in various areas of +operation. For example, while a typical web app might log runtime errors +to it's app.log file, a filesystem change may prevent the whole app from +event being bootstrapped, which may be logged in an apache log or in syslog. + +This plugin attempts to answer the question, "how is my service doing?". +Unfortunately, it won't help you trace down exactly where the events are +coming from if you happen to be watching a number of different logs, but +it will at least let you know that something is wrong and that action +should be taken. + +The plugin can be included multiple times to create graphs for various +differing kinds of services. For example, you may have both webservices +and system cleanup services, and you want to keep an eye on them in +different ways. + +You can accomplish this by linking the plugin twice with different names +and providing different configuration for each instance. + +=head1 CONFIGURATION + +Configuration for this plugin is admittedly complicated. What we're doing +here is defining groups of logfiles that we're searching for various +kinds of events. It is assumed that the _way_ we search for events in the +logfiles is related to the type of logfile; thus, we associate match +criteria with logfile groups. Then, we define services that we want to +track, then mappings of logfile paths to those services. + +(Note that most instances will probably work best when run as root, since +log files are usually (or at least should be) controlled with strict +permissions.) + +Available config options include the following: + + Plugin-specific: + + env._logfiles - (reqd) Shell glob pattern defining logfiles of + type + env._regex - (reqd) egrep pattern for finding events in logs + of type + env.services - (optl) Space-separated list of service names + env.services_autoconf - (optl) Shell glob pattern that expands to paths + whose final member is the name of a service + env._logbinding - (optl) egrep pattern for binding to + a given set of logfiles (based on path) + env._warning - (optl) service-specific warning level override + env._critical - (optl) service-specific critical level override + + Munin-standard: + + env.title - Graph title + env.vlabel - Custom label for the vertical axis + env.warning - Default warning level + env.critical - Default critical level + +For plugin-specific options, the following rules apply: + +* is any arbitrary string. It just has to match between _logfiles + and _regex. Common values are "apache", "nginx", "apt", "syslog", etc. +* is a string derived by passing the service name through a filter + that removes non-alphabet characters from the beginning and replaces all non- + alpha-numeric characters with underscore (\`_\`). +* logfiles are bound to services by matching _logbinding on the full + logfile path. For example, specifying my_site_logbinding=my-site would bind + both /var/log/my-site/errors.log and /srv/www/my-site/logs/app.log to the + defined my-site service. + +=head2 SERVICE AUTOCONF + +Because services are often dynamic and you don't want to have to manually update +config every time you deploy a new service, you have the option of defining a +glob pattern that resolves to a collection of paths whose endpoints are service +names. Because of the way services are deployed in real life, it's fairly common +that paths will exist on your system that can accommodate this. Most often it +will be something like /srv/*/*, which would match all children in /srv/www/ and +/srv/local/. + +If you choose not to use the autoconf feature, you MUST specify services as a +space-separated list of service names in the \`services\` variable. + +=head2 EXAMPLE CONFIG + + [service_events] + user root + env.services_autoconf /srv/*/* + env.cfxsvc_logfiles /srv/*/*/logs/app.log + env.cfxsvc_regex error|alert|crit|emerg + env.phpfpm_logfiles /srv/*/*/logs/php-fpm*.log + env.phpfpm_regex Fatal error + env.apache_logfiles /srv/*/*/logs/errors.log + env.apache_regex error|alert|crit|emerg + env.warning 1 + env.critical 5 + env.my_special_service_warning 100 + env.my_special_service_critical 300 + + +=head1 AUTHOR + +Kael Shipman + +=head1 LICENSE + +MIT LICENSE + +Copyright 2018 Kael Shipman + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + + +# Get list of all currently set env variables +vars="$(printenv | sed -r "s/^([^=]+).*/\1/g")" + +# Certain variables MUST be set; check that they are (using bitmask) +setvars=0 +reqvars=(_logfiles _regex) +while read -u 3 -r v; do + n=0 + while [ $n -lt "${#reqvars[@]}" ]; do + if echo "$v" | egrep -q "${reqvars[$n]}$"; then + !((setvars|=$(( 2 ** $n )) )) + fi + !((n++)) + done +done 3< <(echo "$vars") + + +# Sum all required variables +n=0 +allvars=0 +while [ $n -lt "${#reqvars[@]}" ]; do + !((allvars+=$(( 2 ** $n )))) + !((n++)) +done + +# And scream if something's not set +if ! [ "$setvars" -eq "$allvars" ]; then + >&2 echo "E: Missing some required variables:" + >&2 echo + n=0 + i=1 + while [ $n -lt "${#reqvars[@]}" ]; do + if [ $(( $setvars & $i )) -eq 0 ]; then + >&2 echo " *${reqvars[$n]}" + fi + i=$((i<<1)) + !((n++)) + done + >&2 echo + >&2 echo "Please read the docs." + exit 1 +fi + +# Check for more difficult variables +if [ -z "$services" ] && [ -z "$services_autoconf" ]; then + >&2 echo "E: You must pass either \$services or \$services_autoconf" + exit 1 +fi +if [ -z "$services_autoconf" ] && ! echo "$vars" | grep -q "_logbinding"; then + >&2 echo "E: You must pass either \$*_logbinding (for each service) or \$services_autoconf" + exit 1 +fi + + +# Now go find all log files +LOGFILES= +declare -a LOGFILEMAP +while read -u 3 -r v; do + if echo "$v" | egrep -q "_logfiles$"; then + # Get the name associated with these logfiles + logfiletype="${v%_logfiles}" + # This serves to expand globs while preserving spaces (and also appends the necessary newline) + while IFS= read -u 4 -r -d$'\n' line; do + LOGFILEMAP+=($logfiletype) + LOGFILES="${LOGFILES}$line"$'\n' + done 4< <(IFS= ; for f in ${!v}; do echo "$f"; done) + fi +done 3< <(echo "$vars") + + +# Set some defaults and other values +title="${title:-Important Events per Service}" +vlabel="${vlabel:-events}" + +# If services_autoconf is passed, it is assumed to be a shell glob, the leaves of which are the services +# This also autobinds the service, if not already bound +if [ -n "$services_autoconf" ]; then + declare -a services + IFS= + for s in $services_autoconf; do + s="$(basename "$s")" + services+=("$s") + done + unset IFS +else + services=($services) +fi + + +# Import munin functions +. "$MUNIN_LIBDIR/plugins/plugin.sh" + + +# Now get to the real function definitions + +function config() { + echo "graph_title ${title}" + echo "graph_args --base 1000 -l 0" + echo "graph_vlabel ${vlabel}" + echo "graph_category logevents" + echo "graph_info Lists number of matching lines found in various logfiles associated with each service" + + while read -u 3 -r svc; do + nm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + echo "$nm.label $svc" + print_warning "$nm" + print_critical "$nm" + echo "$nm.info Number of event occurrences for $svc" + done 3< <(IFS=$'\n'; echo "${services[*]}") +} + + + + +function fetch() { + # Load state + touch "$MUNIN_STATEFILE" + . "$MUNIN_STATEFILE" + + local n svcnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches + + # Set service counters to 0 and set any logbindings that aren't yet set + while read -u 3 -r svc; do + svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + typeset "${svcnm}_total=0" + + v="${svcnm}_logbinding" + if [ -z "${!v}" ]; then + typeset "$v=$svc" + fi + done 3< <(IFS=$'\n'; echo "${services[*]}") + + n=0 + while read -u 3 -r logfile; do + # Handling trailing newline + if [ -z "$logfile" ]; then + continue + fi + + # Find which service this logfile is associated with + service= + while read -u 4 -r svc; do + logbinding="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')_logbinding" + if echo "$logfile" | egrep -q "${!logbinding}"; then + service="$svc" + break + fi + done 4< <(IFS=$'\n'; echo "${services[*]}") + + # Skip this log if it's not associated with any service + if [ -z "$service" ]; then + >&2 echo "W: No service associated with log $logfile. Skipping...." + continue + fi + + # Get shell-compatible names for service and logfile + svcnm="$(echo "$service" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + lognm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + + # Get previous line count to determine whether or not the file may have been rotated + prvlines="$(eval "echo \$${lognm}_lines")" + if [ -z "$prvlines" ]; then + prvlines=0 + fi + + # Get the current number of lines in the file + curlines="$(wc -l "$logfile" | egrep -o '^[0-9]+')" + if ! [ "$curlines" -eq "$curlines" ] &>/dev/null; then + curlines=0 + fi + + # If the current line count is less than the previous line count, we've probably rotated. + # Reset to 0. + if [ "$curlines" -lt "$prvlines" ]; then + prvlines=0 + else + prvlines=$((prvlines + 1)) + fi + + # Get incidents starting at the line after the last line we've seen + logmatch="${LOGFILEMAP[$n]}_regex" + matches="$(tail -n +"$prvlines" "$logfile" | egrep -c "${!logmatch}" || true)" + + # Aggregate and add to the correct service counter + svc_counter="${svcnm}_total" + !((matches+=${!svc_counter})) + typeset "$svc_counter=$matches" + + # Update the statefile + sed -i "/^${lognm}_.*/d" "$MUNIN_STATEFILE" + echo "${lognm}_lines=$curlines" >> "$MUNIN_STATEFILE" + !((n++)) + done 3< <(echo "$LOGFILES") + + # Now echo values + while read -u 3 -r svc; do + svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + svc_counter="${svcnm}_total" + echo "${svcnm}.value ${!svc_counter}" + done 3< <(IFS=$'\n'; echo "${services[*]}") + + return 0 +} + + + + + +case "$1" in + autoconf) echo no; exit 0 ;; + config) config ;; + *) fetch ;; +esac + From dcfcbc2faa8c6c99f90f8eebde06eb1bf23986b9 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Fri, 19 Oct 2018 15:27:53 -0500 Subject: [PATCH 335/695] Added detailed filtering capabilities for systemd_units --- plugins/systemd/systemd_units | 65 ++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index dc12d463..5b47fa5f 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -13,21 +13,38 @@ Linux systems with systemd installed. =head1 CONFIGURATION -None needed. You may optionally pass warning and critical values for any of the possible states (active, -reloading, inactive, failed, activating, deactivating) like so: +No configuration is required for this plugin. You may optionally pass warning and critical values for any +of the possible states (active, reloading, inactive, failed, activating, deactivating), and you may +additionally pass both global and state-specific include/exclude filters to include only units you care +about and/or exclude units you don't care about. + +(Note that for failed units, default warning and critical values are set to 0 and 10, respectively. No other +states have default levels set.) + +Value calculations for each state are made using the following algorithm (all filters performed using \`egrep\`): + +1. Global include rules are applied on the output of \`systemctl list-units --all --no-legend\`; +2. Global exclude rules are then applied to the result of that; +3. Then, for each state, this global output is further filtered by include, then exclude rules for the state; +4. Then the result is filtered for the given state and the remaining units counted and listed. + +An example configuration might be something like this: [systemd_units] - env.failed_warning 0 - env.failed_critical 5 - env.inactive_warning 10 - env.inactive_critical 20 - -Note that for failed units, default warning and critical values are set to 0 and 10, respectively. No other -states have default levels set. + env.failed_warning 0 + env.failed_critical 5 + env.inactive_warning 10 + env.inactive_critical 20 + env.exclude boring + env.inactive_exclude sleepy + +In the example above, we've overridden the default warning and critical levels for failed units, added warning +and critical levels for inactive units, then filtered out boring units from all results and filtered out sleepy +units from results for the inactive state. =head1 AUTHOR -Olivier Mehani +Olivier Mehani with contributions from Kael Shipman =head1 LICENSE @@ -51,6 +68,10 @@ states="active \ failed \ activating \ deactivating" + +include="${include:-.*}" +exclude="${exclude:-^$}" + 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)" @@ -73,11 +94,29 @@ EOF } fetch () { - tmp=$(systemctl --no-pager --no-legend --all | awk '{print $1, $3}') + # Get all units, filtering by global include/exclude rules + local state_include state_exclude state_tmp tmp + tmp=$(systemctl --no-pager --no-legend --all | egrep "$include" | egrep -v "$exclude" | awk '{print $1, $3}') + + # For each state, echo the number of units and some extra info, filtering for state-specific include/excludes for state in $states ; do - count=$(echo "$tmp" | grep -c "$state$") + # Get state-specific include/excludes, if present + state_include="$(eval echo "\$${state}_include")" + state_exclude="$(eval echo "\$${state}_exclude")" + state_tmp="$tmp" + + # Filter + if [ -n "$state_include" ]; then + state_tmp="$(echo "$state_tmp" | egrep "$state_include")" + fi + if [ -n "$state_exclude" ]; then + state_tmp="$(echo "$state_tmp" | egrep -v "$state_exclude")" + fi + + # Count and output + count=$(echo "$state_tmp" | grep -c "$state$") echo "$state.value $count" - extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') + extinfo=$(echo "$state_tmp" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') if [ -n "$extinfo" ]; then echo "$state.extinfo" "$extinfo" fi From 19a3cbe397adc495dc1c9675bda6cd3c0b07df84 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Thu, 15 Nov 2018 00:18:47 -0600 Subject: [PATCH 336/695] Addressed PR suggestions (round 1) --- plugins/logevents/loggrepx_ | 47 ++++++++++++++++++-------------- plugins/logevents/service_events | 46 +++++++++++++++++-------------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/plugins/logevents/loggrepx_ b/plugins/logevents/loggrepx_ index b75a4417..2c3a5c02 100755 --- a/plugins/logevents/loggrepx_ +++ b/plugins/logevents/loggrepx_ @@ -1,5 +1,7 @@ #!/bin/bash +set -e + : << =cut =head1 NAME @@ -91,17 +93,18 @@ function config() { echo "graph_title ${title}" echo "graph_args --base 1000 -l 0" echo "graph_vlabel ${title}" - echo "graph_category logevents" + echo "graph_category other" echo "graph_info Lists number of times the given regex is matched in the given log files per period" - local nm logfile lbl + local var_prefix var logfile lbl while read -u 3 -r logfile; do - nm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" - lbl="$(eval "echo \$${nm}_label")" - echo "$nm.label $([ -n "$lbl" ] && echo "$lbl" || echo "$logfile")" - print_warning "$nm" - print_critical "$nm" - echo "$nm.info Lines that match '${regex}' in log file '$logfile'" + var_prefix="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + var="${var_prefix}_label" + lbl="${!var:-0}" + echo "$var_prefix.label $([ -n "$lbl" ] && echo "$lbl" || echo "$logfile")" + print_warning "$var_prefix" + print_critical "$var_prefix" + echo "$var_prefix.info Lines that match '${regex}' in log file '$logfile'" done 3< <(echo "$LOGFILES") } @@ -111,21 +114,23 @@ function config() { function fetch() { # Load state touch "$MUNIN_STATEFILE" - . "$MUNIN_STATEFILE" + local curstate="$(cat "$MUNIN_STATEFILE")" + local nextstate=() - local nm logfile prvlines curlines matches + local var_prefix logfile prvlines curlines matches while read -u 3 -r logfile; do - nm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + # Convert current logfile path to variable prefix + var_prefix="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" # Get running number of lines to determine whether or not the file may have been rotated - prvlines="$(eval "echo \$${nm}_lines")" + prvlines="$(echo "$curstate" | grep "^${var_prefix}_lines=" | cut -f 2 -d "=")" if [ -z "$prvlines" ]; then prvlines=0 fi # Get the current number of lines in the file - curlines="$(wc -l "$logfile" | egrep -o '^[0-9]+')" - if ! [ "$curlines" -eq "$curlines" ] &>/dev/null; then + curlines="$(wc -l < "$logfile")" + if [ -z "$curlines" ]; then curlines=0 fi @@ -138,15 +143,18 @@ function fetch() { fi # Get current number of incidents - matches="$(tail -n +"$prvlines" "$logfile" | egrep -c "${regex}" || true)" + matches="$(tail -n +"$prvlines" "$logfile" | grep -Ec "${regex}" || true)" # Echo the value - echo "$nm.value $matches" + echo "$var_prefix.value $matches" - # Update the statefile - sed -i "/^$nm.*/d" "$MUNIN_STATEFILE" - echo "${nm}_lines=$curlines" >> "$MUNIN_STATEFILE" + # Push onto next state + nextstate+=("${var}_lines=$curlines") done 3< <(echo "$LOGFILES") + + # Write state to munin statefile + (IFS=$'\n'; echo "${nextstate[*]}" > "$MUNIN_STATEFILE") + return 0 } @@ -155,7 +163,6 @@ function fetch() { case "$1" in - autoconf) echo no; exit 0 ;; config) config ;; *) fetch ;; esac diff --git a/plugins/logevents/service_events b/plugins/logevents/service_events index c3fc0ace..0c945bf6 100755 --- a/plugins/logevents/service_events +++ b/plugins/logevents/service_events @@ -154,7 +154,7 @@ reqvars=(_logfiles _regex) while read -u 3 -r v; do n=0 while [ $n -lt "${#reqvars[@]}" ]; do - if echo "$v" | egrep -q "${reqvars[$n]}$"; then + if echo "$v" | grep -Eq "${reqvars[$n]}$"; then !((setvars|=$(( 2 ** $n )) )) fi !((n++)) @@ -203,7 +203,7 @@ fi LOGFILES= declare -a LOGFILEMAP while read -u 3 -r v; do - if echo "$v" | egrep -q "_logfiles$"; then + if echo "$v" | grep -Eq "_logfiles$"; then # Get the name associated with these logfiles logfiletype="${v%_logfiles}" # This serves to expand globs while preserving spaces (and also appends the necessary newline) @@ -244,15 +244,16 @@ function config() { echo "graph_title ${title}" echo "graph_args --base 1000 -l 0" echo "graph_vlabel ${vlabel}" - echo "graph_category logevents" + echo "graph_category other" echo "graph_info Lists number of matching lines found in various logfiles associated with each service" + local var_prefix while read -u 3 -r svc; do - nm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" - echo "$nm.label $svc" - print_warning "$nm" - print_critical "$nm" - echo "$nm.info Number of event occurrences for $svc" + var_prefix="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" + echo "$var_prefix.label $svc" + print_warning "$var_prefix" + print_critical "$var_prefix" + echo "$var_prefix.info Number of event occurrences for $svc" done 3< <(IFS=$'\n'; echo "${services[*]}") } @@ -262,18 +263,19 @@ function config() { function fetch() { # Load state touch "$MUNIN_STATEFILE" - . "$MUNIN_STATEFILE" + local curstate="$(cat "$MUNIN_STATEFILE")" + local nextstate=() - local n svcnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches + local n svcnm varnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches # Set service counters to 0 and set any logbindings that aren't yet set while read -u 3 -r svc; do svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" typeset "${svcnm}_total=0" - v="${svcnm}_logbinding" - if [ -z "${!v}" ]; then - typeset "$v=$svc" + varnm="${svcnm}_logbinding" + if [ -z "$(echo "$curstate" | grep "^${varnm}=" | cut -f 2 -d "=")" ]; then + typeset "$varnm=$svc" fi done 3< <(IFS=$'\n'; echo "${services[*]}") @@ -288,7 +290,7 @@ function fetch() { service= while read -u 4 -r svc; do logbinding="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')_logbinding" - if echo "$logfile" | egrep -q "${!logbinding}"; then + if echo "$logfile" | grep -Eq "${!logbinding}"; then service="$svc" break fi @@ -305,13 +307,13 @@ function fetch() { lognm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" # Get previous line count to determine whether or not the file may have been rotated - prvlines="$(eval "echo \$${lognm}_lines")" + prvlines="$(echo "$curstate" | grep "^${lognm}_lines=" | cut -f 2 -d "=")" if [ -z "$prvlines" ]; then prvlines=0 fi # Get the current number of lines in the file - curlines="$(wc -l "$logfile" | egrep -o '^[0-9]+')" + curlines="$(wc -l < "$logfile")" if ! [ "$curlines" -eq "$curlines" ] &>/dev/null; then curlines=0 fi @@ -326,19 +328,22 @@ function fetch() { # Get incidents starting at the line after the last line we've seen logmatch="${LOGFILEMAP[$n]}_regex" - matches="$(tail -n +"$prvlines" "$logfile" | egrep -c "${!logmatch}" || true)" + matches="$(tail -n +"$prvlines" "$logfile" | grep -Ec "${!logmatch}" || true)" # Aggregate and add to the correct service counter svc_counter="${svcnm}_total" !((matches+=${!svc_counter})) typeset "$svc_counter=$matches" - # Update the statefile - sed -i "/^${lognm}_.*/d" "$MUNIN_STATEFILE" - echo "${lognm}_lines=$curlines" >> "$MUNIN_STATEFILE" + # Push onto next state + nextstate+=("${lognm}_lines=$curlines") + !((n++)) done 3< <(echo "$LOGFILES") + # Write state to munin statefile + (IFS=$'\n'; echo "${nextstate[*]}" > "$MUNIN_STATEFILE") + # Now echo values while read -u 3 -r svc; do svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" @@ -354,7 +359,6 @@ function fetch() { case "$1" in - autoconf) echo no; exit 0 ;; config) config ;; *) fetch ;; esac From b26ba8e6bd65d67aa23cce32e8dd9d5c6d9d12b7 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Thu, 15 Nov 2018 14:27:29 -0600 Subject: [PATCH 337/695] Bugfix and re-positioning --- plugins/{logevents => logs}/loggrepx_ | 4 ++-- plugins/{logevents => logs}/service_events | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename plugins/{logevents => logs}/loggrepx_ (97%) rename plugins/{logevents => logs}/service_events (100%) diff --git a/plugins/logevents/loggrepx_ b/plugins/logs/loggrepx_ similarity index 97% rename from plugins/logevents/loggrepx_ rename to plugins/logs/loggrepx_ index 2c3a5c02..c738ea4d 100755 --- a/plugins/logevents/loggrepx_ +++ b/plugins/logs/loggrepx_ @@ -100,8 +100,8 @@ function config() { while read -u 3 -r logfile; do var_prefix="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" var="${var_prefix}_label" - lbl="${!var:-0}" - echo "$var_prefix.label $([ -n "$lbl" ] && echo "$lbl" || echo "$logfile")" + lbl="${!var:-$logfile}" + echo "$var_prefix.label $lbl" print_warning "$var_prefix" print_critical "$var_prefix" echo "$var_prefix.info Lines that match '${regex}' in log file '$logfile'" diff --git a/plugins/logevents/service_events b/plugins/logs/service_events similarity index 100% rename from plugins/logevents/service_events rename to plugins/logs/service_events From 6c13e1d9cf0855d287f600e47c079c62ed3fa235 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 12:26:29 -0600 Subject: [PATCH 338/695] Added extinfo to list affected logs and improved documentation --- .../example-graphs/service_events-day.png | Bin 0 -> 26147 bytes .../example-graphs/service_events-week.png | Bin 0 -> 27980 bytes plugins/logs/loggrepx_ | 14 +-- plugins/logs/service_events | 87 ++++++++++++++---- 4 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 plugins/logs/example-graphs/service_events-day.png create mode 100644 plugins/logs/example-graphs/service_events-week.png diff --git a/plugins/logs/example-graphs/service_events-day.png b/plugins/logs/example-graphs/service_events-day.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ee90662ff32e40bd52dd14ca925b7856faac47 GIT binary patch literal 26147 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 h2!4 + =head1 LICENSE MIT LICENSE @@ -138,6 +179,7 @@ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + =head1 MAGIC MARKERS #%# family=manual @@ -245,7 +287,7 @@ function config() { echo "graph_args --base 1000 -l 0" echo "graph_vlabel ${vlabel}" echo "graph_category other" - echo "graph_info Lists number of matching lines found in various logfiles associated with each service" + echo "graph_info Lists number of matching lines found in various logfiles associated with each service. Extinfo displays currently affected logs." local var_prefix while read -u 3 -r svc; do @@ -266,7 +308,7 @@ function fetch() { local curstate="$(cat "$MUNIN_STATEFILE")" local nextstate=() - local n svcnm varnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches + local n svcnm varnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches extinfo_var # Set service counters to 0 and set any logbindings that aren't yet set while read -u 3 -r svc; do @@ -306,17 +348,13 @@ function fetch() { svcnm="$(echo "$service" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" lognm="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" - # Get previous line count to determine whether or not the file may have been rotated + # Get previous line count to determine whether or not the file may have been rotated (defaulting to 0) prvlines="$(echo "$curstate" | grep "^${lognm}_lines=" | cut -f 2 -d "=")" - if [ -z "$prvlines" ]; then - prvlines=0 - fi + prvlines="${prvlines:-0}" - # Get the current number of lines in the file + # Get the current number of lines in the file (defaulting to 0 on error) curlines="$(wc -l < "$logfile")" - if ! [ "$curlines" -eq "$curlines" ] &>/dev/null; then - curlines=0 - fi + curlines="${curlines:-0}" # If the current line count is less than the previous line count, we've probably rotated. # Reset to 0. @@ -330,10 +368,17 @@ function fetch() { logmatch="${LOGFILEMAP[$n]}_regex" matches="$(tail -n +"$prvlines" "$logfile" | grep -Ec "${!logmatch}" || true)" - # Aggregate and add to the correct service counter - svc_counter="${svcnm}_total" - !((matches+=${!svc_counter})) - typeset "$svc_counter=$matches" + # If there were matches, aggregate them and add this log to the extinfo for the service + if [ "$matches" -gt 0 ]; then + # Aggregate and add to the correct service counter + svc_counter="${svcnm}_total" + !((matches+=${!svc_counter})) + typeset "$svc_counter=$matches" + + # Add this log to extinfo for service + extinfo_var="${svcnm}_extinfo" + typeset "$extinfo_var=${!extinfo_var}$logfile, " + fi # Push onto next state nextstate+=("${lognm}_lines=$curlines") @@ -348,7 +393,9 @@ function fetch() { while read -u 3 -r svc; do svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" svc_counter="${svcnm}_total" + extinfo_var="${svcnm}_extinfo" echo "${svcnm}.value ${!svc_counter}" + echo "${svcnm}.extinfo ${!extinfo_var}" done 3< <(IFS=$'\n'; echo "${services[*]}") return 0 From c3305389daebe5416064f44db5cd40620d6058f5 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 12:49:40 -0600 Subject: [PATCH 339/695] Added loggrepx_ example graphs --- plugins/logs/example-graphs/loggrepx_-day.png | Bin 0 -> 21749 bytes plugins/logs/example-graphs/loggrepx_-week.png | Bin 0 -> 23896 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/logs/example-graphs/loggrepx_-day.png create mode 100644 plugins/logs/example-graphs/loggrepx_-week.png diff --git a/plugins/logs/example-graphs/loggrepx_-day.png b/plugins/logs/example-graphs/loggrepx_-day.png new file mode 100644 index 0000000000000000000000000000000000000000..20f1228fed2e8ab379c72c576d6831b7251b8b71 GIT binary patch literal 21749 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

    i1gld>uFhw9Z_`e`{d;&Li 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 literal 0 HcmV?d00001 From cc6600b48257a3ad15f563af1f115db7c74f9015 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 16:49:16 -0600 Subject: [PATCH 340/695] Addressed PR comments --- plugins/logs/service_events | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/plugins/logs/service_events b/plugins/logs/service_events index a82044dc..a4dbba76 100755 --- a/plugins/logs/service_events +++ b/plugins/logs/service_events @@ -8,7 +8,7 @@ set -e service_events - Tracks the number of significant event occurrences per service -This plugin is a riff on the loggrep family (\`loggrep\` and my own \`loggrepx_\`). +This plugin is a riff on the loggrep family (C and my own C). However, rather than focusing on single log files, it focuses on providing insight into all "significant events" happening for a given service, which may be found across several log files. @@ -78,15 +78,16 @@ Available config options include the following: For plugin-specific options, the following rules apply: -* is any arbitrary string. It just has to match between _logfiles - and _regex. Common values are "apache", "nginx", "apt", "syslog", etc. +* C<< >> is any arbitrary string. It just has to match between + C<< _logfiles >> and C<< _regex >>. Common values are "apache", + "nginx", "apt", "syslog", etc. * is a string derived by passing the service name through a filter that removes non-alphabet characters from the beginning and replaces all non- - alpha-numeric characters with underscore (\`_\`). -* logfiles are bound to services by matching _logbinding on the full - logfile path. For example, specifying my_site_logbinding=my-site would bind - both /var/log/my-site/errors.log and /srv/www/my-site/logs/app.log to the - defined my-site service. + alpha-numeric characters with underscore (C<_>). +* logfiles are bound to services by matching C<< _logbinding >> on the + full logfile path. For example, specifying C would + bind both F and F + to the defined C service. =head2 SERVICE AUTOCONF @@ -100,7 +101,7 @@ will be something like /srv/*/*, which would match all children in /srv/www/ and /srv/local/. If you choose not to use the autoconf feature, you MUST specify services as a -space-separated list of service names in the \`services\` variable. +space-separated list of service names in the C variable. =head2 EXAMPLE CONFIGS @@ -197,9 +198,9 @@ while read -u 3 -r v; do n=0 while [ $n -lt "${#reqvars[@]}" ]; do if echo "$v" | grep -Eq "${reqvars[$n]}$"; then - !((setvars|=$(( 2 ** $n )) )) + setvars=$((setvars | $(( 2 ** $n )) )) fi - !((n++)) + n=$((n+1)) done done 3< <(echo "$vars") @@ -208,8 +209,8 @@ done 3< <(echo "$vars") n=0 allvars=0 while [ $n -lt "${#reqvars[@]}" ]; do - !((allvars+=$(( 2 ** $n )))) - !((n++)) + allvars=$(( allvars + $(( 2 ** $n )) )) + n=$((n+1)) done # And scream if something's not set @@ -223,7 +224,7 @@ if ! [ "$setvars" -eq "$allvars" ]; then >&2 echo " *${reqvars[$n]}" fi i=$((i<<1)) - !((n++)) + n=$((n+1)) done >&2 echo >&2 echo "Please read the docs." @@ -308,7 +309,7 @@ function fetch() { local curstate="$(cat "$MUNIN_STATEFILE")" local nextstate=() - local n svcnm varnm service svc svc_counter logbinding logfile lognm logmatch prvlines curlines matches extinfo_var + local n svcnm varnm service svc svc_counter_var logbinding logfile lognm logmatch prvlines curlines matches extinfo_var # Set service counters to 0 and set any logbindings that aren't yet set while read -u 3 -r svc; do @@ -354,7 +355,6 @@ function fetch() { # Get the current number of lines in the file (defaulting to 0 on error) curlines="$(wc -l < "$logfile")" - curlines="${curlines:-0}" # If the current line count is less than the previous line count, we've probably rotated. # Reset to 0. @@ -371,9 +371,9 @@ function fetch() { # If there were matches, aggregate them and add this log to the extinfo for the service if [ "$matches" -gt 0 ]; then # Aggregate and add to the correct service counter - svc_counter="${svcnm}_total" - !((matches+=${!svc_counter})) - typeset "$svc_counter=$matches" + svc_counter_var="${svcnm}_total" + matches=$(($matches + ${!svc_counter_var})) + typeset "$svc_counter_var=$matches" # Add this log to extinfo for service extinfo_var="${svcnm}_extinfo" @@ -383,7 +383,7 @@ function fetch() { # Push onto next state nextstate+=("${lognm}_lines=$curlines") - !((n++)) + n=$((n+1)) done 3< <(echo "$LOGFILES") # Write state to munin statefile @@ -392,9 +392,9 @@ function fetch() { # Now echo values while read -u 3 -r svc; do svcnm="$(echo "$svc" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')" - svc_counter="${svcnm}_total" + svc_counter_var="${svcnm}_total" extinfo_var="${svcnm}_extinfo" - echo "${svcnm}.value ${!svc_counter}" + echo "${svcnm}.value ${!svc_counter_var}" echo "${svcnm}.extinfo ${!extinfo_var}" done 3< <(IFS=$'\n'; echo "${services[*]}") From ec802cda2d142f30271ecdaad3831a17f10cf6db Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 17:12:53 -0600 Subject: [PATCH 341/695] Address PR comments --- plugins/systemd/systemd_units | 45 +++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index 5b47fa5f..8f5ea5f1 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -44,7 +44,8 @@ units from results for the inactive state. =head1 AUTHOR -Olivier Mehani with contributions from Kael Shipman +Olivier Mehani +Kael Shipman =head1 LICENSE @@ -74,7 +75,9 @@ exclude="${exclude:-^$}" 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)" + systemctl --state=failed --no-pager --no-legend >/dev/null 2>&1 && \ + echo yes || \ + echo "no (No systemctl or error running it)" } config () { @@ -94,29 +97,31 @@ EOF } fetch () { - # Get all units, filtering by global include/exclude rules - local state_include state_exclude state_tmp tmp - tmp=$(systemctl --no-pager --no-legend --all | egrep "$include" | egrep -v "$exclude" | awk '{print $1, $3}') + # Get all units, filtering by global include/exclude rules + local state_include_var state_include state_exclude_var state_exclude global_unit_list state_unit_list + global_unit_list=$(systemctl --no-pager --no-legend --all | grep -E "$include" | grep -Ev "$exclude" | awk '{print $1, $3}') - # For each state, echo the number of units and some extra info, filtering for state-specific include/excludes + # For each state, echo the number of units and some extra info, filtering for state-specific include/excludes for state in $states ; do - # Get state-specific include/excludes, if present - state_include="$(eval echo "\$${state}_include")" - state_exclude="$(eval echo "\$${state}_exclude")" - state_tmp="$tmp" + # Get state-specific include/excludes, if present + state_include_var="${state}_include" + state_include="${!state_include_var}" + state_exclude_var="${state}_exclude" + state_exclude="${!state_exclude_var}" + state_unit_list="$global_unit_list" - # Filter - if [ -n "$state_include" ]; then - state_tmp="$(echo "$state_tmp" | egrep "$state_include")" - fi - if [ -n "$state_exclude" ]; then - state_tmp="$(echo "$state_tmp" | egrep -v "$state_exclude")" - fi + # Filter + if [ -n "$state_include" ]; then + state_unit_list="$(echo "$state_unit_list" | grep -E "$state_include")" + fi + if [ -n "$state_exclude" ]; then + state_unit_list="$(echo "$state_unit_list" | grep -Ev "$state_exclude")" + fi - # Count and output - count=$(echo "$state_tmp" | grep -c "$state$") + # Count and output + count=$(echo "$state_unit_list" | grep -c "$state$") echo "$state.value $count" - extinfo=$(echo "$state_tmp" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') + extinfo=$(echo "$state_unit_list" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') if [ -n "$extinfo" ]; then echo "$state.extinfo" "$extinfo" fi From 3699102c3aa1e10a08e3adb5bb798e0b324d68a9 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 17:17:57 -0600 Subject: [PATCH 342/695] Fixed documentation for perldoc format --- plugins/systemd/systemd_units | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index 8f5ea5f1..e5075cb5 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -21,9 +21,10 @@ about and/or exclude units you don't care about. (Note that for failed units, default warning and critical values are set to 0 and 10, respectively. No other states have default levels set.) -Value calculations for each state are made using the following algorithm (all filters performed using \`egrep\`): +Value calculations for each state are made using the following algorithm (all filters performed using +C): -1. Global include rules are applied on the output of \`systemctl list-units --all --no-legend\`; +1. Global include rules are applied on the output of C; 2. Global exclude rules are then applied to the result of that; 3. Then, for each state, this global output is further filtered by include, then exclude rules for the state; 4. Then the result is filtered for the given state and the remaining units counted and listed. From 00737cf2fc2320675752502116a15eec6e3419f0 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 17:24:42 -0600 Subject: [PATCH 343/695] More perldoc compatibility --- plugins/logs/loggrepx_ | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/logs/loggrepx_ b/plugins/logs/loggrepx_ index e1fa38c3..69c6ab4d 100755 --- a/plugins/logs/loggrepx_ +++ b/plugins/logs/loggrepx_ @@ -43,10 +43,10 @@ Available config options include the following: NOTE: for any variable with [field] in it, [field] is derived from the full logfile path by simply removing the preceding slash and replacing all non-alphanumerics with underscores. For example, the "warning" field -for the logfile \`/var/log/nginx/errors.log\` would be -\`var_log_nginx_errors_log_warning\`. +for the logfile F would be +F. -One good way to get these names is to run \`munin-run [plugin-name]\` +One good way to get these names is to run C after you've configured the required variables and then just copy/paste the names from the output. From 1d418a1e3a12e103ca9d455e54551caf1ace6cd8 Mon Sep 17 00:00:00 2001 From: Kael Shipman Date: Sun, 18 Nov 2018 18:08:44 -0600 Subject: [PATCH 344/695] Added check for logfile existence at top of loop --- plugins/logs/service_events | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/logs/service_events b/plugins/logs/service_events index a4dbba76..3f08b88a 100755 --- a/plugins/logs/service_events +++ b/plugins/logs/service_events @@ -329,6 +329,13 @@ function fetch() { continue fi + # Make sure the logfile exists + if [ ! -e "$logfile" ]; then + >&2 echo "Logfile '$logfile' doesn't exist. Skipping." + n=$((n+1)) + continue + fi + # Find which service this logfile is associated with service= while read -u 4 -r svc; do From e48f0a3b799e81c7e1ca7827176f4247116dacff Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 20 Nov 2018 01:40:09 +0100 Subject: [PATCH 345/695] Minor cleanup of logs/ plugins * fix spelling mistake (thanks, codespell) * do not hide exit code of command substitution via "local" (thanks, shellcheck) * avoid access of potentially undefined variables (thanks, shellcheck) * fix tabs/spaces * avoid variable substitution in arithmetic substitution * reduce number of successive blank lines at the top level down to two * simplify evaluation of "printenv" output --- plugins/logs/loggrepx_ | 15 +++++---------- plugins/logs/service_events | 38 +++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/plugins/logs/loggrepx_ b/plugins/logs/loggrepx_ index 69c6ab4d..cf156f00 100755 --- a/plugins/logs/loggrepx_ +++ b/plugins/logs/loggrepx_ @@ -83,14 +83,14 @@ OTHER DEALINGS IN THE SOFTWARE. =cut - . "$MUNIN_LIBDIR/plugins/plugin.sh" + +regex=${regex:-} +logfiles=${logfiles:-} LOGFILES="$(IFS= ; for f in $logfiles; do echo "$f"; done)" title="${title:-Log Matches}" - - function config() { echo "graph_title ${title}" echo "graph_args --base 1000 -l 0" @@ -111,13 +111,12 @@ function config() { } - - function fetch() { # Load state touch "$MUNIN_STATEFILE" - local curstate="$(cat "$MUNIN_STATEFILE")" local nextstate=() + local curstate + curstate="$(cat "$MUNIN_STATEFILE")" local var_prefix logfile prvlines curlines matches while read -u 3 -r logfile; do @@ -161,11 +160,7 @@ function fetch() { } - - - case "$1" in config) config ;; *) fetch ;; esac - diff --git a/plugins/logs/service_events b/plugins/logs/service_events index 3f08b88a..d16554f1 100755 --- a/plugins/logs/service_events +++ b/plugins/logs/service_events @@ -83,7 +83,7 @@ For plugin-specific options, the following rules apply: "nginx", "apt", "syslog", etc. * is a string derived by passing the service name through a filter that removes non-alphabet characters from the beginning and replaces all non- - alpha-numeric characters with underscore (C<_>). + alphanumeric characters with underscore (C<_>). * logfiles are bound to services by matching C<< _logbinding >> on the full logfile path. For example, specifying C would bind both F and F @@ -122,12 +122,12 @@ This example uses services autoconf: env.my_special_service_warning 100 env.my_special_service_critical 300 -This example DOESN'T use services autoconf: +This example DOES NOT use services autoconf: [service_events] user root env.services auth.example.com admin.example.com www.example.com - env.auth_example_com_logbinding my-custom-binding[0-9]+ + env.auth_example_com_logbinding my-custom-binding[0-9]+ env.cfxsvc_logfiles /srv/*/*/logs/app.log env.cfxsvc_regex error|alert|crit|emerg env.phpfpm_logfiles /srv/*/*/logs/php-fpm*.log @@ -188,17 +188,19 @@ OTHER DEALINGS IN THE SOFTWARE. =cut +services_autoconf=${services_autoconf:-} + # Get list of all currently set env variables -vars="$(printenv | sed -r "s/^([^=]+).*/\1/g")" +vars=$(printenv | cut -f 1 -d "=") # Certain variables MUST be set; check that they are (using bitmask) setvars=0 reqvars=(_logfiles _regex) while read -u 3 -r v; do n=0 - while [ $n -lt "${#reqvars[@]}" ]; do + while [ "$n" -lt "${#reqvars[@]}" ]; do if echo "$v" | grep -Eq "${reqvars[$n]}$"; then - setvars=$((setvars | $(( 2 ** $n )) )) + setvars=$((setvars | (2 ** n) )) fi n=$((n+1)) done @@ -208,8 +210,8 @@ done 3< <(echo "$vars") # Sum all required variables n=0 allvars=0 -while [ $n -lt "${#reqvars[@]}" ]; do - allvars=$(( allvars + $(( 2 ** $n )) )) +while [ "$n" -lt "${#reqvars[@]}" ]; do + allvars=$(( allvars + 2 ** n )) n=$((n+1)) done @@ -219,8 +221,8 @@ if ! [ "$setvars" -eq "$allvars" ]; then >&2 echo n=0 i=1 - while [ $n -lt "${#reqvars[@]}" ]; do - if [ $(( $setvars & $i )) -eq 0 ]; then + while [ "$n" -lt "${#reqvars[@]}" ]; do + if [ $(( setvars & i )) -eq 0 ]; then >&2 echo " *${reqvars[$n]}" fi i=$((i<<1)) @@ -301,15 +303,13 @@ function config() { } - - function fetch() { - # Load state - touch "$MUNIN_STATEFILE" - local curstate="$(cat "$MUNIN_STATEFILE")" + local curstate n svcnm varnm service svc svc_counter_var logbinding logfile lognm logmatch prvlines curlines matches extinfo_var local nextstate=() - local n svcnm varnm service svc svc_counter_var logbinding logfile lognm logmatch prvlines curlines matches extinfo_var + # Load state + touch "$MUNIN_STATEFILE" + curstate="$(cat "$MUNIN_STATEFILE")" # Set service counters to 0 and set any logbindings that aren't yet set while read -u 3 -r svc; do @@ -379,7 +379,7 @@ function fetch() { if [ "$matches" -gt 0 ]; then # Aggregate and add to the correct service counter svc_counter_var="${svcnm}_total" - matches=$(($matches + ${!svc_counter_var})) + matches=$((matches + ${!svc_counter_var})) typeset "$svc_counter_var=$matches" # Add this log to extinfo for service @@ -409,11 +409,7 @@ function fetch() { } - - - case "$1" in config) config ;; *) fetch ;; esac - From 44b604cdb855305cb4624b69689bab0843b9a024 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 28 Nov 2018 12:11:52 +0100 Subject: [PATCH 346/695] Create plugin timesync_status to monitor systemd ntp. --- plugins/systemd/timesync_status | 136 ++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100755 plugins/systemd/timesync_status diff --git a/plugins/systemd/timesync_status b/plugins/systemd/timesync_status new file mode 100755 index 00000000..0b200c60 --- /dev/null +++ b/plugins/systemd/timesync_status @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +import re +import subprocess +import sys +import textwrap + + +''' +=head1 NAME + +timesync_status - monitor ntp status with systemd-timesyncd + +=head1 APPLICABLE SYSTEMS + +All systems using systemd-timesyncd as its NTP-client. However, this +plugin itself also needs Python 3.5+ to call subprocess.run. + +=head1 CONFIGURATION + +This plugin should work out-of-the-box with autoconf. It does expect +timedatectl to be on $PATH, but that should always be the case in a +normal system. + +=head1 INTERPRETATION + +This plugin shows a graph with one line for every NTP metric it measure. +Metrics are shown with their usual name, and are explained in their +respective info fields. + +This plugin issues no warnings or critical states. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +1.0 + +=head1 AUTHOR + +Bert Peters + +=head1 LICENSE + +GPLv2 +''' + + +def parse_time(value): + value = value.strip() + if ' ' in value: + return sum(parse_time(x) for x in value.split(' ')) + + match = re.match(r'^([+-]?[0-9.]+)([a-z]+)$', value) + if not match: + raise ValueError('Invalid time ' + value) + + value = float(match.group(1)) + suffix = match.group(2) + + if suffix == 'min': + value *= 60 + elif suffix == 'ms': + value /= 1000 + elif suffix == 'us': + value /= 1e6 + + return value + + +def parse_response(data): + values = {} + for line in data.splitlines(): + k, v = line.split(': ', 1) + values[k.strip()] = v.strip() + + return values + + +def retrieve(): + result = subprocess.run(['timedatectl', 'timesync-status'], capture_output=True) + if result.returncode != 0: + sys.exit('timedatectl failed') + + output = result.stdout.decode('utf-8') + values = parse_response(output) + + print('offset.value', parse_time(values['Offset'])) + print('delay.value', parse_time(values['Delay'])) + print('delay.extinfo', 'Server', values['Server']) + print('jitter.value', parse_time(values['Jitter'])) + print('poll.value', parse_time(values['Poll interval'].split('(')[0])) + + +def autoconf(): + result = subprocess.run(['timedatectl', 'status'], capture_output=True) + if result.returncode != 0: + print('no (failed to run timedatectl)') + return + + values = parse_response(result.stdout.decode('utf-8')) + if values['NTP service'] == 'active': + print('yes') + else: + print('no (ntp service not running)') + + +def config(): + print(textwrap.dedent('''\ + graph_title Timesync status + graph_vlabel s + graph_category time + + offset.label Offset + offset.info Time difference between source and local + + delay.label Delay + delay.info Roundtrip time to the NTP-server + + jitter.label Jitter + jitter.info Difference in offset between two subsequent samples + + poll.label Polling time + poll.info Time between two subsequent NTP-polls + ''')) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + retrieve() + elif sys.argv[1] == 'config': + config() + elif sys.argv[1] == 'autoconf': + autoconf() From aa009f60930d24d5e43667abb8544789cd1093dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Arne=20Bj=C3=B8rndal?= Date: Wed, 28 Nov 2018 16:11:02 +0100 Subject: [PATCH 347/695] Add support for configurable thresholds to nullmailer_queue plugin --- plugins/mail/nullmailer_queue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue index 65806b88..a5028f3f 100755 --- a/plugins/mail/nullmailer_queue +++ b/plugins/mail/nullmailer_queue @@ -18,18 +18,22 @@ will need to run as that user or root. Additionally, the nullmailer queue directory may vary; in that case you can specify it with the "queuedir" and "errordir" environment variables. +To add extra warning or critical thresholds set queue_warning, +failed_critical etc. + Example: [nullmailer_queue] user nullmail env.queuedir /var/spool/nullmailer/queue env.errordir /var/spool/nullmailer/failed +env.queue_warning 10 =head1 INTERPRETATION This plugin will draw a stack of 2: the number of emails in the queue, and the number of emails that have permanently failed sending. The latter has -a warning attached, since those should never happen. +a warning attached by default, since those should never happen. =head1 MAGIC MARKERS @@ -50,9 +54,13 @@ GPLv2 =cut +. $MUNIN_LIBDIR/plugins/plugin.sh + queuedir=${queuedir:-/var/spool/nullmailer/queue} errordir=${errordir:-/var/spool/nullmailer/failed} +failed_warning=${failed_warning:-0:0} + case $1 in autoconf) if command -v nullmailer-queue >/dev/null 2>/dev/null; then @@ -76,9 +84,12 @@ queue.info Number of emails currently in the queue failed.label failed failed.draw AREASTACK -failed.warning 0:0 failed.info Number of emails that have permanently failed to send EOF + print_warning queue + print_critical queue + print_warning failed + print_critical failed ;; *) From 69ff4ef307f714fad3289861a6d2fbe784db5504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Arne=20Bj=C3=B8rndal?= Date: Thu, 29 Nov 2018 08:45:02 +0100 Subject: [PATCH 348/695] Fix potential whitespace issue raised in review --- plugins/mail/nullmailer_queue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue index a5028f3f..04aceb3c 100755 --- a/plugins/mail/nullmailer_queue +++ b/plugins/mail/nullmailer_queue @@ -54,7 +54,7 @@ GPLv2 =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" queuedir=${queuedir:-/var/spool/nullmailer/queue} errordir=${errordir:-/var/spool/nullmailer/failed} From f0548a37defad384b6a23844111edbeee8ea2b6d Mon Sep 17 00:00:00 2001 From: Stefan Huehner Date: Thu, 13 Dec 2018 15:20:34 +0100 Subject: [PATCH 349/695] Fix jstat* plugins to work again with java 6+7, fix PU reporting for Java8. jstat column output is not stable across JVM versions (technically version of jstat binary used). New columns are added not add the end of column list but in the middle breaking access via constant index. Following table shows the known columns per version: Oracle JDK / OpenJDK 1.5 .. 1.7 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT Openjdk 1.8 .. 10 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT Openjdk 11 .. 12 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT Earlier commit here added support for java8+ but using version checking if version == 1.5 > Use pre-java8 column format, else new format. As fixing java6+7 would mean more version comparison which is ugly this cmomit changes retrival logic to the following; a.) Parse first line jstat output to find position for each label b.) Then use that position to fetch value from 2nd line That way code auto-adjusts to any ordering change without needing any java version specific code or checks. On the way fix 'Permanent Used' value reporting for java8 which was broken (missing variable rename from PU -> MU when java8 support was added). --- plugins/jvm/jstat__gccount | 28 ++++++------ plugins/jvm/jstat__gctime | 36 ++++++--------- plugins/jvm/jstat__heap | 91 +++++++++++--------------------------- 3 files changed, 55 insertions(+), 100 deletions(-) diff --git a/plugins/jvm/jstat__gccount b/plugins/jvm/jstat__gccount index a6eae247..40215d50 100755 --- a/plugins/jvm/jstat__gccount +++ b/plugins/jvm/jstat__gccount @@ -25,6 +25,7 @@ # Target Java Virtual Machine to monitor are: # Sun JDK 5.0 (http://java.sun.com/javase/) # Sun JDK 8.0 (http://java.sun.com/javase/) +# OpenJDK 1.7 .. 11 (https://openjdk.java.net/) # BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: @@ -53,12 +54,7 @@ get_jdk_type() { if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then echo "bea" else - version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) - if echo "$version" | grep -q '^1\.5\.'; then - echo "sun15" - else - echo "sun" - fi + echo "sun" fi } @@ -89,17 +85,21 @@ print_stats() { if [ "${JDK_TYPE}" = "bea" ]; then # shellcheck disable=SC2016 awk_script='{ YC = $4; OC = $5; print "Young_GC.value " YGC; print "Old_GC.value " FGC; }' - elif [ "${JDK_TYPE}" = "sun15" ]; then - # shellcheck disable=SC2016 - awk_script='{ YGC = $11; FGC = $13; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' else - # example output of jstat for Java 1.8: - # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT - # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 + # List & Order of columns of jstat changes with java versions + # idx["YGC"] is index of YGC column in output (i.e. 13) + # $idx["YGC"] then accesses the value at this position (taken from 2nd line of the output) # shellcheck disable=SC2016 - awk_script='{ YGC = $13; FGC = $15; print "Young_GC.value " YGC; print "Full_GC.value " FGC; }' + awk_script=' + NR==1 { + for (i=1;i<=NF;i++) idx[$i]=i + } + NR==2 { + print "Young_GC.value " $idx["YGC"]; + print "Full_GC.value " $idx["FGC"]; + }' fi - "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | awk "$awk_script" } diff --git a/plugins/jvm/jstat__gctime b/plugins/jvm/jstat__gctime index 21cdec82..af5cd455 100755 --- a/plugins/jvm/jstat__gctime +++ b/plugins/jvm/jstat__gctime @@ -25,6 +25,7 @@ # Target Java Virtual Machine to monitor are: # Sun JDK 5.0 (http://java.sun.com/javase/) # Sun JDK 8.0 (http://java.sun.com/javase/) +# OpenJDK 1.7 .. 11 (https://openjdk.java.net/) # BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: @@ -53,12 +54,7 @@ get_jdk_type() { if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then echo "bea" else - version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) - if echo "$version" | grep -q '^1\.5\.'; then - echo "sun15" - else - echo "sun" - fi + echo "sun" fi } @@ -101,25 +97,21 @@ print_stats() { print "Old_GC.value " OCTime; print "Young_Pause.value " YCPauseTime; print "Old_Pause.value " OCPauseTime; }' - elif [ "${JDK_TYPE}" = "sun15" ]; then - # shellcheck disable=SC2016 - awk_script='{ - YGCT = $12; - FGCT = $14; - print "Young_GC.value " YGCT; - print "Full_GC.value " FGCT; }' else - # example output of jstat for Java 1.8: - # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT - # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 + # List & Order of columns of jstat changes with java versions + # idx["YGC"] is index of YGC column in output (i.e. 13) + # $idx["YGC"] then accesses the value at this position (taken from 2nd line of the output) # shellcheck disable=SC2016 - awk_script='{ - YGCT = $14; - FGCT = $16; - print "Young_GC.value " YGCT; - print "Full_GC.value " FGCT; }' + awk_script=' + (NR==1) { + for (i=1;i<=NF;i++) idx[$i]=i + } + (NR==2) { + print "Young_GC.value " $idx["YGCT"]; + print "Full_GC.value " $idx["FGCT"]; + }' fi - "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | awk "$awk_script" } diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index d6739662..8142273d 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -25,6 +25,7 @@ # Target Java Virtual Machine to monitor are: # Sun JDK 5.0 (http://java.sun.com/javase/) # Sun JDK 8.0 (http://java.sun.com/javase/) +# OpenJDK 1.7 .. 11 (https://openjdk.java.net/) # BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/) # # Parameters: @@ -53,12 +54,7 @@ get_jdk_type() { if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then echo "bea" else - version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1) - if echo "$version" | grep -q '^1\.5\.'; then - echo "sun15" - else - echo "sun" - fi + echo "sun" fi } @@ -114,67 +110,34 @@ print_stats() { print "NurserySize.value " NurserySize * 1024; print "HeapSize.value " HeapSize * 1024; print "UsedHeapSize.value " UsedHeapSize * 1024; }' - elif [ "${JDK_TYPE}" = "sun15" ]; then - # shellcheck disable=SC2016 - awk_script='{ - S0C = $1; - S1C = $2; - S0U = $3; - S1U = $4; - EC = $5; - EU = $6; - OC = $7; - OU = $8; - PC = $9; - PU = $10; - S0F = S0C - S0U; - S1F = S1C - S1U; - EF = EC - EU; - OF = OC - OU; - PF = PC - PU; - print "Eden_Used.value " EU * 1024; - print "Eden_Free.value " EF * 1024; - print "Survivor0_Used.value " S0U * 1024; - print "Survivor0_Free.value " S0F * 1024; - print "Survivor1_Used.value " S1U * 1024; - print "Survivor1_Free.value " S1F * 1024; - print "Old_Used.value " OU * 1024; - print "Old_Free.value " OF * 1024; - print "Permanent_Used.value " PU * 1024; - print "Permanent_Free.value " PF * 1024; }' else - # example output of jstat for Java 1.8: - # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT - # 2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702 + # List & Order of columns of jstat changes with java versions + # idx["YGC"] is index of YGC column in output (i.e. 13) + # $idx["YGC"] then accesses the value at this position (taken from 2nd line of the output) # shellcheck disable=SC2016 - awk_script='{ - S0C = $1; - S1C = $2; - S0U = $3; - S1U = $4; - EC = $5; - EU = $6; - OC = $7; - OU = $8; - MC = $9; - MU = $10; - S0F = S0C - S0U; - S1F = S1C - S1U; - EF = EC - EU; - OF = OC - OU; - MF = MC - MU; - print "Eden_Used.value " EU * 1024; - print "Eden_Free.value " EF * 1024; - print "Survivor0_Used.value " S0U * 1024; - print "Survivor0_Free.value " S0F * 1024; - print "Survivor1_Used.value " S1U * 1024; - print "Survivor1_Free.value " S1F * 1024; - print "Old_Used.value " OU * 1024; - print "Old_Free.value " OF * 1024; - print "Permanent_Used.value " PU * 1024; - print "Permanent_Free.value " MF * 1024; }' + awk_script=' + NR==1 { + for (i=1;i<=NF;i++) idx[$i]=i + } + NR==2 { + S0F = $idx["S0C"] - $idx["S0U"]; + S1F = $idx["S1C"] - $idx["S1U"]; + EF = $idx["EC"] - $idx["EU"]; + OF = $idx["OC"] - $idx["OU"]; + MF = $idx["MC"] - $idx["MU"]; + print "Eden_Used.value " $idx["EU"] * 1024; + print "Eden_Free.value " EF * 1024; + print "Survivor0_Used.value " $idx["S0U"] * 1024; + print "Survivor0_Free.value " S0F * 1024; + print "Survivor1_Used.value " $idx["S1U"] * 1024; + print "Survivor1_Free.value " S1F * 1024; + print "Old_Used.value " $idx["OU"] * 1024; + print "Old_Free.value " OF * 1024; + print "Permanent_Used.value " $idx["MU"] * 1024; + print "Permanent_Free.value " MF * 1024; + }' fi - "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script" + "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | awk "$awk_script" } From 583560e57f1d3a767f5f152a9deb10ce2a71bac4 Mon Sep 17 00:00:00 2001 From: Stefan Huehner Date: Fri, 14 Dec 2018 14:43:31 +0100 Subject: [PATCH 350/695] Fix reporting of 'Permanent Generation' / Metaspace. Fix regression in last commit. While it added auto-detection of changing column position it did not take into account the known change from Java 7 > 8. Java <8 had the Permanent Generation region which jstat columns PU, PC While Java >=8 now has a Metaspace region (for similar purpose) with jstat columns MU,MC. Add re-add detection for that change to fix reporting of the values. To simplify code uses the MU,MC labels internally and when MU,MC are not found in jstat output it fills those with PU,PC values. --- plugins/jvm/jstat__heap | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/jvm/jstat__heap b/plugins/jvm/jstat__heap index 8142273d..e89d434b 100755 --- a/plugins/jvm/jstat__heap +++ b/plugins/jvm/jstat__heap @@ -124,6 +124,11 @@ print_stats() { S1F = $idx["S1C"] - $idx["S1U"]; EF = $idx["EC"] - $idx["EU"]; OF = $idx["OC"] - $idx["OU"]; + # Java <8 has Permanent Generation (PU,PC columns), while >=8 has/names it Metaspace (MU,MC) + if (idx["MU"] == "") { + idx["MU"] = idx["PU"]; + idx["MC"] = idx["PC"]; + } MF = $idx["MC"] - $idx["MU"]; print "Eden_Used.value " $idx["EU"] * 1024; print "Eden_Free.value " EF * 1024; From 13bd1599b0ddd86c48f7d835008fed516c7dbd32 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Dec 2018 15:06:24 +0100 Subject: [PATCH 351/695] nginx_upstream_multi_: migrate to Python3 --- plugins/nginx/nginx_upstream_multi_ | 128 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index c76f85a5..1575e507 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -1,6 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# vim: set fileencoding=utf-8 +#!/usr/bin/env python3 # # Munin plugin to monitor requests number, cache statuses, http status codes and average request times of # specified nginx upstreams. @@ -39,13 +37,19 @@ # #%# family=contrib -import os, sys, re, copy, math +import copy +import math +import os +import re +import sys from time import time + # How we've been called progName = sys.argv[0] progName = progName[progName.rfind("/")+1:] + # Where to store plugin state if "MUNIN_PLUGSTATE" in os.environ: stateDir = os.environ["MUNIN_PLUGSTATE"] @@ -130,62 +134,60 @@ def sanitize(string): if len(sys.argv) == 2 and sys.argv[1] == "config": # Parent graph declaration - print "multigraph nginx_upstream_multi_%s" % siteName.replace(".", "_") - print "graph_title Requests number" - print "graph_vlabel rps" - print "graph_category webserver" + print("multigraph nginx_upstream_multi_%s" % siteName.replace(".", "_")) + print("graph_title Requests number") + print("graph_vlabel rps") + print("graph_category webserver") for upstream in upstreams.keys(): - print "us%s_requests.label %s" % (sanitize(upstream), upstream) + print("us%s_requests.label %s" % (sanitize(upstream), upstream)) # Requests graph declaration if "request" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream)) - print "graph_title Requests number - %s" % upstream - print "graph_vlabel rps" - print "graph_category webserver" - print "us%s_requests.label %s" % (sanitize(upstream), upstream) - print "" + print() + print("multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream))) + print("graph_title Requests number - %s" % upstream) + print("graph_vlabel rps") + print("graph_category webserver") + print("us%s_requests.label %s" % (sanitize(upstream), upstream)) + print() # Times graph declaration if "time" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream)) - print "graph_title Request time - %s" % upstream - print "graph_vlabel sec." - print "graph_category webserver" - print "us%s_times.label average" % (sanitize(upstream)) + print() + print("multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream))) + print("graph_title Request time - %s" % upstream) + print("graph_vlabel sec.") + print("graph_category webserver") + print("us%s_times.label average" % (sanitize(upstream))) for percentile in percentiles: - print "us%s_times_percentile_%s.label %s-percentile" % (sanitize(upstream), percentile, percentile) - print "" + print("us%s_times_percentile_%s.label %s-percentile" % (sanitize(upstream), percentile, percentile)) + print() # HTTP Status codes graph declaration if "http" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream)) - print "graph_title HTTP - %s" % upstream - print "graph_vlabel rps" - print "graph_category webserver" - keylist = httpStatusList.keys() - keylist.sort() - for status in keylist: - print "http%s_%s_status.label %s - %s" % (status, sanitize(upstream), status, httpStatusList[status]["title"]) - print "" + print() + print("multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream))) + print("graph_title HTTP - %s" % upstream) + print("graph_vlabel rps") + print("graph_category webserver") + for status in sorted(httpStatusList.keys()): + print("http%s_%s_status.label %s - %s" % (status, sanitize(upstream), status, httpStatusList[status]["title"])) + print() # Cache status graph declaration if "cache" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream)) - print "graph_title Cache - %s" % upstream - print "graph_vlabel rps" - print "graph_category webserver" + print() + print("multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream))) + print("graph_title Cache - %s" % upstream) + print("graph_vlabel rps") + print("graph_category webserver") for status in cacheStatusList: - print "us%s_%s_cache.label %s" % (sanitize(status), sanitize(upstream), status) - print "" + print("us%s_%s_cache.label %s" % (sanitize(status), sanitize(upstream), status)) + print() else: timeElapsed = now - lastRun @@ -203,7 +205,7 @@ else: try: logHandle = open(logPath, "r") except Exception as e: - print "Log file %s not readable: %s" % (logPath, e.strerror) + print("Log file %s not readable: %s" % (logPath, e.strerror), file=sys.stderr) sys.exit(1) try: @@ -282,7 +284,7 @@ else: lastByteHandle.write(str(logHandle.tell())) lastByteHandle.close() except Exception as e: - print e.strerror + print("Failed to write status file (%s): %s" % (lastBytePath, e.strerror), file=sys.stderr) sys.exit(1) logHandle.close() @@ -293,20 +295,20 @@ else: if timeElapsed > 0: value = upstreams[upstream]["requests"] / timeElapsed - print "us%s_requests.value %s" % (sanitize(upstream), value) + print("us%s_requests.value %s" % (sanitize(upstream), value)) # Requests graph data if "request" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream)) + print() + print("multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream))) value = 0 if timeElapsed > 0: value = upstreams[upstream]["requests"] / timeElapsed - print "us%s_requests.value %s" % (sanitize(upstream), value) - print "" + print("us%s_requests.value %s" % (sanitize(upstream), value)) + print() # Times graph data if "time" in graphs_enabled: @@ -315,9 +317,9 @@ else: if upstreams[upstream]["requests"] > 0: uTime = upstreams[upstream]["time"] / upstreams[upstream]["requests"] upstreams[upstream]["times"].sort() - print "" - print "multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream)) - print "us%s_times.value %s" % (sanitize(upstream), uTime) + print() + print("multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream))) + print("us%s_times.value %s" % (sanitize(upstream), uTime)) for percentile in percentiles: percentileValue = 0 if upstreams[upstream]["requests"] > 0: @@ -329,33 +331,31 @@ else: percentileValue = (upstreams[upstream]["times"][low] + upstreams[upstream]["times"][high]) / 2 else: percentileValue = upstreams[upstream]["times"][int(percentileKey)] - print "us%s_times_percentile_%s.value %s" % (sanitize(upstream), percentile, percentileValue) - print "" + print("us%s_times_percentile_%s.value %s" % (sanitize(upstream), percentile, percentileValue)) + print() # HTTP Status codes graph data if "http" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream)) - keylist = httpStatusList.keys() - keylist.sort() - for status in keylist: + print() + print("multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream))) + for status in sorted(httpStatusList.keys()): value = 0 if timeElapsed > 0: value = upstreams[upstream]["http"][status]["requests"] / timeElapsed - print "http%s_%s_status.value %s" % (status, sanitize(upstream), value) - print "" + print("http%s_%s_status.value %s" % (status, sanitize(upstream), value)) + print() # Cache status graph data if "cache" in graphs_enabled: for upstream in upstreams.keys(): - print "" - print "multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream)) + print() + print("multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream))) for status in cacheStatusList: value = 0 if timeElapsed > 0: value = upstreams[upstream]["cache"][status] / timeElapsed - print "us%s_%s_cache.value %s" % (sanitize(status), sanitize(upstream), value) - print "" + print("us%s_%s_cache.value %s" % (sanitize(status), sanitize(upstream), value)) + print() From 5f9e882bced3a34c167c0bb5603a05f74e8cf3e9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Dec 2018 15:06:52 +0100 Subject: [PATCH 352/695] nginx_upstream_multi_: fix flake8 issues --- plugins/nginx/nginx_upstream_multi_ | 158 ++++++++++++++++------------ 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index 1575e507..3493d075 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -1,29 +1,36 @@ #!/usr/bin/env python3 # -# Munin plugin to monitor requests number, cache statuses, http status codes and average request times of -# specified nginx upstreams. +# Munin plugin to monitor requests number, cache statuses, http status codes and average request +# times of specified nginx upstreams. # # Copyright Igor Borodikhin # # License : GPLv3 # # Configuration parameters: -# env.graphs - which graphs to produce (optional, list of graphs separated by spaces, default - cache http time request) +# env.graphs - which graphs to produce (optional, list of graphs separated by spaces, default - +# cache http time request) # env.log - log file path (mandatory, ex.: /var/log/nginx/upstream.log) -# env.upstream - list of upstreams to monitor (mandatory, including port numbers separated by space, ex.: 10.0.0.1:80 10.0.0.2:8080) -# env.statuses - list of http status codes to monitor (optional, default - all statuses, ex.: 200 403 404 410 500 502) -# env.percentiles - which percentiles to draw on time graphs (optional, list of percentiles separated by spaces, default - 80) +# env.upstream - list of upstreams to monitor (mandatory, including port numbers separated by +# space, e.g.: 10.0.0.1:80 10.0.0.2:8080) +# env.statuses - list of http status codes to monitor (optional, default - all statuses, +# e.g.: 200 403 404 410 500 502) +# env.percentiles - which percentiles to draw on time graphs (optional, list of percentiles +# separated by spaces, default - 80) # # ## Installation -# Copy file to directory /usr/share/munin/pligins/ and create symbolic link(s) for each log file you wish to monitor. +# Copy file to directory /usr/share/munin/pligins/ and create symbolic link(s) for each log file +# you wish to monitor. # # Specify log_format at /etc/nginx/conf.d/upstream.conf: -# log_format upstream "ua=[$upstream_addr] ut=[$upstream_response_time] us=[$upstream_status] cs=[$upstream_cache_status]" +# log_format upstream "ua=[$upstream_addr] ut=[$upstream_response_time] us=[$upstream_status] \ +# cs=[$upstream_cache_status]" # # Use it in your site configuration (/etc/nginx/sites-enabled/anything.conf): # access_log /var/log/nginx/upstream.log upstream; # -# Attention! Because munin-node does not have read permission for nginx log files we need to run it as root. +# Attention! Since the default user (nobody) does not have read permission for nginx log files we +# need to run it as root. # # And specify some options in /etc/munin/plugin-conf.d/munin-node: # @@ -35,7 +42,7 @@ # env.statuses 200 403 404 410 500 502 # env.percentiles 50 80 # -#%# family=contrib +# #%# family=contrib import copy import math @@ -66,17 +73,21 @@ else: logPath = "/var/log/nginx/access.log" # Http statuses list -httpStatusString = ("100:Continue;101:Switching protocols;102:Processing;200:OK;201:Created;202:Accepted;" -"203:Non-Authoritative Information;204:No content;205:Reset content;206:Partial content;207:Multi-status;" -"226:IM used;300:Multiple choices;301:Moved permanently;302:Moved temporarily;303:See other;304:Not modified;" -"305:Use proxy;307:Temporary redirect;400:Bad request;401:Unauthorized;402:Payment required;403:Forbidden;" -"404:Not found;405:Method not allowed;406:Not acceptable;407:Proxy Authentication Required;408:Request timeout;" -"409:Conflict;410:Gone;411:Length required;412:Precondition failed;413:Request entity too large;" -"414:Request URI too large;415:Usupported media type;416:Request range not satisfiable;417:Expectation failed;" -"422:Unprocessable entity;423:Locked;424:Failed dependency;425:Unordered collection;426:Upgrade required;" -"449:Retry with;456:Unrecoverable error;500:Internal server error;501:Not implemented;502:Bad gateway;" -"503:Service unavailable;504:Gateway timeout;505:HTTP version not supported;506:Variant also negotiates;" -"507:Insufficient storage;508:Loop detected;509:Bandwidth limit exceeded;510:Not extended") +httpStatusString = ( + "100:Continue;101:Switching protocols;102:Processing;200:OK;201:Created;202:Accepted;" + "203:Non-Authoritative Information;204:No content;205:Reset content;206:Partial content;" + "207:Multi-status;226:IM used;300:Multiple choices;301:Moved permanently;" + "302:Moved temporarily;303:See other;304:Not modified;305:Use proxy;307:Temporary redirect;" + "400:Bad request;401:Unauthorized;402:Payment required;403:Forbidden;404:Not found;" + "405:Method not allowed;406:Not acceptable;407:Proxy Authentication Required;" + "408:Request timeout;409:Conflict;410:Gone;411:Length required;412:Precondition failed;" + "413:Request entity too large;414:Request URI too large;415:Usupported media type;" + "416:Request range not satisfiable;417:Expectation failed;422:Unprocessable entity;" + "423:Locked;424:Failed dependency;425:Unordered collection;426:Upgrade required;" + "449:Retry with;456:Unrecoverable error;500:Internal server error;501:Not implemented;" + "502:Bad gateway;503:Service unavailable;504:Gateway timeout;505:HTTP version not supported;" + "506:Variant also negotiates;507:Insufficient storage;508:Loop detected;" + "509:Bandwidth limit exceeded;510:Not extended") if "statuses" in os.environ: statuses = os.environ["statuses"].split() @@ -88,11 +99,11 @@ for statusString in httpStatusString.split(";"): [code, title] = statusString.split(":") if len(statuses) > 0 and code in statuses or len(statuses) == 0: httpStatusList[code] = { - "title" : title, - "requests" : 0 + "title": title, + "requests": 0 } -cacheStatusList = { "MISS" : 0, "BYPASS" : 0, "EXPIRED" : 0, "UPDATING" : 0, "STALE" : 0, "HIT" : 0 } +cacheStatusList = {"MISS": 0, "BYPASS": 0, "EXPIRED": 0, "UPDATING": 0, "STALE": 0, "HIT": 0} # Parse upstreams upstreams = {} @@ -101,11 +112,11 @@ if "upstream" in os.environ: upstreamList = upstreamString.split() for upstream in upstreamList: upstreams[upstream] = { - "requests" : 0, - "time" : 0, - "times" : [], - "cache" : copy.deepcopy(cacheStatusList), - "http" : copy.deepcopy(httpStatusList) + "requests": 0, + "time": 0, + "times": [], + "cache": copy.deepcopy(cacheStatusList), + "http": copy.deepcopy(httpStatusList) } else: raise Exception("No upstreams specified") @@ -132,6 +143,7 @@ except OSError: def sanitize(string): return string.replace(".", "_").replace(":", "_").replace("/", "_").replace("-", "_") + if len(sys.argv) == 2 and sys.argv[1] == "config": # Parent graph declaration print("multigraph nginx_upstream_multi_%s" % siteName.replace(".", "_")) @@ -145,7 +157,8 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": if "request" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_requests" + % (sanitize(siteName), sanitize(upstream))) print("graph_title Requests number - %s" % upstream) print("graph_vlabel rps") print("graph_category webserver") @@ -156,32 +169,37 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": if "time" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_times" + % (sanitize(siteName), sanitize(upstream))) print("graph_title Request time - %s" % upstream) print("graph_vlabel sec.") print("graph_category webserver") print("us%s_times.label average" % (sanitize(upstream))) for percentile in percentiles: - print("us%s_times_percentile_%s.label %s-percentile" % (sanitize(upstream), percentile, percentile)) + print("us%s_times_percentile_%s.label %s-percentile" + % (sanitize(upstream), percentile, percentile)) print() # HTTP Status codes graph declaration if "http" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_statuses" + % (sanitize(siteName), sanitize(upstream))) print("graph_title HTTP - %s" % upstream) print("graph_vlabel rps") print("graph_category webserver") for status in sorted(httpStatusList.keys()): - print("http%s_%s_status.label %s - %s" % (status, sanitize(upstream), status, httpStatusList[status]["title"])) + print("http%s_%s_status.label %s - %s" + % (status, sanitize(upstream), status, httpStatusList[status]["title"])) print() # Cache status graph declaration if "cache" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_cache" + % (sanitize(siteName), sanitize(upstream))) print("graph_title Cache - %s" % upstream) print("graph_vlabel rps") print("graph_category webserver") @@ -199,7 +217,7 @@ else: except Exception: lastByte = 0 - if lastByteHandle != None: + if lastByteHandle is not None: lastByteHandle.close() try: @@ -224,59 +242,59 @@ else: if (match): # Extract data address = match.group(1) - time = match.group(2) - status = match.group(3) - cache = match.group(4) + time = match.group(2) + status = match.group(3) + cache = match.group(4) # Replace separators by space address = address.replace(",", " ") address = address.replace(" : ", " ") - address = re.sub("\s+", " ", address) + address = re.sub(r"\s+", " ", address) - time = time.replace(",", " ") - time = time.replace(" : ", " ") - time = re.sub("\s+", " ", time) + time = time.replace(",", " ") + time = time.replace(" : ", " ") + time = re.sub(r"\s+", " ", time) - status = status.replace(",", " ") - status = status.replace(" : ", " ") - status = re.sub("\s+", " ", status) + status = status.replace(",", " ") + status = status.replace(" : ", " ") + status = re.sub(r"\s+", " ", status) - cache = cache.replace(",", " ") - cache = cache.replace(" : ", " ") - cache = re.sub("\s+", " ", cache) + cache = cache.replace(",", " ") + cache = cache.replace(" : ", " ") + cache = re.sub(r"\s+", " ", cache) addresses = address.split() - times = time.split() - statuses = status.split() - caches = cache.split() + times = time.split() + statuses = status.split() + caches = cache.split() index = 0 for uAddress in addresses: if uAddress in upstreams.keys(): try: - uTime = float(times[index]) + uTime = float(times[index]) except ValueError: - uTime = 0 + uTime = 0 if index < len(statuses): - uStatus = statuses[index] + uStatus = statuses[index] else: uStatus = "-" if index < len(caches): - uCache = caches[index] + uCache = caches[index] else: uCache = "-" if uAddress != "-": - upstreams[uAddress]["requests"] += 1 + upstreams[uAddress]["requests"] += 1 if uTime != "-": - upstreams[uAddress]["time"] += uTime + upstreams[uAddress]["time"] += uTime upstreams[uAddress]["times"].append(uTime) if uStatus != "-" and uStatus in upstreams[uAddress]["http"].keys(): upstreams[uAddress]["http"][uStatus]["requests"] += 1 if uCache != "-": - upstreams[uAddress]["cache"][uCache] += 1 + upstreams[uAddress]["cache"][uCache] += 1 index += 1 try: @@ -301,12 +319,11 @@ else: if "request" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream))) - + print("multigraph nginx_upstream_multi_%s.%s_requests" + % (sanitize(siteName), sanitize(upstream))) value = 0 if timeElapsed > 0: value = upstreams[upstream]["requests"] / timeElapsed - print("us%s_requests.value %s" % (sanitize(upstream), value)) print() @@ -318,27 +335,31 @@ else: uTime = upstreams[upstream]["time"] / upstreams[upstream]["requests"] upstreams[upstream]["times"].sort() print() - print("multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_times" + % (sanitize(siteName), sanitize(upstream))) print("us%s_times.value %s" % (sanitize(upstream), uTime)) for percentile in percentiles: percentileValue = 0 if upstreams[upstream]["requests"] > 0: uTime = upstreams[upstream]["time"] / upstreams[upstream]["requests"] percentileKey = int(percentile) * len(upstreams[upstream]["times"]) / 100 - if len(upstreams[upstream]["times"])%2 > 0: + if len(upstreams[upstream]["times"]) % 2 > 0: low = int(math.floor(percentileKey)) high = int(math.ceil(percentileKey)) - percentileValue = (upstreams[upstream]["times"][low] + upstreams[upstream]["times"][high]) / 2 + percentileValue = (upstreams[upstream]["times"][low] + + upstreams[upstream]["times"][high]) / 2 else: percentileValue = upstreams[upstream]["times"][int(percentileKey)] - print("us%s_times_percentile_%s.value %s" % (sanitize(upstream), percentile, percentileValue)) + print("us%s_times_percentile_%s.value %s" + % (sanitize(upstream), percentile, percentileValue)) print() # HTTP Status codes graph data if "http" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_statuses" + % (sanitize(siteName), sanitize(upstream))) for status in sorted(httpStatusList.keys()): value = 0 if timeElapsed > 0: @@ -351,7 +372,8 @@ else: if "cache" in graphs_enabled: for upstream in upstreams.keys(): print() - print("multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream))) + print("multigraph nginx_upstream_multi_%s.%s_cache" + % (sanitize(siteName), sanitize(upstream))) for status in cacheStatusList: value = 0 if timeElapsed > 0: From 734da6b9c8ecf760bb0df553e03308412770c467 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Dec 2018 15:08:13 +0100 Subject: [PATCH 353/695] nginx_upstream_multi_: fix namespace overide of "time" --- plugins/nginx/nginx_upstream_multi_ | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index 3493d075..95425d48 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -49,7 +49,7 @@ import math import os import re import sys -from time import time +import time # How we've been called @@ -131,7 +131,7 @@ if "graphs" in os.environ: else: graphs_enabled = ["cache", "http", "time", "request"] -now = int(time()) +now = int(time.time()) lastBytePath = "%s/nginx_upstream_multi_%s_lastByte.txt" % (stateDir, siteName) try: @@ -242,7 +242,7 @@ else: if (match): # Extract data address = match.group(1) - time = match.group(2) + request_time = match.group(2) status = match.group(3) cache = match.group(4) @@ -251,9 +251,9 @@ else: address = address.replace(" : ", " ") address = re.sub(r"\s+", " ", address) - time = time.replace(",", " ") - time = time.replace(" : ", " ") - time = re.sub(r"\s+", " ", time) + request_time = request_time.replace(",", " ") + request_time = request_time.replace(" : ", " ") + request_time = re.sub(r"\s+", " ", request_time) status = status.replace(",", " ") status = status.replace(" : ", " ") @@ -264,7 +264,7 @@ else: cache = re.sub(r"\s+", " ", cache) addresses = address.split() - times = time.split() + times = request_time.split() statuses = status.split() caches = cache.split() From d5fc30a9bfe62dfa62eeb212d9f6dcc21ffde3b2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Dec 2018 15:19:14 +0100 Subject: [PATCH 354/695] nginx_upstream_multi_: simplify configuration retrieval via environment --- plugins/nginx/nginx_upstream_multi_ | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index 95425d48..95b2ba48 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -58,19 +58,13 @@ progName = progName[progName.rfind("/")+1:] # Where to store plugin state -if "MUNIN_PLUGSTATE" in os.environ: - stateDir = os.environ["MUNIN_PLUGSTATE"] -else: - stateDir = None +stateDir = os.environ.get("MUNIN_PLUGSTATE", None) # Which site configuration we should use siteName = progName[len("nginx_upstream_multi_"):] # Log path -if "log" in os.environ: - logPath = os.environ["log"] -else: - logPath = "/var/log/nginx/access.log" +logPath = os.environ.get("log", "/var/log/nginx/access.log") # Http statuses list httpStatusString = ( @@ -89,10 +83,8 @@ httpStatusString = ( "506:Variant also negotiates;507:Insufficient storage;508:Loop detected;" "509:Bandwidth limit exceeded;510:Not extended") -if "statuses" in os.environ: - statuses = os.environ["statuses"].split() -else: - statuses = [] +# an empty list of wanted statuses is interpreted as: all statuses +statuses = os.environ.get("statuses", "").split() httpStatusList = {} for statusString in httpStatusString.split(";"): @@ -121,19 +113,13 @@ if "upstream" in os.environ: else: raise Exception("No upstreams specified") -if "percentiles" in os.environ: - percentiles = os.environ["percentiles"].split() -else: - percentiles = [80] +percentiles = os.environ.get("percentiles", "80").split() -if "graphs" in os.environ: - graphs_enabled = os.environ["graphs"].split() -else: - graphs_enabled = ["cache", "http", "time", "request"] +graphs_enabled = os.environ.get("graphs", "cache http time request").split() now = int(time.time()) -lastBytePath = "%s/nginx_upstream_multi_%s_lastByte.txt" % (stateDir, siteName) +lastBytePath = os.path.join(stateDir, "nginx_upstream_multi_{}_lastByte.txt".format(siteName)) try: lastRun = os.path.getmtime(lastBytePath) except OSError: From 5fcad700087b26f533123eff017f1a8f99d21806 Mon Sep 17 00:00:00 2001 From: Edward Plainview Date: Tue, 8 Jan 2019 15:54:42 +0100 Subject: [PATCH 355/695] Fix spaces in directory names. Glob directories. --- plugins/disk/dirsizes | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/disk/dirsizes b/plugins/disk/dirsizes index 1d417847..0767243d 100755 --- a/plugins/disk/dirsizes +++ b/plugins/disk/dirsizes @@ -21,6 +21,8 @@ # You can test this plugin by calling it with params "test" and your watchdirs: # ./dirsizes test /dir1,/tmp/dir2 # +# The directories can contain wildcards that are automatically expanded. +# # ############################################################################## # @@ -45,6 +47,17 @@ else { @watchdirs = split( ",", $ENV{"watchdirs"} ); } +# Glob all of the watchdirs. +my @globbed_watchdirs; +foreach my $watchdir ( @watchdirs ) +{ + foreach my $expanded_dir ( glob( $watchdir ) ) + { + push @globbed_watchdirs, $expanded_dir; + } +} +@watchdirs = @globbed_watchdirs; + # Config or read request? if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { @@ -88,9 +101,8 @@ sub getSize { my ($dir) = @_; # Get the size via `du` - my @dirsize = split( ' ', `du -cb $dir | grep "total" | tail -1 ` ); + my @dirsize = split( ' ', `du -cb "$dir" | grep "total" | tail -1 ` ); return @dirsize[0]; } exit 0; - From 4547f33ddab891037b88bcf3c5bcea7fbcd7b057 Mon Sep 17 00:00:00 2001 From: Anthony Baccuet Date: Wed, 16 Jan 2019 10:39:36 +0100 Subject: [PATCH 356/695] Add error message to extinfo --- plugins/mysql/mysql_slave_threads | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/mysql/mysql_slave_threads b/plugins/mysql/mysql_slave_threads index 6669c85d..47a2091d 100755 --- a/plugins/mysql/mysql_slave_threads +++ b/plugins/mysql/mysql_slave_threads @@ -24,6 +24,19 @@ use strict; my $MYSQL = $ENV{mysql} || "mysql"; my $MYSQLOPTS = $ENV{mysqlopts} || ""; +my $status = `$MYSQL $MYSQLOPTS -e 'SHOW SLAVE STATUS\\G'`; + +my $sqlerror = ""; +my $ioerror = ""; + +if ($status =~ /Last_SQL_Error: (.+)/) { + $sqlerror = $1; +} +if ($status =~ /Last_IO_Error: (.+)/) { + $ioerror = $1; +} + + if(defined $ARGV[0] && $ARGV[0] eq 'config') { print < Date: Wed, 16 Jan 2019 09:18:21 -0500 Subject: [PATCH 357/695] Ubiquity Plugin: new controllers require the use of double quoted string to login --- plugins/ubiquiti/unifi_api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ubiquiti/unifi_api b/plugins/ubiquiti/unifi_api index 19dc59b9..86729820 100755 --- a/plugins/ubiquiti/unifi_api +++ b/plugins/ubiquiti/unifi_api @@ -1113,7 +1113,7 @@ sub fetch_data { $curl->setopt($curl->CURLOPT_SSL_VERIFYHOST, (( $APIconfig{"ssl_verify_host"} =~ m/no/i ) ? 0 : 2) ); $curl->setopt($curl->CURL_SSLVERSION_TLSv1, 1); $curl->setopt($curl->CURLOPT_URL, $APIPoint{'login'}); - $curl->setopt($curl->CURLOPT_POSTFIELDS, q[{'username':'] . $APIconfig{"user"} . q[', 'password':'] . $APIconfig{"pass"} . q['}] ); + $curl->setopt($curl->CURLOPT_POSTFIELDS, q[{"username":"] . $APIconfig{"user"} . q[", "password":"] . $APIconfig{"pass"} . q["}] ); $curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'login'}); $retcode = $curl->perform; From 0429e63be5670f57b053411c37e4c8d92258c92b Mon Sep 17 00:00:00 2001 From: Sephtex Date: Sun, 3 Feb 2019 15:10:13 +0100 Subject: [PATCH 358/695] Add support for dash in zfs_list #969 zfs/zfs_list: Add support for dash in zfs_list --- plugins/zfs/zfs_list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index 72134b74..3550fb05 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -31,7 +31,7 @@ fi if [ "$1" = "config" ]; then for i in `zfs list -Hp | awk '{print $1}'`; do values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $i | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) - fsname=`echo $i | sed 's/\//__/g'` + fsname=`echo $i | sed 's/\//__/g' | sed 's/-/_/g'` echo < Date: Sun, 3 Feb 2019 19:10:56 +0100 Subject: [PATCH 359/695] add usage of clean_fieldname Used the correct munin way by soursing "$MUNIN_LIBDIR/plugins/plugin.sh" and use clean_fieldname. For the title I removed the replacement of / by __ and on the multigraph value I added the usage of clean_fieldname to make it backwards compatible. --- plugins/zfs/zfs_list | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index 3550fb05..1e9d48a3 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -16,6 +16,8 @@ # #%# family=auto +. "$MUNIN_LIBDIR/plugins/plugin.sh" + need_multigraph() if [ "$1" = "autoconf" ]; then @@ -31,7 +33,7 @@ fi if [ "$1" = "config" ]; then for i in `zfs list -Hp | awk '{print $1}'`; do values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $i | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) - fsname=`echo $i | sed 's/\//__/g' | sed 's/-/_/g'` + fsname=$(clean_fieldname "$i") echo < Date: Sun, 3 Feb 2019 19:12:54 +0100 Subject: [PATCH 360/695] indentation fix added same indentation of the echo as all the rest in the for loop --- plugins/zfs/zfs_list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index 1e9d48a3..5add8979 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -79,7 +79,7 @@ for i in `zfs list -Hp | awk '{print $1}'`; do values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $i | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) fsname=$(clean_fieldname $(echo "$i" | sed 's/\//__/g')) -echo < Date: Sun, 3 Feb 2019 19:55:16 +0100 Subject: [PATCH 361/695] replace the / by __ anyway To make it more clear that we had a subdevice it's better to use the __ for it's replacement anyway --- plugins/zfs/zfs_list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index 5add8979..c7abf7da 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -33,7 +33,7 @@ fi if [ "$1" = "config" ]; then for i in `zfs list -Hp | awk '{print $1}'`; do values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $i | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) - fsname=$(clean_fieldname "$i") + fsname=$(clean_fieldname $(echo "$i" | sed 's/\//__/g')) echo < Date: Sat, 9 Feb 2019 20:24:35 +0100 Subject: [PATCH 362/695] Change shebang of systemd/systemd_units from sh to bash Running it with sh results in this error: /etc/munin/plugins/systemd_units: 109: /etc/munin/plugins/systemd_units: Bad substitution --- plugins/systemd/systemd_units | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index e5075cb5..47f43541 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # -*- sh -*- : << =cut From 9e08cc92e8fe58988f535750535df9ca1f73b63d Mon Sep 17 00:00:00 2001 From: poiuty Date: Tue, 5 Mar 2019 11:30:01 +0300 Subject: [PATCH 363/695] fix graph_vlabel --- plugins/sphinx/sphinx_queries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sphinx/sphinx_queries b/plugins/sphinx/sphinx_queries index 57db16e2..6cef29f8 100755 --- a/plugins/sphinx/sphinx_queries +++ b/plugins/sphinx/sphinx_queries @@ -25,7 +25,7 @@ if (isset($argc) && $argc > 1) { "graph_info This graph shows the number of queries for last 5 minutes\n" . "graph_category search\n" . "graph_args --base 1000 --lower-limit 0\n" . - "graph_vlabel Connections\n" . + "graph_vlabel Queries\n" . "graph_info The number of current queries\n" . "graph_order current\n" . "graph_total Total\n" . From 438bf0bbb16b4a874e853daa4d15c7e419c5d48d Mon Sep 17 00:00:00 2001 From: Guillaume Chauvin <{ID}+{username}@users.noreply.github.com> Date: Wed, 13 Mar 2019 00:17:23 +0100 Subject: [PATCH 364/695] Plugin postfix-rbl-blocked-mails: adds custom RBLs monitoring --- plugins/postfix/postfix-rbl-blocked-mails | 28 ++++++++++------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/plugins/postfix/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails index cfb525b6..3517fed3 100755 --- a/plugins/postfix/postfix-rbl-blocked-mails +++ b/plugins/postfix/postfix-rbl-blocked-mails @@ -1,12 +1,13 @@ #!/bin/sh # -# Made by Stefan Bhler, Switzerland +# Made by Stefan Bühler, Switzerland # Monitor blocked Mails during Postfix RBL Scan, included Spamhaus, Spamcop, Manitu, MSRBL, NJABL # Allow user to specify logfile through env.logfile LOGFILE=${logfile:-/var/log/mail.log} DATE=$(date '+%b %e %H') +RBLS=${rbls:-spamhaus.org spamcop.net manitu.net msrbl.net njabl.org} get_blocked_by_domain_count() { @@ -31,22 +32,17 @@ if [ "$1" = "config" ]; then echo 'graph_category mail' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel block during RBL' - echo 'spamhaus.label Blocked by Spamhaus.org' - echo 'spamcop.label Blocked by Spamcop' - echo 'manitu.label Blocked by manitu.net' - echo 'msrbl.label Blocked by msrbl.net' - echo 'njabl.label Blocked by njabl.org' + + for RBL in $RBLS + do + echo "${RBL%%.*}.label Blocked by $RBL" + done + exit 0 fi -# sbl-xbl.spamhaus.org or zen.spamhaus.org -printf 'spamhaus.value %s\n' "$(get_blocked_by_domain_count "spamhaus.org")" -# bl.spamcop.net -printf 'spamcop.value %s\n' "$(get_blocked_by_domain_count "spamcop.net")" -# ix.dnsbl.manitu.net -printf 'manitu.value %s\n' "$(get_blocked_by_domain_count "manitu.net")" -# combined.rbl.msrbl.net -printf 'msrbl.value %s\n' "$(get_blocked_by_domain_count "msrbl.net")" -# combined.njabl.org -printf 'njabl.value %s\n' "$(get_blocked_by_domain_count "njabl.org")" +for RBL in $RBLS +do + printf '%s.value %s\n' "${RBL%%.*}" "$(get_blocked_by_domain_count $RBL)" +done \ No newline at end of file From 832ecbad40098fa84fa839ffa1129721ec45adb0 Mon Sep 17 00:00:00 2001 From: wodry Date: Mon, 18 Mar 2019 07:18:44 +0100 Subject: [PATCH 365/695] Fix "TabError: inconsistent use of tabs and spaces in indentation" Fixes ``` munin-run tor_bandwidth File "/etc/munin/plugins/tor_bandwidth", line 450 if router[0] == "r": ^ IndentationError: expected an indented block ``` ans use single quotes constantly also for GeoIP.dat filename --- plugins/tor/tor_ | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 89f047ca..630f9a42 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -29,7 +29,7 @@ user toranon # or any other user/group that is running tor group toranon env.torcachefile 'munin_tor_country_stats.json' env.torconnectmethod 'port' -env.torgeoippath "/usr/share/GeoIP/GeoIP.dat" +env.torgeoippath '/usr/share/GeoIP/GeoIP.dat' env.tormaxcountries 15 env.torport 9051 env.torsocket '/var/run/tor/control' @@ -40,7 +40,7 @@ user toranon # or any other user/group that is running tor group toranon env.torcachefile 'munin_tor_country_stats.json' env.torconnectmethod 'socket' -env.torgeoippath "/usr/share/GeoIP/GeoIP.dat" +env.torgeoippath '/usr/share/GeoIP/GeoIP.dat' env.tormaxcountries 15 env.torport 9051 env.torsocket '/var/run/tor/control' @@ -70,7 +70,7 @@ except ImportError: default_torcachefile = 'munin_tor_country_stats.json' default_torconnectmethod = 'port' -default_torgeoippath = "/usr/share/GeoIP/GeoIP.dat" +default_torgeoippath = '/usr/share/GeoIP/GeoIP.dat' default_tormaxcountries = 15 default_torport = 9051 default_torsocket = '/var/run/tor/control' @@ -445,10 +445,10 @@ class TorRouters(TorPlugin): sys.exit(1) else: routers = response.split('\n') - onr = 0 - for router in routers: - if router[0] == "r": - onr += 1 + onr = 0 + for router in routers: + if router[0] == "r": + onr += 1 print('routers.value {}'.format(onr)) From d272622bb01e7b30fb9799a2ccd6fadeabad821d Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 19 Mar 2019 12:50:28 +0100 Subject: [PATCH 366/695] Exit when retrieving updates fails. --- plugins/other/pacman_pending_updates | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index c5e66b93..7a327995 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -25,7 +25,7 @@ This plugin will draw one line: the number of updates pending. =head1 VERSION - 1.1.0 + 1.1.1 =head1 AUTHOR @@ -54,7 +54,7 @@ EOM ;; *) - updates="$(checkupdates)" + updates="$(checkupdates)" || exit if [ -n "$updates" ]; then echo "updates.value $(echo "$updates" | wc -l)" echo "updates.extinfo $(echo "$updates" | paste -s -d,)" From 17e0fce840ad78921f6751724f28f351a6894cd8 Mon Sep 17 00:00:00 2001 From: heeplr <32984777+heeplr@users.noreply.github.com> Date: Tue, 26 Mar 2019 13:46:37 +0100 Subject: [PATCH 367/695] DOCSIS status monitoring MUNIN Plugin to monitor status of Arris TG3442 / TG2492LG-85 and compatible cable modems --- plugins/router/arris-tg3442 | 250 ++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 plugins/router/arris-tg3442 diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 new file mode 100644 index 00000000..35307c74 --- /dev/null +++ b/plugins/router/arris-tg3442 @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 + +""" +# MUNIN Plugin to monitor status of Arris TG3442 / TG2492LG-85 +# and compatible cable modems +# +# Connect to the web-frontend and get current DOCSIS status of upstream and +# downstream channels. (Signal Power, SNR, Lock Status) +# +# +# Requirements: +# - BeautifulSoup +# - pycryptodome +# +# Configuration: +# [arris] +# env.url http://192.168.100.1 +# env.username admin +# env.password yourpassword +# +# Parameters: +# url - URL to web-frontend +# username - defaults to "admin" +# password - valid password +# +# +# References: https://www.arris.com/products/touchstone-tg3442-cable-voice-gateway/ +# +# +# +# Copyright (c) 2019 Daniel Hiepler +# +# Permission to use, copy, and modify this software with or without fee +# is hereby granted, provided that this entire notice is included in +# all source code copies of any software which is or includes a copy or +# modification of this software. +# +# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR +# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY +# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE +# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR +# PURPOSE. +# +# +# Magic markers +# #%# family=contrib +""" + +import binascii +from bs4 import BeautifulSoup +from Crypto.Cipher import AES +import hashlib +import json +import re +import requests +import sys +import os + + +def login(session, url, username, password): + """login to """ + # get login page + r = session.get(f"{url}") + # parse HTML + h = BeautifulSoup(r.text, "lxml") + # get session id from javascript in head + current_session_id = re.search(r".*var currentSessionId = '(.+)';.*", h.head.text)[1] + + # encrypt password + salt = os.urandom(8) + iv = os.urandom(8) + key = hashlib.pbkdf2_hmac( + 'sha256', + bytes(password.encode("ascii")), + salt, + iterations=1000, + dklen=128/8 + ) + secret = { "Password": password, "Nonce": current_session_id } + plaintext = bytes(json.dumps(secret).encode("ascii")) + associated_data = "loginPassword" + # initialize cipher + cipher = AES.new(key, AES.MODE_CCM, iv) + # set associated data + cipher.update(bytes(associated_data.encode("ascii"))) + # encrypt plaintext + encrypt_data = cipher.encrypt(plaintext) + # append digest + encrypt_data += cipher.digest() + # return + login_data = { + 'EncryptData': binascii.hexlify(encrypt_data).decode("ascii"), + 'Name': username, + 'Salt': binascii.hexlify(salt).decode("ascii"), + 'Iv': binascii.hexlify(iv).decode("ascii"), + 'AuthData': associated_data + } + + # login + r = session.put( + f"{url}/php/ajaxSet_Password.php", + headers={ + "Content-Type": "application/json", + "csrfNonce": "undefined" + }, + data=json.dumps(login_data) + ) + + # parse result + result = json.loads(r.text) + # success? + if result['p_status'] == "Fail": + print("login failure", file=sys.stderr) + exit(-1) + # remember CSRF nonce + csrf_nonce = result['nonce'] + + # prepare headers + session.headers.update({ + "X-Requested-With": "XMLHttpRequest", + "csrfNonce": csrf_nonce, + "Origin": f"{url}/", + "Referer": f"{url}/" + }) + # set credentials cookie + session.cookies.set( + "credential", + "eyAidW5pcXVlIjoiMjgwb2FQU0xpRiIsICJmYW1pbHkiOiI4NTIiLCAibW9kZWxuYW1lIjoiV" + "EcyNDkyTEctODUiLCAibmFtZSI6InRlY2huaWNpYW4iLCAidGVjaCI6dHJ1ZSwgIm1vY2EiOj" + "AsICJ3aWZpIjo1LCAiY29uVHlwZSI6IldBTiIsICJnd1dhbiI6ImYiLCAiRGVmUGFzc3dkQ2h" + "hbmdlZCI6IllFUyIgfQ==" + ) + + # set session + r = session.post(f"{url}/php/ajaxSet_Session.php") + +def docsis_status(session): + """get current DOCSIS status page, parse and return channel data""" + r = session.get(f"{url}/php/status_docsis_data.php") + # extract json from javascript + json_downstream_data = re.search(r".*json_dsData = (.+);.*", r.text)[1] + json_upstream_data = re.search(r".*json_usData = (.+);.*", r.text)[1] + # parse json + downstream_data = json.loads(json_downstream_data) + upstream_data = json.loads(json_upstream_data) + # convert lock status to numeric values + for d in [ upstream_data, downstream_data ]: + for c in d: + if c['LockStatus'] == "ACTIVE" or c['LockStatus'] == "Locked": + c['LockStatus'] = 1 + else: + c['LockStatus'] = 0 + return downstream_data, upstream_data + + +# ----------------------------------------------------------------------------- +if __name__ == "__main__": + # get config + url = os.getenv("url") + username = os.getenv("username") + password = os.getenv("password") + # validate config + if not url or not username or not password: + print("Set url, username and password first.", file=sys.stderr) + exit(1) + # create session + session = requests.Session() + # login with username and password + login(session, url, username, password) + # get DOCSIS status + downstream, upstream = docsis_status(session) + # prepare munin graph info + graph_descriptions = [ + { + "name": "up_signal", + "title": "DOCSIS Upstream signal strength", + "vlabel": "dBmV", + "info": "DOCSIS upstream signal strength by channel", + "data": upstream, + "key": "PowerLevel" + }, + { + "name": "up_lock", + "title": "DOCSIS Upstream lock", + "vlabel": "locked", + "info": "DOCSIS upstream channel lock status", + "data": upstream, + "key": "LockStatus" + }, + { + "name": "down_signal", + "title": "DOCSIS Downstream signal strength", + "vlabel": "dBmV", + "info": "DOCSIS downstream signal strength by channel", + "data": downstream, + "key": "PowerLevel" + }, + { + "name": "down_lock", + "title": "DOCSIS Downstream lock", + "vlabel": "locked", + "info": "DOCSIS downstream channel lock status", + "data": downstream, + "key": "LockStatus" + }, + { + "name": "down_snr", + "title": "DOCSIS Downstream signal/noise ratio", + "vlabel": "dB", + "info": "SNR/MER", + "data": downstream, + "key": "SNRLevel" + } + ] + + # configure ? + if len(sys.argv) > 1 and "config" in sys.argv[1]: + # process all graphs + for g in graph_descriptions: + # graph config + print( + f"multigraph docsis_{g['name']}\n" + f"graph_title {g['title']}\n" \ + f"graph_category network\n" \ + f"graph_vlabel {g['vlabel']}\n" \ + f"graph_info {g['info']}\n" \ + f"graph_scale no\n" + ) + + # channels + for c in g['data']: + # only use channels with PowerLevel + if not c['PowerLevel']: + continue + print( + f"channel_{c['ChannelID']}.label {c['ChannelID']} ({c['Frequency']} MHz)\n" + f"channel_{c['ChannelID']}.info Channel type: {c['ChannelType']}, Modulation: {c['Modulation']}" + ) + + # output values ? + else: + # process all graphs + for g in graph_descriptions: + print(f"multigraph docsis_{g['name']}") + # channels + for c in g['data']: + # only use channels with PowerLevel + if not c['PowerLevel']: + continue + print(f"channel_{c['ChannelID']}.value {c[g['key']]}") From 57558b68bdd2064806cc9289aad72a50fce1447f Mon Sep 17 00:00:00 2001 From: Neszt Tibor Date: Thu, 30 May 2019 09:28:40 +0200 Subject: [PATCH 368/695] Plugin xen-multi: xentop bug workaround There is a bug in xentop, its output is too high in rare cases. https://lists.xenproject.org/archives/html/xen-users/2019-04/msg00020.html Workaround to process 3 iterations after the first one (4 itrations) and choose the lowest cpusecs value line. --- plugins/xen/xen-multi | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/plugins/xen/xen-multi b/plugins/xen/xen-multi index dafaa57a..ad4c6812 100755 --- a/plugins/xen/xen-multi +++ b/plugins/xen/xen-multi @@ -129,7 +129,12 @@ sub trim_label { # Global variables my (%domains,$domain,@domainlist,$munindomain,$cpusecs,$cpupercent,$memk,$nettxk,$netrxk,$vbdrd,$vbdwr); -open (XENTOP,"xentop -b -f -i2 |") or die "Could not execute xentop, $!"; +# There is a bug in xentop, its output is too high in rare cases. +# https://lists.xenproject.org/archives/html/xen-users/2019-04/msg00020.html +# Workaround to process 3 iterations after the first one (4 iterations) and +# choose the lowest cpusecs value line. + +open (XENTOP,"xentop -b -f -i4 |") or die "Could not execute xentop, $!"; # Now we build a hash of hashes to store information while () { @@ -146,13 +151,21 @@ while () { # We need the remaining data only for a normal run if ($ARGV[0] eq "") { - $domains{$domain}{'cpusecs'} = $cpusecs; - $domains{$domain}{'cpupercent'} = $cpupercent; - $domains{$domain}{'mem'} = $memk; - $domains{$domain}{'nettx'} = $nettxk; - $domains{$domain}{'netrx'} = $netrxk; - $domains{$domain}{'vbdrd'} = $vbdrd; - $domains{$domain}{'vbdwr'} = $vbdwr; + # the cnt key counts the iterations + # - skip the first one because xentop gives 0% cpu for the first iteration ( cnt > 1 ) + # - process if not processed yet ( !defined ) or if current cpusecs less than any earlier + # normally the cpusecs is monotonous per domain as it grows so a smaller value means that the previous one was wrong + + $domains{$domain}{'cnt'}++; + if ( $domains{$domain}{'cnt'} > 1 && ( !defined $domains{$domain}{'cpusecs'} || $cpusecs < $domains{$domain}{'cpusecs'} ) ) { + $domains{$domain}{'cpusecs'} = $cpusecs; + $domains{$domain}{'cpupercent'} = $cpupercent; + $domains{$domain}{'mem'} = $memk; + $domains{$domain}{'nettx'} = $nettxk; + $domains{$domain}{'netrx'} = $netrxk; + $domains{$domain}{'vbdrd'} = $vbdrd; + $domains{$domain}{'vbdwr'} = $vbdwr; + } } } From e1731c787eb45d70d0d1a54f3ce07427409c8fd0 Mon Sep 17 00:00:00 2001 From: wodry Date: Mon, 18 Mar 2019 07:48:31 +0100 Subject: [PATCH 369/695] Remove quotes in plugin env parameters Setting `env.torconnectmethod 'port'` leads to the error: ``` munin-run tor_bandwidth env.torconnectmethod contains an invalid value. Please specify either 'port' or 'socket'. ``` Removing the quotes solves that, because the quotes are part of the variable value. ["There is no need to quote the variable content."](http://guide.munin-monitoring.org/en/stable-2.0/plugin/use.html) --- plugins/tor/tor_ | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 630f9a42..d1bb5c8f 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -27,23 +27,23 @@ The default configuration is below [tor_*] user toranon # or any other user/group that is running tor group toranon -env.torcachefile 'munin_tor_country_stats.json' -env.torconnectmethod 'port' -env.torgeoippath '/usr/share/GeoIP/GeoIP.dat' +env.torcachefile munin_tor_country_stats.json +env.torconnectmethod port +env.torgeoippath /usr/share/GeoIP/GeoIP.dat env.tormaxcountries 15 env.torport 9051 -env.torsocket '/var/run/tor/control' +env.torsocket /var/run/tor/control To make it connect through a socket modify this way [tor_*] user toranon # or any other user/group that is running tor group toranon -env.torcachefile 'munin_tor_country_stats.json' -env.torconnectmethod 'socket' -env.torgeoippath '/usr/share/GeoIP/GeoIP.dat' +env.torcachefile munin_tor_country_stats.json +env.torconnectmethod socket +env.torgeoippath /usr/share/GeoIP/GeoIP.dat env.tormaxcountries 15 env.torport 9051 -env.torsocket '/var/run/tor/control' +env.torsocket /var/run/tor/control =head1 COPYRIGHT MIT License From dd94da3bd5675aad47ab8013fe6700fcd4f6e651 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 6 Jun 2019 07:47:52 +1000 Subject: [PATCH 370/695] [router/freebox] Plugin to monitor Freebox information (#985) Signed-off-by: Olivier Mehani --- .../example-graphs/freebox_adsl-day.png | Bin 0 -> 21130 bytes .../freebox_adsl_errors-day.png | Bin 0 -> 18991 bytes .../example-graphs/freebox_traffic-day.png | Bin 0 -> 49128 bytes .../example-graphs/freebox_uptime-day.png | Bin 0 -> 21986 bytes .../example-graphs/freebox_users-day.png | Bin 0 -> 15726 bytes plugins/router/freebox | 479 ++++++++++++++++++ 6 files changed, 479 insertions(+) create mode 100644 plugins/router/example-graphs/freebox_adsl-day.png create mode 100644 plugins/router/example-graphs/freebox_adsl_errors-day.png create mode 100644 plugins/router/example-graphs/freebox_traffic-day.png create mode 100644 plugins/router/example-graphs/freebox_uptime-day.png create mode 100644 plugins/router/example-graphs/freebox_users-day.png create mode 100755 plugins/router/freebox diff --git a/plugins/router/example-graphs/freebox_adsl-day.png b/plugins/router/example-graphs/freebox_adsl-day.png new file mode 100644 index 0000000000000000000000000000000000000000..98ac66fcd225bd91a178effe39055ba83215e8a5 GIT binary patch literal 21130 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 e_0zA-r&(0y?U)}ozCVWxNFJ`V@-)w_5A literal 0 HcmV?d00001 diff --git a/plugins/router/example-graphs/freebox_adsl_errors-day.png b/plugins/router/example-graphs/freebox_adsl_errors-day.png new file mode 100644 index 0000000000000000000000000000000000000000..0d6f4943f811a5e4a8783d3dd3319be172ce9971 GIT binary patch literal 18991 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 I5C$*)2UmGpW&i*H literal 0 HcmV?d00001 diff --git a/plugins/router/example-graphs/freebox_traffic-day.png b/plugins/router/example-graphs/freebox_traffic-day.png new file mode 100644 index 0000000000000000000000000000000000000000..1c5567a4e726252e5dc6d3737bee52902d1e86e0 GIT binary patch literal 49128 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 R&`PmxUcar7hcI~ZKLB(&m!|*# literal 0 HcmV?d00001 diff --git a/plugins/router/freebox b/plugins/router/freebox new file mode 100755 index 00000000..86e6d90a --- /dev/null +++ b/plugins/router/freebox @@ -0,0 +1,479 @@ +#!/bin/bash -u +# -*- sh -*- + +: << =cut + +=head1 NAME + +freebox - Plugin to monitor stats of a Freebox (Free.fr's custom series of routers) + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 CAVEAT + +Only tested on a Freebox v5 with an ADSL uplink. + +=head1 AUTHOR + +Olivier Mehani + +Copyright (C) 2019 Olivier Mehani + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + +=cut + +# shellcheck disable=SC1090 +. "${MUNIN_LIBDIR}/plugins/plugin.sh" + +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +fbx_info_url="http://mafreebox.free.fr/pub/fbx_info.txt" +# +# Example output (including stray whitespaces): +# + +# ______________________________________________________________________ +# +# Etat de la Freebox +# ______________________________________________________________________ +# +# +# Informations générales : +# ======================== +# +# Modèle Freebox ADSL +# Version du firmware 1.5.26 +# Mode de connection Dégroupé +# Temps depuis la mise en route 9 jours, 23 heures, 4 minutes +# +# +# Téléphone : +# =========== +# +# Etat Ok +# Etat du combiné Raccroché +# Sonnerie Inactive +# +# +# Adsl : +# ====== +# +# Etat Showtime +# Protocole ADSL2+ +# Mode Interleaved +# +# Descendant Montant +# -- -- +# Débit ATM 1913 kb/s 945 kb/s +# Marge de bruit 5.10 dB 5.60 dB +# Atténuation 50.00 dB 23.30 dB +# FEC 425249 15503719 +# CRC 5489 0 +# HEC 705 4296208 +# +# Journal de connexion adsl : +# --------------------------- +# +# Date Etat Débit (kb/s) +# -- -- -- +# 21/05/2019 à 08:35:49 Connexion 1913 / 988 +# 21/05/2019 à 08:34:38 Déconnexion +# 20/05/2019 à 21:39:30 Connexion 1156 / 988 +# 20/05/2019 à 21:38:43 Déconnexion +# 20/05/2019 à 14:50:52 Connexion 1925 / 985 +# 20/05/2019 à 14:50:05 Déconnexion +# 20/05/2019 à 07:14:38 Connexion 206 / 833 +# 20/05/2019 à 07:14:10 Déconnexion +# 20/05/2019 à 07:13:50 Connexion 983 / 587 +# 20/05/2019 à 07:12:49 Déconnexion +# 20/05/2019 à 07:09:32 Connexion 1113 / 864 +# 20/05/2019 à 07:08:45 Déconnexion +# 20/05/2019 à 07:07:22 Connexion 1195 / 826 +# 20/05/2019 à 07:06:59 Déconnexion +# 20/05/2019 à 07:06:39 Connexion 1832 / 923 +# 20/05/2019 à 07:05:51 Déconnexion +# 20/05/2019 à 06:58:10 Connexion 1238 / 887 +# 20/05/2019 à 06:57:23 Déconnexion +# 20/05/2019 à 06:56:46 Connexion 1375 / 935 +# 20/05/2019 à 06:55:58 Déconnexion +# 20/05/2019 à 06:55:32 Connexion 1353 / 904 +# 20/05/2019 à 06:54:45 Déconnexion +# 20/05/2019 à 06:50:37 Connexion 1380 / 923 +# 20/05/2019 à 06:49:32 Déconnexion +# +# +# Wifi : +# ====== +# +# Etat Ok +# Modèle Ralink RT2880 +# Canal 1 +# État du réseau Activé +# Ssid NetSSID +# Type de clé WPA (TKIP+AES) +# FreeWifi Désactivé +# FreeWifi Secure Actif +# +# +# Réseau : +# ======== +# +# Adresse MAC Freebox XX:XX:XX:XX:XX:XX +# Adresse IP 203.0.113.60 +# IPv6 Activé +# Mode routeur Activé +# Adresse IP privée 192.0.2.1 +# Adresse IP DMZ 192.0.2.2 +# Adresse IP Freeplayer 192.0.2.0 +# Réponse au ping Activé +# Proxy Wake On Lan Désactivé +# Serveur DHCP Activé +# Plage d'adresses dynamique 192.0.2.100 - 192.0.2.254 +# +# Attributions dhcp : +# ------------------- +# +# Adresse MAC Adresse IP +# -- -- +# XX:XX:XX:XX:XX:XX 192.0.2.101 +# XX:XX:XX:XX:XX:XX 192.0.2.102 +# XX:XX:XX:XX:XX:XX 192.0.2.103 +# XX:XX:XX:XX:XX:XX 192.0.2.104 +# XX:XX:XX:XX:XX:XX 192.0.2.105 +# XX:XX:XX:XX:XX:XX 192.0.2.100 +# XX:XX:XX:XX:XX:XX 192.0.2.3 +# +# Redirections de ports : +# ----------------------- +# +# Protocole Port source Destination Port destination +# -- -- -- -- +# TCP 22 192.0.2.3 22 +# TCP 2222 192.0.2.100 22 +# TCP 1194 192.0.2.3 1194 +# UDP 1194 192.0.2.3 1194 +# TCP 80 192.0.2.3 80 +# +# Interfaces réseau : +# ------------------- +# +# Lien Débit entrant Débit sortant +# -- -- -- +# WAN Ok 1 ko/s 1 ko/s +# Ethernet 0 ko/s 0 ko/s +# USB Non connecté +# Switch 100baseTX-FD 1 ko/s 1 ko/s + +fetch() { + # shellcheck disable=SC2154 + curl -s "$@" +} + +get_line_column() { + local key="$1" + local field_index="$2" + awk '/'"$key"'/ { + if ($('"$field_index"') ~ /^[.0-9]+$/) { + print $('"$field_index"') + } + }' +} + +get_data() { + INFO="$(fetch "${fbx_info_url}" | iconv -f latin1 -t utf8)" + + UPTIME_DAYS=$(get_line_column "Temps depuis la mise en route" "NF-5" <<< "${INFO}") + UPTIME_HOURS=$(get_line_column "Temps depuis la mise en route" "NF-3" <<< "${INFO}") + UPTIME_MINUTES=$(get_line_column "Temps depuis la mise en route" "NF-1" <<< "${INFO}") + + ATM_DOWN=$(get_line_column "ATM" "NF-3" <<< "${INFO}") + ATM_UP=$(get_line_column "ATM" "NF-1" <<< "${INFO}") + + NOISE_DOWN=$(get_line_column "bruit" "NF-3" <<< "${INFO}") + NOISE_UP=$(get_line_column "bruit" "NF-1" <<< "${INFO}") + + ATTENUATION_DOWN=$(get_line_column "Atténuation" "NF-3" <<< "${INFO}") + ATTENUATION_UP=$(get_line_column "Atténuation" "NF-1" <<< "${INFO}") + + FEC_DOWN=$(get_line_column "FEC" "NF-1" <<< "${INFO}") + FEC_UP=$(get_line_column "FEC" "NF" <<< "${INFO}") + CRC_DOWN=$(get_line_column "CRC" "NF-1" <<< "${INFO}") + CRC_UP=$(get_line_column "CRC" "NF" <<< "${INFO}") + HEC_DOWN=$(get_line_column "HEC" "NF-1" <<< "${INFO}") + HEC_UP=$(get_line_column "HEC" "NF" <<< "${INFO}") + + WAN_DOWN=$(get_line_column "WAN" "NF-3" <<< "${INFO}") + WAN_UP=$(get_line_column "WAN" "NF-1" <<< "${INFO}") + ETH_DOWN=$(get_line_column "Ethernet" "NF-3" <<< "${INFO}") + ETH_UP=$(get_line_column "Ethernet" "NF-1" <<< "${INFO}") + USB_DOWN=$(get_line_column "USB" "NF-3" <<< "${INFO}") + USB_UP=$(get_line_column "USB" "NF-1" <<< "${INFO}") + SWITCH_DOWN=$(get_line_column "Switch" "NF-3" <<< "${INFO}") + SWITCH_UP=$(get_line_column "Switch" "NF-1" <<< "${INFO}") + + DHCP_CLIENTS=$(sed -nE '/Attributions dhcp/,/Redirections de ports/{s/^\s*([0-9A-F:]{17}).*$/\1/p}' <<< "${INFO}" | wc -l) + REDIRECT_TCP=$(sed -nE '/Redirections de ports/,/Interfaces réseau/{s/^\s*(TCP).*$/\1/p}' <<< "${INFO}" | wc -l) + REDIRECT_UDP=$(sed -nE '/Redirections de ports/,/Interfaces réseau/{s/^\s*(UDP).*$/\1/p}' <<< "${INFO}" | wc -l) + + if [ -z "${UPTIME_DAYS:-}" ]; then + UPTIME_DAYS=0 + fi + if [ -z "${UPTIME_HOURS:-}" ]; then + UPTIME_HOURS=0 + fi + if [ -z "${UPTIME_MINUTES:-}" ]; then + UPTIME_MINUTES=0 + fi + FREEBOX_UPTIME=$((UPTIME_DAYS*86400+UPTIME_HOURS*3600+UPTIME_MINUTES*60)) + + LAST_CONNECT=$(sed -nE '/Journal de connexion adsl/,+5{s#^\s*([0-9]{2})/([0-9]{2})/([0-9]{4})[^0-9]*([0-9]+):([0-9]+):([0-9]+)\s+Connexion.*#\3-\2-\1T\4:\5:\6#p}' <<< "${INFO}") + CONNECTION_UPTIME=U + if [ -n "${LAST_CONNECT}" ]; then + CONNECTION_UPTIME="$(($(date +%s)-$(date -d "${LAST_CONNECT}" +%s)))" + + fi +} + +graph_config() { + graph="" + if [ -n "${1:-}" ]; then + graph=".$1" + fi + + echo "multigraph freebox${graph}" + + case "$graph" in + .adsl) + echo "graph_title ADSL characteristics" + echo 'graph_category network' + echo 'graph_vlabel dB in (+) / out (-)' + echo 'graph_order noise_down noise attenuation_down attenuation' + + echo 'noise_down.label Noise down' + echo 'noise_down.graph no' + echo 'noise.label Noise' + echo 'noise.negative noise_down' + + echo 'attenuation_down.label Attenuation down' + echo 'attenuation_down.graph no' + echo 'attenuation.label Attenuation' + echo 'attenuation.negative attenuation_down' + ;; + .adsl_errors) + echo 'graph_title ADSL error correction' + echo 'graph_category network' + echo 'graph_vlabel errors in (+) / out (-)' + echo 'graph_order fec_down fec crc_down crc hec_down hec' + + echo 'fec_down.label FEC down' + echo 'fec_down.graph no' + echo 'fec.label FEC' + echo 'fec.negative fec_down' + + echo 'crc_down.label CRC down' + echo 'crc_down.graph no' + echo 'crc.label CRC' + echo 'crc.negative crc_down' + + echo 'hec_down.label HEC down' + echo 'hec_down.graph no' + echo 'hec.label HEC' + echo 'hec.negative hec_down' + ;; + .traffic) + echo 'graph_title Traffic' + echo 'graph_category network' + echo 'graph_vlabel bits per second in (+) / out (-)' + echo 'graph_order atm_down atm wan_down wan eth_down eth usb_down usb switch_down switch' + + echo 'atm_down.label ATM down' + echo 'atm_down.graph no' + echo 'atm_down.cdef atm_down,1000,*' + echo 'atm.label ATM sync' + echo 'atm.negative atm_down' + echo 'atm.cdef atm,1000,*' + + echo 'wan_down.label WAN down' + echo 'wan_down.graph no' + echo 'wan_down.cdef wan_down,8000,*' + echo 'wan.label WAN' + echo 'wan.negative wan_down' + echo 'wan.cdef wan,8000,*' + + echo 'eth_down.label ETH down' + echo 'eth_down.graph no' + echo 'eth_down.cdef eth_down,8000,*' + echo 'eth.label Ethernet' + echo 'eth.negative eth_down' + echo 'eth.cdef eth,8000,*' + + echo 'usb_down.label USB down' + echo 'usb.label USB' + echo 'usb_down.graph no' + echo 'usb_down.cdef usb_down,8000,*' + echo 'usb.negative usb_down' + echo 'usb.cdef usb,8000,*' + + echo 'switch_down.label Switch down' + echo 'switch_down.graph no' + echo 'switch_down.cdef switch_down,8000,*' + echo 'switch.label Switch' + echo 'switch.cdef switch,8000,*' + echo 'switch.negative switch_down' + ;; + .uptime) + echo 'graph_title Uptimes' + echo 'graph_category network' + echo 'graph_vlabel days' + echo 'graph_args --logarithmic' + + echo 'freebox.label Freebox' + echo 'freebox.draw AREA' + echo 'freebox.cdef freebox,86400,/' + + echo 'connection.label Connection' + echo 'connection.cdef connection,86400,/' + ;; + .users) + echo 'graph_title Network users' + echo 'graph_category network' + echo 'graph_vlabel count' + + echo 'dhcp.label DHCP leases' + echo 'redirect_tcp.label TCP redirections' + echo 'redirect_udp.label UDP redirections' + ;; + *) + echo 'graph_title Freebox connection' + echo 'graph_category network' + echo 'graph_vlabel bits per second in (+) / out (-)' + echo 'graph_order main_atm_down main_atm main_wan_down main_wan' + + # XXX: parameters duplicated from traffic + echo 'main_wan_down.label WAN down' + echo 'main_wan_down.graph no' + echo 'main_wan_down.cdef main_wan_down,8000,*' + echo 'main_wan.label WAN' + echo 'main_wan.negative main_wan_down' + echo 'main_wan.cdef main_wan,8000,*' + + echo 'main_atm_down.label ATM down' + echo 'main_atm_down.graph no' + echo 'main_atm_down.cdef main_atm_down,1000,*' + echo 'main_atm.label ATM sync' + echo 'main_atm.negative main_atm_down' + echo 'main_atm.cdef main_atm,1000,*' + ;; + + esac + echo +} + +graph_data() { + graph="" + if [ -n "${1:-}" ]; then + graph=".$1" + fi + + echo "multigraph freebox${graph}" + case "$graph" in + .adsl) + echo "noise.value ${NOISE_UP:-U}" + echo "noise_down.value ${NOISE_DOWN:-U}" + + echo "attenuation.value ${ATTENUATION_UP:-U}" + echo "attenuation_down.value ${ATTENUATION_DOWN:-U}" + ;; + .adsl_errors) + echo "fec.value ${FEC_UP:-U}" + echo "fec_down.value ${FEC_DOWN:-U}" + + echo "crc.value ${CRC_UP:-U}" + echo "crc_down.value ${CRC_DOWN:-U}" + + echo "hec.value ${HEC_UP:-U}" + echo "hec_down.value ${HEC_DOWN:-U}" + ;; + .traffic) + echo "atm.value ${ATM_UP:-U}" + echo "atm_down.value ${ATM_DOWN:-U}" + + echo "wan.value ${WAN_UP:-U}" + echo "wan_down.value ${WAN_DOWN:-U}" + + echo "eth.value ${ETH_UP:-U}" + echo "eth_down.value ${ETH_DOWN:-U}" + + echo "usb.value ${USB_UP:-U}" + echo "usb_down.value ${USB_DOWN:-U}" + + echo "switch.value ${SWITCH_UP:-U}" + echo "switch_down.value ${SWITCH_DOWN:-U}" + ;; + .uptime) + echo "freebox.value ${FREEBOX_UPTIME:-U}" + echo "connection.value ${CONNECTION_UPTIME:-U}" + ;; + .users) + echo "dhcp.value ${DHCP_CLIENTS:-U}" + echo "redirect_tcp.value ${REDIRECT_TCP:-U}" + echo "redirect_udp.value ${REDIRECT_UDP:-U}" + ;; + *) + echo "main_wan.value ${WAN_UP:-U}" + echo "main_wan_down.value ${WAN_DOWN:-U}" + + echo "main_atm.value ${ATM_UP:-U}" + echo "main_atm_down.value ${ATM_DOWN:-U}" + + esac + echo +} + +main() { + case ${1:-} in + autoconf) + for CMD in curl iconv; do + if ! command -v "${CMD}" >/dev/null; then + echo "no (${CMD} not found)" + fi + done + + if curl --connect-timeout 1 -qso /dev/null "${fbx_info_url}"; then + echo 'yes' + else + echo "no (failed to retrieve ${fbx_info_url})" + fi + ;; + config) + graph_config + graph_config adsl + graph_config adsl_errors + graph_config traffic + graph_config uptime + graph_config users + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then + main + fi + ;; + *) + get_data + graph_data + graph_data adsl + graph_data adsl_errors + graph_data traffic + graph_data uptime + graph_data users + ;; + esac +} + +main "${1:-}" From 92db831bc3abed6b7bad5a30e62437371e733fd7 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 14 Apr 2019 11:44:09 +1000 Subject: [PATCH 371/695] [network/upnpc_] don't let the shell expand ${graph_period} Also, update license and add SPDX identifier Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 7e42fdea..c50037c6 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -28,7 +28,7 @@ Olivier Mehani =head1 LICENSE -GPLv2 +SPDX-License-Identifier: GPL-2.0-or-later =head1 MAGIC MARKERS @@ -81,7 +81,8 @@ up.negative down EOF ;; "traffic") - cat << EOF + # ${graph_period} is not a shell variable + cat << 'EOF' graph_title Uplink traffic graph_args --base 1024 -l 0 graph_category network @@ -97,7 +98,8 @@ up.negative down EOF ;; "pkts") - cat << EOF + # ${graph_period} is not a shell variable + cat << 'EOF' graph_title Uplink packets graph_args --base 1000 -l 0 graph_category network From 691e52b8681de8a4b5eb3418a211b04afa58d4ee Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 11 Apr 2019 22:56:28 +1000 Subject: [PATCH 372/695] [internode_usage] New plugin for Australian ISP Internode * Add BUGS and SPDX license * Add dirtyconfig support * Use ideal usage as warning --- .../example-graphs/internode_current-day.png | Bin 0 -> 31827 bytes .../example-graphs/internode_daily-week.png | Bin 0 -> 25938 bytes .../example-graphs/internode_usage-month.png | Bin 0 -> 27786 bytes plugins/isp/internode_usage | 224 ++++++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 plugins/isp/example-graphs/internode_current-day.png create mode 100644 plugins/isp/example-graphs/internode_daily-week.png create mode 100644 plugins/isp/example-graphs/internode_usage-month.png create mode 100755 plugins/isp/internode_usage diff --git a/plugins/isp/example-graphs/internode_current-day.png b/plugins/isp/example-graphs/internode_current-day.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbd9f06a8e16fe43c2f08e817a30afbc52b8a35 GIT binary patch literal 31827 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 + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + +=cut + +# shellcheck disable=SC1090 +. "${MUNIN_LIBDIR:-.}/plugins/plugin.sh" + +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +if ! command -v curl >/dev/null; then + echo "curl not found" >&2 + exit 1 +fi +if ! command -v xpath >/dev/null; then + echo "xpath (Perl XML::LibXML) not found" >&2 + exit 1 +fi +if ! command -v bc >/dev/null; then + echo "bc not found" >&2 + exit 1 +fi + +if [ -z "${internode_api_url:-}" ]; then + internode_api_url="https://customer-webtools-api.internode.on.net/api/v1.5" +fi + +xpath_extract() { + xpath="$1" + xpath -q -n -e "${xpath}" | \ + sed 's/<\([^>]*\)>\([^<]*\)<[^>]*>/\2/' +} + +xpath_extract_attribute() { + xpath="$1" + xpath -q -n -e "${xpath}" | \ + sed 's/.*="\([^"]\+\)".*/\1/' +} + +fetch() { + # shellcheck disable=SC2154 + curl -u "${internode_api_login}:${internode_api_password}" -s "$@" +} + +get_data() { + SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service")" + SERVICE_USERNAME="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/username")" + SERVICE_QUOTA="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/quota")" + SERVICE_PLAN="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan")" + SERVICE_ROLLOVER="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/rollover")" + SERVICE_INTERVAL="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan-interval" | sed 's/ly$//')" + + HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history")" + TODAY_TIMESTAMP="$(date +%s)" + DAILY_DATE="$(date +%Y-%m-%d -d yesterday)" + DAILY_TIMESTAMP="$(date -d "${DAILY_DATE} $(date +%H:%M:%S)" +%s)" + DAILY_USAGE="$(echo "${HISTORY_XML}" | xpath_extract "internode/api/usagelist/usage[@day=\"${DAILY_DATE}\"]/traffic")" + + USAGE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/usage")" + SERVICE_USAGE="$(echo "${USAGE_XML}" | xpath_extract "internode/api/traffic")" + + USAGE_CRITICAL="${SERVICE_QUOTA}" + USAGE_WARNING="$(echo "${SERVICE_QUOTA}*.75" | bc -ql)" + + TODAY="$(date +%s)" + FIRST_DAY="$(date +%s --date "${SERVICE_ROLLOVER} -1 ${SERVICE_INTERVAL}")" + LAST_DAY="$(date +%s --date "${SERVICE_ROLLOVER}")" + BILLING_PERIOD="(${LAST_DAY}-${FIRST_DAY})" + IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${TODAY})/${BILLING_PERIOD})" | bc -ql)" +} + +graph_config() { + graph="" + if [ -n "${1:-}" ]; then + graph=".$1" + fi + + echo "multigraph internode_usage_${SERVICE_ID}${graph}" + + case "$graph" in + .current) + echo "graph_title Hourly rate for ${SERVICE_USERNAME}" + echo 'graph_category network' + # ${graph_period} is not a shell variable + # shellcheck disable=SC2016 + echo 'graph_vlabel bytes per ${graph_period}' + # XXX: this seems to be updated twice per hour; + # the data from this graph may be nonsense + echo 'graph_period hour' + + echo "hourly_rate.label Hourly usage" + echo "hourly_rate.type DERIVE" + echo "hourly_rate.min 0" + + ;; + .daily) + echo "graph_title Previous-day usage for ${SERVICE_USERNAME}" + echo 'graph_category network' + # ${graph_period} is not a shell variable + # shellcheck disable=SC2016 + echo 'graph_vlabel bytes per ${graph_period}' + echo 'graph_period day' + + echo "daily_rate.label Previous-day usage" + echo "daily_rate.type GAUGE" + + ;; + *) + #.usage) + echo "graph_title Uplink usage for ${SERVICE_USERNAME}" + echo 'graph_category network' + echo 'graph_vlabel bytes' + echo 'graph_period hour' + + echo "usage.label Total usage" + echo "usage.draw AREA" + echo "usage.extinfo ${SERVICE_PLAN}; rollover: ${SERVICE_ROLLOVER}" + echo "ideal.label Ideal usage" + echo "ideal.draw LINE" + echo "ideal.colour FFA500" + + echo "usage.critical ${USAGE_CRITICAL}" + echo "usage.warning ${IDEAL_USAGE}" + echo "ideal.critical 0:" + echo "ideal.warning 0:" + ;; + esac + echo +} + +graph_data() { + graph="" + if [ -n "${1:-}" ]; then + graph=".${1}" + fi + + echo "multigraph internode_usage_${SERVICE_ID}${graph}" + case "${graph}" in + .current) + echo "hourly_rate.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}" + ;; + .daily) + echo "daily_rate.value ${DAILY_TIMESTAMP}:${DAILY_USAGE:-U}" + ;; + *) + echo "usage.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}" + echo "ideal.value ${TODAY_TIMESTAMP}:${IDEAL_USAGE:-U}" + ;; + esac + echo +} + +main() { + case ${1:-} in + config) + graph_config + graph_config usage + graph_config current + graph_config daily + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then + main + fi + ;; + *) + graph_data + graph_data usage + graph_data current + graph_data daily + ;; + esac +} + +# Determine the service ID from the name of the symlink +SERVICE_ID="$(echo "${0}" | sed -n 's/^.*internode_usage_//p')" +if [ -z "${SERVICE_ID}" ]; then + API_XML="$(fetch "${internode_api_url}")" + # Otherwise, get the first service in the list + SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")" +fi + +get_data + +main ${1:-} From 407adbd620d4f9eedc5b833c42c3fb52c5ca8f04 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 6 Jun 2019 20:56:54 +0200 Subject: [PATCH 373/695] fix merge issues * use 2 newlines to separate toplevel objects * use string comparision for argument parsing instead of substring search * remove hashes * use perldoc style --- plugins/router/arris-tg3442 | 105 +++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 44 deletions(-) mode change 100644 => 100755 plugins/router/arris-tg3442 diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 old mode 100644 new mode 100755 index 35307c74..088b50e5 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -1,49 +1,65 @@ #!/usr/bin/env python3 """ -# MUNIN Plugin to monitor status of Arris TG3442 / TG2492LG-85 -# and compatible cable modems -# -# Connect to the web-frontend and get current DOCSIS status of upstream and -# downstream channels. (Signal Power, SNR, Lock Status) -# -# -# Requirements: -# - BeautifulSoup -# - pycryptodome -# -# Configuration: -# [arris] -# env.url http://192.168.100.1 -# env.username admin -# env.password yourpassword -# -# Parameters: -# url - URL to web-frontend -# username - defaults to "admin" -# password - valid password -# -# -# References: https://www.arris.com/products/touchstone-tg3442-cable-voice-gateway/ -# -# -# -# Copyright (c) 2019 Daniel Hiepler -# -# Permission to use, copy, and modify this software with or without fee -# is hereby granted, provided that this entire notice is included in -# all source code copies of any software which is or includes a copy or -# modification of this software. -# -# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR -# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY -# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE -# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR -# PURPOSE. -# -# -# Magic markers -# #%# family=contrib +=head1 NAME + +arris - MUNIN Plugin to monitor status of Arris TG3442 / TG2492LG-85 + and compatible cable modems + +=head1 DESCRIPTION +Connect to the web-frontend and get current DOCSIS status of upstream and +downstream channels. (Signal Power, SNR, Lock Status) + + +=head1 REQUIREMENTS +- BeautifulSoup +- pycryptodome + + +=head1 CONFIGURATION + +=head2 Example +[arris] +env.url http://192.168.100.1 +env.username admin +env.password yourpassword + + +=head2 Parameters +url - URL to web-frontend +username - defaults to "admin" +password - valid password + + +=head1 REFERENCES +https://www.arris.com/products/touchstone-tg3442-cable-voice-gateway/ + + +=head1 AUTHOR + + Copyright (c) 2019 Daniel Hiepler + Copyright (c) 2004-2009 Nicolas Stransky + Copyright (c) 2018 Lars Kruse + + +=head1 LICENSE + Permission to use, copy, and modify this software with or without fee + is hereby granted, provided that this entire notice is included in + all source code copies of any software which is or includes a copy or + modification of this software. + + THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + PURPOSE. + + +=head1 MAGIC MARKERS + + #%# family=contrib + +=cut """ import binascii @@ -134,6 +150,7 @@ def login(session, url, username, password): # set session r = session.post(f"{url}/php/ajaxSet_Session.php") + def docsis_status(session): """get current DOCSIS status page, parse and return channel data""" r = session.get(f"{url}/php/status_docsis_data.php") @@ -214,7 +231,7 @@ if __name__ == "__main__": ] # configure ? - if len(sys.argv) > 1 and "config" in sys.argv[1]: + if len(sys.argv) > 1 and "config" == sys.argv[1]: # process all graphs for g in graph_descriptions: # graph config From 6cb5c9e104d9a1a95adba7ce6e97651232c3595e Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 10 Jun 2019 16:55:12 +0200 Subject: [PATCH 374/695] Properly report unknowns --- plugins/other/pacman_pending_updates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index 7a327995..00c2ecdb 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -54,7 +54,7 @@ EOM ;; *) - updates="$(checkupdates)" || exit + updates="$(checkupdates)" || echo "updates.value U" && exit if [ -n "$updates" ]; then echo "updates.value $(echo "$updates" | wc -l)" echo "updates.extinfo $(echo "$updates" | paste -s -d,)" From edc30c185267b4e851d02369aeb8b8478f09a9db Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 23 Jun 2019 21:22:01 +1000 Subject: [PATCH 375/695] [file_length_] Add configurable type Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ index f5269584..f698cf40 100755 --- a/plugins/system/file_length_ +++ b/plugins/system/file_length_ @@ -8,13 +8,17 @@ file_length_ - Plugin to monitor the length of specified files Useful for things such as lists (white, black, user, ...). +The type of metric defaults to GAUGE, but it can be configured to, say, DERIVE, +which is useful for monitoring the rate of message in error logs. + =head1 CONFIGURATION [file_length_IDENTIFIER] env.files FILEPATHGLOB1 FILEPATHGLOB2 ... - env.category DEFAULTS_TO_system + env.category system env.title OPTIONAL_TITLE env.logarithmic 1 + env.type GAUGE =head1 AUTHOR @@ -22,7 +26,7 @@ Olivier Mehani (based on disk/log_sizes) =head1 LICENSE -GPLv2 +SPDX-License-Identifier: GPL-2.0-only =head1 MAGIC MARKERS @@ -43,6 +47,7 @@ FILES=${files:-/var/log/messages} # shellcheck disable=SC2116 disable=SC2086 FILES=$(echo $FILES) +TYPE=${type:-GAUGE} if [ "$1" = "config" ] ; then # shellcheck disable=SC2154 @@ -63,12 +68,16 @@ EOF MF=$(clean_fieldname "$F") BF=$(basename "$F") echo "$MF.label ${BF}" + echo "$MF.type ${TYPE}" done else + HAS_GNU_STAT=$(stat --help | grep GNU) for F in $FILES; do MF=$(echo "$F" | sed 's/[-\/\.]/_/g') echo "$MF.value $(wc -l < "$F")" - echo "$MF.extinfo $(stat --printf="%sB\n" "$F")" + if [ -n "${HAS_GNU_STAT}" ]; then + echo "$MF.extinfo $(stat --printf="%sB\n" "$F")" + fi done fi From 1de77292c94f321e7721c4c34988c401377734c9 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 27 Jun 2019 12:42:47 +1000 Subject: [PATCH 376/695] [file_lenght_] Various fixes * Only show extinfo if stat is the GNU version * Unify variable references * Better legend when type is not GAUGE Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ index f698cf40..90810d55 100755 --- a/plugins/system/file_length_ +++ b/plugins/system/file_length_ @@ -36,20 +36,20 @@ SPDX-License-Identifier: GPL-2.0-only # needs shellcheck -x /usr/share/munin/plugins/plugin.sh # shellcheck source=/usr/share/munin/plugins/plugin.sh -. "$MUNIN_LIBDIR/plugins/plugin.sh" +. "${MUNIN_LIBDIR}/plugins/plugin.sh" -NAME=$(echo "$0" | sed 's/.*_//') -TITLE=${title:-File lengths for $NAME} +NAME=$(echo "${0}" | sed 's/.*_//') +TITLE=${title:-File lengths for ${NAME}} CATEGORY=${category:-system} FILES=${files:-/var/log/messages} # we want globbing to happen here # shellcheck disable=SC2116 disable=SC2086 -FILES=$(echo $FILES) +FILES=$(echo ${FILES}) TYPE=${type:-GAUGE} -if [ "$1" = "config" ] ; then +if [ "${1}" = "config" ] ; then # shellcheck disable=SC2154 if [ "${logarithmic}" = "1" ]; then graph_args="-o" @@ -60,24 +60,32 @@ if [ "$1" = "config" ] ; then graph_title ${TITLE} graph_args ${graph_args} --base 1000 graph_category ${CATEGORY} -graph_info This graph shows the length of ${FILES} -graph_vlabel length (lines) EOF +if [ "${TYPE}" = "GAUGE" ]; then + echo "graph_info This graph shows the length of ${FILES}" + echo "graph_vlabel length (lines)" +else + echo "graph_info This graph shows the addition of new lines in ${FILES}" + # ${graph_period} is not a shell variable + # shellcheck disable=SC2016 + echo 'graph_vlabel new lines per ${graph_period}' +fi - for F in $FILES; do - MF=$(clean_fieldname "$F") - BF=$(basename "$F") - echo "$MF.label ${BF}" - echo "$MF.type ${TYPE}" + for F in ${FILES}; do + MF=$(clean_fieldname "${F}") + BF=$(basename "${F}") + echo "${MF}.label ${BF}" + echo "${MF}.type ${TYPE}" + echo "${MF}.min 0" done else - HAS_GNU_STAT=$(stat --help | grep GNU) - for F in $FILES; do - MF=$(echo "$F" | sed 's/[-\/\.]/_/g') - echo "$MF.value $(wc -l < "$F")" + HAS_GNU_STAT=$(stat --help 2>&1| grep GNU) + for F in ${FILES}; do + MF=$(echo "${F}" | sed 's/[-\/\.]/_/g') + echo "${MF}.value $(wc -l < "${F}")" if [ -n "${HAS_GNU_STAT}" ]; then - echo "$MF.extinfo $(stat --printf="%sB\n" "$F")" + echo "${MF}.extinfo $(stat --printf="%sB\n" "${F}")" fi done fi From c4a08bfa53ee326df91d64b668e7b98105f659d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20S?= Date: Thu, 11 Jul 2019 11:43:32 +0200 Subject: [PATCH 377/695] libvirt/kvm_*: Simple regex to match VM name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Debian Stretch the cmdline is something like: qemu-system-x86_64-enable-kvm-nameguest=vmname,debug-threads=on-S-[…] Without null characters: qemu-system-x86_64 -enable-kvm -name guest=vmname,debug-threads=on[…] We need to match only guest=vmname, so the regex "^.*guest=([a-zA-Z0-9.-_-]*).*$" is simpler and match the VM name. The precedent regex "^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$" was not matching it. BTW, It seems that \x00 does not match correctly null characters so I removed it. --- plugins/libvirt/kvm_cpu | 2 +- plugins/libvirt/kvm_io | 2 +- plugins/libvirt/kvm_mem | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index c9cad306..ff696fc7 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -68,7 +68,7 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*guest=([a-zA-Z0-9.-_-]*).*$",r"\1", cmdline.readline())) return result def list_pids(): diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 972f4983..0e0cc386 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -85,7 +85,7 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*guest=([a-zA-Z0-9.-_-]*).*$",r"\1", cmdline.readline())) return result def list_pids(): diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index 7e8d2793..aeb1af7e 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -82,7 +82,7 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*guest=([a-zA-Z0-9.-_-]*).*$",r"\1", cmdline.readline())) return result def list_pids(): From bb4d6c64f4a94986f8e16d59064c5519b1e6638c Mon Sep 17 00:00:00 2001 From: Tomohiro Hosaka Date: Tue, 16 Jul 2019 03:20:06 +0900 Subject: [PATCH 378/695] fix uptime_bsd. compile time is not included since FreeBSD 12.0-RELEASE due to Reproducible Builds https://wiki.freebsd.org/ReproducibleBuilds/Base https://reviews.freebsd.org/D4347 sysctl output samples: % (11.2-RELEASE) sysctl kern.version kern.version: FreeBSD 11.2-RELEASE-p10 #0: Mon May 13 21:20:50 UTC 2019 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC % (12.0-RELEASE) sysctl kern.version kern.version: FreeBSD 12.0-RELEASE-p7 GENERIC --- plugins/bsd/uptime_bsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/bsd/uptime_bsd b/plugins/bsd/uptime_bsd index 898a1a09..98f80792 100755 --- a/plugins/bsd/uptime_bsd +++ b/plugins/bsd/uptime_bsd @@ -48,7 +48,7 @@ use Date::Parse; my $kern=`sysctl -n kern.version`; $kern=~ /:\s+(.*\S)\s+\w+\@/; #print "Compile: $1\n"; -$kern=str2time($1); +$kern= $1 ? str2time($1) : undef; my $boot=`sysctl -n kern.boottime`; # OpenBSD will return seconds from the epoch if ($ostype ne "OpenBSD") { @@ -59,5 +59,5 @@ if ($ostype ne "OpenBSD") { my $now=time; -print "compile.value ",($now-$kern)/60/60/24,"\n"; +print "compile.value ",($now-$kern)/60/60/24,"\n" if $kern; print "uptime.value ",($now-$boot)/60/60/24,"\n"; From 7fa8734a26d8b78da5b181197136c1e58ac5763c Mon Sep 17 00:00:00 2001 From: HORINOUCHI Masato Date: Sun, 26 May 2019 00:47:24 +0900 Subject: [PATCH 379/695] Add chrony_drift. --- plugins/chrony/chrony_drift | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 plugins/chrony/chrony_drift diff --git a/plugins/chrony/chrony_drift b/plugins/chrony/chrony_drift new file mode 100755 index 00000000..26327d62 --- /dev/null +++ b/plugins/chrony/chrony_drift @@ -0,0 +1,88 @@ +#! /bin/sh +# -*- sh -*- + +: <<=cut + +=head1 NAME + +chrony_drift - Munin plugin to monitor the Chrony drift value. + +=head1 APPLICABLE SYSTEMS + +Any chronyd host. + +=head1 CONFIGURATION + +The following configuration parameters are used by this plugin: + + [chrony_drift] + env.driftfile - Path to driftfile. + +=head2 DEFAULT CONFIGURATION + + [chrony_drift] + env.driftfile "/var/lib/chrony/chrony.drift" + +=head1 USAGE + +Link this plugin to /etc/munin/plugins/ and restart the munin-node. + +=head1 AUTHOR + +HORINOUCHI Masato 2019-07-16 + +=head1 LICENSE + +Same as munin. + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf + +=cut + +driftfile=${driftfile:-'/var/lib/chrony/chrony.drift'} + +do_autoconf () { + if [ -r "$driftfile" ]; then + echo "yes" + else + echo "no (could not read driftfile '$driftfile'.)" + fi +} + + +do_config () { + cat <<'EOM' +graph_title Chrony Drift +graph_args --base 1000 +graph_vlabel Parts Per Million +graph_category time +drift.label System Clock Gain/Loss Rate +error_bound.label Estimate of Error Bound +graph_info The rate at which the system clock gains or loses time relative to real time. +EOM +} + + +do_ () { + if [ -r "$driftfile" ]; then + echo "drift.value $(awk '{print $1;}' "$driftfile")" + echo "error_bound.value $(awk '{print $2;}' "$driftfile")" + else + echo "drift.value U" + echo "error_bound.value U" + fi +} + + +case $1 in + autoconf|config|'') + do_"$1" + ;; + *) + echo "Don't know how to do that" >&2 + exit 1 + ;; +esac From ddced538d57b4a7490fa701919c3c1a988863fb1 Mon Sep 17 00:00:00 2001 From: HORINOUCHI Masato Date: Sun, 26 May 2019 00:46:17 +0900 Subject: [PATCH 380/695] Add ntp_drift. --- plugins/ntp/ntp_drift | 85 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100755 plugins/ntp/ntp_drift diff --git a/plugins/ntp/ntp_drift b/plugins/ntp/ntp_drift new file mode 100755 index 00000000..a19f4c83 --- /dev/null +++ b/plugins/ntp/ntp_drift @@ -0,0 +1,85 @@ +#! /bin/sh +# -*- sh -*- + +: <<=cut + +=head1 NAME + +ntp_drift - Munin plugin to monitor the NTP drift value. + +=head1 APPLICABLE SYSTEMS + +Any ntpd host. + +=head1 CONFIGURATION + +The following configuration parameters are used by this plugin: + + [ntp_drift] + env.driftfile - Path to driftfile. + +=head2 DEFAULT CONFIGURATION + + [ntp_drift] + env.driftfile "/var/lib/ntp/ntp.drift" + +=head1 USAGE + +Link this plugin to /etc/munin/plugins/ and restart the munin-node. + +=head1 AUTHOR + +HORINOUCHI Masato 2019-07-16 + +=head1 LICENSE + +Same as munin. + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf + +=cut + +driftfile=${driftfile:-'/var/lib/ntp/ntp.drift'} + +do_autoconf () { + if [ -r "$driftfile" ]; then + echo "yes" + else + echo "no (could not read driftfile '$driftfile'.)" + fi +} + + +do_config () { + cat <<'EOM' +graph_title NTP drift +graph_args --base 1000 +graph_vlabel Parts Per Million +graph_category time +drift.label Frequency Offset +graph_info The frequency of the local clock oscillator. A single floating point number, which records the frequency offset measured in parts-per-million (PPM). +EOM +} + + +do_ () { + if [ -r "$driftfile" ]; then + echo "drift.value $(cat "$driftfile")" + else + echo "drift.value U" + fi +} + + +case $1 in + autoconf|config|'') + do_"$1" + ;; + *) + echo "Don't know how to do that" >&2 + exit 1 + ;; +esac From 53c76b86154efbeea3df216172edf86f6228e35e Mon Sep 17 00:00:00 2001 From: flotat stephane Date: Thu, 18 Jul 2019 14:52:32 +0200 Subject: [PATCH 381/695] Remove double quote for env.driftfile variable --- plugins/chrony/chrony_drift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/chrony/chrony_drift b/plugins/chrony/chrony_drift index 26327d62..ddaf4aab 100755 --- a/plugins/chrony/chrony_drift +++ b/plugins/chrony/chrony_drift @@ -21,7 +21,7 @@ The following configuration parameters are used by this plugin: =head2 DEFAULT CONFIGURATION [chrony_drift] - env.driftfile "/var/lib/chrony/chrony.drift" + env.driftfile /var/lib/chrony/chrony.drift =head1 USAGE From 93f4ff09a9111c42a32bbb695213c1fcad97f44c Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 18 Jul 2019 21:48:14 +0200 Subject: [PATCH 382/695] Adding a link to explain "bbox" --- plugins/router/bbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/router/bbox b/plugins/router/bbox index 523ced40..8f2ee9f7 100644 --- a/plugins/router/bbox +++ b/plugins/router/bbox @@ -1,5 +1,5 @@ #! /bin/sh -# Parse bbox information +# Parse bbox information - https://fr.wikipedia.org/wiki/Bbox # (c) 2018 - GPLv2 - Steve Schnepp # # Configuration is done via ENV vars, here is the default : From af28042128e5cbb3f76e2d8a3f54df7c6df6b31a Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 18 Jul 2019 22:44:49 +0200 Subject: [PATCH 383/695] bbox: quoting the variables --- plugins/router/bbox | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/router/bbox b/plugins/router/bbox index 8f2ee9f7..64b4a792 100644 --- a/plugins/router/bbox +++ b/plugins/router/bbox @@ -87,15 +87,15 @@ then exit 0 fi -TMPFILE=$(mktemp -d) -trap 'rm -Rf $TMPFILE' EXIT +TMPFILE="$(mktemp -d)" +trap 'rm -Rf "$TMPFILE"' EXIT -cd $TMPFILE +cd "$TMPFILE" -curl -s http://$IP/api/v1/wan/ip > "ip.json" & -curl -s http://$IP/api/v1/wan/ip/stats > "ip-stats.json" & -curl -s http://$IP/api/v1/wan/xdsl > "xdsl.json" & -curl -s http://$IP/api/v1/wan/xdsl/stats > "xdsl-stats.json" & +curl -s "http://$IP/api/v1/wan/ip" > "ip.json" & +curl -s "http://$IP/api/v1/wan/ip/stats" > "ip-stats.json" & +curl -s "http://$IP/api/v1/wan/xdsl" > "xdsl.json" & +curl -s "http://$IP/api/v1/wan/xdsl/stats" > "xdsl-stats.json" & wait From 59fcde25d13d9a423c28ce51fd4975b603b58d66 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 18 Jul 2019 22:50:38 +0200 Subject: [PATCH 384/695] bbox: using jq -r to force numeric evaluation --- plugins/router/bbox | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/router/bbox b/plugins/router/bbox index 64b4a792..0ae62a18 100644 --- a/plugins/router/bbox +++ b/plugins/router/bbox @@ -109,32 +109,32 @@ then fi echo multigraph bbox_adsl_bw -echo up.value $(jq ".[].wan.xdsl.up.bitrates" < xdsl.json) -echo down.value $(jq ".[].wan.xdsl.down.bitrates" < xdsl.json) +echo up.value $(jq -r ".[].wan.xdsl.up.bitrates" < xdsl.json) +echo down.value $(jq -r ".[].wan.xdsl.down.bitrates" < xdsl.json) echo multigraph bbox_adsl_att -echo up.value $(jq ".[].wan.xdsl.up.attenuation" < xdsl.json) -echo down.value $(jq ".[].wan.xdsl.down.attenuation" < xdsl.json) +echo up.value $(jq -r ".[].wan.xdsl.up.attenuation" < xdsl.json) +echo down.value $(jq -r ".[].wan.xdsl.down.attenuation" < xdsl.json) echo multigraph bbox_adsl_snr -echo up.value $(jq ".[].wan.xdsl.up.noise" < xdsl.json) -echo down.value $(jq ".[].wan.xdsl.down.noise" < xdsl.json) +echo up.value $(jq -r ".[].wan.xdsl.up.noise" < xdsl.json) +echo down.value $(jq -r ".[].wan.xdsl.down.noise" < xdsl.json) echo multigraph bbox_adsl_pkt -echo up.value $(jq ".[].wan.ip.stats.tx.packets" < ip-stats.json) -echo down.value $(jq ".[].wan.ip.stats.rx.packets" < ip-stats.json) -echo up_f.value $(jq ".[].wan.xdsl.stats.remote_fec" < xdsl-stats.json) -echo down_f.value $(jq ".[].wan.xdsl.stats.local_fec" < xdsl-stats.json) -echo up_c.value $(jq ".[].wan.xdsl.stats.remote_hec" < xdsl-stats.json) -echo down_c.value $(jq ".[].wan.xdsl.stats.local_hec" < xdsl-stats.json) -echo up_u.value $(jq ".[].wan.xdsl.stats.remote_crc" < xdsl-stats.json) -echo down_u.value $(jq ".[].wan.xdsl.stats.local_crc" < xdsl-stats.json) +echo up.value $(jq -r ".[].wan.ip.stats.tx.packets" < ip-stats.json) +echo down.value $(jq -r ".[].wan.ip.stats.rx.packets" < ip-stats.json) +echo up_f.value $(jq -r ".[].wan.xdsl.stats.remote_fec" < xdsl-stats.json) +echo down_f.value $(jq -r ".[].wan.xdsl.stats.local_fec" < xdsl-stats.json) +echo up_c.value $(jq -r ".[].wan.xdsl.stats.remote_hec" < xdsl-stats.json) +echo down_c.value $(jq -r ".[].wan.xdsl.stats.local_hec" < xdsl-stats.json) +echo up_u.value $(jq -r ".[].wan.xdsl.stats.remote_crc" < xdsl-stats.json) +echo down_u.value $(jq -r ".[].wan.xdsl.stats.local_crc" < xdsl-stats.json) echo multigraph bbox_adsl_bytes -echo up.value $(jq ".[].wan.ip.stats.tx.bytes" < ip-stats.json | tr -d '"') -echo down.value $(jq ".[].wan.ip.stats.rx.bytes" < ip-stats.json | tr -d '"') +echo up.value $(jq -r ".[].wan.ip.stats.tx.bytes" < ip-stats.json | tr -d '"') +echo down.value $(jq -r ".[].wan.ip.stats.rx.bytes" < ip-stats.json | tr -d '"') echo multigraph bbox_adsl_uptime -echo uptime.value $(jq ".[].wan.xdsl.showtime" < xdsl.json) +echo uptime.value $(jq -r ".[].wan.xdsl.showtime" < xdsl.json) exit 0 From f88edd08636fb7f1b8e9abc8f6e1702757fb0500 Mon Sep 17 00:00:00 2001 From: tsaavik Date: Fri, 19 Jul 2019 11:02:03 -0700 Subject: [PATCH 385/695] Added Redis replication backlog graph --- plugins/redis/redis_ | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/plugins/redis/redis_ b/plugins/redis/redis_ index 55474435..e2704a55 100755 --- a/plugins/redis/redis_ +++ b/plugins/redis/redis_ @@ -59,7 +59,7 @@ if ( $autoconf ) { my $suggest = ( defined $ARGV[0] and $ARGV[0] eq "suggest" ); if ( $suggest ) { if ( defined( $sock ) ) { - my @plugins = ('connected_clients', 'key_ratio', 'keys_per_sec', 'per_sec', 'used_keys', 'used_memory'); + my @plugins = ('connected_clients', 'key_ratio', 'keys_per_sec', 'per_sec', 'repl_backlog_size', 'used_keys', 'used_memory'); foreach my $plugin (@plugins) { print "$plugin\n"; } @@ -128,7 +128,7 @@ switch ($0) { print "missratio.draw STACK\n"; exit 0; } - + my $total = $hash->{'keyspace_hits'} + $hash->{'keyspace_misses'}; my $hitratio = 0; my $missratio = 0; @@ -143,7 +143,7 @@ switch ($0) { case "per_sec" { if ( $config ) { - print "graph_title ${TITLE_PREFIX}Per second\n"; + print "graph_title ${TITLE_PREFIX}Requests Per second\n"; print "graph_vlabel per \${graph_period}\n"; print "graph_category search\n"; print "graph_args -l 0\n"; @@ -158,6 +158,20 @@ switch ($0) { print "connections.value ". $hash->{'total_connections_received'} ."\n"; } + case "repl_backlog_size" { + if ( $config ) { + my $maxclients= get_config("maxclients")->{"maxclients"}; + print "graph_title ${TITLE_PREFIX}replication backlog\n"; + print "graph_vlabel replication backlog\n"; + print "graph_category search\n"; + print "graph_args -l 0\n"; + print "repl_backlog_size.label bytes behind master\n"; + exit 0; + } + + print "repl_backlog_size.value " . $hash->{'repl_backlog_size'} . "\n"; + } + case "used_memory" { if ( $config ) { @@ -177,7 +191,7 @@ switch ($0) { print "used_memory_rss.value ". $hash->{'used_memory_rss'} ."\n"; print "used_memory_peak.value ". $hash->{'used_memory_peak'} ."\n"; } - + case "used_keys" { my $dbs; foreach my $key (keys %{$hash}) { @@ -210,18 +224,18 @@ switch ($0) { close ($sock); sub get_conn { - + my $sock; - + if( $UNIX_SOCKET && -S $UNIX_SOCKET ){ - + $sock = IO::Socket::UNIX->new( Type => SOCK_STREAM(), Peer => $UNIX_SOCKET, ); - + }else{ - + $sock = IO::Socket::INET->new( PeerAddr => $HOST, PeerPort => $PORT, @@ -229,7 +243,7 @@ sub get_conn { Proto => 'tcp' ); } - + if ( defined( $PASSWORD ) ) { print $sock "AUTH ", $PASSWORD, "\r\n"; my $result = <$sock> || die "can't read socket: $!"; From 59f057f88b70a0b9d8320fc2671b1a19a5086561 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 20 Jul 2019 17:31:32 +1000 Subject: [PATCH 386/695] [ssl-certificate-expiry] Add asynchronous update via cron Also, cleanup the script to be better POSIX sh compatible, and add -u to the shebang. --- plugins/ssl/ssl-certificate-expiry | 89 +++++++++++++++++++----------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index 7c69face..338c3a58 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -1,11 +1,11 @@ -#!/bin/sh +#!/bin/sh -u # -*- sh -*- : << =cut =head1 NAME -ssl-certificate-expiry - Plugin to monitor CERTificate expiration on multiple services and ports +ssl-certificate-expiry - Plugin to monitor Certificate expiration on multiple services and ports =head1 CONFIGURATION @@ -29,13 +29,26 @@ For example: ssl-certificate-expiry_192.0.2.42_636 ssl-certificate-expiry_2001:0DB8::badc:0fee_485 -=head1 AUTHOR +=head2 Cron setup -Pactrick Domack (ssl_) -Olivier Mehani (ssl-certificate-expiry) +To avoid having to run the SSL checks during the munin-update, it is possible +to run it from cron, and save a cachefile to be read during the update, This is +particularly useful when checking a large number of certificates, or when some +of the hosts are slow. -Copyright (C) 2013 Patrick Domack -Copyright (C) 2017 Olivier Mehani +To do so, add a cron job running the plugin with cron as the argument: + + */5 * * * * /usr/sbin/munin-run/ssl-certificate-expiry cron + + should be the user that has write permission to the MUNIN_PLUGSTATE. + +=head1 AUTHORS + + * Pactrick Domack (ssl_) + * Olivier Mehani (ssl-certificate-expiry) + + * Copyright (C) 2013 Patrick Domack + * Copyright (C) 2017, 2019 Olivier Mehani =head1 LICENSE @@ -44,11 +57,12 @@ Copyright (C) 2017 Olivier Mehani # shellcheck disable=SC1090 . "${MUNIN_LIBDIR}/plugins/plugin.sh" -if [ "${MUNIN_DEBUG}" = 1 ]; then +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then set -x fi HOSTPORT=${0##*ssl-certificate-expiry_} +CACHEFILE="${MUNIN_PLUGSTATE}/$(basename "${0}").cache" if [ "${HOSTPORT}" != "${0}" ] \ && [ ! -z "${HOSTPORT}" ]; then @@ -59,11 +73,6 @@ fi # Read data including a certificate from stdin and output the (fractional) number of days left # until the expiry of this certificate. The output is empty if parsing failed. parse_valid_days_from_certificate() { - local input_data - local valid_until_string - local valid_until_epoch - local now_epoch - local input_data input_data=$(cat) if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ @@ -81,8 +90,8 @@ parse_valid_days_from_certificate() { print_expire_days() { - local host="$1" - local port="$2" + host="$1" + port="$2" # Wrap IPv6 addresses in square brackets echo "$host" | grep -q ':' && host="[$host]" @@ -92,36 +101,52 @@ print_expire_days() { | parse_valid_days_from_certificate } +main() { + for service in $services; do + if echo "$service" | grep -q "_"; then + host=$(echo "$service" | cut -f 1 -d "_") + port=$(echo "$service" | cut -f 2 -d "_") + else + host=$service + port=443 + fi + fieldname="$(clean_fieldname "$service")" + valid_days=$(print_expire_days "$host" "$port") + [ -z "$valid_days" ] && valid_days="U" + printf "%s.value %s\\n" "$fieldname" "$valid_days" + done +} -case $1 in +case ${1:-} in config) - echo "graph_title SSL Certificates Expiration" echo 'graph_args --base 1000' echo 'graph_vlabel days left' echo 'graph_category security' - echo "graph_info This graph shows the days left for the certificate" + echo "graph_info This graph shows the numbers of days before certificate expiry" for service in $services; do fieldname=$(clean_fieldname "$service") echo "${fieldname}.label $(echo "${service}" | sed 's/_/:/')" - print_thresholds "${fieldname}" + print_thresholds "${fieldname}" warning critical done + exit 0 + ;; + cron) + TMP=$(mktemp "${CACHEFILE}.XXXXXXXX") + trap 'rm -f "${TMP}"' EXIT + main > "${TMP}" + chmod 0644 "${TMP}" + mv "${TMP}" "${CACHEFILE}" + exit 0 ;; esac +if [ -e "${CACHEFILE}" ]; then + cat "${CACHEFILE}" + rm "${CACHEFILE}" + exit 0 +fi -for service in $services; do - if echo "$service" | grep -q "_"; then - host=$(echo "$service" | cut -f 1 -d "_") - port=$(echo "$service" | cut -f 2 -d "_") - else - host=$service - port=443 - fi - fieldname="$(clean_fieldname "$service")" - valid_days=$(print_expire_days "$host" "$port") - [ -z "$valid_days" ] && valid_days="U" - printf "%s.value %s\n" "$fieldname" "$valid_days" -done +main From b6a41513d700ef0b7cc93ddd6d1a90b5f44511a7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 22 Jul 2019 13:43:10 +0200 Subject: [PATCH 387/695] Plugin bitcoind_: fix base64 encoding for credentials Python2.7 (as well as Python3.x) refused to execute the previously used encoding (`foo.encode('base64')`). It is unknown, whether it worked before with an older Python version. --- plugins/currency/bitcoin/bitcoind_ | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 293b72cc..efc50efe 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -50,6 +50,7 @@ Copyright (C) 2012 Mike Koss =cut""" +import base64 import json import os import re @@ -236,9 +237,9 @@ class Proxy(object): } request = urllib.request.Request(self.service.url, json.dumps(data)) if self.service.username: - # Strip the newline from the b64 encoding! - b64 = ('%s:%s' % (self.service.username, self.service.password)).encode('base64')[:-1] - request.add_header('Authorization', 'Basic %s' % b64) + auth_string = '%s:%s' % (self.service.username, self.service.password) + auth_b64 = base64.urlsafe_b64encode(auth_string.encode()).decode() + request.add_header('Authorization', 'Basic %s' % auth_b64) try: body = urllib.request.urlopen(request).read() From 793b75b9ee84167150dcacc1e66c5400978d9896 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 22 Jul 2019 21:37:28 +1000 Subject: [PATCH 388/695] squash! [ssl-certificate-expiry] Add asynchronous update via cron revert local --- plugins/ssl/ssl-certificate-expiry | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index 338c3a58..f19cb5df 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -1,5 +1,6 @@ #!/bin/sh -u # -*- sh -*- +# shellcheck disable=SC2039 : << =cut @@ -73,6 +74,11 @@ fi # Read data including a certificate from stdin and output the (fractional) number of days left # until the expiry of this certificate. The output is empty if parsing failed. parse_valid_days_from_certificate() { + local input_data + local valid_until_string + local valid_until_epoch + local now_epoch + local input_data input_data=$(cat) if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ @@ -90,8 +96,8 @@ parse_valid_days_from_certificate() { print_expire_days() { - host="$1" - port="$2" + local host="$1" + local port="$2" # Wrap IPv6 addresses in square brackets echo "$host" | grep -q ':' && host="[$host]" From 7e995a0210a71c226363bc94ed16185804a4ec41 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 22 Jul 2019 21:49:28 +1000 Subject: [PATCH 389/695] squash! squash! [ssl-certificate-expiry] Add asynchronous update via cron * use find to detect only recent cache files * reduce cron periodicity to an hour * use variable instead of TMP file --- plugins/ssl/ssl-certificate-expiry | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index f19cb5df..f338a8f5 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -39,9 +39,13 @@ of the hosts are slow. To do so, add a cron job running the plugin with cron as the argument: - */5 * * * * /usr/sbin/munin-run/ssl-certificate-expiry cron + * * * /usr/sbin/munin-run/ssl-certificate-expiry cron should be the user that has write permission to the MUNIN_PLUGSTATE. + should be a number between 0 and 59 when the check should run every hour. + +If, for any reason, the cron script stops running, the script will revert to +uncached updates after the cache file is older than an hour. =head1 AUTHORS @@ -120,6 +124,7 @@ main() { valid_days=$(print_expire_days "$host" "$port") [ -z "$valid_days" ] && valid_days="U" printf "%s.value %s\\n" "$fieldname" "$valid_days" + echo "${fieldname}.extinfo Last checked: $(date)" done } @@ -139,19 +144,16 @@ case ${1:-} in exit 0 ;; cron) - TMP=$(mktemp "${CACHEFILE}.XXXXXXXX") - trap 'rm -f "${TMP}"' EXIT - main > "${TMP}" - chmod 0644 "${TMP}" - mv "${TMP}" "${CACHEFILE}" + UPDATE="$(main)" + echo "${UPDATE}" > "${CACHEFILE}" + chmod 0644 "${CACHEFILE}" exit 0 ;; esac -if [ -e "${CACHEFILE}" ]; then +if [ -n "$(find "${CACHEFILE}" -mmin -60 2>/dev/null)" ]; then cat "${CACHEFILE}" - rm "${CACHEFILE}" exit 0 fi From 509471b070ab71eaa36856b40d92724e0d66bc58 Mon Sep 17 00:00:00 2001 From: tsaavik Date: Mon, 22 Jul 2019 15:26:46 -0700 Subject: [PATCH 390/695] Added Redis lag graph for slave0 node replication --- plugins/redis/redis_ | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/redis/redis_ b/plugins/redis/redis_ index e2704a55..fd0fc73c 100755 --- a/plugins/redis/redis_ +++ b/plugins/redis/redis_ @@ -59,7 +59,7 @@ if ( $autoconf ) { my $suggest = ( defined $ARGV[0] and $ARGV[0] eq "suggest" ); if ( $suggest ) { if ( defined( $sock ) ) { - my @plugins = ('connected_clients', 'key_ratio', 'keys_per_sec', 'per_sec', 'repl_backlog_size', 'used_keys', 'used_memory'); + my @plugins = ('connected_clients', 'key_ratio', 'keys_per_sec', 'per_sec', 'repl_backlog_size', 'repl_lag', 'used_keys', 'used_memory'); foreach my $plugin (@plugins) { print "$plugin\n"; } @@ -160,7 +160,6 @@ switch ($0) { case "repl_backlog_size" { if ( $config ) { - my $maxclients= get_config("maxclients")->{"maxclients"}; print "graph_title ${TITLE_PREFIX}replication backlog\n"; print "graph_vlabel replication backlog\n"; print "graph_category search\n"; @@ -172,6 +171,21 @@ switch ($0) { print "repl_backlog_size.value " . $hash->{'repl_backlog_size'} . "\n"; } + case "repl_lag" { + if ( $config ) { + print "graph_title ${TITLE_PREFIX}replication lag\n"; + print "graph_vlabel replication lag\n"; + print "graph_category search\n"; + print "graph_args -l 0\n"; + print "repl_backlog_size.label amount behind master\n"; + exit 0; + } + + if ($hash->{slave0} =~ /lag=(\d+)/) { + print "repl_backlog_size.value " . $1 . "\n"; + } + } + case "used_memory" { if ( $config ) { From f5de3d19ec9911f046400555d451340024f6e4c7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 23 Jul 2019 01:30:34 +0200 Subject: [PATCH 391/695] Plugin bitcoind_: fix POST request body format Python3 mandates "bytes" as the request body data type. --- plugins/currency/bitcoin/bitcoind_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index efc50efe..8a5c9d76 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -235,7 +235,7 @@ class Proxy(object): 'params': args, 'id': self.id, } - request = urllib.request.Request(self.service.url, json.dumps(data)) + request = urllib.request.Request(self.service.url, json.dumps(data).encode()) if self.service.username: auth_string = '%s:%s' % (self.service.username, self.service.password) auth_b64 = base64.urlsafe_b64encode(auth_string.encode()).decode() From 101c1f7c9bc98680771a69621a7debff4adef232 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 19 Jul 2019 08:16:07 +1000 Subject: [PATCH 392/695] [php_fpm_process] Add queue and request information Breaking changes: * php_fpm_slowrequests is superseded by php_fpm_requests Signed-off-by: Olivier Mehani --- plugins/php/php_fpm_process | 224 +++++++++++++++++++++++++----------- 1 file changed, 158 insertions(+), 66 deletions(-) diff --git a/plugins/php/php_fpm_process b/plugins/php/php_fpm_process index 873b814e..d4ff5191 100755 --- a/plugins/php/php_fpm_process +++ b/plugins/php/php_fpm_process @@ -25,7 +25,7 @@ You have to put this in your plugin.conf.d folder # If your php process is listening on Unix Socket [php_fpm_process] - env.sock /var/run/php5-fpm.sock + env.sock /var/run/php-fpm.sock env.path /status =head1 MAGIC MARKERS @@ -35,15 +35,16 @@ You have to put this in your plugin.conf.d folder =head1 VERSION - v1.0 + v2.0.0 =head1 AUTHOR Minitux +Olivier Mehani =head1 LICENSE -GNU General Public License, version 3 +SPDX-License-Identifier: GPL-3.0 =cut @@ -56,6 +57,10 @@ my $body = ""; my $IDLE = 0; my $ACTIVE = 0; my $TOTAL = 0; +my $LISTEN = 0; +my $MAX = 0; +my $LEN = 0; +my $CONNECTIONS = 0; my $SLOW_REQUESTS = 0; my $PLUGIN_NAME = basename($0); @@ -67,67 +72,104 @@ my $UNIX_SOCK = $ENV{'sock'}; my $sock; if ($UNIX_SOCK) { - use IO::Socket::UNIX; - $sock = IO::Socket::UNIX->new( - Peer => $UNIX_SOCK, - ); - if (!$sock) { - print "Server maybe down, unabled to connect to $UNIX_SOCK"; - exit 2; - } + use IO::Socket::UNIX; + $sock = IO::Socket::UNIX->new( + Peer => $UNIX_SOCK, + ); + if (!$sock) { + print "Server maybe down, unabled to connect to $UNIX_SOCK"; + exit 2; + } } else { - use IO::Socket::INET; - $sock = IO::Socket::INET->new( - PeerAddr => $SERVERADDR, - PeerPort => $PORT, - ); - if (!$sock) { - print "Server maybe down, unabled to connect to $SERVERADDR:$PORT"; - exit 2; - } + use IO::Socket::INET; + $sock = IO::Socket::INET->new( + PeerAddr => $SERVERADDR, + PeerPort => $PORT, + ); + if (!$sock) { + print "Server maybe down, unabled to connect to $SERVERADDR:$PORT"; + exit 2; + } } my $client = FCGI::Client::Connection->new( sock => $sock ); my ( $stdout, $stderr, $appstatus ) = $client->request( - +{ - REQUEST_METHOD => 'GET', - SCRIPT_FILENAME => '', - QUERY_STRING => '', - SCRIPT_NAME => $PATH, - }, - '' - ); + +{ + REQUEST_METHOD => 'GET', + SCRIPT_FILENAME => '', + QUERY_STRING => '', + SCRIPT_NAME => $PATH, + }, + '' +); + +# +# Example output: +# +# pool: www +# process manager: dynamic +# start time: 23/Jun/2019:12:13:50 +0200 +# start since: 577793 +# accepted conn: 37211 +# listen queue: 0 +# max listen queue: 0 +# listen queue len: 0 +# idle processes: 6 +# active processes: 1 +# total processes: 7 +# max active processes: 13 +# max children reached: 0 +# slow requests: 0 + +# +# ...with ?full added to the query string, each child is also described: +# +# ************************ +# pid: 56027 +# state: Running +# start time: 18/Jul/2019:01:02:15 +0200 +# start since: 45279 +# requests: 776 +# request duration: 1043 +# request method: GET +# request URI: /fpm-status?full +# content length: 0 +# user: - +# script: - +# last request cpu: 0.00 +# last request memory: 0 +# $stdout =~ s/\r//g; while($stdout =~ /([^\n]*)\n?/g) { - if(!$1) { - $ish = 0; - next; - } - if($ish == 1) { - $header .= $1."\n"; - } else { - $body .= $1."\n"; - } + if(!$1) { + $ish = 0; + next; + } + if($ish == 1) { + $header .= $1."\n"; + } else { + $body .= $1."\n"; + } } if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { - if($body =~ m/pool:\s+(.*?)\n/) { - $pool = $1; - } + if($body =~ m/pool:\s+(.*?)\n/) { + $pool = $1; + } - print <<"EOF"; + print <<"EOF"; multigraph ${PLUGIN_NAME}_process -graph_title php5-fpm processes for $pool +graph_title php-fpm processes for $pool graph_args --base 1000 -l 0 graph_vlabel Processes graph_scale yes -graph_category processes -graph_info This graph shows the php5-fpm process manager status from pool: $pool +graph_category appserver +graph_info This graph shows the php-fpm process manager status from pool $pool active.label Active processes active.type GAUGE active.draw AREA @@ -140,23 +182,52 @@ total.label Total processes total.type GAUGE total.draw LINE2 total.info The number of idle + active processes +max.label Max processes +max.type GAUGE +max.draw LINE +max.info The maximum number of active processes since FPM has started -multigraph ${PLUGIN_NAME}_slowrequests -graph_title php5-fpm slow requests $pool +multigraph ${PLUGIN_NAME}_queues +graph_title php-fpm queues for $pool graph_args --base 1000 -l 0 -graph_vlabel Slow requests +graph_vlabel Queue graph_scale yes -graph_category processes -graph_info This graph shows the php5-fpm slow request from pool: $pool -slow_requests.label Slow requests -slow_requests.type DERIVE -slow_requests.draw LINE2 -slow_requests.min 0 -slow_requests.info evolution of slow requests +graph_category appserver +graph_info This graph shows the php-fpm queue from pool $pool +listen.label Listen queue +listen.type GAUGE +listen.draw LINE +listen.info The number of pending requests in the queue +max.label Max listen queue +max.type GAUGE +max.draw LINE +max.info The maximum number of pending requests in the queue +len.label Queue len +len.type GAUGE +len.draw LINE +len.info The number of pending connections in the queue + +multigraph ${PLUGIN_NAME}_requests +graph_title php-fpm requests for $pool +graph_args --base 1000 -l 0 +graph_vlabel Requests +graph_scale yes +graph_category appserver +graph_info This graph shows the php-fpm request rate from pool $pool +connections.label Connections +connections.type DERIVE +connections.draw LINE +connections.min 0 +connections.info evolution of connections +slow.label Slow requests +slow.type DERIVE +slow.draw LINE +slow.min 0 +slow.info evolution of slow requests (longer than request_slowlog_timeout) EOF - exit 0 + exit 0 } # print $body; @@ -164,21 +235,42 @@ EOF print "multigraph ${PLUGIN_NAME}_process\n"; if($body =~ m/idle processes: (.*?)\n/) { - $IDLE = $1; - print "idle.value ".$IDLE."\n"; + $IDLE = $1; + print "idle.value ".$IDLE."\n"; } if($body =~ m/active processes: (.*?)\n/) { - $ACTIVE = $1; - print "active.value ".$ACTIVE."\n"; + $ACTIVE = $1; + print "active.value ".$ACTIVE."\n"; } if($body =~ m/total processes: (.*?)\n/) { - $TOTAL = $1; - print "total.value ".$TOTAL."\n"; + $TOTAL = $1; + print "total.value ".$TOTAL."\n"; +} +if($body =~ m/max active processes: (.*?)\n/) { + $MAX = $1; + print "max.value ".$MAX."\n"; } -if($body =~ m/slow requests: (.*?)\n/) { - $SLOW_REQUESTS = $1; - print "\n"; - print "multigraph ${PLUGIN_NAME}_slowrequests\n"; - print "slow_requests.value ".$SLOW_REQUESTS."\n"; +if($body =~ m/listen queue: (.*?)\n/) { + $LISTEN= $1; + print "multigraph ${PLUGIN_NAME}_queues\n"; + print "listen.value ".$LISTEN."\n"; + if($body =~ m/max listen queue: (.*?)\n/) { + $MAX = $1; + print "max.value ".$MAX."\n"; + } + if($body =~ m/listen queue len: (.*?)\n/) { + $LEN = $1; + print "len.value ".$LEN."\n"; + } +} + +print "multigraph ${PLUGIN_NAME}_requests\n"; +if($body =~ m/accepted conn: (.*?)\n/) { + $CONNECTIONS = $1; + print "connections.value ".$CONNECTIONS."\n"; +} +if($body =~ m/slow requests: (.*?)\n/) { + $SLOW_REQUESTS = $1; + print "slow.value ".$SLOW_REQUESTS."\n"; } From e5f7966dfb54109a5ef0a70f280224b26aeaca07 Mon Sep 17 00:00:00 2001 From: rantal Date: Tue, 23 Jul 2019 18:03:33 +0300 Subject: [PATCH 393/695] Update megaraid-controller-information to support "megacli" name of tools executable --- plugins/disk/megaraid-controller-information | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/megaraid-controller-information b/plugins/disk/megaraid-controller-information index fd83fbac..cebf96da 100755 --- a/plugins/disk/megaraid-controller-information +++ b/plugins/disk/megaraid-controller-information @@ -71,7 +71,7 @@ chomp $Parameters; my ($Adapter,$Type)=split(/-/,$Parameters); # Locate MegaCli64 application and remove whitespace -my $Command=`which MegaCli64`; +my $Command=`which MegaCli64`; if ( ! $Command ) { $Command=`which megacli`; } chomp $Command; # Use this to define future parameters to monitor From ea051b7f3ce7a210f91a174c5ab335fd3f7b5173 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 23 Jul 2019 21:07:05 +0200 Subject: [PATCH 394/695] Plugin bitcoind_: use defaults for missing environment variables Previously undefined environment variables were treated as "None" instead of using the supplied defaults (e.g. for "rpchost" or "rpcport"). --- plugins/currency/bitcoin/bitcoind_ | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 8a5c9d76..cf938d9e 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -174,7 +174,9 @@ def parse_conf(filename): def get_env_options(*vars): options = Options() for var in vars: - options[var] = os.getenv(var) + value = os.getenv(var) + if value is not None: + options[var] = os.getenv(var) return options From a0aa955a2df0e21594427c48f6181c1f97c4ff71 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 23 Jul 2019 21:08:31 +0200 Subject: [PATCH 395/695] Plugin bitcoind_: improve style --- plugins/currency/bitcoin/bitcoind_ | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index cf938d9e..ea42ea20 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -194,12 +194,13 @@ class Options(dict): if self.get(name) is None: missing.append(name) if len(missing) > 0: - print("Missing required setting%s: %s." - % ('s' if len(missing) > 1 else '', ', '.join(missing)), file=sys.stderr) + print("Missing required setting%s: %s." % ('s' if len(missing) > 1 else '', + ', '.join(missing)), + file=sys.stderr) sys.exit(1) -class ServiceProxy(object): +class ServiceProxy: """ Proxy for a JSON-RPC web service. Calls to a function attribute generates a JSON-RPC call to the host service. If a callback @@ -219,7 +220,7 @@ class ServiceProxy(object): return Proxy(self, method, id=self.id) -class Proxy(object): +class Proxy: def __init__(self, service, method, id=None): self.service = service self.method = method From fd45fe6c8ee501c07f7eb028b75164bdda4c6a60 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 24 Jul 2019 13:32:42 +0200 Subject: [PATCH 396/695] Plugin bitcoind_: unify exit behaviour --- plugins/currency/bitcoin/bitcoind_ | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index ea42ea20..bd8498c0 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -84,7 +84,7 @@ def main(): if command == 'suggest': for var_name in request_labels.keys(): print(var_name) - return + return True if command == 'config': print('graph_category htc') @@ -92,7 +92,7 @@ def main(): print('graph_vlabel %s' % labels[1]) for label in line_labels: print('%s.label %s' % (label, label)) - return + return True # Munin should send connection options via environment vars bitcoin_options = get_env_options('rpcconnect', 'rpcport', 'rpcuser', 'rpcpassword') @@ -104,10 +104,10 @@ def main(): if not conf_file: print("Missing environment settings (rpcuser/rcpassword or bitcoin_configfile)", file=sys.stderr) - sys.exit(1) + return False elif not os.path.exists(conf_file): print("Configuration file does not exist: {}".format(conf_file), file=sys.stderr) - sys.exit(1) + return False else: bitcoin_options = parse_conf(conf_file) @@ -123,11 +123,11 @@ def main(): if error: if command == 'autoconf': print('no') - return + return True else: # TODO: Better way to report errors to Munin-node. print("Could not connect to Bitcoin server.", file=sys.stderr) - sys.exit(1) + return False if request_var in ('transactions', 'block_age'): (info, error) = bitcoin.getblockhash(info['blocks']) @@ -142,7 +142,7 @@ def main(): if command == 'autoconf': print('yes') - return + return True for label in line_labels: print("%s.value %s" % (label, info[label])) @@ -197,7 +197,7 @@ class Options(dict): print("Missing required setting%s: %s." % ('s' if len(missing) > 1 else '', ', '.join(missing)), file=sys.stderr) - sys.exit(1) + return False class ServiceProxy: @@ -268,4 +268,4 @@ def get_json_url(url): if __name__ == "__main__": - main() + sys.exit(0 if main() else 1) From 88e027bebd255c8c5807c05144eca2bce4345633 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 24 Jul 2019 13:40:00 +0200 Subject: [PATCH 397/695] Plugin bitcoind_: demystify "autoconf" handling --- plugins/currency/bitcoin/bitcoind_ | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index bd8498c0..9963a8ee 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -118,16 +118,20 @@ def main(): username=bitcoin_options.rpcuser, password=bitcoin_options.rpcpassword) - (info, error) = bitcoin.getinfo() + (info, connect_error) = bitcoin.getinfo() + if connect_error: + error = "Could not connect to Bitcoin server: {}".format(connect_error) + + if command == 'autoconf': + if error: + print('no ({})'.format(error)) + else: + print('yes') + return True if error: - if command == 'autoconf': - print('no') - return True - else: - # TODO: Better way to report errors to Munin-node. - print("Could not connect to Bitcoin server.", file=sys.stderr) - return False + print(error, file=sys.stderr) + return False if request_var in ('transactions', 'block_age'): (info, error) = bitcoin.getblockhash(info['blocks']) @@ -140,10 +144,6 @@ def main(): if memory_pool: info['waiting'] = len(memory_pool) - if command == 'autoconf': - print('yes') - return True - for label in line_labels: print("%s.value %s" % (label, info[label])) From db7b2f28ab4a87edfde7e1d2dafed0f20cc31e72 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 24 Jul 2019 13:48:01 +0200 Subject: [PATCH 398/695] Plugin bitcoind_: unify error output --- plugins/currency/bitcoin/bitcoind_ | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 9963a8ee..43373360 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -99,27 +99,28 @@ def main(): bitcoin_options.rpcconnect = bitcoin_options.get('rpcconnect', '127.0.0.1') bitcoin_options.rpcport = bitcoin_options.get('rpcport', '8332') + error = None if bitcoin_options.get('rpcuser') is None: conf_file = os.getenv("bitcoin_configfile") if not conf_file: - print("Missing environment settings (rpcuser/rcpassword or bitcoin_configfile)", - file=sys.stderr) - return False + error = "Missing environment settings: rpcuser/rcpassword or bitcoin_configfile" elif not os.path.exists(conf_file): - print("Configuration file does not exist: {}".format(conf_file), file=sys.stderr) - return False + error = "Configuration file does not exist: {}".format(conf_file) else: bitcoin_options = parse_conf(conf_file) - bitcoin_options.require('rpcuser', 'rpcpassword') + if not error: + try: + bitcoin_options.require('rpcuser', 'rpcpassword') + except KeyError as exc: + error = str(exc).strip("'") - bitcoin = ServiceProxy('http://%s:%s' % (bitcoin_options.rpcconnect, - bitcoin_options.rpcport), - username=bitcoin_options.rpcuser, - password=bitcoin_options.rpcpassword) - - (info, connect_error) = bitcoin.getinfo() - if connect_error: + if not error: + bitcoin = ServiceProxy('http://%s:%s' % (bitcoin_options.rpcconnect, + bitcoin_options.rpcport), + username=bitcoin_options.rpcuser, + password=bitcoin_options.rpcpassword) + (info, connect_error) = bitcoin.getinfo() error = "Could not connect to Bitcoin server: {}".format(connect_error) if command == 'autoconf': @@ -194,10 +195,8 @@ class Options(dict): if self.get(name) is None: missing.append(name) if len(missing) > 0: - print("Missing required setting%s: %s." % ('s' if len(missing) > 1 else '', - ', '.join(missing)), - file=sys.stderr) - return False + raise KeyError("Missing required setting{}: {}." + .format('s' if len(missing) > 1 else '', ', '.join(missing))) class ServiceProxy: From 4864783fca22127827b7dccb5039ba44f49fb891 Mon Sep 17 00:00:00 2001 From: rantal Date: Wed, 24 Jul 2019 18:27:18 +0300 Subject: [PATCH 399/695] zpool_capacity: Added fragmentation % graph Added fragmentation % graph --- plugins/zfs/zpool_capacity | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zpool_capacity b/plugins/zfs/zpool_capacity index 921b7c20..360d246e 100755 --- a/plugins/zfs/zpool_capacity +++ b/plugins/zfs/zpool_capacity @@ -7,9 +7,10 @@ zpool_capacity - Munin plugin to monitor ZFS capacity These functions are implemented: - capacity : to monitor zpool capacity % - allocated : to monitor zpool allocated bytes - dedup : to monitor zpool dedup and compress ratio + capacity : to monitor zpool capacity % + fragmentation : to monitor zpool fragmantation % + allocated : to monitor zpool allocated bytes + dedup : to monitor zpool dedup and compress ratio Tested with Solaris 10 and 11, OpenIndiana Hipster, FreeBSD 11, CentOS 7 @@ -34,6 +35,8 @@ K.Cima https://github.com/shakemid + Fragmentation graph by https://github.com/rantal + =head1 LICENSE GPLv2 @@ -54,7 +57,7 @@ set -o nounset # Global variables plugin_name=zpool_capacity -functions='capacity allocated dedup' +functions='capacity fragmentation allocated dedup' zpool_cmd=/sbin/zpool zfs_cmd=/sbin/zfs @@ -92,6 +95,20 @@ preconfig() { ${p} GAUGE LINE2 ${p}" done ;; + fragmentation) + global_attr=" + graph_title ZFS storage pool - Fragmentation + graph_category fs + graph_args --base 1000 --lower-limit 0 --upper-limit 100 + graph_vlabel % fragmentation + graph_info ZFS storage pool - Fragmentation + " + for p in $pool_list + do + data_attr="${data_attr} + ${p} GAUGE LINE2 ${p}" + done + ;; allocated) global_attr=" graph_title ZFS storage pool - Allocated bytes @@ -168,6 +185,9 @@ get_stats() { capacity) "$zpool_cmd" list -H -o name,capacity | sed 's/%$//' ;; + fragmentation) + "$zpool_cmd" list -H -o name,fragmentation | sed 's/%$//' + ;; allocated) ( "$zpool_cmd" list -H -o name,allocated \ | awk '{ print $1"_allocated", $2 }' From 5bebfeea9e60ab1b92f485330bd460c41f336a4c Mon Sep 17 00:00:00 2001 From: rantal Date: Thu, 25 Jul 2019 14:46:08 +0300 Subject: [PATCH 400/695] Update megaraid-controller-information --- plugins/disk/megaraid-controller-information | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/megaraid-controller-information b/plugins/disk/megaraid-controller-information index cebf96da..1b54aebd 100755 --- a/plugins/disk/megaraid-controller-information +++ b/plugins/disk/megaraid-controller-information @@ -71,7 +71,7 @@ chomp $Parameters; my ($Adapter,$Type)=split(/-/,$Parameters); # Locate MegaCli64 application and remove whitespace -my $Command=`which MegaCli64`; if ( ! $Command ) { $Command=`which megacli`; } +my $Command=`which MegaCli64 megacli | head -n 1`; chomp $Command; # Use this to define future parameters to monitor From 81f5e695993de90faaf8890c8b40c6077508fe3f Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 16:00:08 +0200 Subject: [PATCH 401/695] Adding +x flag --- plugins/router/bbox | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 plugins/router/bbox diff --git a/plugins/router/bbox b/plugins/router/bbox old mode 100644 new mode 100755 From 77fb960536d0ed1a475e04a12e5666c2d112c765 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 16:00:28 +0200 Subject: [PATCH 402/695] travis: adding a dependency --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f9b78219..325b0e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ install: - cpanm --notest FCGI::Client - cpanm --notest File::ReadBackwards - cpanm --notest Graphics::ColorObject + - cpanm --notest Graphics::ColorNames - cpanm --notest IPC::Run3 - cpanm --notest IPC::ShareLite - cpanm --notest JSON From 36aa913ce9ade2c4f52e9396fde745c12f9a0023 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 16:30:43 +0200 Subject: [PATCH 403/695] travis: move to bionic --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 325b0e9c..ab8d5a39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ --- -dist: trusty +dist: bionic install: - sudo apt-get update - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint shellcheck @@ -32,7 +32,7 @@ install: - cpanm --notest FCGI::Client - cpanm --notest File::ReadBackwards - cpanm --notest Graphics::ColorObject - - cpanm --notest Graphics::ColorNames + - cpanm --notest Graphics::ColorNames::HTML - cpanm --notest IPC::Run3 - cpanm --notest IPC::ShareLite - cpanm --notest JSON From 6c2a996879ed6002b8ebb20182e25cc71fd58060 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 16:37:42 +0200 Subject: [PATCH 404/695] travis: upgrade to an always current version of PHP --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ab8d5a39..28839adc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: bionic install: - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint shellcheck + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php-cli gawk ksh zsh pylint shellcheck - 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 From 58f0ab64f317f4cb536ba74c981f93c5dedc792e Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 16:54:43 +0200 Subject: [PATCH 405/695] travis: remove the check for the nvidia plugin --- .travis.yml | 3 ++- plugins/gpu/nvidia_smi_.nocheck | 0 t/test.t | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 plugins/gpu/nvidia_smi_.nocheck diff --git a/.travis.yml b/.travis.yml index 28839adc..8120a60d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,8 +58,9 @@ install: - cpanm --notest XML::Simple - cpanm --notest XML::Smart - cpanm --notest XML::Twig - - cpanm --notest nvidia::ml - cpanm --notest experimental + # Modules used bu plugins, but not compiling from CPAN + # - cpanm --notest nvidia::ml # Modules used by plugins, but missing on cpan # - File::Tail::Multi # - Sun::Solaris::Kstat diff --git a/plugins/gpu/nvidia_smi_.nocheck b/plugins/gpu/nvidia_smi_.nocheck new file mode 100644 index 00000000..e69de29b diff --git a/t/test.t b/t/test.t index db30c126..f0782718 100644 --- a/t/test.t +++ b/t/test.t @@ -48,7 +48,11 @@ sub process_file { my ( $file, $filename, $interpreter, $arguments ) = @_; use v5.10.1; - if ( ! -x $file ) { + if ( -r "$file.nocheck") { + skip( sprintf("\nFile '%s' has a .nocheck flag. Ignoring\n", $file), 1); + pass("Not pretending everything is ok"); + } + elsif ( ! -x $file ) { # missing executable flag diag( sprintf("\nFile '%s' lacks executable permission bits. Maybe try 'chmod +x $file'?\n", From 9cd0bba835dbf8bb222923ab1863b02c6b1881a7 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 20:50:08 +0200 Subject: [PATCH 406/695] fix plugins for new rules --- plugins/assp/assp-envelope-recipient-statistics | 2 +- plugins/assp/assp-general-runtime-information | 2 +- plugins/assp/assp-message-statistics | 2 +- plugins/assp/assp-smtp-connection-statistics | 2 +- plugins/assp/assp-smtp-handler-statistics | 2 +- plugins/quake/qstat | 2 +- plugins/quake/qstatet_ | 2 +- plugins/quake/qstatqw_ | 2 +- plugins/sensors/allnet__ | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/assp/assp-envelope-recipient-statistics b/plugins/assp/assp-envelope-recipient-statistics index e21af08b..d5062bb2 100755 --- a/plugins/assp/assp-envelope-recipient-statistics +++ b/plugins/assp/assp-envelope-recipient-statistics @@ -66,7 +66,7 @@ my @muninlabel = ( ); # ============= SANITY CHECKS ================ -unless( defined(@ARGV) ){ +unless( @ARGV ){ $ARGV[0] = ""; } diff --git a/plugins/assp/assp-general-runtime-information b/plugins/assp/assp-general-runtime-information index 32aa0349..a8535373 100755 --- a/plugins/assp/assp-general-runtime-information +++ b/plugins/assp/assp-general-runtime-information @@ -46,7 +46,7 @@ my @muninlabel = ( # General Runtime Information ); # ============= SANITY CHECKS ================ -unless( defined(@ARGV) ){ +unless( @ARGV ){ $ARGV[0] = ""; } diff --git a/plugins/assp/assp-message-statistics b/plugins/assp/assp-message-statistics index 78032338..92b1c667 100755 --- a/plugins/assp/assp-message-statistics +++ b/plugins/assp/assp-message-statistics @@ -78,7 +78,7 @@ my @muninlabel = ( ); # ============= SANITY CHECKS ================ -unless( defined(@ARGV) ){ +unless( @ARGV ){ $ARGV[0] = ""; } diff --git a/plugins/assp/assp-smtp-connection-statistics b/plugins/assp/assp-smtp-connection-statistics index ddddd1e0..a41f9f21 100755 --- a/plugins/assp/assp-smtp-connection-statistics +++ b/plugins/assp/assp-smtp-connection-statistics @@ -56,7 +56,7 @@ my @muninlabel = ( ); # ============= SANITY CHECKS ================ -unless( defined(@ARGV) ){ +unless( @ARGV ){ $ARGV[0] = ""; } diff --git a/plugins/assp/assp-smtp-handler-statistics b/plugins/assp/assp-smtp-handler-statistics index c80e5083..3041b481 100755 --- a/plugins/assp/assp-smtp-handler-statistics +++ b/plugins/assp/assp-smtp-handler-statistics @@ -46,7 +46,7 @@ my @muninlabel = ( # General Runtime Information ); # ============= SANITY CHECKS ================ -unless( defined(@ARGV) ){ +unless( @ARGV ){ $ARGV[0] = ""; } diff --git a/plugins/quake/qstat b/plugins/quake/qstat index d0657b0d..32c645c6 100755 --- a/plugins/quake/qstat +++ b/plugins/quake/qstat @@ -60,7 +60,7 @@ quake_stat() { port=$3 fi - if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + if [ -n "$gametype" ] && [ -n "$gametype" ] && [ -n "$gametype" ]; then dummy=$("$qstat_exe" -raw ";" -nh "-$gametype" "${ip}:${port}") playervalue=$(echo "$dummy" | cut -d\; -f6) diff --git a/plugins/quake/qstatet_ b/plugins/quake/qstatet_ index d792f235..8e68ad34 100755 --- a/plugins/quake/qstatet_ +++ b/plugins/quake/qstatet_ @@ -60,7 +60,7 @@ quake_stat() { port=$3 fi - if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + if [ -n "$gametype" ] && [ -n "$gametype" ] && [ -n "$gametype" ]; then stat_output=$("$qstat_exe" -P -pa -sort P "-$gametype" "${ip}:${port}" | grep frags) playervalue=$(echo "$stat_output" | grep -cwv 0ms) maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) diff --git a/plugins/quake/qstatqw_ b/plugins/quake/qstatqw_ index df29156d..908b95f4 100755 --- a/plugins/quake/qstatqw_ +++ b/plugins/quake/qstatqw_ @@ -60,7 +60,7 @@ quake_stat() { port=$3 fi - if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + if [ -n "$gametype" ] && [ -n "$gametype" ] && [ -n "$gametype" ]; then stat_output=$("$qstat_exe" -P -pa -sort P "-${gametype}" "${ip}:${port}" | grep team) playervalue=$(echo "$stat_output" | grep -cwv 0ms) maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) diff --git a/plugins/sensors/allnet__ b/plugins/sensors/allnet__ index dcd70a6d..feec5c13 100755 --- a/plugins/sensors/allnet__ +++ b/plugins/sensors/allnet__ @@ -166,7 +166,7 @@ if (defined($ENV{'beextratolerant'})) { } } #print($sensordata->[0]); -foreach $k (keys($sensordata)) { +foreach $k (keys(%$sensordata)) { if ($k =~ m/^n(\d+)$/) { # Special handling: Could be output from the OLD XML interface. my $nr = $1; if (defined($sensordata->{'s'.$nr}) From f42fdaa669e2bb0a987a297c03477399e4dbc624 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 21:03:42 +0200 Subject: [PATCH 407/695] t: ignore empty files --- t/test.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/test.t b/t/test.t index f0782718..e994a62e 100644 --- a/t/test.t +++ b/t/test.t @@ -18,6 +18,7 @@ sub wanted { ( ( $dev, $ino, $mode, $nlink, $uid, $gid ) = lstat($_) ) && -f _ + && -s _ && ( ( $interpreter, $arguments ) = hashbang("$_") ) && ($interpreter) && ++$num_plugins From 878e733430580581b4874c5b612ddba810443760 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 21:15:35 +0200 Subject: [PATCH 408/695] t: fixing skip --- t/test.t | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/t/test.t b/t/test.t index e994a62e..1d02813f 100644 --- a/t/test.t +++ b/t/test.t @@ -50,8 +50,10 @@ sub process_file { use v5.10.1; if ( -r "$file.nocheck") { - skip( sprintf("\nFile '%s' has a .nocheck flag. Ignoring\n", $file), 1); - pass("Not pretending everything is ok"); + SKIP: { + skip( sprintf("\nFile '%s' has a .nocheck flag. Ignoring\n", $file), 1); + pass("Not pretending everything is ok"); + } } elsif ( ! -x $file ) { # missing executable flag From 34b55fe3b50b8d6832506413a181c693516633a4 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sat, 27 Jul 2019 21:23:35 +0200 Subject: [PATCH 409/695] travis: do not use sudo, but the apt plugin --- .travis.yml | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8120a60d..02de897f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,26 @@ --- dist: bionic -install: - - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python python3 ruby php-cli gawk ksh zsh pylint shellcheck - - 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 - - sudo apt-get --no-install-recommends install libsys-virt-perl +addons: + apt: + packages: + - devscripts + - python + - python3 + - ruby + - php-cli + - gawk + - ksh + - zsh + - pylint + - shellcheck + - pkg-config + - libdb-dev + - libvirt-dev + - libexpat-dev + - munin-node + - libsys-virt-perl +install: # we use locally installed cpan modules instead of travis' builtin 'homebrew' perl support # The homebrew variation does not work for us, since: # * it is built without threading From 2fe1beccc2158c6b2d3b918b558d436d1ae92c8d Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Sun, 28 Jul 2019 11:05:53 +0200 Subject: [PATCH 410/695] Revert "travis: do not use sudo, but the apt plugin" This reverts commit 34b55fe3b50b8d6832506413a181c693516633a4. --- .travis.yml | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02de897f..8120a60d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,13 @@ --- dist: bionic -addons: - apt: - packages: - - devscripts - - python - - python3 - - ruby - - php-cli - - gawk - - ksh - - zsh - - pylint - - shellcheck - - pkg-config - - libdb-dev - - libvirt-dev - - libexpat-dev - - munin-node - - libsys-virt-perl - install: + - sudo apt-get update + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php-cli gawk ksh zsh pylint shellcheck + - 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 + - sudo apt-get --no-install-recommends install libsys-virt-perl + # we use locally installed cpan modules instead of travis' builtin 'homebrew' perl support # The homebrew variation does not work for us, since: # * it is built without threading From a207c6009b5e20259759c4228583c9bd63ba2839 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 25 Jul 2019 20:43:15 +1000 Subject: [PATCH 411/695] [router/freebox] Use DERIVE for error correction Signed-off-by: Olivier Mehani --- plugins/router/freebox | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/router/freebox b/plugins/router/freebox index 86e6d90a..8d395b1a 100755 --- a/plugins/router/freebox +++ b/plugins/router/freebox @@ -274,18 +274,30 @@ graph_config() { echo 'graph_order fec_down fec crc_down crc hec_down hec' echo 'fec_down.label FEC down' + echo 'fec_down.type DERIVE' + echo 'fec_down.min 0' echo 'fec_down.graph no' echo 'fec.label FEC' + echo 'fec.type DERIVE' + echo 'fec.min 0' echo 'fec.negative fec_down' echo 'crc_down.label CRC down' + echo 'crc_down.type DERIVE' + echo 'crc_down.min 0' echo 'crc_down.graph no' echo 'crc.label CRC' + echo 'crc.type DERIVE' + echo 'crc.min 0' echo 'crc.negative crc_down' echo 'hec_down.label HEC down' + echo 'hec_down.type DERIVE' + echo 'hec_down.min 0' echo 'hec_down.graph no' echo 'hec.label HEC' + echo 'hec.type DERIVE' + echo 'hec.min 0' echo 'hec.negative hec_down' ;; .traffic) From b8002a6ce739af6d0b96803a8cfee11b7db7dc85 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 28 Jul 2019 22:45:19 +1000 Subject: [PATCH 412/695] [router/freebox] Update errors example graph Signed-off-by: Olivier Mehani --- .../freebox_adsl_errors-day.png | Bin 18991 -> 29283 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/plugins/router/example-graphs/freebox_adsl_errors-day.png b/plugins/router/example-graphs/freebox_adsl_errors-day.png index 0d6f4943f811a5e4a8783d3dd3319be172ce9971..d823874b225d322ed0b154fd33bc7333e4f2690e 100644 GIT binary patch literal 29283 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 I5C$*)2UmGpW&i*H From d6d5fa80be6d12abb6329f5b91d647fa41fba59f Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Tue, 27 Nov 2018 17:07:29 +0100 Subject: [PATCH 413/695] Added plugins for certificates * plugin to monitor certificiate lifetime * plugin to monitor letsencrypt certificate issue limit --- plugins/ssl/certificate_file_expiry | 72 +++++++++++++++++++++++++++++ plugins/ssl/letsencrypt_weekly | 62 +++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100755 plugins/ssl/certificate_file_expiry create mode 100755 plugins/ssl/letsencrypt_weekly diff --git a/plugins/ssl/certificate_file_expiry b/plugins/ssl/certificate_file_expiry new file mode 100755 index 00000000..ef20deb0 --- /dev/null +++ b/plugins/ssl/certificate_file_expiry @@ -0,0 +1,72 @@ +#!/bin/sh +: << =cut +=head1 NAME + +certficate_file_expiry - check the certificate validity of your certfificates + += head1 CONFIGURATION + +Installing: Add list of your certificates prefixed by the type in munin plugin-conf.d + +For openvpn ca.crt and crl.pem +[certficate_file_expiry] +user root +env.CERTS crl:/etc/openvpn/easy-rsa/keys/crl.pem x509:/etc/openvpn/easy-rsa/keys/ca.crt + +For letsencrypt certficates +[certficate_file_expiry] +user root +env.CERTS x509:/etc/letsencrypt/live/domain1.example.com/cert.pem x509:/etc/letsencrypt/live/domain2.example.com/cert.pem + +Warning and Critical levels can also be configured with env variables like this +[certficate_file_expiry] +... +# warn when certificate will be invalid within 5 days +env.warning 5: +# critical when certificate will be invalid within 1 day +env.critical 1: + +=head1 Dependencies + +Dependencies: openssl + +=head1 AUTHOR + +andreas perhab - andreas.perhab@wt-io-it.at +https://www.wt-io-it.at/ + +=head1 LICENSE + +GPLv2 + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +if [ "$1" = "config" ] ; then + echo "graph_title Certificate validity" + echo "graph_args --logarithmic --base 1000" + echo "graph_vlabel certificate validity in days" + echo "graph_category security" +fi + +now=$(date +%s) +warning=${warning:-5:} +critical=${critical:-1:} +for cert in ${CERTS}; do + cert_type=${cert%:*} + cert_file=${cert#*:} + cert_name=$(clean_fieldname "$cert_file") + if [ "$1" = "config" ] ; then + echo "${cert_name}.label ${cert_file}" + print_warning "$cert_name" + print_critical "$cert_name" + elif [ "$1" = "" ] ; then + validity=$(/usr/bin/openssl "$cert_type" -text -noout -in "$cert_file" | grep -E '(Next Update|Not After)') + validity=${validity#*:} + validity=$(date --date="$validity" +%s) + validity=$((validity - now)) + validity=$(echo "$validity" | awk '{ print ($1 / 86400) }') + echo "${cert_name}.value $validity" + fi +done diff --git a/plugins/ssl/letsencrypt_weekly b/plugins/ssl/letsencrypt_weekly new file mode 100755 index 00000000..96b905fd --- /dev/null +++ b/plugins/ssl/letsencrypt_weekly @@ -0,0 +1,62 @@ +#!/bin/sh +: << =cut +=head1 NAME + +letsencrypt_weekly - monitor the number of CSRs by week for /etc/letsencrypt/csr/ + +see https://letsencrypt.org/docs/rate-limits/ + += head1 CONFIGURATION + +You can configure the warning and critical limits for this plugin: + +[letsencrypt_weekly] +# warn when more than 40 certificates have been requested in the last week +env.warning :40 +# critical when more than 50 certificates have been requested in the last week +env.critical :50 + +=head1 Dependencies + +Dependencies: openssl + +=head1 AUTHOR + +andreas perhab - andreas.perhab@wt-io-it.at +https://www.wt-io-it.at/ + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +warning=${warning:-:40} +critical=${critical:-:50} #letsencrypt doesn't allow more than 50 certificates per week +# see https://letsencrypt.org/docs/rate-limits/ + +if [ "$1" = "autoconf" ] ; then + test -d /etc/letsencrypt/csr/ && echo "yes" || echo "no (directory /etc/letsencrypt/csr does not exist)" +elif [ "$1" = "config" ] ; then + echo "graph_title Letsencrypt certificate requests during last week" + echo "graph_args --base 1000" + echo "graph_vlabel Number of certificates" + echo "graph_category security" + echo "letsencrypt_weekly.label Letsencrypt certificates last week" + print_warning "letsencrypt_weekly" + print_critical "letsencrypt_weekly" +elif [ "$1" = "" ] ; then + if existing_certs=$(find /etc/letsencrypt/csr/ -mtime -7 -type f 2>/dev/null); then + value=$(echo "$existing_certs" | wc -l) + else + value="U" + fi + echo "letsencrypt_weekly.value $value" +fi From 72f0546b04e31d8fef103bbc485ca6dff6690541 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 31 Jul 2019 03:33:13 +0200 Subject: [PATCH 414/695] Plugin pacman_pending_updates: fix and clarify conditionals The "checkupdates" compound statement (" ... || ... && ...") did not work due to the equal precendence of both operators (introduced in 6cb5c9e104d9a1a95adba7ce6e97651232c3595e). Closes: #1004 Thanks to Ken-ichi Mito for reporting this issue. --- plugins/other/pacman_pending_updates | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index 00c2ecdb..ce5e7f27 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -50,16 +50,22 @@ EOM ;; autoconf) - hash checkupdates &> /dev/null && echo yes || echo "no (checkupdates not found)" + if hash checkupdates >/dev/null 2>&1; then + echo yes + else + echo "no (checkupdates not found)" + fi ;; *) - updates="$(checkupdates)" || echo "updates.value U" && exit - if [ -n "$updates" ]; then - echo "updates.value $(echo "$updates" | wc -l)" - echo "updates.extinfo $(echo "$updates" | paste -s -d,)" - else - echo updates.value 0 + if updates="$(checkupdates)"; then + if [ -n "$updates" ]; then + echo "updates.value $(echo "$updates" | wc -l)" + echo "updates.extinfo $(echo "$updates" | paste -s -d,)" + else + echo updates.value 0 + fi + echo "updates.value U" && exit fi ;; esac From c9cc2f27f6f80526ed9b844adc87fe16502253da Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 31 Jul 2019 03:36:39 +0200 Subject: [PATCH 415/695] Plugin pacman_pending_updates: switch to /bin/sh; update version The plugin does not use any bash-specific features. Thus it is not necessary to depend on bash. --- plugins/other/pacman_pending_updates | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index ce5e7f27..2f67031a 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh : <<=cut @@ -25,7 +25,7 @@ This plugin will draw one line: the number of updates pending. =head1 VERSION - 1.1.1 + 1.2.0 =head1 AUTHOR From a975706fff37f47b773da07279812d3c90ad0281 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 31 Jul 2019 19:31:15 +0200 Subject: [PATCH 416/695] plugins: another round of defined(@array) fixing --- plugins/apache/apache_smaps | 2 +- plugins/other/proc_ | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/apache/apache_smaps b/plugins/apache/apache_smaps index abc0974e..905fe471 100755 --- a/plugins/apache/apache_smaps +++ b/plugins/apache/apache_smaps @@ -37,7 +37,7 @@ if (!eval "require Linux::Smaps;") { my $PNAME = exists $ENV{'pname'} ? $ENV{'pname'} : "httpd"; my $PUSER = exists $ENV{'puser'} ? $ENV{'puser'} : "apache"; -if (defined(@ARGV) && ($ARGV[0] eq 'config')) { +if (@ARGV && ($ARGV[0] eq 'config')) { print "graph_title Apache Smaps\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel Bytes\n"; diff --git a/plugins/other/proc_ b/plugins/other/proc_ index 473da807..751a5699 100755 --- a/plugins/other/proc_ +++ b/plugins/other/proc_ @@ -242,7 +242,7 @@ sub populate_stats # Bail-function. sub usage { - if (defined(@_) && "@_" ne "") { + if (@_ && "@_" ne "") { print STDERR "@_" . "\n\n"; } print STDERR "Known arguments: suggest, config, autoconf.\n"; From 2124a49d57a09d074d82b5387db15dcacd24b9fd Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 31 Jul 2019 20:36:24 +0200 Subject: [PATCH 417/695] travis: add Graphics::ColorNames::Windows --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8120a60d..a5ee5998 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ install: - cpanm --notest File::ReadBackwards - cpanm --notest Graphics::ColorObject - cpanm --notest Graphics::ColorNames::HTML + - cpanm --notest Graphics::ColorNames::Windows - cpanm --notest IPC::Run3 - cpanm --notest IPC::ShareLite - cpanm --notest JSON From b526aee0c04c5953f76e9b76024b9f1535e78c27 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 31 Jul 2019 20:48:00 +0200 Subject: [PATCH 418/695] Revert "Plugin pacman_pending_updates: switch to /bin/sh; update version" This reverts commit c9cc2f27f6f80526ed9b844adc87fe16502253da. Actually, it depends on a bashism at line 53: if hash checkupdates >/dev/null 2>&1; then --- plugins/other/pacman_pending_updates | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates index 2f67031a..ce5e7f27 100755 --- a/plugins/other/pacman_pending_updates +++ b/plugins/other/pacman_pending_updates @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash : <<=cut @@ -25,7 +25,7 @@ This plugin will draw one line: the number of updates pending. =head1 VERSION - 1.2.0 + 1.1.1 =head1 AUTHOR From d66fef2dc6b4bd6aeee09c6673f87a2564390415 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Fri, 9 Aug 2019 16:52:20 -0400 Subject: [PATCH 419/695] asterisk: command replies are now embedded in server responses Asterisk now replies to "Action: command" with the response embedded, e.g. with "Response: Success" instead of "Response: Follows". We need to adjust how we parse the server response otherwise we can't grab any data at all from the AMI. Closes: #887 --- plugins/asterisk/asterisk | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 19443cec..861a4d10 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -77,30 +77,32 @@ use strict; use Munin::Plugin; use IO::Socket; +# See the following and its subpages for change history in the AMI protocol: +# https://wiki.asterisk.org/wiki/display/AST/Asterisk+Manager+Interface+%28AMI%29+Changes sub asterisk_command { my ($socket, $command) = @_; my $line, my $reply; $socket->print("Action: command\nCommand: $command\n\n"); - # Response: (Error|Follows|???) + # Response: (Error|Follows|Success) $line = $socket->getline; - if ($line !~ /^Response: Follows\r?\n$/) { + if ($line !~ /^Response: Success\r?\n$/) { while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} return undef; } - # Privilege: Command + # Message: Command output follows $line = $socket->getline; + print STDERR "Command: got response: $line\n"; # Until we get the --END COMMAND-- marker, it's the command's output. - while ( $line = $socket->getline and $line !~ /^--END COMMAND--\r?\n$/ ) { + while ( $line = $socket->getline and $line =~ /^Output:/ ) { + print STDERR "Command: got response: $line\n"; + # Don't keep the "Output: " part of the response + substr($line, 0, 8, ''); $reply .= $line; } - - # And then wait for the empty line that says we're done - while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} - return $reply; } @@ -237,13 +239,13 @@ my $meetme_response = asterisk_command($socket, "meetme list"); #* Total number of MeetMe users: 1 my $sipchannels_response = asterisk_command($socket, "sip show channels"); -#Peer User/ANR Call ID Seq (Tx/Rx) Format -#192.168.1.135 yann 6902112b3e0 00101/00002 g729 +#Peer User/ANR Call ID Format Hold Last Message Expiry Peer +#192.168.1.135 yann 1341929961-161 00101/00002 No Rx: INVITE g729 #1 active SIP channel(s) my $iaxchannels_response = asterisk_command($socket, "iax2 show channels"); -#Channel Peer Username ID (Lo/Rem) Seq (Tx/Rx) Lag Jitter JitBuf Format -#IAX2/rodolphe@rodolp 10.8.53.6 rodolphe 00003/01287 00006/00004 00000ms 0148ms 0000ms gsm +#Channel Peer Username ID (Lo/Rem) Seq (Tx/Rx) Lag Jitter JitBuf Format FirstMsg LastMsg +#IAX2/rodolphe@rodolp 10.8.53.6 rodolphe 00003/01287 00006/00004 00000ms 0148ms 0000ms gsm Rx:NEW Tx:ACK #1 active IAX channel(s) # After all the data is fetched we can proceed to process it, the From 700b30e124fd0ecb0075cd22e69d86a9bf06c97d Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Fri, 9 Aug 2019 17:00:50 -0400 Subject: [PATCH 420/695] asterisk: fix typoes in multi-graph configuration --- plugins/asterisk/asterisk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 861a4d10..2436a069 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -208,7 +208,7 @@ END print < Date: Sat, 10 Aug 2019 02:34:27 -0400 Subject: [PATCH 421/695] asterisk: another typo in config prevents graphs from showing up This typo causes the following error during munin-update: asterisk_codecs.graphs_title Asterisk channels per codec' : Parse error in /var/lib/munin/datafile for asterisk_codecs.graphs_title: This error completely prevents munin-update from creating graphs for the asterisk plugin. --- plugins/asterisk/asterisk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 2436a069..24941659 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -192,7 +192,7 @@ END print < Date: Sat, 10 Aug 2019 12:40:33 -0400 Subject: [PATCH 422/695] asterisk: add graph for confbridge The MeetMe Asterisk application has been deprecated since Asterisk 1.8. Debian has also stopped distributing the module with version 11.5.1. Users now need to migrate to using the ConfBridge application. Since the application still exists, and some ppl in other Linux distros probably still use it, we'll keep the code for parsing meetme info for some time. --- plugins/asterisk/asterisk | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 5c28a6df..8d278b94 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -16,8 +16,11 @@ This plugin will produce multiple graphs showing: - the number of messages in all voicemail boxes (replaces asterisk_voicemail); - - the number of active MeetMe conferences and users connected to them - (replace asterisk_meetme and asterisk_meetmeusers, respectively); + - DEPRECATED: the number of active MeetMe conferences and users connected to + them (replace asterisk_meetme and asterisk_meetmeusers, respectively); + + - the number of active ConfBridge conferences (e.g. non-empty ones) and users + connected to them - the number of active channels for a given codec, for both SIP and IAX2 channels (replaces asterisk_sipchannels and asterisk_codecs). @@ -191,6 +194,16 @@ END print < Date: Sat, 10 Aug 2019 13:32:50 -0400 Subject: [PATCH 423/695] asterisk: Add configuration options to disable conference graphs Since the MeetMe application is not available anymore on Debian, it makes no sense to keep the graph around. We do however want to keep code around for parsing stats from MeetMe. So we'll add two "binary" flags that'll let users which of these two graphs they want. The default values will favor getting information from ConfBridge which is the suggested replacement for MeetMe. An added bonus of this is that some users might decide to disable both graphs if they use neither applications, which was impossible before. --- plugins/asterisk/asterisk | 117 ++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 8d278b94..5b6d72ce 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -30,13 +30,15 @@ This plugin will produce multiple graphs showing: The following configuration parameters are used by this plugin [asterisk] - env.host - hostname to connect to - env.port - port number to connect to - env.username - username used for authentication - env.secret - secret used for authentication - env.channels - The channel types to look for - env.codecsx - List of codec IDs (hexadecimal values) - env.codecs - List of codecs names, matching codecsx order + env.host - hostname to connect to + env.port - port number to connect to + env.username - username used for authentication + env.secret - secret used for authentication + env.channels - The channel types to look for + env.codecsx - List of codec IDs (hexadecimal values) + env.codecs - List of codecs names, matching codecsx order + env.meetme - Set to 1 to enable graphs for the MeetMe application + env.confbridge - Set to 1 to enable graphs for the ConfBridge application The "username" and "secret" parameters are mandatory, and have no defaults. @@ -49,6 +51,8 @@ defaults. env.channels Zap IAX2 SIP env.codecsx 0x2 0x4 0x8 env.codecs gsm ulaw alaw + env.meetme 0 + env.confbridge 1 =head2 WILDCARD CONFIGURATION @@ -122,6 +126,9 @@ my @CHANNELS = exists $ENV{'channels'} ? split ' ',$ENV{'channels'} : qw(Zap IAX my @CODECS = exists $ENV{'codecs'} ? split ' ',$ENV{'codecs'} : qw(gsm ulaw alaw); my @CODECSX = exists $ENV{'codecsx'} ? split ' ',$ENV{'codecsx'} : qw(0x2 0x4 0x8); +my $meetme_enabled = $ENV{'meetme'} || '0'; +my $confbridge_enabled = $ENV{'confbridge'} || '1'; + my $line, my $error; my $socket = new IO::Socket::INET(PeerAddr => $peeraddr, PeerPort => $peerport, @@ -182,7 +189,8 @@ graph_category voip total.label Total messages END -print < Date: Sat, 10 Aug 2019 14:05:08 -0400 Subject: [PATCH 424/695] asterisk: turn output into debug-only lines I apparently forgot to clear out two lines of debugging output from the script before submitting a pull request. Those lines of output are super useful for debugging whenever things are not working as expected (e.g. asterisk has changed its output). So it'll be more productive to keep them around but only output them if MUNIN_DEBUG is set. --- plugins/asterisk/asterisk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 5c28a6df..798f9aee 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -88,17 +88,19 @@ sub asterisk_command { # Response: (Error|Follows|Success) $line = $socket->getline; if ($line !~ /^Response: Success\r?\n$/) { - while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) { + print STDERR "COMMAND: Ignoring unwanted line: $line" if $Munin::Plugin::DEBUG; + } return undef; } # Message: Command output follows $line = $socket->getline; - print STDERR "Command: got response: $line\n"; + print STDERR "COMMAND got response: $line" if $Munin::Plugin::DEBUG; # Until we get the --END COMMAND-- marker, it's the command's output. while ( $line = $socket->getline and $line =~ /^Output:/ ) { - print STDERR "Command: got response: $line\n"; + print STDERR "COMMAND: got response: $line" if $Munin::Plugin::DEBUG; # Don't keep the "Output: " part of the response substr($line, 0, 8, ''); $reply .= $line; From 5fff12db96571dce4117435ec23178cb63f5ea15 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 10 Aug 2019 21:43:36 -0400 Subject: [PATCH 425/695] asterisk: change tabs for spaces for indentation the plugin currently has inconsistencies with how lines are indented: some lines only use tab characters while all other lines use spaces. Let's make the coding style more uniform by using spaces everywhere. --- plugins/asterisk/asterisk | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 5b6d72ce..938bbbdd 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -131,8 +131,8 @@ my $confbridge_enabled = $ENV{'confbridge'} || '1'; my $line, my $error; my $socket = new IO::Socket::INET(PeerAddr => $peeraddr, - PeerPort => $peerport, - Proto => 'tcp') + PeerPort => $peerport, + Proto => 'tcp') or $error = "Could not create socket: $!"; if ( $socket ) { @@ -403,9 +403,9 @@ END } foreach my $codec (@CODECSX) { if ($fields[4] eq "$codec") { - $results[$i] = $results[$i] + 1; - $found = 1; - last; + $results[$i] = $results[$i] + 1; + $found = 1; + last; } $i++; } @@ -426,9 +426,9 @@ END } foreach my $codec (@CODECS) { if ($fields[8] eq "$codec") { - $results[$i] = $results[$i] + 1; - $found = 1; - last; + $results[$i] = $results[$i] + 1; + $found = 1; + last; } $i++; } From b8eba4d75c75bca54e4db4b3f121e2e60e958228 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 10 Aug 2019 21:45:18 -0400 Subject: [PATCH 426/695] asterisk: rename options for enabling/disabling graphs With this "enable_" prefix, it will be clearer to users that those options are boolean flags. --- plugins/asterisk/asterisk | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 938bbbdd..da633741 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -30,15 +30,15 @@ This plugin will produce multiple graphs showing: The following configuration parameters are used by this plugin [asterisk] - env.host - hostname to connect to - env.port - port number to connect to - env.username - username used for authentication - env.secret - secret used for authentication - env.channels - The channel types to look for - env.codecsx - List of codec IDs (hexadecimal values) - env.codecs - List of codecs names, matching codecsx order - env.meetme - Set to 1 to enable graphs for the MeetMe application - env.confbridge - Set to 1 to enable graphs for the ConfBridge application + env.host - hostname to connect to + env.port - port number to connect to + env.username - username used for authentication + env.secret - secret used for authentication + env.channels - The channel types to look for + env.codecsx - List of codec IDs (hexadecimal values) + env.codecs - List of codecs names, matching codecsx order + env.enable_meetme - Set to 1 to enable graphs for the MeetMe application + env.enable_confbridge - Set to 1 to enable graphs for the ConfBridge application The "username" and "secret" parameters are mandatory, and have no defaults. @@ -51,8 +51,8 @@ defaults. env.channels Zap IAX2 SIP env.codecsx 0x2 0x4 0x8 env.codecs gsm ulaw alaw - env.meetme 0 - env.confbridge 1 + env.enable_meetme 0 + env.enable_confbridge 1 =head2 WILDCARD CONFIGURATION @@ -126,8 +126,8 @@ my @CHANNELS = exists $ENV{'channels'} ? split ' ',$ENV{'channels'} : qw(Zap IAX my @CODECS = exists $ENV{'codecs'} ? split ' ',$ENV{'codecs'} : qw(gsm ulaw alaw); my @CODECSX = exists $ENV{'codecsx'} ? split ' ',$ENV{'codecsx'} : qw(0x2 0x4 0x8); -my $meetme_enabled = $ENV{'meetme'} || '0'; -my $confbridge_enabled = $ENV{'confbridge'} || '1'; +my $meetme_enabled = $ENV{'enable_meetme'} || '0'; +my $confbridge_enabled = $ENV{'enable_confbridge'} || '1'; my $line, my $error; my $socket = new IO::Socket::INET(PeerAddr => $peeraddr, From e14659d02c76e7b52b3d7d9e83591ba8bec86909 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 10 Aug 2019 21:46:30 -0400 Subject: [PATCH 427/695] asterisk: use eq instead of == for comparing a variable to a string sumpfralle suggested this change on github, since the comparisons are meant to be string comparisons. --- plugins/asterisk/asterisk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index da633741..e7f37c2a 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -258,7 +258,7 @@ my $voicemail_response = asterisk_command($socket, "voicemail show users"); #2 voicemail users configured. my $meetme_response; -if ($meetme_enabled == '1') { +if ($meetme_enabled eq '1') { $meetme_response = asterisk_command($socket, "meetme list"); #Conf Num Parties Marked Activity Creation #5500 0001 N/A 00:00:03 Static @@ -266,7 +266,7 @@ if ($meetme_enabled == '1') { } my $confbridge_response; -if ($confbridge_enabled == '1') { +if ($confbridge_enabled eq '1') { $confbridge_response = asterisk_command($socket, "confbridge list"); #Conference Bridge Name Users Marked Locked Muted #================================ ====== ====== ====== ===== From 0e0de011365beb4418b1b0e699b504b783b1448f Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sun, 11 Aug 2019 00:34:30 -0400 Subject: [PATCH 428/695] asterisk: the confbridge graph is in the wrong category This was a copy/paste error that slipped by unnoticed :\ --- plugins/asterisk/asterisk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index bceb70da..777a34cd 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -209,7 +209,7 @@ if ($confbridge_enabled == '1') { multigraph asterisk_confbridge graph_title Asterisk ConfBridge statistics graph_args --base 1000 -l 0 -graph_category asterisk +graph_category voip users.label Connected users conferences.label Active conferences END From ce6e67fffc89f25290eec5bab3aa7b2f02766b9a Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sat, 10 Aug 2019 11:33:27 +0200 Subject: [PATCH 429/695] lxc: working on buster (gandalf), and almost working on virtual (jessie), except lxc_net --- plugins/lxc/lxc-lib | 63 +++++++++++++++++ plugins/lxc/lxc_cpu | 90 +++++++++++------------- plugins/lxc/lxc_cpu_time | 72 ++++++++++---------- plugins/lxc/lxc_logins | 71 +++++++++++++++++++ plugins/lxc/lxc_net | 143 ++++++++++++++++++--------------------- plugins/lxc/lxc_proc | 100 +++++++++++++-------------- plugins/lxc/lxc_ram | 135 ++++++++++++++---------------------- 7 files changed, 371 insertions(+), 303 deletions(-) create mode 100644 plugins/lxc/lxc-lib create mode 100755 plugins/lxc/lxc_logins diff --git a/plugins/lxc/lxc-lib b/plugins/lxc/lxc-lib new file mode 100644 index 00000000..90f90e6b --- /dev/null +++ b/plugins/lxc/lxc-lib @@ -0,0 +1,63 @@ +: << =cut + +=head1 NAME + +lxc-lib -- some base functions for the lxc_* plugins + +=head1 AUTHOR + +schaefer@alphanet.ch + +=head1 LICENSE + +GPLv3 + +=cut + +lxcpath=${lxcpath:-/var/lib/lxc} + +function active_guests { + local g active i ok + for g in $(lxc-ls | sort -u) + do + # handle optional exclude list in $1 + ok=1 + for i in $1 + do + if [ "$i" = "$g" ]; then + ok=0 + fi + done + + if [ $ok = 1 ]; then + if lxc-info -n $g 2>&1 | grep -qs RUNNING; then + active="$active $g" + fi + fi + done + + echo $active +} + +function lxc_cgroup { + # lxc3 + lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' + + # lxc < 3 + #lxc-cgroup $* +} + +function lxc_clean_fieldname { + # workaround for bug leaving out the first number + echo $(clean_fieldname ${1::1}$1) +} + +function lxc_netdev { + local g=$1 dev + + if [ -f $lxcpath/$g/config ]; then + grep lxc.net.0.veth.pair $lxcpath/$g/config | awk '{print $NF;}' + else + echo unknown + fi +} diff --git a/plugins/lxc/lxc_cpu b/plugins/lxc/lxc_cpu index 909a4628..2f76ff85 100755 --- a/plugins/lxc/lxc_cpu +++ b/plugins/lxc/lxc_cpu @@ -20,10 +20,12 @@ This plugin needs root privilege. vajtsz vajtsz@gmail.com mitty mitty@mitty.jp +(many changes schaefer@alphanet.ch) =head1 LICENSE -Unknown license +2-clause BSD License +or GPLv3 license, at your option =head1 MAGIC MARKERS @@ -34,59 +36,49 @@ Unknown license . $MUNIN_LIBDIR/plugins/plugin.sh -guest_names=`lxc-ls | sort -u` -for guest in $guest_names; do - if lxc-info -n $guest 2>&1 | grep -qs RUNNING ; then - active="$active $guest" - fi -done -guest_names="$active" +. $MUNIN_LIBDIR/plugins/lxc-lib - - -f_comm='lxc-cgroup ' +active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + if [ -r /proc/stat ]; then + echo yes + exit 0 + else + echo "no (no /proc/stat)" + exit 0 + fi fi if [ "$1" = "config" ]; then - - echo 'graph_title CPU Usage ' - echo 'graph_args -l 0 --base 1000' - echo 'graph_vlabel USER_HZ' - echo 'graph_category cpu' - - - for guest_name in $guest_names; do - guest="$(clean_fieldname $guest_name)" - echo 'cpu_user_'$guest'.label '$guest_name': User' - echo 'cpu_user_'$guest'.type DERIVE' - echo 'cpu_user_'$guest'.min 0' - echo 'cpu_system_'$guest'.label '$guest_name': System' - echo 'cpu_system_'$guest'.type DERIVE' - echo 'cpu_system_'$guest'.min 0' - done - exit 0 + cat <&1 | grep -qs RUNNING ; then - active="$active $guest" - fi -done -guest_names="$active" +. $MUNIN_LIBDIR/plugins/lxc-lib - -f_comm='lxc-cgroup ' +active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + if [ -r /proc/stat ]; then + echo yes + exit 0 + else + echo "no (no /proc/stat)" + exit 0 + fi fi if [ "$1" = "config" ]; then + cat </tasks # Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks # Ubuntu 12.04 with cgroup-lite: /sys/fs/cgroup/cpuacct/lxc//tasks # Ubuntu 12.04 with cgroup-bin: /sys/fs/cgroup/cpuacct/sysdefault/lxc//tasks -# Ubuntu 14.04: /sys/fs/cgroup/systemd/lxc//tasks +# and with cgmanager on jessie count_processes () { [ -z "$1" ] && return 0 @@ -51,11 +57,10 @@ count_processes () { return `wc -l < $SYSFS` fi fi - + for SYSFS in \ /sys/fs/cgroup/$1/tasks \ /sys/fs/cgroup/lxc/$1/tasks \ - /sys/fs/cgroup/systemd/lxc/$1/tasks \ /sys/fs/cgroup/cpuacct/lxc/$1/tasks \ /sys/fs/cgroup/cpuacct/sysdefault/lxc/$1/tasks \ ; do @@ -64,60 +69,51 @@ count_processes () { fi done + if [ -e /usr/bin/cgm ]; then + return `cgm getvalue cpu lxc/$1 tasks 2>/dev/null | wc -l` + fi + return 0 } - -guest_names=`lxc-ls | sort -u` -for guest in $guest_names; do - if lxc-info -n $guest 2>&1 | grep -qs RUNNING ; then - active="$active $guest" - fi -done -guest_names="$active" - - - -f_comm='lxc-cgroup ' - if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + if [ -r /proc/stat ]; then + echo yes + exit 0 + else + echo "no (no /proc/stat)" + exit 0 + fi fi if [ "$1" = "config" ]; then - - echo 'graph_title Processes ' - echo 'graph_args -l 0 --base 1000' - echo 'graph_vlabel Number of processes' - echo 'graph_category processes' - - - for guest_name in $guest_names; do - guest="$(clean_fieldname $guest_name)" - echo 'lxc_proc_'$guest'.label '$guest_name': processes' - echo 'lxc_proc_'$guest'.type GAUGE' - echo 'lxc_proc_'$guest'.min 0' - done - exit 0 + cat <&1 | grep -qs RUNNING ; then - active="$active $guest" - fi -done -guest_names="$active" - - -f_comm='lxc-cgroup ' - - -do_autoconf() { - if [ -r /proc/stat ]; then - echo yes - else - echo "no (no /proc/stat)" - fi -} - - -do_config() { - echo 'graph_title Memory ' - echo 'graph_args -l 0 --base 1024' - echo 'graph_vlabel byte' - echo 'graph_category memory' - - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - echo "mem_usage_$guest.label $guest_name: Mem usage" - echo "mem_usage_$guest.type GAUGE" - if [ "$areastack" = "true" ]; then - echo "mem_usage_$guest.draw AREASTACK" - fi - echo "mem_cache_$guest.label $guest_name: Cache" - echo "mem_cache_$guest.type GAUGE" - echo "mem_active_$guest.label $guest_name: Active" - echo "mem_active_$guest.type GAUGE" - echo "mem_inactive_$guest.label $guest_name: Inactive" - echo "mem_inactive_$guest.type GAUGE" - done -} - - -do_fetch() { - for guest_name in $guest_names; do - guest="$(clean_fieldname "$guest_name")" - - value=$($f_comm -n "$guest_name" memory.usage_in_bytes) - echo "mem_usage_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_cache | awk '{print($2)}') - echo "mem_cache_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_active_anon | awk '{print($2)}') - echo "mem_active_$guest.value $value" - - value=$($f_comm -n "$guest_name" memory.stat | grep total_inactive_anon | awk '{print($2)}') - echo "mem_inactive_$guest.value $value" - done -} +. $MUNIN_LIBDIR/plugins/lxc-lib +active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - do_autoconf -elif [ "$1" = "config" ]; then - do_config - if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi -else - do_fetch + if [ -r /proc/stat ]; then + echo yes + exit 0 + else + echo "no (no /proc/stat)" + exit 0 + fi fi + +if [ "$1" = "config" ]; then + cat < Date: Sat, 10 Aug 2019 11:43:35 +0200 Subject: [PATCH 430/695] lxc < 3 support --- plugins/lxc/lxc-lib | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/lxc-lib b/plugins/lxc/lxc-lib index 90f90e6b..577cc70d 100644 --- a/plugins/lxc/lxc-lib +++ b/plugins/lxc/lxc-lib @@ -44,7 +44,7 @@ function lxc_cgroup { lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' # lxc < 3 - #lxc-cgroup $* +# lxc-cgroup $* } function lxc_clean_fieldname { @@ -56,7 +56,11 @@ function lxc_netdev { local g=$1 dev if [ -f $lxcpath/$g/config ]; then - grep lxc.net.0.veth.pair $lxcpath/$g/config | awk '{print $NF;}' + # lxc3 + egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' + + # lxc < 3 +# egrep '^lxc.network.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' else echo unknown fi From 2cfba9f8408e10c9a951faa00b38bda895696719 Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sat, 10 Aug 2019 11:48:51 +0200 Subject: [PATCH 431/695] Comment --- plugins/lxc/README | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 plugins/lxc/README diff --git a/plugins/lxc/README b/plugins/lxc/README new file mode 100644 index 00000000..915f8168 --- /dev/null +++ b/plugins/lxc/README @@ -0,0 +1,7 @@ +This works for lxc 3 (and, also, if you the two special cases in +lxc-lib, namely lxc-cgroup and lxc-netdev, you can support lxc < 3 too. + +Tested on Debian buster and Debian jessie. + +See the individual files for copyright and installation/configuration +information. From ab67ad805dc474184baa116eac91bf1d5e4e07bd Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sat, 10 Aug 2019 11:51:09 +0200 Subject: [PATCH 432/695] Comment --- plugins/lxc/README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lxc/README b/plugins/lxc/README index 915f8168..a437d22f 100644 --- a/plugins/lxc/README +++ b/plugins/lxc/README @@ -5,3 +5,5 @@ Tested on Debian buster and Debian jessie. See the individual files for copyright and installation/configuration information. + +This is version 1.0 by schaefer@alphanet.ch From 9ef366c71a649d645aea5d9f4a12e5d984fdd2e5 Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 09:40:39 +0200 Subject: [PATCH 433/695] - autoconf cleanup - better lxc_clean_fieldname workaround supporting non numeric compatibility - lxc_netdev now compatible with lxc3 and lxc2 at a small CPU cost - lxc_cgroup now compatible with lxc3 and lxc2; may however fail with lxc2 if your config file contains cruft --- plugins/lxc/README | 2 +- plugins/lxc/lxc-lib | 34 +++++++++++++++++++++++----------- plugins/lxc/lxc_cpu | 8 +------- plugins/lxc/lxc_cpu_time | 8 +------- plugins/lxc/lxc_logins | 8 +------- plugins/lxc/lxc_proc | 8 +------- plugins/lxc/lxc_ram | 8 +------- 7 files changed, 29 insertions(+), 47 deletions(-) diff --git a/plugins/lxc/README b/plugins/lxc/README index a437d22f..941ff402 100644 --- a/plugins/lxc/README +++ b/plugins/lxc/README @@ -6,4 +6,4 @@ Tested on Debian buster and Debian jessie. See the individual files for copyright and installation/configuration information. -This is version 1.0 by schaefer@alphanet.ch +This is version 1.1 by schaefer@alphanet.ch diff --git a/plugins/lxc/lxc-lib b/plugins/lxc/lxc-lib index 577cc70d..a88e9528 100644 --- a/plugins/lxc/lxc-lib +++ b/plugins/lxc/lxc-lib @@ -40,28 +40,40 @@ function active_guests { } function lxc_cgroup { - # lxc3 + # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' - - # lxc < 3 -# lxc-cgroup $* } function lxc_clean_fieldname { - # workaround for bug leaving out the first number - echo $(clean_fieldname ${1::1}$1) + # clean_fieldname should be used on the whole identifier, not just the + # lxc instance ID, because if it is only numeric, the first digit will + # be replaced with a "_"; this is a work-around + case "${1::1}" in + [0-9]) echo $(clean_fieldname ${1::1}$1);; # 104 -> __104 + *) echo $(clean_fieldname $1);; + esac } function lxc_netdev { local g=$1 dev if [ -f $lxcpath/$g/config ]; then - # lxc3 - egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' - - # lxc < 3 -# egrep '^lxc.network.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' + # lxc 3 vs < 3 + egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null | awk '{print $NF;}' \ + || egrep '^lxc.network.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' else echo unknown fi } + +# BUGS +# - I don't think this is enough or even appropriate +function lxc_autoconf { + if [ -r /proc/stat ]; then + echo yes + exit 0 + else + echo "no (no /proc/stat)" + exit 0 + fi +} diff --git a/plugins/lxc/lxc_cpu b/plugins/lxc/lxc_cpu index 2f76ff85..f501cad2 100755 --- a/plugins/lxc/lxc_cpu +++ b/plugins/lxc/lxc_cpu @@ -41,13 +41,7 @@ or GPLv3 license, at your option active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + lxc_autoconf fi if [ "$1" = "config" ]; then diff --git a/plugins/lxc/lxc_cpu_time b/plugins/lxc/lxc_cpu_time index 8730a5a9..c483a916 100755 --- a/plugins/lxc/lxc_cpu_time +++ b/plugins/lxc/lxc_cpu_time @@ -41,13 +41,7 @@ or GPLv3 license, at your option active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + lxc_autoconf fi if [ "$1" = "config" ]; then diff --git a/plugins/lxc/lxc_logins b/plugins/lxc/lxc_logins index d1fc3257..014aec33 100755 --- a/plugins/lxc/lxc_logins +++ b/plugins/lxc/lxc_logins @@ -38,13 +38,7 @@ GPLv3 active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + lxc_autoconf fi if [ "$1" = "config" ]; then diff --git a/plugins/lxc/lxc_proc b/plugins/lxc/lxc_proc index e91274c9..f5c83704 100755 --- a/plugins/lxc/lxc_proc +++ b/plugins/lxc/lxc_proc @@ -77,13 +77,7 @@ count_processes () { } if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + lxc_autoconf fi if [ "$1" = "config" ]; then diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 68e80038..bf6a4134 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -41,13 +41,7 @@ or GPLv3 license, at your option active_guests=$(active_guests) if [ "$1" = "autoconf" ]; then - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi + lxc_autoconf fi if [ "$1" = "config" ]; then From 89e0f1c72956091948554b66500e7d7ab35399ea Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 09:43:39 +0200 Subject: [PATCH 434/695] Update --- plugins/lxc/README | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/README b/plugins/lxc/README index 941ff402..ff81420f 100644 --- a/plugins/lxc/README +++ b/plugins/lxc/README @@ -1,5 +1,7 @@ -This works for lxc 3 (and, also, if you the two special cases in -lxc-lib, namely lxc-cgroup and lxc-netdev, you can support lxc < 3 too. +This works for lxc 3 (and lxc 2, as long as you don't have +cruft in your config file, test it with: + lxc-cgroup -o /dev/stdout -l INFO -n 104 cpuacct.usage +-- with 104 a valid lxc instance) Tested on Debian buster and Debian jessie. From 241b0c60672d7c244e54619c0540fa05ec49f5e2 Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 10:11:24 +0200 Subject: [PATCH 435/695] Fix for lxc < 3 --- plugins/lxc/lxc-lib | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/lxc-lib b/plugins/lxc/lxc-lib index a88e9528..bf9687ae 100644 --- a/plugins/lxc/lxc-lib +++ b/plugins/lxc/lxc-lib @@ -59,8 +59,9 @@ function lxc_netdev { if [ -f $lxcpath/$g/config ]; then # lxc 3 vs < 3 - egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null | awk '{print $NF;}' \ - || egrep '^lxc.network.veth.pair' $lxcpath/$g/config | awk '{print $NF;}' + (egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null \ + || egrep '^lxc.network.veth.pair' $lxcpath/$g/config + ) | awk '{print $NF;}' else echo unknown fi From 5105020fcfadf6adb45133c604db55639de7596e Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 11:02:10 +0200 Subject: [PATCH 436/695] - added systemd possibility for stats - cleanup --- plugins/lxc/lxc_proc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/lxc_proc b/plugins/lxc/lxc_proc index f5c83704..be44d9c8 100755 --- a/plugins/lxc/lxc_proc +++ b/plugins/lxc/lxc_proc @@ -47,6 +47,7 @@ active_guests=$(active_guests) # Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks # Ubuntu 12.04 with cgroup-lite: /sys/fs/cgroup/cpuacct/lxc//tasks # Ubuntu 12.04 with cgroup-bin: /sys/fs/cgroup/cpuacct/sysdefault/lxc//tasks +# Ubuntu 14.04 /sys/fs/cgroup/systemd/lxc//tasks # and with cgmanager on jessie count_processes () { [ -z "$1" ] && return 0 @@ -61,9 +62,10 @@ count_processes () { for SYSFS in \ /sys/fs/cgroup/$1/tasks \ /sys/fs/cgroup/lxc/$1/tasks \ + /sys/fs/cgroup/systemd/lxc/$1/tasks \ /sys/fs/cgroup/cpuacct/lxc/$1/tasks \ - /sys/fs/cgroup/cpuacct/sysdefault/lxc/$1/tasks \ - ; do + /sys/fs/cgroup/cpuacct/sysdefault/lxc/$1/tasks + do if [ -e $SYSFS ]; then return `wc -l < $SYSFS` fi From 50a998b460f8ed33f09fd122793eb7f702b32c11 Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 11:02:35 +0200 Subject: [PATCH 437/695] areastack option --- plugins/lxc/lxc_ram | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index bf6a4134..8160d289 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -12,10 +12,18 @@ lxc_ram - Plugin to monitor LXC memory usage. [lxc_*] user root + [lxc_ram] + env.areastack true + =head1 INTERPRETATION This plugin needs root privilege. +If env.areastack is set to true, all memory usages of containers will be +drawn as stacked area charts. +This option changes graph order, all of 'Mem usage' comes first and then others. +(default: false) + =head1 AUTHOR vajtsz vajtsz@gmail.com @@ -40,6 +48,9 @@ or GPLv3 license, at your option active_guests=$(active_guests) +# configurable: true/false +areastack=${areastack:-false} + if [ "$1" = "autoconf" ]; then lxc_autoconf fi @@ -58,6 +69,12 @@ EOF cat < Date: Sun, 11 Aug 2019 11:02:50 +0200 Subject: [PATCH 438/695] version --- plugins/lxc/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lxc/README b/plugins/lxc/README index ff81420f..2f81a090 100644 --- a/plugins/lxc/README +++ b/plugins/lxc/README @@ -8,4 +8,4 @@ Tested on Debian buster and Debian jessie. See the individual files for copyright and installation/configuration information. -This is version 1.1 by schaefer@alphanet.ch +This is version 1.2 by schaefer@alphanet.ch From 154ffc100ba925e426efadf151b43209112993c8 Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 11:12:24 +0200 Subject: [PATCH 439/695] Fix --- plugins/lxc/README | 2 +- plugins/lxc/lxc_ram | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/README b/plugins/lxc/README index 2f81a090..b259f704 100644 --- a/plugins/lxc/README +++ b/plugins/lxc/README @@ -8,4 +8,4 @@ Tested on Debian buster and Debian jessie. See the individual files for copyright and installation/configuration information. -This is version 1.2 by schaefer@alphanet.ch +This is version 1.3 by schaefer@alphanet.ch diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 8160d289..15a48898 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -69,7 +69,7 @@ EOF cat < Date: Sun, 11 Aug 2019 18:45:25 +0200 Subject: [PATCH 440/695] Initial version of multigraph version --- plugins/lxc/README | 11 ---- plugins/lxc/lxc-lib | 80 --------------------------- plugins/lxc/lxc_cpu | 78 -------------------------- plugins/lxc/lxc_cpu_time | 71 ------------------------ plugins/lxc/lxc_logins | 65 ---------------------- plugins/lxc/lxc_net | 116 --------------------------------------- plugins/lxc/lxc_proc | 115 -------------------------------------- plugins/lxc/lxc_ram | 98 --------------------------------- 8 files changed, 634 deletions(-) delete mode 100644 plugins/lxc/README delete mode 100644 plugins/lxc/lxc-lib delete mode 100755 plugins/lxc/lxc_cpu delete mode 100755 plugins/lxc/lxc_cpu_time delete mode 100755 plugins/lxc/lxc_logins delete mode 100755 plugins/lxc/lxc_net delete mode 100755 plugins/lxc/lxc_proc delete mode 100755 plugins/lxc/lxc_ram diff --git a/plugins/lxc/README b/plugins/lxc/README deleted file mode 100644 index b259f704..00000000 --- a/plugins/lxc/README +++ /dev/null @@ -1,11 +0,0 @@ -This works for lxc 3 (and lxc 2, as long as you don't have -cruft in your config file, test it with: - lxc-cgroup -o /dev/stdout -l INFO -n 104 cpuacct.usage --- with 104 a valid lxc instance) - -Tested on Debian buster and Debian jessie. - -See the individual files for copyright and installation/configuration -information. - -This is version 1.3 by schaefer@alphanet.ch diff --git a/plugins/lxc/lxc-lib b/plugins/lxc/lxc-lib deleted file mode 100644 index bf9687ae..00000000 --- a/plugins/lxc/lxc-lib +++ /dev/null @@ -1,80 +0,0 @@ -: << =cut - -=head1 NAME - -lxc-lib -- some base functions for the lxc_* plugins - -=head1 AUTHOR - -schaefer@alphanet.ch - -=head1 LICENSE - -GPLv3 - -=cut - -lxcpath=${lxcpath:-/var/lib/lxc} - -function active_guests { - local g active i ok - for g in $(lxc-ls | sort -u) - do - # handle optional exclude list in $1 - ok=1 - for i in $1 - do - if [ "$i" = "$g" ]; then - ok=0 - fi - done - - if [ $ok = 1 ]; then - if lxc-info -n $g 2>&1 | grep -qs RUNNING; then - active="$active $g" - fi - fi - done - - echo $active -} - -function lxc_cgroup { - # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) - lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' -} - -function lxc_clean_fieldname { - # clean_fieldname should be used on the whole identifier, not just the - # lxc instance ID, because if it is only numeric, the first digit will - # be replaced with a "_"; this is a work-around - case "${1::1}" in - [0-9]) echo $(clean_fieldname ${1::1}$1);; # 104 -> __104 - *) echo $(clean_fieldname $1);; - esac -} - -function lxc_netdev { - local g=$1 dev - - if [ -f $lxcpath/$g/config ]; then - # lxc 3 vs < 3 - (egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null \ - || egrep '^lxc.network.veth.pair' $lxcpath/$g/config - ) | awk '{print $NF;}' - else - echo unknown - fi -} - -# BUGS -# - I don't think this is enough or even appropriate -function lxc_autoconf { - if [ -r /proc/stat ]; then - echo yes - exit 0 - else - echo "no (no /proc/stat)" - exit 0 - fi -} diff --git a/plugins/lxc/lxc_cpu b/plugins/lxc/lxc_cpu deleted file mode 100755 index f501cad2..00000000 --- a/plugins/lxc/lxc_cpu +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -# -*- sh -*- - -: << =cut - -=head1 NAME - -lxc_cpu - Plugin to monitor LXC CPU usage - -=head1 CONFIGURATION - - [lxc_*] - user root - -=head1 INTERPRETATION - -This plugin needs root privilege. - -=head1 AUTHOR - -vajtsz vajtsz@gmail.com -mitty mitty@mitty.jp -(many changes schaefer@alphanet.ch) - -=head1 LICENSE - -2-clause BSD License -or GPLv3 license, at your option - -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf - -=cut - -. $MUNIN_LIBDIR/plugins/plugin.sh - -. $MUNIN_LIBDIR/plugins/lxc-lib - -active_guests=$(active_guests) - -if [ "$1" = "autoconf" ]; then - lxc_autoconf -fi - -if [ "$1" = "config" ]; then - cat </tasks -# Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks -# Ubuntu 12.04 with cgroup-lite: /sys/fs/cgroup/cpuacct/lxc//tasks -# Ubuntu 12.04 with cgroup-bin: /sys/fs/cgroup/cpuacct/sysdefault/lxc//tasks -# Ubuntu 14.04 /sys/fs/cgroup/systemd/lxc//tasks -# and with cgmanager on jessie -count_processes () { - [ -z "$1" ] && return 0 - - if [ -n "$cgrouppath" ]; then - SYSFS=$cgrouppath/$1/tasks - if [ -e $SYSFS ]; then - return `wc -l < $SYSFS` - fi - fi - - for SYSFS in \ - /sys/fs/cgroup/$1/tasks \ - /sys/fs/cgroup/lxc/$1/tasks \ - /sys/fs/cgroup/systemd/lxc/$1/tasks \ - /sys/fs/cgroup/cpuacct/lxc/$1/tasks \ - /sys/fs/cgroup/cpuacct/sysdefault/lxc/$1/tasks - do - if [ -e $SYSFS ]; then - return `wc -l < $SYSFS` - fi - done - - if [ -e /usr/bin/cgm ]; then - return `cgm getvalue cpu lxc/$1 tasks 2>/dev/null | wc -l` - fi - - return 0 -} - -if [ "$1" = "autoconf" ]; then - lxc_autoconf -fi - -if [ "$1" = "config" ]; then - cat < Date: Sun, 11 Aug 2019 18:46:55 +0200 Subject: [PATCH 441/695] First multigraph version --- plugins/lxc/lxc-multigraph | 377 +++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100755 plugins/lxc/lxc-multigraph diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph new file mode 100755 index 00000000..ee920004 --- /dev/null +++ b/plugins/lxc/lxc-multigraph @@ -0,0 +1,377 @@ +#!/bin/bash +# -*- sh -*- + +: << =cut + +=head1 NAME + +lxc-multigraph - Plugin to monitor LXC containers (v2.0) + +=head1 CONFIGURATION + + [lxc-multigraph] + user root + + # memory usage of containers drawn as + # stacked area charts (changes graph + # order) -- defaults to false + # env.ram_areastack true + + # lxc container path, default below + #env.lxcpath /var/lib/lxc + + # exclude the following containers + # (default none excluded) + #env.exclude container1 container2 + + # path where tasks sysfs files are stored, + # set this if the various attempts in the + # code don't work + # (default none) + #env.cgrouppath /sys/fs/cgroup/cpuacct/lxc/ + +=head1 INTERPRETATION + +This plugin needs root privilege. + +This plugin has been tested with lxc 3 and +lx2 (on Debian buster and Debian jessie, +respectively). + +For the network graphs to work, you need +to have in every container's config file +a line defining the virtual network interface +path (else lxc will use a random name at +each container's start); see the lxc_netdev() +function below. + +If using lxc 2, make sure you do not have cruft +in your container config files, you can test +it with: + lxc-cgroup -o /dev/stdout -l INFO -n 104 cpuacct.usage +-- with 104 a valid lxc instance), if you +get a warning, fix the config file. + +For the logins graph, you need the bc command, and, +in each container, the users command. + +Tested on Debian buster and Debian jessie. + +=head1 AUTHOR + +vajtsz vajtsz@gmail.com +mitty mitty@mitty.jp +alphanet schaefer@alphanet.ch (many changes and multigraph) + +See https://framagit.org/alphanet/various-stuff +(plugins/lcx) + +=head1 LICENSE + +2-clause BSD License +or GPLv3 license or later, at your option + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +. $MUNIN_LIBDIR/plugins/plugin.sh + +lxcpath=${lxcpath:-/var/lib/lxc} + +function active_guests { + local g active i ok + for g in $(lxc-ls | sort -u) + do + # handle optional exclude list in $1 + ok=1 + for i in $1 + do + if [ "$i" = "$g" ]; then + ok=0 + fi + done + + if [ $ok = 1 ]; then + if lxc-info -n $g 2>&1 | grep -qs RUNNING; then + active="$active $g" + fi + fi + done + + echo $active +} + +function lxc_cgroup { + # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) + lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' +} + +function lxc_netdev { + local g=$1 dev + + if [ -f $lxcpath/$g/config ]; then + # lxc 3 vs < 3 + (egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null \ + || egrep '^lxc.network.veth.pair' $lxcpath/$g/config + ) | awk '{print $NF;}' + else + echo unknown + fi +} + +# find proper sysfs and count it +# Debian 6.0: /sys/fs/cgroup//tasks +# Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks +# Ubuntu 12.04 with cgroup-lite: /sys/fs/cgroup/cpuacct/lxc//tasks +# Ubuntu 12.04 with cgroup-bin: /sys/fs/cgroup/cpuacct/sysdefault/lxc//tasks +# Ubuntu 14.04 /sys/fs/cgroup/systemd/lxc//tasks +# and with cgmanager on jessie +lxc_count_processes () { + local SYSFS + + [ -z "$1" ] && return 0 + + if [ -n "$cgrouppath" ]; then + SYSFS="$cgrouppath/$1/tasks" + if [ -e $SYSFS ]; then + return $(wc -l < $SYSFS) + fi + fi + + for SYSFS in \ + /sys/fs/cgroup/"$1"/tasks \ + /sys/fs/cgroup/lxc/"$1"/tasks \ + /sys/fs/cgroup/systemd/lxc/"$1"/tasks \ + /sys/fs/cgroup/cpuacct/lxc/"$1"/tasks \ + /sys/fs/cgroup/cpuacct/sysdefault/lxc/"$1"/tasks + do + if [ -e $SYSFS ]; then + return $(wc -l < $SYSFS) + fi + done + + if [ -e /usr/bin/cgm ]; then + return $(cgm getvalue cpu "lxc/$1" tasks 2>/dev/null | wc -l) + fi + + return 0 +} + +active_guests=$(active_guests $exclude) + +if [ "$1" = "autoconf" ]; then + if [ ! -r /proc/net/dev ]; then + echo "no (/proc/net/dev cannot be read)" + exit 0 + fi + if [ ! -e "$lxcpath" ]; then + echo "no ($lxcpath is not present)" + exit 0 + fi + + echo yes +fi + +# --- CONFIG OUTPUT + +if [ "$1" = "config" ]; then + cat < Date: Sun, 11 Aug 2019 18:50:00 +0200 Subject: [PATCH 442/695] Sections and small fix --- plugins/lxc/lxc-multigraph | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index ee920004..4d79a3f4 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -82,6 +82,8 @@ or GPLv3 license or later, at your option lxcpath=${lxcpath:-/var/lib/lxc} +# --- FUNCTIONS + function active_guests { local g active i ok for g in $(lxc-ls | sort -u) @@ -161,8 +163,12 @@ lxc_count_processes () { return 0 } +# --- BASIC DEFINES + active_guests=$(active_guests $exclude) +# --- AUTOCONF + if [ "$1" = "autoconf" ]; then if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev cannot be read)" @@ -357,7 +363,7 @@ done echo "multigraph lxc_proc" for n in $active_guests do - count_processes "$n" + lxc_count_processes "$n" tmp=$? if [ $tmp -eq 0 ]; then tmp=$(lxc_cgroup -n "$n" tasks | wc -l) From c0fe6dc3c740ecde18a858a926d47470ed7c112e Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Sun, 11 Aug 2019 19:07:51 +0200 Subject: [PATCH 443/695] Compatibility with older field names --- plugins/lxc/lxc-multigraph | 87 ++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 4d79a3f4..5a0e206a 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -57,6 +57,9 @@ in each container, the users command. Tested on Debian buster and Debian jessie. +The double __ for values is for compatibility +with the previous implementation. + =head1 AUTHOR vajtsz vajtsz@gmail.com @@ -198,9 +201,9 @@ EOF for i in user system do cat < Date: Sun, 11 Aug 2019 23:31:16 +0200 Subject: [PATCH 444/695] Plugin lxc: unify whitespace --- plugins/lxc/lxc-multigraph | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 5a0e206a..1067ca15 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -110,11 +110,13 @@ function active_guests { echo $active } + function lxc_cgroup { # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' } + function lxc_netdev { local g=$1 dev @@ -128,6 +130,7 @@ function lxc_netdev { fi } + # find proper sysfs and count it # Debian 6.0: /sys/fs/cgroup//tasks # Ubuntu 12.04 with fstab: /sys/fs/cgroup/lxc//tasks @@ -166,6 +169,7 @@ lxc_count_processes () { return 0 } + # --- BASIC DEFINES active_guests=$(active_guests $exclude) @@ -218,7 +222,7 @@ graph_category lxc EOF for n in $active_guests - do + do cat < Date: Sun, 11 Aug 2019 23:36:12 +0200 Subject: [PATCH 445/695] Plugin lxc: exit after autoconf Previously processing continued after a successful autoconf operation. --- plugins/lxc/lxc-multigraph | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 1067ca15..37c4dcc3 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -179,14 +179,12 @@ active_guests=$(active_guests $exclude) if [ "$1" = "autoconf" ]; then if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev cannot be read)" - exit 0 - fi - if [ ! -e "$lxcpath" ]; then + elif [ ! -e "$lxcpath" ]; then echo "no ($lxcpath is not present)" - exit 0 + else + echo yes fi - - echo yes + exit 0 fi # --- CONFIG OUTPUT From ab6f4e3c6efcab485b6a0ba04757c4227f8e4d9f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 11 Aug 2019 23:40:21 +0200 Subject: [PATCH 446/695] Plugin lxc: fix configurable RAM graphing Previously (before the multigraph conversion) the configuration setting for "lxc_ram" was called "areastack". The multigraph conversion documented a new configuration setting "ram_areastack", but it was not used, yet. Thus we can switch to a more descriptive name: "ram_display_stacked". --- plugins/lxc/lxc-multigraph | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 37c4dcc3..5ea96a40 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -15,7 +15,7 @@ lxc-multigraph - Plugin to monitor LXC containers (v2.0) # memory usage of containers drawn as # stacked area charts (changes graph # order) -- defaults to false - # env.ram_areastack true + # env.ram_display_stacked true # lxc container path, default below #env.lxcpath /var/lib/lxc @@ -84,6 +84,8 @@ or GPLv3 license or later, at your option . $MUNIN_LIBDIR/plugins/plugin.sh lxcpath=${lxcpath:-/var/lib/lxc} +ram_display_stacked=${ram_display_stacked:-false} + # --- FUNCTIONS @@ -307,15 +309,16 @@ EOF for n in $active_guests do - cat < Date: Sun, 11 Aug 2019 23:52:33 +0200 Subject: [PATCH 447/695] Plugin lxc: draw stacked RAM graphs by default Previously "ram_display_stacked" defaulted to "false" (non-stacked graphs). The new default should be suitable for most situations. --- plugins/lxc/lxc-multigraph | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 5ea96a40..817e32e0 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -12,10 +12,10 @@ lxc-multigraph - Plugin to monitor LXC containers (v2.0) [lxc-multigraph] user root - # memory usage of containers drawn as - # stacked area charts (changes graph - # order) -- defaults to false - # env.ram_display_stacked true + # The memory usage of containers are by default drawn as stacked area + # charts. Alternatively a non-stacked graph with lines can be configured. + # Default: true + #env.ram_display_stacked true # lxc container path, default below #env.lxcpath /var/lib/lxc @@ -84,7 +84,7 @@ or GPLv3 license or later, at your option . $MUNIN_LIBDIR/plugins/plugin.sh lxcpath=${lxcpath:-/var/lib/lxc} -ram_display_stacked=${ram_display_stacked:-false} +ram_display_stacked=${ram_display_stacked:-true} # --- FUNCTIONS @@ -309,10 +309,10 @@ EOF for n in $active_guests do - if [ "$ram_display_stacked" = "true" ]; then - draw_style="AREASTACK" - else + if [ "$ram_display_stacked" != "true" ]; then draw_style="LINE1" + else + draw_style="AREASTACK" fi cat < Date: Sun, 11 Aug 2019 23:58:15 +0200 Subject: [PATCH 448/695] Plugin lxc: function "lxc_count_processes" now outputs its result Previously the result was returned as the exit code of the function (which would prevent the usage of "set -e"). Additionally the fallback value that was previously calculated outside of the function is now part of it. --- plugins/lxc/lxc-multigraph | 44 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 817e32e0..a06c1c0e 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -141,34 +141,37 @@ function lxc_netdev { # Ubuntu 14.04 /sys/fs/cgroup/systemd/lxc//tasks # and with cgmanager on jessie lxc_count_processes () { + local guest_name="$1" local SYSFS - [ -z "$1" ] && return 0 + [ -z "$guest_name" ] && return 0 if [ -n "$cgrouppath" ]; then - SYSFS="$cgrouppath/$1/tasks" + SYSFS="$cgrouppath/$guest_name/tasks" if [ -e $SYSFS ]; then - return $(wc -l < $SYSFS) + wc -l <"$SYSFS" + return fi fi for SYSFS in \ - /sys/fs/cgroup/"$1"/tasks \ - /sys/fs/cgroup/lxc/"$1"/tasks \ - /sys/fs/cgroup/systemd/lxc/"$1"/tasks \ - /sys/fs/cgroup/cpuacct/lxc/"$1"/tasks \ - /sys/fs/cgroup/cpuacct/sysdefault/lxc/"$1"/tasks + /sys/fs/cgroup/"$guest_name"/tasks \ + /sys/fs/cgroup/lxc/"$guest_name"/tasks \ + /sys/fs/cgroup/systemd/lxc/"$guest_name"/tasks \ + /sys/fs/cgroup/cpuacct/lxc/"$guest_name"/tasks \ + /sys/fs/cgroup/cpuacct/sysdefault/lxc/"$guest_name"/tasks do if [ -e $SYSFS ]; then - return $(wc -l < $SYSFS) + wc -l <"$SYSFS" + return fi done if [ -e /usr/bin/cgm ]; then - return $(cgm getvalue cpu "lxc/$1" tasks 2>/dev/null | wc -l) + cgm getvalue cpu "lxc/$guest_name" tasks 2>/dev/null | wc -l + else + lxc_cgroup -n "$guest_name" tasks | wc -l fi - - return 0 } @@ -359,24 +362,23 @@ for n in $active_guests do device=$(lxc_netdev "$n") if [ "$device" = "unknown" ]; then - continue + value_up="U" + value_down="U" + else + value_up=$(egrep "^ *${device}:" /proc/net/dev | awk '{print $10;}') + value_down=$(egrep "^ *${device}:" /proc/net/dev | awk '{print $2;}') fi cat < Date: Mon, 12 Aug 2019 00:02:46 +0200 Subject: [PATCH 449/695] Plugin lxc: explicitly assign all accepted environment variables --- plugins/lxc/lxc-multigraph | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index a06c1c0e..83a0598d 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -84,7 +84,11 @@ or GPLv3 license or later, at your option . $MUNIN_LIBDIR/plugins/plugin.sh lxcpath=${lxcpath:-/var/lib/lxc} +# containers to be ignored +exclude=${exclude:-} ram_display_stacked=${ram_display_stacked:-true} +# try to guess the location, if empty +cgrouppath=${cgrouppath:-} # --- FUNCTIONS From e91f7ecba76f4a83705434621d7fdebe590788b7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 00:12:11 +0200 Subject: [PATCH 450/695] Plugin lxc: remove ineffective usage of "bc" The command "wc -w" already outputs the final result. Thus we do not need this command (and do not have to depend on it). --- plugins/lxc/lxc-multigraph | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 83a0598d..ce759d04 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -52,8 +52,8 @@ it with: -- with 104 a valid lxc instance), if you get a warning, fix the config file. -For the logins graph, you need the bc command, and, -in each container, the users command. +For the logins graph, the "users" command is required in each +container. Tested on Debian buster and Debian jessie. @@ -358,7 +358,7 @@ done echo "multigraph lxc_logins" for n in $active_guests do - echo $(clean_fieldname "logins__${n}").value $(lxc-attach -n "$n" users | wc -w | bc) + echo $(clean_fieldname "logins__${n}").value $(lxc-attach -n "$n" users | wc -w) done echo "multigraph lxc_net" From 0d593014b4baf33c5c12b88cbac83df4b49fccf2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 00:17:15 +0200 Subject: [PATCH 451/695] Plugin lxc: fix printing of "${graph_period}" This specific text token ("${graph_period}") is interpreted by munin and should not be interpreted by the shell. Thus it needs to be escaped. --- plugins/lxc/lxc-multigraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index ce759d04..24cda4bc 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -257,7 +257,7 @@ EOF multigraph lxc_net graph_title Network traffic graph_args --base 1000 -graph_vlabel bits in (-) / out (+) per ${graph_period} +graph_vlabel bits in (-) / out (+) per \${graph_period} graph_category lxc graph_info This graph shows the traffic of active LXC containers. EOF From 89003f01882e89e78b6bd3a6a1694f7e927f9e4e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 00:38:38 +0200 Subject: [PATCH 452/695] Plugin lxc: fix quoting issues --- plugins/lxc/lxc-multigraph | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 24cda4bc..e238d2d0 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -81,7 +81,8 @@ or GPLv3 license or later, at your option =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" + lxcpath=${lxcpath:-/var/lib/lxc} # containers to be ignored @@ -106,14 +107,14 @@ function active_guests { fi done - if [ $ok = 1 ]; then - if lxc-info -n $g 2>&1 | grep -qs RUNNING; then + if [ "$ok" = 1 ]; then + if lxc-info -n "$g" 2>&1 | grep -qs RUNNING; then active="$active $g" fi fi done - echo $active + echo "$active" } @@ -126,10 +127,10 @@ function lxc_cgroup { function lxc_netdev { local g=$1 dev - if [ -f $lxcpath/$g/config ]; then + if [ -f "$lxcpath/$g/config" ]; then # lxc 3 vs < 3 - (egrep '^lxc.net.0.veth.pair' $lxcpath/$g/config 2>/dev/null \ - || egrep '^lxc.network.veth.pair' $lxcpath/$g/config + (egrep '^lxc.net.0.veth.pair' "$lxcpath/$g/config" 2>/dev/null \ + || egrep '^lxc.network.veth.pair' "$lxcpath/$g/config" ) | awk '{print $NF;}' else echo unknown @@ -152,20 +153,20 @@ lxc_count_processes () { if [ -n "$cgrouppath" ]; then SYSFS="$cgrouppath/$guest_name/tasks" - if [ -e $SYSFS ]; then + if [ -e "$SYSFS" ]; then wc -l <"$SYSFS" return fi fi for SYSFS in \ - /sys/fs/cgroup/"$guest_name"/tasks \ - /sys/fs/cgroup/lxc/"$guest_name"/tasks \ - /sys/fs/cgroup/systemd/lxc/"$guest_name"/tasks \ - /sys/fs/cgroup/cpuacct/lxc/"$guest_name"/tasks \ - /sys/fs/cgroup/cpuacct/sysdefault/lxc/"$guest_name"/tasks + "/sys/fs/cgroup/$guest_name/tasks" \ + "/sys/fs/cgroup/lxc/$guest_name/tasks" \ + "/sys/fs/cgroup/systemd/lxc/$guest_name/tasks" \ + "/sys/fs/cgroup/cpuacct/lxc/$guest_name/tasks" \ + "/sys/fs/cgroup/cpuacct/sysdefault/lxc/$guest_name/tasks" do - if [ -e $SYSFS ]; then + if [ -e "$SYSFS" ]; then wc -l <"$SYSFS" return fi @@ -212,7 +213,7 @@ EOF for i in user system do cat < Date: Mon, 12 Aug 2019 00:40:15 +0200 Subject: [PATCH 453/695] Plugin lxc: simplify interface of "lxc_netdev" --- plugins/lxc/lxc-multigraph | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index e238d2d0..177c681d 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -132,8 +132,6 @@ function lxc_netdev { (egrep '^lxc.net.0.veth.pair' "$lxcpath/$g/config" 2>/dev/null \ || egrep '^lxc.network.veth.pair' "$lxcpath/$g/config" ) | awk '{print $NF;}' - else - echo unknown fi } @@ -266,7 +264,7 @@ EOF for n in $active_guests do device=$(lxc_netdev "$n") - if [ "$device" = "unknown" ]; then + if [ -z "$device" ]; then continue fi bps="U" @@ -366,7 +364,7 @@ echo "multigraph lxc_net" for n in $active_guests do device=$(lxc_netdev "$n") - if [ "$device" = "unknown" ]; then + if [ -z "$device" ]; then value_up="U" value_down="U" else From 914d1c5f25d00c735cc0e18d83ceff1fac26247f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 00:43:25 +0200 Subject: [PATCH 454/695] Plugin lxc: clarify network bandwidth calculation --- plugins/lxc/lxc-multigraph | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 177c681d..183fcc8f 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -269,7 +269,8 @@ EOF fi bps="U" if [ -r "/sys/class/net/$device/speed" ]; then - bps=$(($(cat "/sys/class/net/$device/speed") * 1000 * 1000)) + megabit_per_second=$(cat "/sys/class/net/$device/speed") + bps=$((megabit_per_second * 1000 * 1000)) fi cat < Date: Mon, 12 Aug 2019 00:47:53 +0200 Subject: [PATCH 455/695] Plugin lxc: omit output of "max" if the network interface speed is unknown The "U" value is not defined for the "max" field. --- plugins/lxc/lxc-multigraph | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 183fcc8f..6bd54a7a 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -267,25 +267,26 @@ EOF if [ -z "$device" ]; then continue fi - bps="U" - if [ -r "/sys/class/net/$device/speed" ]; then - megabit_per_second=$(cat "/sys/class/net/$device/speed") - bps=$((megabit_per_second * 1000 * 1000)) - fi cat < Date: Mon, 12 Aug 2019 00:49:27 +0200 Subject: [PATCH 456/695] Plugin lxc: clarify variable names used in functions --- plugins/lxc/lxc-multigraph | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 6bd54a7a..312442eb 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -95,21 +95,21 @@ cgrouppath=${cgrouppath:-} # --- FUNCTIONS function active_guests { - local g active i ok - for g in $(lxc-ls | sort -u) + local guest_name active i ok + for guest_name in $(lxc-ls | sort -u) do # handle optional exclude list in $1 ok=1 for i in $1 do - if [ "$i" = "$g" ]; then + if [ "$i" = "$guest_name" ]; then ok=0 fi done if [ "$ok" = 1 ]; then - if lxc-info -n "$g" 2>&1 | grep -qs RUNNING; then - active="$active $g" + if lxc-info -n "$guest_name" 2>&1 | grep -qs RUNNING; then + active="$active $guest_name" fi fi done @@ -125,12 +125,12 @@ function lxc_cgroup { function lxc_netdev { - local g=$1 dev + local guest_name="$1" - if [ -f "$lxcpath/$g/config" ]; then + if [ -f "$lxcpath/$guest_name/config" ]; then # lxc 3 vs < 3 - (egrep '^lxc.net.0.veth.pair' "$lxcpath/$g/config" 2>/dev/null \ - || egrep '^lxc.network.veth.pair' "$lxcpath/$g/config" + (egrep '^lxc.net.0.veth.pair' "$lxcpath/$guest_name/config" 2>/dev/null \ + || egrep '^lxc.network.veth.pair' "$lxcpath/$guest_name/config" ) | awk '{print $NF;}' fi } From 6413375cdc9b5e663bec2e8494e92a09f7338f3d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 00:56:24 +0200 Subject: [PATCH 457/695] Plugin lxc: simplify processing in function "active_guests" --- plugins/lxc/lxc-multigraph | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 312442eb..aa63f679 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -95,7 +95,7 @@ cgrouppath=${cgrouppath:-} # --- FUNCTIONS function active_guests { - local guest_name active i ok + local guest_name i ok for guest_name in $(lxc-ls | sort -u) do # handle optional exclude list in $1 @@ -109,12 +109,10 @@ function active_guests { if [ "$ok" = 1 ]; then if lxc-info -n "$guest_name" 2>&1 | grep -qs RUNNING; then - active="$active $guest_name" + echo "$guest_name" fi fi done - - echo "$active" } From 276e96c0ddf458d06f156b2bf17d825830c5c925 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:04:09 +0200 Subject: [PATCH 458/695] Plugin lxc: remove ineffective "sort -u" from function "active_guests" The output of "lxc-ls" is not line-splitted. Thus all active guests are printed in a single line and "sort -u" (removal of non-unique entries) had no effect. --- plugins/lxc/lxc-multigraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index aa63f679..72eeb382 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -96,7 +96,7 @@ cgrouppath=${cgrouppath:-} function active_guests { local guest_name i ok - for guest_name in $(lxc-ls | sort -u) + for guest_name in $(lxc-ls) do # handle optional exclude list in $1 ok=1 From 46626b9953a72828c97c3e20c7060d08baaf9cfb Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:08:22 +0200 Subject: [PATCH 459/695] Plugin lxc: simplify implementation of function "active_guests" --- plugins/lxc/lxc-multigraph | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 72eeb382..f9521971 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -95,20 +95,13 @@ cgrouppath=${cgrouppath:-} # --- FUNCTIONS function active_guests { - local guest_name i ok + local excludes="$1" + local guest_name for guest_name in $(lxc-ls) do # handle optional exclude list in $1 - ok=1 - for i in $1 - do - if [ "$i" = "$guest_name" ]; then - ok=0 - fi - done - - if [ "$ok" = 1 ]; then - if lxc-info -n "$guest_name" 2>&1 | grep -qs RUNNING; then + if ! echo "$excludes" | grep -qwF "$guest_name"; then + if lxc-info -n "$guest_name" --state 2>/dev/null | grep -qw RUNNING; then echo "$guest_name" fi fi From 199b4517cb541c66ecb6835d6faf06b45e3c24f1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:11:31 +0200 Subject: [PATCH 460/695] Plugin lxc: use "grep -E" instead of "egrep" --- plugins/lxc/lxc-multigraph | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index f9521971..220e7bb4 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -120,8 +120,8 @@ function lxc_netdev { if [ -f "$lxcpath/$guest_name/config" ]; then # lxc 3 vs < 3 - (egrep '^lxc.net.0.veth.pair' "$lxcpath/$guest_name/config" 2>/dev/null \ - || egrep '^lxc.network.veth.pair' "$lxcpath/$guest_name/config" + (grep -EF '^lxc.net.0.veth.pair' "$lxcpath/$guest_name/config" 2>/dev/null \ + || grep -EF '^lxc.network.veth.pair' "$lxcpath/$guest_name/config" ) | awk '{print $NF;}' fi } @@ -361,8 +361,8 @@ do value_up="U" value_down="U" else - value_up=$(egrep "^ *${device}:" /proc/net/dev | awk '{print $10;}') - value_down=$(egrep "^ *${device}:" /proc/net/dev | awk '{print $2;}') + value_up=$(grep -E "^ *${device}:" /proc/net/dev | awk '{print $10;}') + value_down=$(grep -E "^ *${device}:" /proc/net/dev | awk '{print $2;}') fi cat < Date: Mon, 12 Aug 2019 01:12:29 +0200 Subject: [PATCH 461/695] Plugin lxc: fix handling of "excludes" parameter Previously only the first item in the "excludes" environment variable was really excluded. --- plugins/lxc/lxc-multigraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 220e7bb4..417a42e5 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -171,7 +171,7 @@ lxc_count_processes () { # --- BASIC DEFINES -active_guests=$(active_guests $exclude) +active_guests=$(active_guests "$exclude") # --- AUTOCONF From 33b2f3ed8b3b6ab8b10492433e2bf704c95bf435 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:23:05 +0200 Subject: [PATCH 462/695] Plugin lxc: use comprehensible names for variables --- plugins/lxc/lxc-multigraph | 116 ++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 417a42e5..dd337cae 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -197,14 +197,14 @@ graph_vlabel USER_HZ graph_category lxc EOF - for n in $active_guests + for guest_name in $active_guests do - for i in user system + for cpu_usage in user system do cat < Date: Mon, 12 Aug 2019 01:27:39 +0200 Subject: [PATCH 463/695] Plugin lxc: simplify interface of "lxc_cgroup" and rename function --- plugins/lxc/lxc-multigraph | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index dd337cae..2c699538 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -109,9 +109,11 @@ function active_guests { } -function lxc_cgroup { +function get_lxc_cgroup_info { + local guest_name="$1" + local field="$2" # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) - lxc-cgroup -o /dev/stdout -l INFO $* | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' + lxc-cgroup -o /dev/stdout -l INFO -n "$guest_name" "$field" | sed 's/^.*lxc_cgroup.c:main:[0-9][0-9]* - //' } @@ -164,7 +166,7 @@ lxc_count_processes () { if [ -e /usr/bin/cgm ]; then cgm getvalue cpu "lxc/$guest_name" tasks 2>/dev/null | wc -l else - lxc_cgroup -n "$guest_name" tasks | wc -l + get_lxc_cgroup_info "$guest_name" "tasks" | wc -l fi } @@ -337,14 +339,14 @@ for guest_name in $active_guests do for cpu_usage in user system do - echo "$(clean_fieldname "cpu_${cpu_usage}__${guest_name}").value $(lxc_cgroup -n "$guest_name" cpuacct.stat | grep "$cpu_usage" | awk '{ print $2; }')" + echo "$(clean_fieldname "cpu_${cpu_usage}__${guest_name}").value $(get_lxc_cgroup_info "$guest_name" "cpuacct.stat" | grep "$cpu_usage" | awk '{ print $2; }')" done done echo "multigraph lxc_cpu_time" for guest_name in $active_guests do - echo "$(clean_fieldname "cpu_time__${guest_name}").value $(lxc_cgroup -n "$guest_name" cpuacct.usage)" + echo "$(clean_fieldname "cpu_time__${guest_name}").value $(get_lxc_cgroup_info "$guest_name" "cpuacct.usage")" done echo "multigraph lxc_logins" @@ -381,9 +383,9 @@ echo "multigraph lxc_ram" for guest_name in $active_guests do cat < Date: Mon, 12 Aug 2019 01:38:16 +0200 Subject: [PATCH 464/695] Plugin lxc: separate function for title beautification This avoids a bashism. --- plugins/lxc/lxc-multigraph | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 2c699538..f97f925f 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -171,6 +171,13 @@ lxc_count_processes () { } +# change the first character of a string to upper case +title_case() { + local text="$1" + printf "%s%s" "$(echo "$text" | cut -c 1 | tr "[:lower:]" "[:upper:]")" "$(echo "$text" | cut -c 2-)" +} + + # --- BASIC DEFINES active_guests=$(active_guests "$exclude") @@ -204,7 +211,7 @@ EOF for cpu_usage in user system do cat < Date: Mon, 12 Aug 2019 01:39:30 +0200 Subject: [PATCH 465/695] Plugin lxc: change the plugin from "bash" to "sh" No more bashisms were left. --- plugins/lxc/lxc-multigraph | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index f97f925f..0becc68e 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # -*- sh -*- : << =cut @@ -94,7 +94,7 @@ cgrouppath=${cgrouppath:-} # --- FUNCTIONS -function active_guests { +active_guests() { local excludes="$1" local guest_name for guest_name in $(lxc-ls) @@ -109,7 +109,7 @@ function active_guests { } -function get_lxc_cgroup_info { +get_lxc_cgroup_info() { local guest_name="$1" local field="$2" # lxc3 (lxc < 3: may output some warnings if there is cruft in your config dir) @@ -117,7 +117,7 @@ function get_lxc_cgroup_info { } -function lxc_netdev { +lxc_netdev() { local guest_name="$1" if [ -f "$lxcpath/$guest_name/config" ]; then From 4356a024a7c018206f26f253a0bcf0c7eb1e623d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:42:52 +0200 Subject: [PATCH 466/695] Plugin lxc: clarify name for function "active_guests" Previously a variable and a function used the same name. This was no problem, but it can cause confusion. --- plugins/lxc/lxc-multigraph | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 0becc68e..70f7abad 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -94,7 +94,7 @@ cgrouppath=${cgrouppath:-} # --- FUNCTIONS -active_guests() { +get_active_guests() { local excludes="$1" local guest_name for guest_name in $(lxc-ls) @@ -180,7 +180,7 @@ title_case() { # --- BASIC DEFINES -active_guests=$(active_guests "$exclude") +active_guests=$(get_active_guests "$exclude") # --- AUTOCONF From f00f28cf6fadb589ecea14305cf22e12002838f3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:49:48 +0200 Subject: [PATCH 467/695] Plugin lxc: use functions for top-level activities This clarifies the flow of control and allows to use MUNIN_CAP_DIRTYCONFIG later. --- plugins/lxc/lxc-multigraph | 134 +++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 59 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 70f7abad..d273b543 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -178,13 +178,7 @@ title_case() { } -# --- BASIC DEFINES - -active_guests=$(get_active_guests "$exclude") - -# --- AUTOCONF - -if [ "$1" = "autoconf" ]; then +do_autoconf() { if [ ! -r /proc/net/dev ]; then echo "no (/proc/net/dev cannot be read)" elif [ ! -e "$lxcpath" ]; then @@ -192,12 +186,13 @@ if [ "$1" = "autoconf" ]; then else echo yes fi - exit 0 -fi +} -# --- CONFIG OUTPUT -if [ "$1" = "config" ]; then +do_config() { + local active_guests guest_name draw_style + active_guests=$(get_active_guests "$exclude") + cat <&2 "Invalid action requested (none of: autoconf / config / '')" + exit 1 +esac From 59cb851cf5ed377f3c4164c63535335cd7d0421e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:51:43 +0200 Subject: [PATCH 468/695] Plugin lxc: add support for MUNIN_CAP_DIRTYCONFIG --- plugins/lxc/lxc-multigraph | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index d273b543..bc7866de 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -402,6 +402,7 @@ case "${1:-}" in ;; config) do_config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = 1 ]; then do_fetch; fi exit 0 ;; "") From 856572ad9b02d39d46cb8f333f956e893323cbfd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:02:31 +0200 Subject: [PATCH 469/695] Plugin lxc: verify the availability of "lxc-ls" Previously "autoconf" only verified, that /var/lib/lxc exists. But this could be just a remainder of a formerly installed lxc package. --- plugins/lxc/lxc-multigraph | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index bc7866de..102349e9 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -183,6 +183,8 @@ do_autoconf() { echo "no (/proc/net/dev cannot be read)" elif [ ! -e "$lxcpath" ]; then echo "no ($lxcpath is not present)" + elif [ -z "$(which lxc-ls)" ]; then + echo "no ('lxc-ls' is not available in PATH)" else echo yes fi From 3f8ef105ff86465321b8f0831d147f3afb45e940 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:05:33 +0200 Subject: [PATCH 470/695] Plugin lxc: enable "set -eu" for better error handling The code should be suitable for "exit-on-error" and "error-on-unset". --- plugins/lxc/lxc-multigraph | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 102349e9..4b2bbe67 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -81,6 +81,9 @@ or GPLv3 license or later, at your option =cut +set -eu + + . "$MUNIN_LIBDIR/plugins/plugin.sh" From f69587c28cb51dd78aa3377a7e9ae05a4d84f476 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:14:30 +0200 Subject: [PATCH 471/695] Plugin lxc: move to category "virtualization" See http://guide.munin-monitoring.org/en/latest/reference/graph-category.html#well-known-categories --- plugins/lxc/lxc-multigraph | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 4b2bbe67..7cffcfb1 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -203,7 +203,7 @@ multigraph lxc_cpu graph_title CPU Usage graph_args -l 0 --base 1000 graph_vlabel USER_HZ -graph_category lxc +graph_category virtualization EOF for guest_name in $active_guests @@ -224,7 +224,7 @@ multigraph lxc_cpu_time graph_title CPU time graph_args -l 0 --base 1000 graph_vlabel nanosec -graph_category lxc +graph_category virtualization EOF for guest_name in $active_guests @@ -240,7 +240,7 @@ EOF multigraph lxc_logins graph_title Logins -graph_category lxc +graph_category virtualization EOF for guest_name in $active_guests @@ -257,7 +257,7 @@ multigraph lxc_net graph_title Network traffic graph_args --base 1000 graph_vlabel bits in (-) / out (+) per \${graph_period} -graph_category lxc +graph_category virtualization graph_info This graph shows the traffic of active LXC containers. EOF @@ -295,7 +295,7 @@ multigraph lxc_proc graph_title Processes graph_args -l 0 --base 1000 graph_vlabel Number of processes -graph_category lxc +graph_category virtualization EOF for guest_name in $active_guests do @@ -312,7 +312,7 @@ multigraph lxc_ram graph_title Memory graph_args -l 0 --base 1024 graph_vlabel byte -graph_category lxc +graph_category virtualization EOF for guest_name in $active_guests From 2f1be9f90810976f225ceee01ab04d78418d1526 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:16:54 +0200 Subject: [PATCH 472/695] Plugin lxc: update list of authors --- plugins/lxc/lxc-multigraph | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 7cffcfb1..0b6c85d7 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -65,6 +65,7 @@ with the previous implementation. vajtsz vajtsz@gmail.com mitty mitty@mitty.jp alphanet schaefer@alphanet.ch (many changes and multigraph) +Lars Kruse See https://framagit.org/alphanet/various-stuff (plugins/lcx) From 3ebcb8b5500e664c36c5d09892af815dc428efac Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:17:06 +0200 Subject: [PATCH 473/695] Plugin lxc: remove reference to framagit repository Plugins maintained within the contrib repository of munin should not refer to other locations as their primary source. (otherwise they should be maintained in that primary source and not in the contrib repository) --- plugins/lxc/lxc-multigraph | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc-multigraph index 0b6c85d7..90770d47 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc-multigraph @@ -67,9 +67,6 @@ mitty mitty@mitty.jp alphanet schaefer@alphanet.ch (many changes and multigraph) Lars Kruse -See https://framagit.org/alphanet/various-stuff -(plugins/lcx) - =head1 LICENSE 2-clause BSD License From 858f3e3af439284ab6a73a030fd7a19bbcec8770 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 01:55:53 +0200 Subject: [PATCH 474/695] Plugin lxc: rename to "lxc_guests" The plugin handles details of LXC guests. Thus we should leave the remainder of the namespace to other plugins. --- plugins/lxc/{lxc-multigraph => lxc_guests} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename plugins/lxc/{lxc-multigraph => lxc_guests} (99%) diff --git a/plugins/lxc/lxc-multigraph b/plugins/lxc/lxc_guests similarity index 99% rename from plugins/lxc/lxc-multigraph rename to plugins/lxc/lxc_guests index 90770d47..6d42bedb 100755 --- a/plugins/lxc/lxc-multigraph +++ b/plugins/lxc/lxc_guests @@ -5,11 +5,11 @@ =head1 NAME -lxc-multigraph - Plugin to monitor LXC containers (v2.0) +lxc_guests - collect statistics about containers virtualized via LXC =head1 CONFIGURATION - [lxc-multigraph] + [lxc_guests] user root # The memory usage of containers are by default drawn as stacked area From 6e452ac6dc480300780768bc6b7586a9abc0a854 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 02:23:48 +0200 Subject: [PATCH 475/695] Plugin lxc: remove fieldname compatibility attempt with double underscores The attempt of compatibility (introduced in c0fe6dc3c) did not work, since the previous implementation *replaced* the first character (if it was invalid - e.g. a digit) instead of prepending an underscore. --- plugins/lxc/lxc_guests | 86 +++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/plugins/lxc/lxc_guests b/plugins/lxc/lxc_guests index 6d42bedb..0b201a3d 100755 --- a/plugins/lxc/lxc_guests +++ b/plugins/lxc/lxc_guests @@ -57,8 +57,6 @@ container. Tested on Debian buster and Debian jessie. -The double __ for values is for compatibility -with the previous implementation. =head1 AUTHOR @@ -209,9 +207,9 @@ EOF for cpu_usage in user system do cat < Date: Tue, 24 Jul 2018 02:25:33 +0200 Subject: [PATCH 476/695] travis: install requirements via apt --- .travis.yml | 113 ++++++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5ee5998..81734ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,65 +1,76 @@ --- dist: bionic -install: - - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python python3 ruby php-cli gawk ksh zsh pylint shellcheck - - 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 - - sudo apt-get --no-install-recommends install libsys-virt-perl +addons: + apt: + packages: + - devscripts + - python + - python3 + - ruby + - php5-cli + - gawk + - ksh + - zsh + - pylint + - shellcheck + - pkg-config + - libdb-dev + - libvirt-dev + - libexpat-dev - # we use locally installed cpan modules instead of travis' builtin 'homebrew' perl support - # The homebrew variation does not work for us, since: - # * it is built without threading - # * the 'libvirt' plugin requires Sys::Virt, which is not available via cpan anymore - thus we need to install the Debian package 'libsys-virt-perl', which was built with threading + # Munin/Plugin.pm is in "munin-node" on precise + - munin-node + + # Modules used by test script + - libsys-virt-perl + - libcapture-tiny-perl + + # Modules used by plugins + - libberkeleydb-perl + - libcache-memcached-perl + - libgraphics-colornames-www-perl + - libdbd-pg-perl + - libdata-dump-perl + - libdate-manip-perl + - libdatetime-format-iso8601-perl + - libdevice-serialport-perl + - libfile-readbackwards-perl + - libgraphics-colorobject-perl + - libipc-run3-perl + - libipc-sharelite-perl + - libjson-perl + - libjson-any-perl + - libmail-sendmail-perl + - libmodern-perl-perl + - libmoosex-poe-perl + - libnet-dns-perl + - libnet-openssh-perl + - libnet-snmp-perl + - libnet-telnet-perl + - libnet-telnet-cisco-perl + - libpoe-perl + - libproc-processtable-perl + - libredis-perl + - libswitch-perl + - libtext-iconv-perl + - libwww-mechanize-treebuilder-perl + - libyaml-perl + - libxml-libxml-perl + - libxml-simple-perl + - libxml-smart-perl + - libxml-twig-perl + - libexperimental-perl + +before_install: - cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) - - # Modules used by test script - - cpanm --notest Capture::Tiny - cpanm --notest File::Find - cpanm --notest Test::More - - # Modules used by plugins + # more Modules used by plugins - cpanm --notest Asterisk::AMI - - cpanm --notest BerkeleyDB - - cpanm --notest Cache::Memcached - - cpanm --notest DBD::Pg - - cpanm --notest Data::Dump - - cpanm --notest Date::Manip - cpanm --notest Date::Parse - - cpanm --notest DateTime::Format::ISO8601 - - cpanm --notest Device::SerialPort - cpanm --notest FCGI::Client - - cpanm --notest File::ReadBackwards - - cpanm --notest Graphics::ColorObject - - cpanm --notest Graphics::ColorNames::HTML - - cpanm --notest Graphics::ColorNames::Windows - - cpanm --notest IPC::Run3 - - cpanm --notest IPC::ShareLite - - cpanm --notest JSON - - cpanm --notest JSON::Any - - cpanm --notest Mail::Sendmail - - cpanm --notest Modern::Perl - - cpanm --notest MooseX::POE - - cpanm --notest Net::DNS - - cpanm --notest Net::OpenSSH - - cpanm --notest Net::SNMP - - cpanm --notest Net::Telnet - - cpanm --notest Net::Telnet::Cisco - - cpanm --notest POE - cpanm --notest POE::Component::IRC - cpanm --notest POE::Quickie - - cpanm --notest Proc::ProcessTable - - cpanm --notest Redis - - cpanm --notest Switch - - cpanm --notest Text::Iconv - - cpanm --notest WWW::Mechanize::TreeBuilder - - cpanm --notest YAML - - cpanm --notest XML::LibXML - - cpanm --notest XML::Simple - - cpanm --notest XML::Smart - - cpanm --notest XML::Twig - - cpanm --notest experimental # Modules used bu plugins, but not compiling from CPAN # - cpanm --notest nvidia::ml # Modules used by plugins, but missing on cpan From 64c755a053b1f342c71bd4a0ccb624f3e83a5343 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 05:27:24 +0200 Subject: [PATCH 477/695] travis: add more missing packages --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 81734ec0..88f6d03b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,10 +49,12 @@ addons: - libnet-telnet-perl - libnet-telnet-cisco-perl - libpoe-perl + - libpoe-component-irc-perl - libproc-processtable-perl - libredis-perl - libswitch-perl - libtext-iconv-perl + - libwww-mechanize-perl - libwww-mechanize-treebuilder-perl - libyaml-perl - libxml-libxml-perl From 9695f9d635e4ffcc4f3a6d8136c4c320eb5c1d92 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 05:37:38 +0200 Subject: [PATCH 478/695] travis: update packages to Ubuntu Bionic --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 88f6d03b..ea1aab50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ addons: - python - python3 - ruby - - php5-cli + - php-cli - gawk - ksh - zsh From fe379b6f0590eb58a3c5273a5f47fae9aeb6e4f5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 12 Aug 2019 05:49:33 +0200 Subject: [PATCH 479/695] Plugin amr: fix regex issue An unescpaed left brace is rejected by recent versions of perl. --- plugins/amr/amr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/amr/amr b/plugins/amr/amr index e6ae91e9..c200ccec 100755 --- a/plugins/amr/amr +++ b/plugins/amr/amr @@ -133,7 +133,7 @@ sub parse while (<$log>) { # \d protects us against HTML injection here, be careful when changing - if (m,SCM:{ID:(\d+) +.* +Consumption: +(\d+) +,) { + if (m,SCM:\{ID:(\d+) +.* +Consumption: +(\d+) +,) { $stations{$1} = $2; $signals{$1}++; } From 5633f925bf026ddacd6d583b37e162b0acae7b82 Mon Sep 17 00:00:00 2001 From: mafri <47006694+mafri27@users.noreply.github.com> Date: Mon, 12 Aug 2019 09:51:48 +0200 Subject: [PATCH 480/695] create icinga plugin to monitor check results --- plugins/icinga/icinga_checks | 112 +++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 plugins/icinga/icinga_checks diff --git a/plugins/icinga/icinga_checks b/plugins/icinga/icinga_checks new file mode 100644 index 00000000..9e835b09 --- /dev/null +++ b/plugins/icinga/icinga_checks @@ -0,0 +1,112 @@ +#!/bin/sh + +: << =cut +=head1 NAME + +icinga_checks - Plugin to monitor results of icinga monitoring + +=head1 CONFIGURATION + +No configuration + +=head1 AUTHOR + +mafri with help by sumpfralle and ndo84bw + +=head1 LICENSE + +GPLv3 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +ICINGACLI=${ICINGACLI:-$(command -v icingacli)} +JQ=${JQ:-$(command -v jq)} + + +if [ "$1" = "autoconf" ] ; then + if [ ! -x "$ICINGACLI" ]; then + echo "no (could not find 'icingacli')" + elif [ ! -x "$JQ" ]; then + echo "no (could not find 'jq')" + else + echo "yes" + fi + exit +fi + +set -e + +if [ "$1" = "config" ]; then + + echo "multigraph icinga_host_checks" + echo "graph_title Icinga Host Checks" + echo 'graph_args --base 1000' + echo 'graph_vlabel Count' + echo 'graph_category icinga' + echo "up.label Up" + echo "down.label Down" + echo "unreachable.label Unreachable" + echo "pending.label Pending" + echo "up.draw AREA" + echo "down.draw STACK" + echo "unreachable.draw STACK" + echo "pending.draw STACK" + + + echo "multigraph icinga_service_checks" + echo "graph_title Icinga Service Checks" + echo 'graph_args --base 1000' + echo 'graph_vlabel Count' + echo 'graph_category icinga' + echo "ok.label Ok" + echo "warning.label Warning" + echo "critical.label Critical" + echo "unknown.label Unknown" + echo "pending.label Pending" + echo "ok.draw AREA" + echo "warning.draw STACK" + echo "critical.draw STACK" + echo "unknown.draw STACK" + echo "pending.draw STACK" + + exit +fi + +if [ ! -x "$ICINGACLI" ]; then + echo "could not find 'icingacli'" >&2 + exit 1 +elif [ ! -x "$JQ" ]; then + echo "could not find 'jq'" >&2 + exit 1 +fi + +output=$("$ICINGACLI" monitoring list hosts --format=json) +host_up=$( echo "$output" | "$JQ" -r '.[] | select(.host_state == 0) | .host_name' | wc -l ) +host_down=$( echo "$output" | "$JQ" -r '.[] | select(.host_state == 1) | .host_name' | wc -l ) +host_pend=$( echo "$output" | "$JQ" -r '.[] | select(.host_state == 2) | .host_name' | wc -l ) +host_unre=$( echo "$output" | "$JQ" -r '.[] | select(.host_state == 3) | .host_name' | wc -l ) + +echo "multigraph icinga_host_checks" +echo "up.value $host_up" +echo "down.value $host_down" +echo "pending.value $host_pend" +echo "unreachable.value $host_unre" + +output=$("$ICINGACLI" monitoring list services --format=json) +service_ok=$( echo "$output" | "$JQ" -r '.[] | select(.service_state == 0) | .host_name + .service_name' | wc -l ) +service_warn=$(echo "$output" | "$JQ" -r '.[] | select(.service_state == 1) | .host_name + .service_name' | wc -l ) +service_crit=$(echo "$output" | "$JQ" -r '.[] | select(.service_state == 2) | .host_name + .service_name' | wc -l ) +service_pend=$(echo "$output" | "$JQ" -r '.[] | select(.service_state == 3) | .host_name + .service_name' | wc -l ) +service_unkn=$(echo "$output" | "$JQ" -r '.[] | select(.service_state == 4) | .host_name + .service_name' | wc -l ) + +echo "multigraph icinga_service_checks" +echo "ok.value $service_ok" +echo "warning.value $service_warn" +echo "critical.value $service_crit" +echo "unknown.value $service_unkn" +echo "pending.value $service_pend" From bc2f7098e2afbfb3502cad3415ee90525dbb35fc Mon Sep 17 00:00:00 2001 From: mafri Date: Mon, 12 Aug 2019 10:21:21 +0200 Subject: [PATCH 481/695] set execute to icinga_checks --- plugins/icinga/icinga_checks | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 plugins/icinga/icinga_checks diff --git a/plugins/icinga/icinga_checks b/plugins/icinga/icinga_checks old mode 100644 new mode 100755 From 7fed3b9765978940ac17359b059a5f6e737e91fb Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 03:59:54 +0200 Subject: [PATCH 482/695] Fix multiple shellcheck issues --- plugins/debian/deborphan | 2 +- plugins/debian/debsecan_ | 2 +- plugins/ejabberd/ejabberd_stats_ | 4 ++-- plugins/ilias/ilias_session | 3 +++ plugins/minecraft/minecraft-users-ram_ | 2 +- plugins/munin/munin_events | 2 +- plugins/nfs-freebsd/nfs_client | 10 +++++----- plugins/nfs-freebsd/nfs_client_cache | 10 +++++----- plugins/nfs-freebsd/nfsd | 10 +++++----- plugins/nscd/nscd_ | 18 +++++++++++------- plugins/quake/qstatcod4and5_ | 2 +- plugins/rabbitmq/rabbitmq_connections | 2 +- plugins/raspberry-pi/raspi_temp | 2 +- plugins/ssl/ssl-certificate-expiry | 2 +- plugins/ssl/ssl_ | 2 +- plugins/syncthing/strelaysrv_ | 16 ++++++++-------- plugins/syncthing/syncthing_ | 14 +++++++------- plugins/zfs/zfs-filesystem-graph | 6 ++++-- 18 files changed, 59 insertions(+), 50 deletions(-) diff --git a/plugins/debian/deborphan b/plugins/debian/deborphan index 10d5ac44..633ac7df 100755 --- a/plugins/debian/deborphan +++ b/plugins/debian/deborphan @@ -76,7 +76,7 @@ EOF else for CAT in ${CATEGORIES}; do CATFIELD=$(clean_fieldname "${CAT}") - CATDATA=$(echo "${OUT}" | sed -n "s#${CAT} \+##p") + CATDATA=$(echo "${OUT}" | sed -n 's#'"${CAT}"' \+##p') echo "${CATFIELD}.value $(echo "${CATDATA}" | wc -l)" echo "${CATFIELD}.extinfo $(echo "${CATDATA}" | tr '\n' ' ')" done diff --git a/plugins/debian/debsecan_ b/plugins/debian/debsecan_ index c78a797d..7ef84e1d 100755 --- a/plugins/debian/debsecan_ +++ b/plugins/debian/debsecan_ @@ -169,7 +169,7 @@ case "${MODE}" in esac # Reformat the output of the cut|sort|uniq... to a more human-friendly "item (count)" format -CVECOUNTRE="s/^ *\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/" +CVECOUNTRE='s/^ *\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/' # shellcheck disable=SC2005 disable=SC2046 # The nested $(echo ...)s are needed to yet the newlines diff --git a/plugins/ejabberd/ejabberd_stats_ b/plugins/ejabberd/ejabberd_stats_ index 9eb7c184..42200fad 100755 --- a/plugins/ejabberd/ejabberd_stats_ +++ b/plugins/ejabberd/ejabberd_stats_ @@ -22,7 +22,7 @@ #%# capabilities=autoconf suggest EJABBERDCTL=${ejabberdctl:-$(which ejabberdctl)} -SCRIPT_NAME=$(basename $0) +SCRIPT_NAME=$(basename "$0") STATS_TYPE="${SCRIPT_NAME/ejabberd_stats_/}" function ejabberd_stats_connected_users() { @@ -81,4 +81,4 @@ SUGGESTIONS ;; esac -ejabberd_stats_${STATS_TYPE} $1 +"ejabberd_stats_${STATS_TYPE}" "$1" diff --git a/plugins/ilias/ilias_session b/plugins/ilias/ilias_session index 5f85a7c4..ad227ebf 100755 --- a/plugins/ilias/ilias_session +++ b/plugins/ilias/ilias_session @@ -154,6 +154,7 @@ config() { # -v varname # True if the shell variable varname is set (has been assigned a value). # https://stackoverflow.com/a/45385463/2683737 + # shellcheck disable=SC2102 while [[ -v d_attr[$i,field] ]]; do field=${d_attr[$i,field]} echo "$field.type ${d_attr[$i,type]}" @@ -181,6 +182,7 @@ fetch() { # create an array of queries i=0 + # shellcheck disable=SC2102 while [[ -v d_attr[$i,field] ]]; do query+=("${d_attr[$i,sql]}") ((++i)) @@ -211,6 +213,7 @@ fetch() { # extract result and echo it to stdout, which is # captured by Munin i=0 + # shellcheck disable=SC2102 while [[ -v d_attr[$i,field] ]]; do echo "${d_attr[$i,field]}.value ${results[$i]}" ((++i)) diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 0520935d..1de3aaa7 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -49,7 +49,7 @@ fi PLAYERCOUNT=$(LC_LANG=C netstat -tn | grep ":$MC_PORT " | grep ESTABLISHED | wc -l) MC_PID=$(netstat -tlpn | grep ":$MC_PORT " | sed -n -e '1p' | awk '{ print $7 }' | cut -d '/' -f1) -if [ ! -z "${MC_PID}" ] +if [ -n "${MC_PID}" ] then #is running MEMGiB=$(ps -p "$MC_PID" -o rss | sed -n '2p' | awk '{ print $1 / 1024 / 1024 }') CPU=$(top -bp "$MC_PID" -n 1 | sed -n '$p' | awk '{ print $10 / 100; }') diff --git a/plugins/munin/munin_events b/plugins/munin/munin_events index 2a355bd9..73f36dd0 100755 --- a/plugins/munin/munin_events +++ b/plugins/munin/munin_events @@ -71,7 +71,7 @@ do_value() { FIELD="$1" EVENT_LABEL="$2" - EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" + 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 diff --git a/plugins/nfs-freebsd/nfs_client b/plugins/nfs-freebsd/nfs_client index 312fb6f2..ef930f4e 100755 --- a/plugins/nfs-freebsd/nfs_client +++ b/plugins/nfs-freebsd/nfs_client @@ -37,16 +37,16 @@ if [ "$1" = "autoconf" ]; then fi fi -labels=`$NFSSTAT -c | grep -iv "[0-9]" | grep -v ":" | sed 's/X\ /x_/' | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26}' | tr '[A-Z]' '[a-z]'` -values=`$NFSSTAT -c | grep -i "[0-9]" | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26}'` +labels=$("$NFSSTAT" -c | grep -iv "[0-9]" | grep -v ":" | sed 's/X\ /x_/' | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26}' | tr '[:upper:]' '[:lower:]') +values=$("$NFSSTAT" -c | grep -i "[0-9]" | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26}') -larray=( $labels ) -varray=( $values ) +read -r -a larray <<<"$labels" +read -r -a varray <<<"$values" if [ "$1" = "config" ]; then echo 'graph_title NFSv3 Client' echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel requests / ${graph_period}' + echo "graph_vlabel requests / \${graph_period}" echo 'graph_total total' echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done diff --git a/plugins/nfs-freebsd/nfs_client_cache b/plugins/nfs-freebsd/nfs_client_cache index 9c5d3441..3be86392 100755 --- a/plugins/nfs-freebsd/nfs_client_cache +++ b/plugins/nfs-freebsd/nfs_client_cache @@ -37,16 +37,16 @@ if [ "$1" = "autoconf" ]; then fi fi -labels=`$NFSSTAT -c | grep -iv "[0-9]" | tail -n 2 | sed 's/BioRLHits/BioRL\ Hits/g' | awk '{print $1"_"$2,$1"_"$3,$4"_"$5,$4"_"$6,$7"_"$8,$7"_"$9,$10"_"$11,$10"_"$12}' | tr '\n' ' ' | tr '[A-Z]' '[a-z]'` -values=`$NFSSTAT -c | grep -i "[0-9]" | tail -n 2 | tr '\n' ' '` +labels=$("$NFSSTAT" -c | grep -iv "[0-9]" | tail -n 2 | sed 's/BioRLHits/BioRL\ Hits/g' | awk '{print $1"_"$2,$1"_"$3,$4"_"$5,$4"_"$6,$7"_"$8,$7"_"$9,$10"_"$11,$10"_"$12}' | tr '\n' ' ' | tr '[:upper:]' '[:lower:]') +values=$("$NFSSTAT" -c | grep -i "[0-9]" | tail -n 2 | tr '\n' ' ') -larray=( $labels ) -varray=( $values ) +read -r -a larray <<<"$labels" +read -r -a varray <<<"$values" if [ "$1" = "config" ]; then echo 'graph_title NFSv3 Client Cache' echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel requests / ${graph_period}' + echo "graph_vlabel requests / \${graph_period}" echo 'graph_total total' echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done diff --git a/plugins/nfs-freebsd/nfsd b/plugins/nfs-freebsd/nfsd index d551b844..d2e3d848 100755 --- a/plugins/nfs-freebsd/nfsd +++ b/plugins/nfs-freebsd/nfsd @@ -38,17 +38,17 @@ if [ "$1" = "autoconf" ]; then fi fi -labels=`$NFSSTAT -s | grep -iv "[0-9]" | grep -v ":" | sed 's/Server\ //' | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30}' | tr '[A-Z]' '[a-z]'` -values=`$NFSSTAT -s | grep -i "[0-9]" | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30}'` +labels=$("$NFSSTAT" -s | grep -iv "[0-9]" | grep -v ":" | sed 's/Server\ //' | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30}' | tr '[:upper:]' '[:lower:]') +values=$("$NFSSTAT" -s | grep -i "[0-9]" | tr '\n' ' ' | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30}') -larray=( $labels ) -varray=( $values ) +read -r -a larray <<<"$labels" +read -r -a varray <<<"$values" if [ "$1" = "config" ]; then echo 'graph_title NFSv3 Server' echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel requests / ${graph_period}' + echo "graph_vlabel requests / \${graph_period}" echo 'graph_total total' echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done diff --git a/plugins/nscd/nscd_ b/plugins/nscd/nscd_ index b8919a55..00bde9f8 100755 --- a/plugins/nscd/nscd_ +++ b/plugins/nscd/nscd_ @@ -33,19 +33,23 @@ #%# family=auto #%# capabilities=autoconf suggest -source $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" NSCD_CFG=${nscd_cfg:-/etc/nscd.conf} AUTOCONF_CHECK=$(nscd -g | grep -ic 'yes.*cache is enabled') -SUGGEST_CHECK=$(nscd -g | grep -iB2 'yes.*cache is enabled' | awk {'print $1'} | head -1) -MODE=$(basename $0 | sed 's/^nscd_//g' | tr '_' '.') +SUGGEST_CHECK=$(nscd -g | grep -iB2 'yes.*cache is enabled' | awk '{print $1}' | head -1) +MODE=$(basename "$0" | sed 's/^nscd_//g' | tr '_' '.') case $1 in autoconf) - [ -r "$NSCD_CFG" ] && [ $AUTOCONF_CHECK -gt 0 ] && echo yes || echo "no (nscd config not found or no database enabled)" + if [ -r "$NSCD_CFG" ] && [ "$AUTOCONF_CHECK" -gt 0 ]; then + echo yes + else + echo "no (nscd config not found or no database enabled)" + fi exit 0 ;; suggest) - echo $SUGGEST_CHECK + echo "$SUGGEST_CHECK" exit 0 ;; config) @@ -66,7 +70,7 @@ CONFIG ;; fetch|*) nscd -g | awk "/^$MODE cache/ {printline = 1; print; next} /^.*cache:/ {printline = 0} printline" | \ - egrep '(suggested size|cache hit rate|current number of cached values|maximum number of cached values)' | \ - sed 's/%//' | awk {' if (NR==1) print "suggestedsize.value " $1; if (NR==2) print "cachehitrate.value " $1; if (NR==3) print "currnumber.value " $1; if (NR==4) print "maxnumber.value " $1'} + grep -E '(suggested size|cache hit rate|current number of cached values|maximum number of cached values)' | \ + sed 's/%//' | awk '{ if (NR==1) print "suggestedsize.value " $1; if (NR==2) print "cachehitrate.value " $1; if (NR==3) print "currnumber.value " $1; if (NR==4) print "maxnumber.value " $1}' ;; esac diff --git a/plugins/quake/qstatcod4and5_ b/plugins/quake/qstatcod4and5_ index cf027e34..f1fd36d1 100755 --- a/plugins/quake/qstatcod4and5_ +++ b/plugins/quake/qstatcod4and5_ @@ -64,7 +64,7 @@ quake_stat() { port=$3 fi - if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + if [ -n "$gametype" ] && [ -n "$gametype" ] && [ -n "$gametype" ]; then dummy=$("$qstat_exe" -P -pa -sort P "-$gametype" "${ip}:${port}" | grep frags | wc -l) playervalue=$dummy diff --git a/plugins/rabbitmq/rabbitmq_connections b/plugins/rabbitmq/rabbitmq_connections index fd755968..73f770ec 100755 --- a/plugins/rabbitmq/rabbitmq_connections +++ b/plugins/rabbitmq/rabbitmq_connections @@ -96,4 +96,4 @@ else connections=U fi -printf "connections.value %s\n" "$connections" +printf 'connections.value %s\n' "$connections" diff --git a/plugins/raspberry-pi/raspi_temp b/plugins/raspberry-pi/raspi_temp index 55f85e45..5918b4e8 100755 --- a/plugins/raspberry-pi/raspi_temp +++ b/plugins/raspberry-pi/raspi_temp @@ -48,4 +48,4 @@ EOM exit 0;; esac -echo "temp.value $(vcgencmd measure_temp | sed -n "s/temp=\(.*\)\'C/\1/p")" +echo "temp.value $(vcgencmd measure_temp | sed -n 's/temp=\(.*\)'"'"'C/\1/p')" diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index f338a8f5..d2d56f30 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -70,7 +70,7 @@ HOSTPORT=${0##*ssl-certificate-expiry_} CACHEFILE="${MUNIN_PLUGSTATE}/$(basename "${0}").cache" if [ "${HOSTPORT}" != "${0}" ] \ - && [ ! -z "${HOSTPORT}" ]; then + && [ -n "${HOSTPORT}" ]; then services="${HOSTPORT}" fi diff --git a/plugins/ssl/ssl_ b/plugins/ssl/ssl_ index 496453f1..5c1c4fcc 100755 --- a/plugins/ssl/ssl_ +++ b/plugins/ssl/ssl_ @@ -84,4 +84,4 @@ cert=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" - days_left=$(echo "$cert" | parse_valid_days_from_certificate) [ -n "$days_left" ] || days_left="U" -printf "expire.value %s\n" "$days_left" +printf 'expire.value %s\n' "$days_left" diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ index dabc504e..a5a31927 100755 --- a/plugins/syncthing/strelaysrv_ +++ b/plugins/syncthing/strelaysrv_ @@ -57,10 +57,10 @@ EOM NC=$(echo "$STATUS" | $JQ '.numConnections ') NK=$(echo "$STATUS" | $JQ '.numPendingSessionKeys ') NP=$(echo "$STATUS" | $JQ '.numProxies ') - printf "strelaysrv_num_sessions.value %s\n" "$NS" - printf "strelaysrv_num_connections.value %s\n" "$NC" - printf "strelaysrv_num_pending.value %s\n" "$NK" - printf "strelaysrv_num_proxies.value %s\n" "$NP" + printf 'strelaysrv_num_sessions.value %s\n' "$NS" + printf 'strelaysrv_num_connections.value %s\n' "$NC" + printf 'strelaysrv_num_pending.value %s\n' "$NK" + printf 'strelaysrv_num_proxies.value %s\n' "$NP" esac } @@ -77,7 +77,7 @@ EOM *) STATUS=$(getstatus) UPTIME=$(echo "$STATUS" | "$JQ" '.uptimeSeconds') - printf "strelaysrv_uptime.value %s\n" "$UPTIME" + printf 'strelaysrv_uptime.value %s\n' "$UPTIME" esac } @@ -94,7 +94,7 @@ EOM *) STATUS=$(getstatus) GOROUTINE=$(echo "$STATUS" | "$JQ" '.goNumRoutine') - printf "strelaysrv_goroutine.value %s\n" "$GOROUTINE" + printf 'strelaysrv_goroutine.value %s\n' "$GOROUTINE" esac } @@ -113,7 +113,7 @@ EOM *) STATUS=$(getstatus) BP=$(echo "$STATUS" | "$JQ" '.bytesProxied ') - printf "strelaysrv_proxied.value %s\n" "$BP" + printf 'strelaysrv_proxied.value %s\n' "$BP" esac } @@ -132,7 +132,7 @@ EOM *) STATUS=$(getstatus) TRANSFER=$(echo "$STATUS" | "$JQ" '.kbps10s1m5m15m30m60m[2] ') - printf "strelaysrv_transfer.value %s\n" "$TRANSFER" + printf 'strelaysrv_transfer.value %s\n' "$TRANSFER" esac } diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ index 8c7fc8a9..05efc7dd 100755 --- a/plugins/syncthing/syncthing_ +++ b/plugins/syncthing/syncthing_ @@ -57,7 +57,7 @@ EOM *) STATUS=$(getstatus) CPU=$(echo "$STATUS" | "$JQ" '.cpuPercent') - printf "syncthing_cpu.value %s\n" "$CPU" + printf 'syncthing_cpu.value %s\n' "$CPU" esac } @@ -80,8 +80,8 @@ EOM STATUS=$(getstatus) ALL=$(echo "$STATUS" | "$JQ" '.alloc') SYS=$(echo "$STATUS" | "$JQ" '.sys') - printf "syncthing_mem_all.value %s\n" "$ALL" - printf "syncthing_mem_sys.value %s\n" "$SYS" + printf 'syncthing_mem_all.value %s\n' "$ALL" + printf 'syncthing_mem_sys.value %s\n' "$SYS" esac } @@ -98,7 +98,7 @@ EOM *) STATUS=$(getstatus) UPTIME=$(echo "$STATUS" | "$JQ" '.uptime') - printf "syncthing_uptime.value %s\n" "$UPTIME" + printf 'syncthing_uptime.value %s\n' "$UPTIME" esac } @@ -115,7 +115,7 @@ EOM *) STATUS=$(getstatus) GOROUTINES=$(echo "$STATUS" | "$JQ" '.goroutines') - printf "syncthing_goroutine.value %s\n" "$GOROUTINES" + printf 'syncthing_goroutine.value %s\n' "$GOROUTINES" esac } @@ -142,8 +142,8 @@ EOM CONNECTIONS=$("$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections") IBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .inBytesTotal') OBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .outBytesTotal') - printf "syncthing_transfer_up.value %s\n" "$IBT" - printf "syncthing_transfer_down.value %s\n" "$OBT" + printf 'syncthing_transfer_up.value %s\n' "$IBT" + printf 'syncthing_transfer_down.value %s\n' "$OBT" esac } diff --git a/plugins/zfs/zfs-filesystem-graph b/plugins/zfs/zfs-filesystem-graph index 1324f95d..8bc3ca07 100755 --- a/plugins/zfs/zfs-filesystem-graph +++ b/plugins/zfs/zfs-filesystem-graph @@ -21,7 +21,7 @@ # For now this plugin does not allow "_" in the name of a zpool or filesystems # -myname=`basename $0 | sed 's/^zfs_fs_//g' | sed -e 's/_/\//g'` +myname=$(basename "$0" | sed 's/^zfs_fs_//g' | sed -e 's/_/\//g') if [ "$1" = "autoconf" ]; then # Makes little sense to autoconf if you can't suggest @@ -33,7 +33,9 @@ if [ "$1" = "suggest" ]; then exit 0 fi -values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $myname | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) +read -r -a values <<<"$(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota "$myname" \ + | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}')" + if [ "$1" = "config" ]; then From be192b5226583ab3e6dfa9972f1f6c7a47dcb593 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 05:01:44 +0200 Subject: [PATCH 483/695] Fix multiple python code issues (detected by flake8) --- plugins/currency/ethereum/etherscan_balance_ | 2 +- plugins/monit/monit_parser | 2 +- plugins/network/sockstat-via-procfs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/currency/ethereum/etherscan_balance_ b/plugins/currency/ethereum/etherscan_balance_ index 03c323d3..e3bc3d2b 100755 --- a/plugins/currency/ethereum/etherscan_balance_ +++ b/plugins/currency/ethereum/etherscan_balance_ @@ -110,7 +110,7 @@ except ValueError: try: eth = int(etherscan_balance['result']) -except (KeyError, ValueError) as exc: +except (KeyError, ValueError): print("JSON result error!", file=sys.stderr) sys.exit(9) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index f7fb73a0..aefe5615 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -76,7 +76,7 @@ def get_monit_status_xml(): auth_value = "Basic %s" % auth_base64_str req.add_header("Authorization", auth_value) conn = urllib.request.urlopen(req) - except urllib.error.URLError as exc: + except urllib.error.URLError: conn = None if conn is None: raise RuntimeError("Failed to open monit status URL: {}".format(MONIT_XML_URL)) diff --git a/plugins/network/sockstat-via-procfs b/plugins/network/sockstat-via-procfs index 88afe093..8311eaa7 100755 --- a/plugins/network/sockstat-via-procfs +++ b/plugins/network/sockstat-via-procfs @@ -29,7 +29,7 @@ if len(sys.argv) > 1: print('%s.label %s' % (name, label)) sys.exit(0) -re_num = re.compile('(\d+)') +re_num = re.compile(r'(\d+)') sockstat = open('/proc/net/sockstat').read() numbers = re_num.findall(sockstat) From e68a0308fe6d7cc7ee10b255fc0f359afa183b96 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 04:00:53 +0200 Subject: [PATCH 484/695] Plugin zfs-filesystem-graph: fix autoconf and remove "suggest" --- plugins/zfs/zfs-filesystem-graph | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/zfs/zfs-filesystem-graph b/plugins/zfs/zfs-filesystem-graph index 8bc3ca07..6021e17e 100755 --- a/plugins/zfs/zfs-filesystem-graph +++ b/plugins/zfs/zfs-filesystem-graph @@ -20,18 +20,20 @@ # ** WARNING ** # For now this plugin does not allow "_" in the name of a zpool or filesystems # +# #%# capabilities=autoconf +# myname=$(basename "$0" | sed 's/^zfs_fs_//g' | sed -e 's/_/\//g') if [ "$1" = "autoconf" ]; then - # Makes little sense to autoconf if you can't suggest - echo no + if which zfs >/dev/null; then + echo yes + else + echo "no (missing executable 'zfs')" + fi exit 0 fi -if [ "$1" = "suggest" ]; then - exit 0 -fi read -r -a values <<<"$(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota "$myname" \ | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}')" From 94434e4bd8fa739b60728bbf268241664d450a24 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 04:03:15 +0200 Subject: [PATCH 485/695] Plugin zfs-filesystem-graph: fix here-document --- plugins/zfs/zfs-filesystem-graph | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zfs-filesystem-graph b/plugins/zfs/zfs-filesystem-graph index 6021e17e..57661038 100755 --- a/plugins/zfs/zfs-filesystem-graph +++ b/plugins/zfs/zfs-filesystem-graph @@ -41,7 +41,8 @@ read -r -a values <<<"$(zfs get -p usedbydataset,usedbychildren,usedbysnapshots, if [ "$1" = "config" ]; then - echo < Date: Tue, 13 Aug 2019 04:44:46 +0200 Subject: [PATCH 486/695] Plugin ejabberd_resources_: fix shellcheck issues --- plugins/ejabberd/ejabberd_resources_ | 62 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/plugins/ejabberd/ejabberd_resources_ b/plugins/ejabberd/ejabberd_resources_ index d820b2de..37196951 100755 --- a/plugins/ejabberd/ejabberd_resources_ +++ b/plugins/ejabberd/ejabberd_resources_ @@ -27,8 +27,8 @@ #%# capabilities=autoconf suggest EJABBERDCTL_CFG=${ejabberdctl_cfg:-/etc/ejabberd/ejabberdctl.cfg} -source $EJABBERDCTL_CFG 2>/dev/null -source $MUNIN_LIBDIR/plugins/plugin.sh +. "$EJABBERDCTL_CFG" 2>/dev/null +. "$MUNIN_LIBDIR/plugins/plugin.sh" EJABBERDCTL=${ejabberdctl:-$(which ejabberdctl)} @@ -45,7 +45,7 @@ function ejabberd_exec() { echo "$1" | su - ejabberd -c "$ERL_CALL -e -n $ERLANG_NODE" | sed -re 's/^\{ok, (.*)\}$/\1/' } -SCRIPT_NAME=$(basename $0) +SCRIPT_NAME=$(basename "$0") RESOURCE_TYPE="${SCRIPT_NAME/ejabberd_resources_/}" RESOURCE_BASE=1000 [ "$RESOURCE_TYPE" = "memory" ] && RESOURCE_BASE=1024 @@ -57,8 +57,10 @@ function hosts_list() { function ejabberd_report_online_users() { [ "$1" = "config" ] && echo 'graph_vlabel users' for host in $(hosts_list); do - local clean_host=$(clean_fieldname $host) - local ejabberd_command="length(ejabberd_sm:get_vh_session_list(\"$host\"))" + local clean_host + local ejabberd_command + clean_host=$(clean_fieldname "$host") + ejabberd_command="length(ejabberd_sm:get_vh_session_list(\"$host\"))" if [ "$1" = "config" ]; then cat < Date: Mon, 12 Aug 2019 06:01:49 +0200 Subject: [PATCH 487/695] tests: enable shellcheck for shell-based plugins and flake8 for python-based plugins Many plugins are still failing these tests. These expected failures are listed in a file (t/test-exception-wrapper.expected-failures) A wrapper script is used for running the tests and comparing the result with the expectation (based on the file being listed in the above file). This allows to test all new plugins, while ignoring all known failures. --- .travis.yml | 2 + t/test-exception-wrapper | 55 +++ t/test-exception-wrapper.expected-failures | 492 +++++++++++++++++++++ t/test.t | 71 ++- 4 files changed, 599 insertions(+), 21 deletions(-) create mode 100755 t/test-exception-wrapper create mode 100644 t/test-exception-wrapper.expected-failures diff --git a/.travis.yml b/.travis.yml index ea1aab50..10cd4ddb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,9 @@ addons: packages: - devscripts - python + - python-flake8 - python3 + - python3-flake8 - ruby - php-cli - gawk diff --git a/t/test-exception-wrapper b/t/test-exception-wrapper new file mode 100755 index 00000000..08e854d3 --- /dev/null +++ b/t/test-exception-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Run a check for a given file. A failure is tolerated (and expected), if the filename is listed +# in a file containing expected failures. +# +# Parameters: SHELL_DIALECT SCRIPT_FILENAME +# +# See EXPECTED_FAILURES_LIST_FILENAME for the filename pattern of a file, containing the relative +# names of all scripts, that are expected to fail. This wrapper script will fail, if the exit +# status of the test does not match the expectated result (i.e. it fails but should pass or it +# passes while being listed in the EXPECTED_FAILURES_LIST_FILENAME file). +# + +set -eu + +[ $# -lt 2 ] && echo >&2 "Insufficient number of arguments: expecting SCRIPT_FILENAME and one or more COMMAND tokens" && exit 1 + + +SCRIPT_FILENAME="$1" +shift +EXPECTED_FAILURES_LIST_FILENAME="$0.expected-failures" + +REPOSITORY_DIR=$(cd "$(dirname "$0")/.." && pwd) + + +[ ! -e "$SCRIPT_FILENAME" ] && echo >&2 "Failed to find script: $SCRIPT_FILENAME" && exit 3 + + +# determine, whether the script is mentioned in the exclusion file +relative_script_filename=$(realpath --relative-to "$REPOSITORY_DIR" "$SCRIPT_FILENAME") +if grep --quiet --line-regexp --fixed-strings --no-messages "$relative_script_filename" "$EXPECTED_FAILURES_LIST_FILENAME"; then + is_expected_to_fail=1 +else + is_expected_to_fail=0 +fi + +# check the returncode of the test +if "$@" "$SCRIPT_FILENAME"; then + has_failed=0 +else + has_failed=1 +fi + + +# complain, if the result did not meet our expectation +if [ "$is_expected_to_fail" = "$has_failed" ]; then + # the check returned the expected result + exit 0 +elif [ "$has_failed" = "1" ]; then + echo >&2 "ERROR: the script '$SCRIPT_FILENAME' should pass the test, but it failed" + exit 4 +else + echo >&2 "ERROR: the script '$SCRIPT_FILENAME' was expected to fail the test, but it passed" + exit 5 +fi diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures new file mode 100644 index 00000000..38d18647 --- /dev/null +++ b/t/test-exception-wrapper.expected-failures @@ -0,0 +1,492 @@ +plugins/accounting/accounting_ +plugins/amavis/amavis_awk +plugins/amavis/amavis-debian +plugins/amule/amule_queue +plugins/amule/amule_shares +plugins/amule/amule_transfers +plugins/amule/amule_uptime +plugins/apache/apache_cache_disk_count +plugins/apache/apache_memmory +plugins/apache/apache_servers +plugins/apache/apache_threads +plugins/apache/apache_tmemmory +plugins/apache/apache_users +plugins/apache/page_load +plugins/apache/qpid_bytedepth +plugins/apache/qpid_discardsring +plugins/apache/qpid_enqueuebytes +plugins/apache/qpid_enqueuecount +plugins/apache/qpid_msgdepth +plugins/apt/deb_packages/deb_packages.py +plugins/arangodb/arangodb_ +plugins/arp/arp_ +plugins/arp/arp_bsd_ +plugins/asterisk/asterisk_channels +plugins/asterisk/asterisk_inuse +plugins/backuppc/backuppc +plugins/battery/acpi-battery +plugins/battery/battery_ +plugins/bigbrother/b3error_ +plugins/bind/bind9_resolver_stats +plugins/bind/bind9_server_stats +plugins/bind/bind9_socket_stats +plugins/bsd/df_abs_bsd +plugins/bsd/df_bsd +plugins/bsd/freebsd_hdd_power_state +plugins/bsd/spamd-blacklist-bsd +plugins/bsd/spamd-tarpit-bsd +plugins/cacti/cacti-host +plugins/cacti/cacti_poller_time +plugins/cacti/cacti_rrds +plugins/celery/celery_tasks +plugins/celery/celery_tasks_states +plugins/ceph/ceph-osd-info +plugins/change.org/changeorg_signature_count +plugins/chat/tinychat_users_ +plugins/cherokee/munin-plugin-for-cherokee +plugins/chilli/chilli_sessions_ +plugins/chrony/chrony +plugins/clamav/clamav +plugins/condor/condor_activity_ +plugins/condor/condor_ops_ +plugins/condor/condor_queue_ +plugins/condor/condor_states_ +plugins/courier/courier_log +plugins/cpu/cpu_ +plugins/cpu/cpuload_ +plugins/cpu/cpuutil +plugins/cpu/multicpu1sec +plugins/cpu/process_count +plugins/cpu/process_cpushare +plugins/currency/bitcoin/btcguild_hashrate_ +plugins/currency/bitcoin/slush_hashrate_ +plugins/currency/bitcoin/slush_reward_ +plugins/currency/ethereum/ethermine_hashrate_ +plugins/currency/nanopool/nanopool_ +plugins/currency/zcash/zcash_flypool_hashrate_ +plugins/cyrus/cyrus-imapd +plugins/db2/db2_cnx +plugins/dhcp/dhcpd-pools +plugins/disk/df_with_nfs +plugins/disk/dm_cache_occupancy_ +plugins/disk/dm_cache_statistics_ +plugins/disk/du +plugins/disk/du_multidirs +plugins/disk/du_pattern +plugins/disk/e2 +plugins/disk/file_age +plugins/disk/freedisk +plugins/disk/hdsentinel +plugins/disk/iostat-xfrs +plugins/disk/log_sizes +plugins/disk/lvm_ +plugins/disk/lvm_snap_used +plugins/disk/megaraid-hdd-temperature-using-megacli +plugins/disk/quota2percent_ +plugins/disk/raid-mismatch-count +plugins/disk/scsi_queue +plugins/disk/snmp__areca_ +plugins/disk/useddisk +plugins/disk/xfs_frag +plugins/dkim/dkimproxy_mails +plugins/dovecot/dovecot +plugins/dovecot/dovecot_stats_ +plugins/dspam/dspam_ +plugins/dspam/dspam_activity +plugins/dvb/2wcomdsr_ +plugins/dvb/femon +plugins/ejabberd/ejabberd_resources_ +plugins/emc/emc_vnx_block_lun_perfdata +plugins/emc/emc_vnx_file_ +plugins/fan/dell_fans +plugins/fan/ibmfan +plugins/fax/faxstat +plugins/firebird/firebird +plugins/freeradius/freeradius +plugins/freeradius/freeradius_queue +plugins/ftp/pure-ftpd +plugins/ftp/pure-ftpd-bw +plugins/ftp/pureftpd_count +plugins/ftp/pure-ftpd-logs +plugins/ftp/vsftpd-rel +plugins/games/cstat +plugins/git/git_commit_behind +plugins/glance/glance_size_ +plugins/glance/glance_status +plugins/glassfish/glassfish_counters_ +plugins/google/googlecode +plugins/google/google-rank +plugins/gpu/amd_gpu_ +plugins/gunicorn/gunicorn_memory_status +plugins/gunicorn/gunicorn_status +plugins/haproxy/haproxy_abort_backend +plugins/haproxy/haproxy_active_backend +plugins/haproxy/haproxy-bytes +plugins/haproxy/haproxy_bytes_backend +plugins/haproxy/haproxy_bytes_compressor_backend +plugins/haproxy/haproxy_bytes_compressor_frontend +plugins/haproxy/haproxy_bytes_frontend +plugins/haproxy/haproxy-connection-errors +plugins/haproxy/haproxy_denied_backend +plugins/haproxy/haproxy_denied_frontend +plugins/haproxy/haproxy-downtime +plugins/haproxy/haproxy-errors +plugins/haproxy/haproxy_errors_backend +plugins/haproxy/haproxy_errors_frontend +plugins/haproxy/haproxy-failed-checks +plugins/haproxy/haproxy_queue_backend +plugins/haproxy/haproxy_rate_backend +plugins/haproxy/haproxy_rate_frontend +plugins/haproxy/haproxy_reqrate_frontend +plugins/haproxy/haproxy_response_compressor_backend +plugins/haproxy/haproxy_response_compressor_frontend +plugins/haproxy/haproxy-response-errors +plugins/haproxy/haproxy_responses_backend +plugins/haproxy/haproxy_responses_frontend +plugins/haproxy/haproxy-sessions +plugins/haproxy/haproxy_sessions_backend +plugins/haproxy/haproxy-sessions-by-servers +plugins/haproxy/haproxy_sessions_frontend +plugins/haproxy/haproxy_sessions_total_backend +plugins/haproxy/haproxy_sessions_total_frontend +plugins/haproxy/haproxy_warnings_backend +plugins/harddisks/powered_up_ +plugins/horde/horde +plugins/http/http_pagespeed +plugins/http/http_responsecode +plugins/http/vhost_requests_ +plugins/http/wget_page +plugins/i2p/i2p_ +plugins/icecast/icecast_ +plugins/icecast/icecast2 +plugins/icecast/icecast2_ +plugins/icecast/icecast2_all +plugins/icecast/icecast2_stats_ +plugins/imapproxy/imapproxy_multi +plugins/ip6/ip6_ +plugins/ipvs/ipvs_active +plugins/ipvs/ipvs_bps +plugins/ipvs/ipvs_conn +plugins/ipvs/ipvs_cps +plugins/isp/internode_usage +plugins/jmx/plugin/jmx_ +plugins/jvm/jstat__gccount +plugins/jvm/jstat__gctime +plugins/jvm/jstat__heap +plugins/keystone/keystone_stats +plugins/libvirt/kvm_cpu +plugins/libvirt/kvm_io +plugins/libvirt/kvm_mem +plugins/libvirt/kvm_net +plugins/libvirt/munin-libvirtpy +plugins/lighttpd/lighttpd_ +plugins/logins/logins +plugins/logs/service_events +plugins/lxc/lxc_cpu +plugins/lxc/lxc_cpu_time +plugins/lxc/lxc_net +plugins/lxc/lxc_proc +plugins/lxd/lxd_disk +plugins/lxd/lxd_mem +plugins/mail/imap_bandwidth +plugins/mail/mail_connections +plugins/mailman/mailman-queue-check +plugins/mail/postfwd-rbl +plugins/memory/kmemsum +plugins/memory/multimemory +plugins/memory/proc_mem +plugins/memory/proc_memory_status +plugins/mixminion/mixminion +plugins/mod_jk/mod_jk +plugins/moinmoin/moinoin_pages +plugins/mongodb/mongo_btree +plugins/mongodb/mongo_collection_ +plugins/mongodb/mongo_conn +plugins/mongodb/mongo_lag +plugins/mongodb/mongo_lock +plugins/mongodb/mongo_mem +plugins/mongodb/mongo_ops +plugins/mumble/mumble_users +plugins/mumble/murmur-stats +plugins/munin/healthcheck_log +plugins/munin/healthcheck_process +plugins/munin/healthcheck_url +plugins/munin/update +plugins/mysql/hs_read +plugins/mysql/hs_write +plugins/mysql/mysql_aggregate_ +plugins/mysql/mysql_report +plugins/mysql/mysql_size_ondisk +plugins/mythtv/dvb-signal +plugins/netapp/snmp__netapp_cifs +plugins/netapp/snmp__netapp_cpu +plugins/network/bgpd +plugins/network/brc_rssi +plugins/network/ddclient +plugins/network/denyhosts +plugins/network/ethtool_ +plugins/network/fwbuilder_ +plugins/network/hfsc +plugins/network/hfsc_sep +plugins/network/hostsdeny +plugins/network/host_traffic +plugins/network/if1sec_ +plugins/network/ifem_ +plugins/network/if_uptime +plugins/network/iperf_ +plugins/network/ip_forward_ +plugins/network/ipfwcnt_ +plugins/network/ipfwnat_ +plugins/network/ipt_accounting_ +plugins/network/ipt_basic_ +plugins/network/ldap_connections +plugins/network/linux_if/linux_if +plugins/network/mtr100_ +plugins/network/netatalk +plugins/network/netatalk3 +plugins/network/net_hosts_ +plugins/network/nsd3 +plugins/network/olsrd +plugins/network/packetloss +plugins/network/proc_netstat +plugins/network/psad +plugins/network/shorewall_ +plugins/network/shorewall_acc +plugins/network/shorewall-accounting_ +plugins/network/smtp_hello_ +plugins/network/sockstat +plugins/network/tc_ +plugins/network/tc_drops_ +plugins/network/tc_packets_ +plugins/network/traffic +plugins/network/traffic_ipt +plugins/network/transmission +plugins/network/umts_sig +plugins/network/upnpc_ +plugins/network/vnstat +plugins/network/vnstat_ +plugins/nfs/nfsv4 +plugins/nginx/nginx-cache-multi_ +plugins/nginx/nginx_upstream +plugins/nginx/nginx_vhost_traffic +plugins/nginx/nginx_working_set +plugins/nova/nova_floating_ips +plugins/nova/nova_instance_ +plugins/nova/nova_instance_launched +plugins/nova/nova_instance_timing +plugins/nova/nova_services +plugins/ntp/ntpdate_ +plugins/ntp/ntp_kernel_pll_prec +plugins/ntp/ntp_pool_score_ +plugins/openntpd/openntp_offset +plugins/openvz/openvz_ +plugins/oracle/oracle_connections +plugins/oracle/oracle_sysstat +plugins/ossec/ossec_active_response +plugins/ossec/ossec_agents +plugins/ossec/ossec_alerts +plugins/other/beanstalkd +plugins/other/cm2 +plugins/other/earthquakes-stronger-than-m4-and-m5-plus-solar-act +plugins/other/foldingathome_activecpu +plugins/other/globesurfer +plugins/other/hookbox +plugins/other/listeners +plugins/other/pid +plugins/other/s9y +plugins/other/services +plugins/pdns/pdns_errors +plugins/pdns/pdns_queries +plugins/pdns/pdns_rec_qtypes +plugins/pf/pf +plugins/pf/pf_bytes +plugins/pf/pf_ipv4_ipv6_packets +plugins/pf/pf_openbsd +plugins/pf/pf_packets +plugins/pf/pf_states +plugins/php/eaccelerator +plugins/php/eaccelerator-python +plugins/php/php5-fpm_status +plugins/php/php_apc_ +plugins/php/php-cgi +plugins/php/php_errors_ +plugins/php/php_opcache +plugins/php/php_sessions +plugins/php/php_time_execution +plugins/ping/fping_ +plugins/ping/pinger +plugins/ping/ping_host +plugins/ping/ping-with-ceil +plugins/poseidon/snmp__poseidon-sensors +plugins/postfix/greyfix +plugins/postfix/policyd-spf-python +plugins/postfix/postfix_filtered +plugins/postfix/postfix_filtered_awk +plugins/postfix/postfix_mailfiltered +plugins/postfix/postfix_mailqueue_ +plugins/postfix/postfix_mail_stats +plugins/postfix/postfix_mail_stats1 +plugins/postfix/postfix-policyd +plugins/postfix/postfix-queue-size +plugins/postfix/postfix-rbl-blocked-mails +plugins/postfix/postgrey +plugins/postgresql/pgbouncer_client_connections +plugins/postgresql/pgbouncer_maxwait +plugins/postgresql/pgbouncer_server_connections +plugins/postgresql/postgresql_active_backends +plugins/postgresql/postgresql_active_backends_by_database +plugins/postgresql/postgresql_active_locks +plugins/postgresql/postgresql_database_ratio +plugins/postgresql/postgresql_database_size +plugins/postgresql/postgresql_tablespace_size +plugins/postgresql/postgres_queries2_ +plugins/postgresql/slony_lag_events_ +plugins/postgresql/slony_lag_time +plugins/postgresql/slony_lag_time_ +plugins/power5/consumed_cpu_cycles +plugins/power5/cpu_entitlemens +plugins/power5/cpu_in_lpar +plugins/power5/weight_of_a_lpar +plugins/power/apc_status +plugins/power/eatonups_ +plugins/powermta/pmta_ +plugins/powermta/powermta_vmta_recpients +plugins/power/nut +plugins/printer/dell_5310n_health_ +plugins/printer/dell_5310n_pages_ +plugins/printer/hp2600_count_ +plugins/printer/hp2600_status_ +plugins/printer/oki_c5500_health_ +plugins/printer/oki_c5500_pages_ +plugins/printer/toshiba_5520c_byfunction_black_ +plugins/printer/toshiba_5520c_byfunction_fullcolor_ +plugins/printer/toshiba_5520c_print_ +plugins/printer/toshiba_5520c_scan_ +plugins/printer/xerox-wc3220 +plugins/printer/xerox-wc7232-consumables +plugins/puma/puma_ +plugins/qmail/qmailconn +plugins/qmail/queuestats +plugins/qmail/spamdyke +plugins/rabbitmq/rabbitmq_consumers +plugins/rabbitmq/rabbitmq_messages +plugins/rabbitmq/rabbitmq_messages_unacknowledged +plugins/rabbitmq/rabbitmq_messages_uncommitted +plugins/rabbitmq/rabbitmq_queue_memory +plugins/radiator/radiator_acct_lag +plugins/radiator/radiator_acct_ppm +plugins/radiator/radiator_auth_lag +plugins/radiator/radiator_auth_ppm +plugins/raspberry-pi/cpu_freq_1sec +plugins/requesttracker/rt_ticket_loadtime +plugins/rethinkdb/rethinkdb_node_io +plugins/riak/riak_fsm_time_95 +plugins/riak/riak_memory +plugins/riak/riak_node +plugins/router/arris-tm502g_ +plugins/router/bbox +plugins/router/cisco-epc3010_ +plugins/router/conexant_adsl +plugins/router/dartybox +plugins/router/dsl-connection-speed +plugins/router/dsl-stats +plugins/router/freeboxuptime +plugins/router/motorola_sb6141 +plugins/router/snmp__juniper +plugins/router/snmp__juniper_spu +plugins/router/speedport_300 +plugins/rsync/rsyncd_bytes +plugins/rsync/rsyncd_count +plugins/sar/iostat-cputps-average +plugins/scalix/scalix_clients +plugins/scalix/scalix_processes +plugins/scalix/scalix_queues +plugins/senderbase/senderbase +plugins/senderscore/senderscore +plugins/sensors/alertme_keyfobsathome +plugins/sensors/alertme_power +plugins/sensors/cleware +plugins/sensors/nvclock +plugins/sensors/sequoia_websens +plugins/sge/sge_job_stats +plugins/sge/sge_queue_ +plugins/sge/sge_queue_xml_ +plugins/smstools/smstools_ +plugins/snmp/snmp__airport +plugins/snmp/snmp___bri_se_ +plugins/snmp/snmp__brocade_ifs +plugins/snmp/snmp__fn +plugins/snmp/snmp__webthermometer +plugins/solaris/io_disk +plugins/solaris/zones_cpu +plugins/solaris/zones_mem +plugins/solr/solr +plugins/solr/solr4_ +plugins/solr/solrmulticore +plugins/spamassasin/sa-learn +plugins/sphinx/sphinx_documents +plugins/squid/squid_efficiency +plugins/squid/squid_times +plugins/ssh/hostdenied +plugins/ssh/openssh-denyhosts +plugins/ssh/sshd_log +plugins/streaming/packetship_ +plugins/swift/swift-async_ +plugins/swift/swift-quarantined_ +plugins/swift/swift-replication-time_ +plugins/synology/snmp__synology +plugins/system/auth +plugins/system/blockhosts +plugins/systemd/systemd_units +plugins/system/file_length_ +plugins/system/read_serial_temperature +plugins/tarsnap/tarsnap +plugins/tcp/tcp-retransmissions +plugins/tcp/tcp-states +plugins/tinydns/tinydns +plugins/tinydns/tinydns_err +plugins/tor/tor_ +plugins/tor/tor_traffic +plugins/trafic_ro/trafic_ro_24h +plugins/tv/hdhomerun_ +plugins/twemproxy/nutcracker_requests_ +plugins/unicorn/unicorn_ +plugins/user/membyuser +plugins/user/multipsu +plugins/user/system_users +plugins/uwsgi/uwsgi_ +plugins/varnish/varnish_devicedetect +plugins/vmware/esxcli_env_ +plugins/vmware/esxi +plugins/vmware/esxi__sensors +plugins/vmware/vm_cpu_load +plugins/vpn/openvpn_as_mtime +plugins/vpn/openvpn_as_traffic +plugins/vpn/openvpn_as_ttime +plugins/vpn/openvpn_as_users +plugins/vserver/vserver_limit_hits +plugins/vserver/vserver_limits +plugins/vserver/vserver_procs +plugins/weather/openweather_ +plugins/websphere/webspherelogin +plugins/wifi/ath9k_ +plugins/wifi/wifi_signal +plugins/wordpress/wordpress +plugins/wordpress/wordpress-multisite +plugins/wordpress/wordpress-mu-or-network +plugins/wowza/wowza-media-server +plugins/xastir/xastir +plugins/yum/yum_activity +plugins/zfs/zfsarcstats-counters +plugins/zfs/zfs_cache_efficiency +plugins/zfs/zfs_list +plugins/zfs/zfsonlinux_stats_ +plugins/zfs/zfs_stats_ +plugins/zfs/zlist +plugins/zfs/zpool_iostat +plugins/zimbra/zimbra-mailboxsizes +plugins/znc/znc_logs.py +plugins/zope/zeomonitor +plugins/zope/zope_cache_parameters +plugins/zope/zope_conflict_errors +plugins/zope/zope_db_activity diff --git a/t/test.t b/t/test.t index 1d02813f..205ac5d4 100644 --- a/t/test.t +++ b/t/test.t @@ -25,7 +25,7 @@ sub wanted { && process_file( $_, $name, $interpreter, $arguments ); } -File::Find::find( { wanted => \&wanted }, 'plugins' ); +File::Find::find( { wanted => \&wanted, no_chdir => 1 }, 'plugins' ); sub hashbang { my ($filename) = @_; @@ -64,7 +64,7 @@ sub process_file { } elsif ( $interpreter =~ m{/bin/sh} ) { subtest $filename => sub { - plan tests => 2; + plan tests => 3; run_check( { command => [ 'sh', '-n', $file ], description => 'sh syntax check' @@ -89,6 +89,11 @@ sub process_file { description => 'checkbashisms' } ); + run_check( + { command => [ 't/test-exception-wrapper', $file, 'shellcheck', '--exclude=SC1090,SC2009,SC2126,SC2230', '--shell=dash' ], + description => 'shellcheck' + } + ); }; } elsif ( $interpreter =~ m{/bin/ksh} ) { @@ -101,19 +106,27 @@ sub process_file { } ); run_check( - { command => [ 'shellcheck', $file ], + { command => [ 't/test-exception-wrapper', $file, 'shellcheck', '--shell=ksh' ], description => 'shellcheck' } ); } } elsif ( $interpreter =~ m{bash} ) { - run_check( - { command => [ 'bash', '-n', $file ], - description => 'bash syntax check', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'bash', '-n', $file ], + description => 'bash syntax check', + filename => $filename + } + ); + run_check( + { command => [ 't/test-exception-wrapper', $file, 'shellcheck', '--exclude=SC1090,SC2009,SC2126,SC2230', '--shell=bash' ], + description => 'shellcheck' + } + ); + } } elsif ( $interpreter =~ m{/bin/zsh} ) { run_check( @@ -139,20 +152,36 @@ sub process_file { ); } elsif ( $interpreter =~ m{python3} ) { - run_check( - { command => [ 'python3', '-m', 'py_compile', $file ], - description => 'python3 compile', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'python3', '-m', 'py_compile', $file ], + description => 'python3 compile', + filename => $filename + } + ); + run_check( + { command => [ 't/test-exception-wrapper', $file, 'python3', '-m', 'flake8' ], + description => 'python3-flake8' + } + ); + } } elsif ( $interpreter =~ m{python} ) { - run_check( - { command => [ 'python', '-m', 'py_compile', $file ], - description => 'python compile', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'python', '-m', 'py_compile', $file ], + description => 'python compile', + filename => $filename + } + ); + run_check( + { command => [ 't/test-exception-wrapper', $file, 'python', '-m', 'flake8' ], + description => 'python-flake8' + } + ); + } } elsif ( $interpreter =~ m{php} ) { run_check( From ebdbf1a92c5079d393754199044c60a88d576d31 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:03:07 +0200 Subject: [PATCH 488/695] Plugins weather_*: specify regular expressions as raw strings New versions of flake8 already complain about the non-raw nature of the strings (respectively the improper escaping in the regular expression). --- plugins/weather/weather_ | 6 +++--- plugins/weather/weather_press_ | 2 +- plugins/weather/weather_temp_ | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/weather/weather_ b/plugins/weather/weather_ index c083df41..ab8ebc39 100755 --- a/plugins/weather/weather_ +++ b/plugins/weather/weather_ @@ -7,9 +7,9 @@ import urllib url = 'http://www.weather.com/weather/today/%s' -re_tmp = re.compile('realTemp: "(\d+)"') -re_hum = re.compile('relativeHumidity: "(\d+)"') -re_loc = re.compile('locName: "([\w ]+)"') +re_tmp = re.compile(r'realTemp: "(\d+)"') +re_hum = re.compile(r'relativeHumidity: "(\d+)"') +re_loc = re.compile(r'locName: "([\w ]+)"') # code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] code = os.environ.get('code', sys.argv[0][(sys.argv[0].rfind('_') + 1):]) diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index ce55e3a2..0682998b 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -18,7 +18,7 @@ import re url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' -re_hpa = re.compile('Pressure.*\((\d+) hPa\)') +re_hpa = re.compile(r'Pressure.*\((\d+) hPa\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index 2addff40..dd07b1db 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -18,8 +18,8 @@ import re 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\)') +re_C = re.compile(r'Temperature:.*\((-?\d+\.?\d?) C\)') +re_DewC = re.compile(r'Dew.*\((-?\d+\.?\d?) C\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] From 24ab44ca232b94631295c9bd71e4cb7baf7f57bc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:28:20 +0200 Subject: [PATCH 489/695] Plugin tor_: fix code style issues reported by flake8 --- plugins/tor/tor_ | 38 ++++++++++++---------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index d1bb5c8f..4fee2eca 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -50,6 +50,10 @@ MIT License =head1 AUTHOR Pierre-Alain TORET + +=head1 MAGIC MARKERS + #%# family=auto + #%# capabilities=autoconf suggest ''' from __future__ import print_function @@ -75,10 +79,6 @@ default_tormaxcountries = 15 default_torport = 9051 default_torsocket = '/var/run/tor/control' -#%# family=auto -#%# capabilities=autoconf suggest - - class ConnectionError(Exception): """Error connecting to the controller""" @@ -110,11 +110,14 @@ def authenticate(controller): def gen_controller(): connect_method = os.environ.get('torconnectmethod', default_torconnectmethod) if connect_method == 'port': - return stem.control.Controller.from_port(port=int(os.environ.get('torport', default_torport))) + return stem.control.Controller.from_port(port=int(os.environ.get('torport', + default_torport))) elif connect_method == 'socket': - return stem.control.Controller.from_socket_file(path=os.environ.get('torsocket', default_torsocket)) + return stem.control.Controller.from_socket_file(path=os.environ.get('torsocket', + default_torsocket)) else: - print("env.torconnectmethod contains an invalid value. Please specify either 'port' or 'socket'.", file=sys.stderr) + print("env.torconnectmethod contains an invalid value. " + "Please specify either 'port' or 'socket'.", file=sys.stderr) sys.exit(1) @@ -149,7 +152,7 @@ class TorPlugin(object): print('no (failed to import the required python module "stem": {})'.format(e)) try: - import GeoIP + import GeoIP # noqa: F401 except ImportError as e: print('no (failed to import the required python module "GeoIP": {})'.format(e)) @@ -167,7 +170,8 @@ class TorPlugin(object): @staticmethod def suggest(): - options = ['bandwidth', 'connections', 'countries', 'dormant', 'flags', 'routers', 'traffic'] + options = ['bandwidth', 'connections', 'countries', 'dormant', 'flags', 'routers', + 'traffic'] for option in options: print(option) @@ -262,8 +266,8 @@ class TorCountries(TorPlugin): # Configure plugin self.cache_dir_name = os.environ.get('torcachedir', None) if self.cache_dir_name is not None: - self.cache_dir_name = os.path.join(self.cache_dir_name, - os.environ.get('torcachefile', default_torcachefile)) + self.cache_dir_name = os.path.join( + self.cache_dir_name, os.environ.get('torcachefile', default_torcachefile)) max_countries = os.environ.get('tormaxcountries', default_tormaxcountries) self.max_countries = int(max_countries) @@ -300,7 +304,7 @@ class TorCountries(TorPlugin): try: with open(self.cache_dir_name) as f: countries_num = json.load(f) - except: + except (IOError, ValueError): # Fallback if cache_dir_name is not set, unreadable or any other # error countries_num = self.top_countries() @@ -425,9 +429,7 @@ class TorRouters(TorPlugin): 'vlabel': 'routers', 'category': 'network', 'info': 'known Tor onion routers'} - labels = {'routers': {'label': 'routers', 'min': 0, 'type': 'GAUGE'} } - - + labels = {'routers': {'label': 'routers', 'min': 0, 'type': 'GAUGE'}} TorPlugin.conf_from_dict(graph, labels) def fetch(self): @@ -437,8 +439,6 @@ class TorRouters(TorPlugin): except stem.connection.AuthenticationFailure as e: print('Authentication failed ({})'.format(e)) return - - response = controller.get_info('ns/all', None) if response is None: print("Error while reading ns/all from Tor daemon", file=sys.stderr) @@ -524,7 +524,8 @@ def main(): elif __file__.endswith('_traffic'): provider = TorTraffic() else: - print('Unknown plugin name, try "suggest" for a list of possible ones.', file=sys.stderr) + print('Unknown plugin name, try "suggest" for a list of possible ones.', + file=sys.stderr) sys.exit(1) if param == 'config': @@ -535,5 +536,6 @@ def main(): print('Unknown parameter "{}"'.format(param), file=sys.stderr) sys.exit(1) + if __name__ == '__main__': main() diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 38d18647..f6607032 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -445,7 +445,6 @@ plugins/tcp/tcp-retransmissions plugins/tcp/tcp-states plugins/tinydns/tinydns plugins/tinydns/tinydns_err -plugins/tor/tor_ plugins/tor/tor_traffic plugins/trafic_ro/trafic_ro_24h plugins/tv/hdhomerun_ From a3319a6dc92fc25dbe6f98eab684b702befbbfff Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:29:17 +0200 Subject: [PATCH 490/695] Plugin tor_: switch to python3 --- plugins/tor/tor_ | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 4fee2eca..0c809f89 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -1,5 +1,4 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +#!/usr/bin/python3 ''' =head1 NAME tor_ @@ -56,7 +55,6 @@ Pierre-Alain TORET #%# capabilities=autoconf suggest ''' -from __future__ import print_function import collections import json import os From 117c508716501eb0c6341c75db398a993f159ffe Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:46:32 +0200 Subject: [PATCH 491/695] Plugin solr4_: format perldoc header --- plugins/solr/solr4_ | 104 ++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/plugins/solr/solr4_ b/plugins/solr/solr4_ index 3de709e3..346fe4a6 100755 --- a/plugins/solr/solr4_ +++ b/plugins/solr/solr4_ @@ -1,50 +1,62 @@ #!/usr/bin/env python -# -# Copyright (c) 2013, Antonio Verni, me.verni@gmail.com -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. -# -# Solr 4.* munin graph plugin -# Project repo: https://github.com/averni/munin-solr -# -# Plugin configuration parameters: -# -# [solr_*] -# env.host_port -# env.url -# env.qpshandler_ -# -# Example: -# [solr_*] -# env.host_port solrhost:8080 -# env.url /solr -# env.qpshandler_select /select -# -# Install plugins: -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_numdocs_core_1 -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_requesttimes_select -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_qps -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_qps_core_1_select -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_indexsize -# ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_memory -# -# +""" +=head1 NAME + +Solr 4.* munin graph plugin + + +=head1 CONFIGURATION + +Plugin configuration parameters: + +[solr_*] + env.host_port + env.url + env.qpshandler_ + +Example: +[solr_*] + env.host_port solrhost:8080 + env.url /solr + env.qpshandler_select /select + +Install plugins: + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_numdocs_core_1 + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_requesttimes_select + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_qps + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_qps_core_1_select + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_indexsize + ln -s /usr/share/munin/plugins/solr_.py /etc/munin/plugins/solr_memory + + +=head1 AUTHOR + +Copyright (c) 2013, Antonio Verni, me.verni@gmail.com + + +=head1 LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +Project repo: https://github.com/averni/munin-solr + +""" import sys From be1dfde5da20285d8e5b21b48615f5f48edd219b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:55:35 +0200 Subject: [PATCH 492/695] Plugin solr4_: fix code style issues reported by flake8 --- plugins/solr/solr4_ | 69 +++++++++++++--------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/plugins/solr/solr4_ b/plugins/solr/solr4_ index 346fe4a6..5f0637ff 100755 --- a/plugins/solr/solr4_ +++ b/plugins/solr/solr4_ @@ -59,10 +59,11 @@ Project repo: https://github.com/averni/munin-solr """ -import sys -import os -import httplib import json +import httplib +import os +import sys + def parse_params(): plugname = os.path.basename(sys.argv[0]).split('_', 2)[1:] @@ -72,7 +73,7 @@ def parse_params(): 'core': plugname[1] if len(plugname) > 1 else '', 'params': {} } - if plugname[0] in[ 'qps', 'requesttimes']: + if plugname[0] in['qps', 'requesttimes']: data = params['core'].rsplit('_', 1) handler = data.pop() params['params'] = { @@ -82,19 +83,21 @@ def parse_params(): params['core'] = '' else: params['core'] = data[0] - elif plugname[0] == 'indexsize': + elif plugname[0] == 'indexsize': params['params']['core'] = params['core'] return params + ############################################################################# # Datasources class CheckException(Exception): pass + class JSONReader: @classmethod - def readValue(cls, struct, path, convert = None): + def readValue(cls, struct, path, convert=None): if not path[0] in struct: return -1 obj = struct[path[0]] @@ -106,6 +109,7 @@ class JSONReader: return convert(obj) return obj + class SolrCoresAdmin: def __init__(self, host, solrurl): self.host = host @@ -119,7 +123,8 @@ class SolrCoresAdmin: res = conn.getresponse() data = res.read() if res.status != 200: - raise CheckException("Cores status fetch failed: %s\n%s" %( str(res.status), res.read())) + raise CheckException("Cores status fetch failed: %s\n%s" + % (str(res.status), res.read())) self.data = json.loads(data) def getCores(self): @@ -128,7 +133,7 @@ class SolrCoresAdmin: cores = JSONReader.readValue(self.data, ['status']) return cores.keys() - def indexsize(self, core = None): + def indexsize(self, core=None): if not self.data: self.fetchcores() if core: @@ -138,9 +143,11 @@ class SolrCoresAdmin: else: ret = {} for core in self.getCores(): - ret[core] = JSONReader.readValue(self.data, ['status', core, 'index', 'sizeInBytes']) + ret[core] = JSONReader.readValue(self.data, + ['status', core, 'index', 'sizeInBytes']) return ret + class SolrCoreMBean: def __init__(self, host, solrurl, core): self.host = host @@ -155,7 +162,7 @@ class SolrCoreMBean: res = conn.getresponse() data = res.read() if res.status != 200: - raise CheckException("MBean fetch failed: %s\n%s" %( str(res.status), res.read())) + raise CheckException("MBean fetch failed: %s\n%s" % (str(res.status), res.read())) raw_data = json.loads(data) data = {} self.data = { @@ -176,24 +183,24 @@ class SolrCoreMBean: res = conn.getresponse() data = res.read() if res.status != 200: - raise CheckException("System fetch failed: %s\n%s" %( str(res.status), res.read())) + raise CheckException("System fetch failed: %s\n%s" % (str(res.status), res.read())) self.data['system'] = json.loads(data) - def _readInt(self, path): return self._read(path, int) def _readFloat(self, path): return self._read(path, float) - def _read(self, path, convert = None): + def _read(self, path, convert=None): if self.data is None: self._fetch() return JSONReader.readValue(self.data, path, convert) def _readCache(self, cache): result = {} - for key, ftype in [('lookups', int), ('hits', int), ('inserts', int), ('evictions', int), ('hitratio', float)]: + for key, ftype in [('lookups', int), ('hits', int), ('inserts', int), ('evictions', int), + ('hitratio', float)]: path = ['solr-mbeans', 'CACHE', cache, 'stats', 'cumulative_%s' % key] result[key] = self._read(path, ftype) result['size'] = self._readInt(['solr-mbeans', 'CACHE', cache, 'stats', 'size']) @@ -240,6 +247,7 @@ class SolrCoreMBean: data[k] = int(data[k]) return data + ############################################################################# # Graph Templates @@ -337,6 +345,7 @@ max.label Max max.colour ff0000 """ + ############################################################################# # Graph management @@ -351,7 +360,8 @@ class SolrMuninGraph: return SolrCoreMBean(self.hostport, self.solrurl, core) def _cacheConfig(self, cacheType, cacheName): - return CACHE_GRAPH_TPL.format(core=self.params['core'], cacheType=cacheType, cacheName=cacheName) + return CACHE_GRAPH_TPL.format(core=self.params['core'], cacheType=cacheType, + cacheName=cacheName) def _format4Value(self, value): if isinstance(value, basestring): @@ -362,18 +372,20 @@ class SolrMuninGraph: return "%.6f" return "%s" - def _cacheFetch(self, cacheType, fields = None): + def _cacheFetch(self, cacheType, fields=None): fields = fields or ['size', 'lookups', 'hits', 'inserts', 'evictions'] hits_fields = ['lookups', 'hits', 'inserts'] size_fields = ['size', 'evictions'] results = [] solrmbean = self._getMBean(self.params['core']) data = getattr(solrmbean, cacheType)() - results.append('multigraph solr_{core}_{cacheType}_hit_rates'.format(core=self.params['core'], cacheType=cacheType)) + results.append('multigraph solr_{core}_{cacheType}_hit_rates' + .format(core=self.params['core'], cacheType=cacheType)) for label in hits_fields: vformat = self._format4Value(data[label]) results.append(("%s.value " + vformat) % (label, data[label])) - results.append('multigraph solr_{core}_{cacheType}_size'.format(core=self.params['core'], cacheType=cacheType)) + results.append('multigraph solr_{core}_{cacheType}_size' + .format(core=self.params['core'], cacheType=cacheType)) for label in size_fields: results.append("%s.value %d" % (label, data[label])) return "\n".join(results) @@ -397,12 +409,14 @@ class SolrMuninGraph: def qpsConfig(self): cores = self._getCores() - graph = [QPSCORE_GRAPH_TPL.format(core=c, gtype='LINESTACK1') for pos,c in enumerate(cores) ] + graph = [QPSCORE_GRAPH_TPL.format(core=c, gtype='LINESTACK1') + for pos, c in enumerate(cores)] return QPSMAIN_GRAPH_TPL.format( cores_qps_graphs='\n'.join(graph), handler=self.params['params']['handler'], core=self.params['core'], - cores_qps_cdefs='%s,%s' % (','.join(map(lambda x: 'qps_%s' % x, cores)),','.join(['+']*(len(cores)-1))), + cores_qps_cdefs='%s,%s' % (','.join(map(lambda x: 'qps_%s' % x, cores)), + ','.join(['+'] * (len(cores)-1))), gorder=','.join(cores) ) @@ -411,12 +425,14 @@ class SolrMuninGraph: cores = self._getCores() for c in cores: mbean = self._getMBean(c) - results.append('qps_%s.value %d' % (c, mbean.requestcount(self.params['params']['handler']))) + results.append('qps_%s.value %d' + % (c, mbean.requestcount(self.params['params']['handler']))) return '\n'.join(results) def requesttimesConfig(self): cores = self._getCores() - graphs = [REQUESTTIMES_GRAPH_TPL.format(core=c, handler=self.params['params']['handler']) for c in cores ] + graphs = [REQUESTTIMES_GRAPH_TPL.format(core=c, handler=self.params['params']['handler']) + for c in cores] return '\n'.join(graphs) def requesttimes(self): @@ -438,7 +454,7 @@ class SolrMuninGraph: def indexsizeConfig(self): cores = self._getCores() - graph = [ INDEXSIZECORE_GRAPH_TPL.format(core=c) for c in cores] + graph = [INDEXSIZECORE_GRAPH_TPL.format(core=c) for c in cores] return INDEXSIZE_GRAPH_TPL.format(cores=" ".join(cores), cores_config="\n".join(graph)) def indexsize(self): @@ -458,7 +474,6 @@ class SolrMuninGraph: return MEMORYUSAGE_GRAPH_TPL.format(availableram=memory['max'] * 1.05) def memory(self): - results = [] cores = self._getCores() mbean = self._getMBean(cores[0]) memory = mbean.memory() @@ -488,13 +503,13 @@ class SolrMuninGraph: def queryresultcache(self): return self._cacheFetch('queryresultcache') + if __name__ == '__main__': params = parse_params() SOLR_HOST_PORT = os.environ.get('host_port', 'localhost:8080').replace('http://', '') - SOLR_URL = os.environ.get('url', '/solr') + SOLR_URL = os.environ.get('url', '/solr') if SOLR_URL[0] != '/': SOLR_URL = '/' + SOLR_URL mb = SolrMuninGraph(SOLR_HOST_PORT, SOLR_URL, params) if hasattr(mb, params['op']): - print getattr(mb, params['op'])(params['type']) - + print(getattr(mb, params['op'])(params['type'])) diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index f6607032..5e2c29ef 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -421,7 +421,6 @@ plugins/solaris/io_disk plugins/solaris/zones_cpu plugins/solaris/zones_mem plugins/solr/solr -plugins/solr/solr4_ plugins/solr/solrmulticore plugins/spamassasin/sa-learn plugins/sphinx/sphinx_documents From a730d96a1f8882fc8a3356774081c60fa83bfa7f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:41:15 +0200 Subject: [PATCH 493/695] Remove plugin solr The plugin saw only one substantial commit (its initial addition to the repository). This very first version of the plugin was already broken (undefined variable "core1") and was never fixed. Thus it feels sane to assume, that the plugin was never used. --- plugins/solr/solr | 111 --------------------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 112 deletions(-) delete mode 100755 plugins/solr/solr diff --git a/plugins/solr/solr b/plugins/solr/solr deleted file mode 100755 index 836da03a..00000000 --- a/plugins/solr/solr +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python -import sys, os -import urllib2 -try: - from xml.etree import cElementTree as ET -except ImportError: - try: - import cElementTree as ET - except ImportError: - sys.exit(1) - -SOLR_PORT=8389 -SOLR_HOST="localhost" -# Parameters: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# Magic markers (Used by munin-config and some installation scripts. -# Optional): -# author: Tarjei Huse (tarjei - at - kraken.no) -# -#%# family=auto -#%# capabilities=autoconf - -def parseArgs(): - "Parses the name of the file " - parts = sys.argv[0].split("_") - params = { - 'cores' : [core1], - 'valueName' : "avgTimePerRequest" - } - if len(parts) == 1: - return params - - params['valueName'] = parts[1] - if len(parts) < 3: - return params - params['cores'] = parts[2:] - return params - -def printGraphdef(name, label, info, _min, _max, _type): - - print "%s.label %s" % (name, label) -# print "%s.info %s" % (name, info) - print "%s.max %d" % (name, _max) - print "%s.min %d" % (name, _min) - print "%s.type %s" % (name, _type) -def fetchUrl(core): - if ("URL" in os.environ): - URL=os.environ['URL'] - else: - URL="http://%s:%d/%s/admin/stats.jsp" - - response = urllib2.urlopen(URL % (SOLR_HOST, SOLR_PORT, core)) - return parse(response) - -def parse(response): - root = ET.parse(response) - queues = root.findall("/solr-info/QUERYHANDLER/entry") - return queues - -def fetchFile(): - f = open("/tmp/stats.jsp.html") - return parse(f) - -def getEntry(entries, entryName): - for entry in entries: - name = entry.findtext("name").strip() - if (name != entryName): - continue - return entry.find("stats") - -def getValue(entry, valueName): - for stat in entry: - if stat.get('name') == valueName: - return stat.text - #print "Could not find %s for entry" % valueName - return 0 - -if len(sys.argv) > 1: - if sys.argv[1]== "autoconf": - # check connection - sys.exit(0) - elif sys.argv[1] == "config": - params = parseArgs() - - print 'graph_title Solr %s' % params['valueName'] - print 'graph_args -l 0 ' - print 'graph_vlabel Size %s' % params['valueName'] - print 'graph_category search' - print 'graph_info Info for cores: %s' % ( ",".join(params['cores'])) - - for core in params['cores']: - #print core, params['valueName'] - print "%s-%s.label %s-%s" % (core, params['valueName'], params['valueName'], core) - print "%s-%s.type GAUGE" % (core, params['valueName']) - print "%s-%s.min 0" % (core, params['valueName']) - sys.exit(0) - - -params = parseArgs() -for core in params['cores']: - #print core, params['valueName'] - queues = fetchUrl(core) - searcher = getEntry(queues, "org.apache.solr.handler.StandardRequestHandler") - value = getValue(searcher, params['valueName']).strip() - print "%s-%s.value %s" % (core, params['valueName'], value) - - -sys.exit(0) diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 5e2c29ef..43975fd6 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -420,7 +420,6 @@ plugins/snmp/snmp__webthermometer plugins/solaris/io_disk plugins/solaris/zones_cpu plugins/solaris/zones_mem -plugins/solr/solr plugins/solr/solrmulticore plugins/spamassasin/sa-learn plugins/sphinx/sphinx_documents From 8b69c8a1fe0dca7df8845fad1da8804b5c7b9854 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 13 Aug 2019 23:58:19 +0200 Subject: [PATCH 494/695] Remove plugin solrmulticore The plugin was introduced in 2011 and superseded by the solr4_ plugin introduced in 2013. --- plugins/solr/solrmulticore | 181 --------------------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 182 deletions(-) delete mode 100755 plugins/solr/solrmulticore diff --git a/plugins/solr/solrmulticore b/plugins/solr/solrmulticore deleted file mode 100755 index a3c22149..00000000 --- a/plugins/solr/solrmulticore +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) Rodolphe Franceschi -# -# 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 2 -# 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, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# -# This plugin monitors a SOLR server configured for multicore by automatically -# getting core names from SOLR default page. -# -# Tested on SOLR 1.4.0 -# -# Parameters: -# config (required) -# autoconf (optional - used by munin-config) -# -# For the full list of options, refer to PLUGINOPTIONSLIST variable -# -# Example of symlink creation on Debian Lenny -# ln -s /usr/share/munin/plugins/solrmulticore_ /etc/munin/plugins/solrmulticore_avgRequestsPerSecond -# -# Magic markers (Used by munin-config and some installation scripts. -# Optional): -#%# family=auto -#%# capabilities=autoconf - -import sys, os -import urllib2 -import HTMLParser, urllib - -try: - from xml.etree import cElementTree as ET -except ImportError: - try: - import cElementTree as ET - except ImportError: - sys.exit(1) - - -## GLOBALS -SOLR_PORT = 8983 -SOLR_HOST = "localhost" -PLUGINOPTIONSLIST = { - "avgRequestsPerSecond" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Average requests per second' }, - "avgTimePerRequest" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Average time per request' }, - "errors" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Number of errors' }, - "timeouts" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Number of timeouts' }, - "requests" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler', 'label': 'Total number of requests' }, - "numDocs" : { 'xmlpath': '/solr-info/CORE/entry', 'xmlparententryname': 'searcher', 'label': 'Number of documents' }, -# "" : { 'xmlpath': '/solr-info/QUERYHANDLER/entry', 'xmlparententryname': 'org.apache.solr.handler.StandardRequestHandler' }, -} - -# Automatic extraction of core names from SOLR default page if empty array -SOLR_CORES = [] -# If you do not want automatic gathering, feel free to put your array manually -#SOLR_CORES = [ "core0", "core1" ] - - -## FUNCTIONS -def getSolrCoreNameList(): - url = "http://%s:%s/solr/" % (SOLR_HOST, SOLR_PORT) - class linkParser(HTMLParser.HTMLParser): - def __init__(self): - HTMLParser.HTMLParser.__init__(self) - self.links = [] - def handle_starttag(self, tag, attrs): - if tag=='a': - self.links.append(dict(attrs)['href']) - - htmlSource = urllib.urlopen(url).read(200000) - p = linkParser() - p.feed(htmlSource) - - # Remove link to . (First one !) - p.links.pop(0) - - dealcores = [ ] - - for link in p.links: - dealcores.append ( link.split("/")[0] ) - - return dealcores - - - -def parseArgs(): - "Parses the name of the file " - parts = sys.argv[0].split("_") - - params = { } - params['valueName'] = parts[1] - - # Automatic / Manual Mode for core names - if (len(SOLR_CORES) == 0): - params['cores'] = getSolrCoreNameList() - else: - params['cores'] = SOLR_CORES - - params['cores'].sort() - - return params - -# For multicore / Solr 1.3, URL is like that -def fetchUrl(core, xmlPath): - URL="http://%s:%d/solr/%s/admin/stats.jsp" - - URLFULL = URL % (SOLR_HOST, SOLR_PORT, core) - response = urllib2.urlopen(URLFULL) - return parseXmlResponse(response, xmlPath) - -def parseXmlResponse(response, xmlPath): - root = ET.parse(response) - queues = root.findall(xmlPath) - return queues - -#def fetchFile(): -# f = open("/tmp/stats.jsp.html") -# return parseXmlResponse(f) - -def getEntry(entries, entryName): - for entry in entries: - name = entry.findtext("name").strip() - if (name != entryName): - continue - return entry.find("stats") - -def getValue(entry, valueName): - for stat in entry: - if stat.get('name') == valueName: - return stat.text - #print "Could not find %s for entry" % valueName - return 0 - - - - -## MAIN -if len(sys.argv) > 1: - if sys.argv[1]== "autoconf": - # check connection - sys.exit(0) - elif sys.argv[1] == "config": - params = parseArgs() - - # Extracting Generic graph datas - print 'graph_title %s' % ( PLUGINOPTIONSLIST[params['valueName']]['label'] ) - print "graph_args --base 1000"; - print 'graph_vlabel Size %s' % params['valueName'] - print 'graph_category search' - print 'graph_info Info for cores: %s' % ( ",".join(params['cores']) ) - - # Iterations for core datas - for core in params['cores']: - #print core, params['valueName'] - print "%s.label %s" % (core, core) - print "%s.type GAUGE" % (core) - print "%s.min 0" % (core) - sys.exit(0) - - -params = parseArgs() -for core in params['cores']: - #print core, params['valueName'] - queues = fetchUrl(core, PLUGINOPTIONSLIST[params['valueName']]['xmlpath']) - searcher = getEntry(queues, PLUGINOPTIONSLIST[params['valueName']]['xmlparententryname']) - value = getValue(searcher, params['valueName']).strip() - print "%s.value %s" % (core, value) - -sys.exit(0) diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 43975fd6..ab11b793 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -420,7 +420,6 @@ plugins/snmp/snmp__webthermometer plugins/solaris/io_disk plugins/solaris/zones_cpu plugins/solaris/zones_mem -plugins/solr/solrmulticore plugins/spamassasin/sa-learn plugins/sphinx/sphinx_documents plugins/squid/squid_efficiency From dacb55010a847d9f360e4cd60b50a0fb5ff7786a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:02:40 +0200 Subject: [PATCH 495/695] Plugin snmp__synology: fix code style issues reported by flake8 --- plugins/synology/snmp__synology | 149 +++++++++++---------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 76 insertions(+), 74 deletions(-) diff --git a/plugins/synology/snmp__synology b/plugins/synology/snmp__synology index 00f98d95..b81dee6a 100755 --- a/plugins/synology/snmp__synology +++ b/plugins/synology/snmp__synology @@ -29,8 +29,8 @@ has been configured correctly. =head1 MAGIC MARKERS - #%# family=snmpauto - #%# capabilities=snmpconf + #%# family=snmpauto + #%# capabilities=snmpconf =head1 VERSION @@ -63,60 +63,63 @@ disktable_model = '1.3.6.1.4.1.6574.2.1.1.3' disktable_temp = '1.3.6.1.4.1.6574.2.1.1.6' sys_temperature = '1.3.6.1.4.1.6574.1.2.0' + class SynologySNMPClient(object): - def __init__(self, host, port, community): - self.hostname = host - self.transport = cmdgen.UdpTransportTarget((host, int(port))) - self.auth = cmdgen.CommunityData('test-agent', community) - self.gen = cmdgen.CommandGenerator() + def __init__(self, host, port, community): + self.hostname = host + self.transport = cmdgen.UdpTransportTarget((host, int(port))) + self.auth = cmdgen.CommunityData('test-agent', community) + self.gen = cmdgen.CommandGenerator() - def _get_disks(self): - disk_table = '1.3.6.1.4.1.6574.2.1' - errorIndication, errorStatus, errorIndex, varBindTable = self.gen.bulkCmd( - self.auth, - self.transport, - 0, 24, - disk_table) + def _get_disks(self): + disk_table = '1.3.6.1.4.1.6574.2.1' + errorIndication, errorStatus, errorIndex, varBindTable = self.gen.bulkCmd( + self.auth, + self.transport, + 0, 24, + disk_table) - if errorIndication: - logging.error("SNMP bulkCmd for devices failed: %s, %s, %s" % (errorIndication, errorStatus, errorIndex)) - return + if errorIndication: + logging.error("SNMP bulkCmd for devices failed: %s, %s, %s", + errorIndication, errorStatus, errorIndex) + return - devices = {} - for row in varBindTable: - for oid, value in row: - oid = str(oid) - if not oid.startswith(disk_table): - continue + devices = {} + for row in varBindTable: + for oid, value in row: + oid = str(oid) + if not oid.startswith(disk_table): + continue - disk_id = oid[oid.rindex('.')+1:] + disk_id = oid[oid.rindex('.')+1:] - values = devices.get(disk_id, [None, None, None]) - if oid.startswith(disktable_id): - values[0] = str(value).strip() - if oid.startswith(disktable_model): - values[1] = str(value).strip() - if oid.startswith(disktable_temp): - values[2] = int(value) - devices[disk_id] = values + values = devices.get(disk_id, [None, None, None]) + if oid.startswith(disktable_id): + values[0] = str(value).strip() + if oid.startswith(disktable_model): + values[1] = str(value).strip() + if oid.startswith(disktable_temp): + values[2] = int(value) + devices[disk_id] = values - for x in sorted(devices.keys()): - yield tuple([x] + devices[x]) + for x in sorted(devices.keys()): + yield tuple([x] + devices[x]) - def _get_sys_temperature(self): - errorIndication, errorStatus, errorIndex, varBindTable = self.gen.getCmd( - self.auth, - self.transport, - sys_temperature) + def _get_sys_temperature(self): + errorIndication, errorStatus, errorIndex, varBindTable = self.gen.getCmd( + self.auth, + self.transport, + sys_temperature) - if errorIndication: - logging.error("SNMP getCmd for %s failed: %s, %s, %s" % (sys_temperature, errorIndication, errorStatus, errorIndex)) - return None + if errorIndication: + logging.error("SNMP getCmd for %s failed: %s, %s, %s", + sys_temperature, errorIndication, errorStatus, errorIndex) + return None - return int(varBindTable[0][1]) + return int(varBindTable[0][1]) - def print_config(self): - print """multigraph synology_hdd_temperatures + def print_config(self): + print """multigraph synology_hdd_temperatures host_name {hostname} graph_title HDD temperatures on {hostname} graph_vlabel Temperature in °C @@ -124,14 +127,13 @@ graph_args --base 1000 graph_category sensors graph_info HDD temperatures on {hostname}""".format(hostname=self.hostname) - for id, name, model, temp in self._get_disks(): - print """disk{disk_id}.info Temperature of {name} ({model}) + for id, name, model, temp in self._get_disks(): + print """disk{disk_id}.info Temperature of {name} ({model}) disk{disk_id}.label {name} ({model}) disk{disk_id}.type GAUGE disk{disk_id}.min 0""".format(disk_id=id, name=name, model=model) - - print """multigraph synology_sys_temperature + print """multigraph synology_sys_temperature host_name {hostname} graph_title System temperatures of {hostname} graph_vlabel Temperature in °C @@ -144,13 +146,13 @@ sys_temp.type GAUGE sys_temp.min 0 """.format(hostname=self.hostname) - def execute(self): - print """multigraph synology_hdd_temperatures""" - for id, name, model, temp in self._get_disks(): - print """disk{disk_id}.value {temp}""".format(disk_id=id, temp=temp) + def execute(self): + print """multigraph synology_hdd_temperatures""" + for id, name, model, temp in self._get_disks(): + print """disk{disk_id}.value {temp}""".format(disk_id=id, temp=temp) - print """multigraph synology_sys_temperature""" - print "sys_temp.value {temp}".format(temp=self._get_sys_temperature()) + print """multigraph synology_sys_temperature""" + print "sys_temp.value {temp}".format(temp=self._get_sys_temperature()) host = None @@ -159,30 +161,31 @@ community = os.getenv('community', None) debug = bool(os.getenv('MUNIN_DEBUG', os.getenv('DEBUG', 0))) if debug: - logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-7s %(message)s') + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-7s %(message)s') try: - match = re.search("^(?:|.*\/)snmp_([^_]+)_synology$", sys.argv[0]) - host = match.group(1) - match = re.search("^([^:]+):(\d+)$", host) - if match is not None: - host = match.group(1) - port = match.group(2) + match = re.search(r"^(?:|.*\/)snmp_([^_]+)_synology$", sys.argv[0]) + host = match.group(1) + match = re.search(r"^([^:]+):(\d+)$", host) + if match is not None: + host = match.group(1) + port = match.group(2) except Exception as ex: - logging.error("Caught exception: %s" % ex) + logging.error("Caught exception: %s" % ex) if "snmpconf" in sys.argv[1:]: - print "require 1.3.6.1.4.1.6574.2.1.1" - sys.exit(0) + print "require 1.3.6.1.4.1.6574.2.1.1" + sys.exit(0) else: - if not (host and port and community): - print "# Bad configuration. Cannot run with Host=%s, port=%s and community=%s" % (host, port, community) - sys.exit(1) + if not (host and port and community): + print("# Bad configuration. Cannot run with Host=%s, port=%s and community=%s" + % (host, port, community)) + sys.exit(1) - c = SynologySNMPClient(host, port, community) + c = SynologySNMPClient(host, port, community) - if "config" in sys.argv[1:]: - c.print_config() - else: - c.execute() + if "config" in sys.argv[1:]: + c.print_config() + else: + c.execute() diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index ab11b793..6665211e 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -431,7 +431,6 @@ plugins/streaming/packetship_ plugins/swift/swift-async_ plugins/swift/swift-quarantined_ plugins/swift/swift-replication-time_ -plugins/synology/snmp__synology plugins/system/auth plugins/system/blockhosts plugins/systemd/systemd_units From ba732fd7ec3226e9f15d81df8d41e343b5ac58fc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:09:29 +0200 Subject: [PATCH 496/695] Plugin snmp__airport: fix code style issues reported by flake8 --- plugins/snmp/snmp__airport | 37 +++++++++++++++------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/plugins/snmp/snmp__airport b/plugins/snmp/snmp__airport index a68188d6..adc16e4b 100755 --- a/plugins/snmp/snmp__airport +++ b/plugins/snmp/snmp__airport @@ -49,24 +49,27 @@ Please install the Python bindings for libsnmp. On Debian/Ubuntu machines this package is named 'libsnmp-python'""" sys.exit(-3) -DEBUG=None -CMDS=['type', 'rates', 'time', 'lastrefresh', 'signal', 'noise', 'rate', 'rx', - 'tx', 'rxerr', 'txerr'] -CMD=None -DESTHOST=None -NUMCLIENTS=None -NUMDHCPCLIENTS=None -WANIFINDEX=None +DEBUG = None +CMDS = ['type', 'rates', 'time', 'lastrefresh', 'signal', 'noise', 'rate', 'rx', 'tx', 'rxerr', + 'txerr'] +CMD = None +DESTHOST = None +NUMCLIENTS = None +NUMDHCPCLIENTS = None +WANIFINDEX = None + def dbg(text): """Print some debugging text if DEBUG=1 is in our environment""" if DEBUG is not None: print "DEBUG: %s" % text + def usage(): """Print some usage information about ourselves""" print __doc__ + def parseName(name): """Examing argv[0] (i.e. the name of this script) for the hostname we should be talking to and the type of check we want to run. The hostname should be @@ -88,6 +91,7 @@ def parseName(name): dbg("parseName found an inconsistent name: '%s'" % name) return None + def tableToDict(table, num): """The netsnmp library returns a tuple with all of the data, it is not in any way formatted into rows. This function converts the data into a structured @@ -129,6 +133,7 @@ def tableToDict(table, num): return clientTable + def getNumClients(): """Returns the number of wireless clients connected to the Airport we are examining. This will only ever be polled via SNMP once per invocation. If @@ -148,6 +153,7 @@ def getNumClients(): dbg("getNumClients: found %d clients" % NUMCLIENTS) return NUMCLIENTS + def getNumDHCPClients(): """Returns the number of DHCP clients with currently active leases. This will only ever be polled via SNMP once per invocation. If called a second @@ -167,6 +173,7 @@ def getNumDHCPClients(): dbg("getNumDHCPClients: found %d clients" % NUMDHCPCLIENTS) return NUMDHCPCLIENTS + def getExternalInterface(): """Returns the index of the WAN interface of the Airport. This will only ever be polled via SNMP once per invocation, per getNum*Clients(). See @@ -176,8 +183,8 @@ def getExternalInterface(): if WANIFINDEX is None: interfaces = list(netsnmp.snmpwalk(netsnmp.Varbind(iFaceNames), - Version=2, DestHost=DESTHOST, - Community='public')) + Version=2, DestHost=DESTHOST, + Community='public')) dbg("getExternalInterface: found interfaces: %s" % interfaces) try: WANIFINDEX = interfaces.index('mgi1') + 1 @@ -189,14 +196,17 @@ def getExternalInterface(): dbg("getExternalInterface: found mgi1 at index: %d" % WANIFINDEX) return WANIFINDEX + def getExternalInOctets(): """Returns the number of octets of inbound traffic on the WAN interface""" return getOctets('In') + def getExternalOutOctets(): """Returns the number of octets of outbound traffic on the WAN interface""" return getOctets('Out') + def getOctets(direction): """Returns the number of octets of traffic on the WAN interface in the requested direction""" @@ -211,6 +221,7 @@ def getOctets(direction): Version=2, DestHost=DESTHOST, Community='public')[0]) + def getWanSpeed(): """Returns the speed of the WAN interface""" ifSpeed = "1.3.6.1.2.1.2.2.1.5.%s" % getExternalInterface() @@ -219,12 +230,13 @@ def getWanSpeed(): wanSpeed = int(netsnmp.snmpget(netsnmp.Varbind(ifSpeed), Version=2, DestHost=DESTHOST, Community='public')[0]) - except: + except: # noqa: E722 (TODO: specify the expected exceptions) dbg("getWanSpeed: Unable to probe for data, defaultint to 10000000") wanSpeed = 10000000 return wanSpeed + def getData(): """Returns a dictionary populated with all of the wireless clients and their metadata""" @@ -246,6 +258,7 @@ def getData(): return clients + def main(clients=None): """This function fetches metadata about wireless clients if needed, then displays whatever values have been requested""" @@ -263,6 +276,7 @@ def main(clients=None): for client in clients: print "MAC_%s.value %s" % (client, clients[client][CMD]) + if __name__ == '__main__': clients = None if os.getenv('DEBUG') == '1': @@ -352,4 +366,3 @@ send.min 0""" % (speed, speed) sys.exit(0) else: main(clients) - diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 6665211e..b14a17ad 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -412,7 +412,6 @@ plugins/sge/sge_job_stats plugins/sge/sge_queue_ plugins/sge/sge_queue_xml_ plugins/smstools/smstools_ -plugins/snmp/snmp__airport plugins/snmp/snmp___bri_se_ plugins/snmp/snmp__brocade_ifs plugins/snmp/snmp__fn From 275c82f47957ef86558217e78dba174cbc95dbae Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:28:44 +0200 Subject: [PATCH 497/695] Plugin btcguild_hashrate_: fix code style issues reported by flake8 --- plugins/currency/bitcoin/btcguild_hashrate_ | 7 +++---- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/currency/bitcoin/btcguild_hashrate_ b/plugins/currency/bitcoin/btcguild_hashrate_ index 25e9f2a0..edae22ea 100755 --- a/plugins/currency/bitcoin/btcguild_hashrate_ +++ b/plugins/currency/bitcoin/btcguild_hashrate_ @@ -7,7 +7,7 @@ import urllib2 import json URL = 'https://www.btcguild.com/api.php?api_key=' -API_KEY = sys.argv[0][(sys.argv[0].rfind('_')+1):] +API_KEY = sys.argv[0][(sys.argv[0].rfind('_')+1):] STATS = URL + API_KEY @@ -17,8 +17,8 @@ command = '' if len(sys.argv) > 1: command = sys.argv[1] -header = {'User-Agent':'Mozilla/5.0'} -request = urllib2.Request(STATS,headers=header) +header = {'User-Agent': 'Mozilla/5.0'} +request = urllib2.Request(STATS, headers=header) mining_stats_raw = urllib2.urlopen(request) mining_stats = json.load(mining_stats_raw) workers = mining_stats['workers'] @@ -38,4 +38,3 @@ for worker in workers: hash_rate = workers[worker]['hash_rate'] label = workers[worker]['worker_name'] print label + ".value %d" % int(hash_rate) - diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index b14a17ad..3c347618 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -58,7 +58,6 @@ plugins/cpu/cpuutil plugins/cpu/multicpu1sec plugins/cpu/process_count plugins/cpu/process_cpushare -plugins/currency/bitcoin/btcguild_hashrate_ plugins/currency/bitcoin/slush_hashrate_ plugins/currency/bitcoin/slush_reward_ plugins/currency/ethereum/ethermine_hashrate_ From c466adc4d335d9b8adf24646fc557f64976a5ba1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:29:25 +0200 Subject: [PATCH 498/695] Plugin ethermine_hashrate_: fix code style issues reported by flake8 --- plugins/currency/ethereum/ethermine_hashrate_ | 9 +++++---- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/currency/ethereum/ethermine_hashrate_ b/plugins/currency/ethereum/ethermine_hashrate_ index 4b640409..550743b8 100755 --- a/plugins/currency/ethereum/ethermine_hashrate_ +++ b/plugins/currency/ethereum/ethermine_hashrate_ @@ -20,8 +20,8 @@ ethermine_hashrate__ =head1 INTERPRETATION -This plugin shows the ethermine.org mining pool hashrate (MH/s) of a given ethereum address and rig name. -Hashrate is queried via ethermine.org API L. +This plugin shows the ethermine.org mining pool hashrate (MH/s) of a given ethereum address and rig +name. Hashrate is queried via ethermine.org API L. =head1 VERSION @@ -102,11 +102,12 @@ except ValueError: try: workers = mining_stats['workers'] -except: +except KeyError: print("JSON result error!", file=sys.stderr) sys.exit(9) -# ethermine.org sometimes has caching errors. You can see data from other miner. Always check your rig name. +# ethermine.org sometimes has caching errors. You can see data from other miner. Always check your +# rig name. for worker in workers: if workers[worker]['worker'] == miner: hash_rate = workers[worker]['hashrate'] diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 3c347618..cd0d0d11 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -60,7 +60,6 @@ plugins/cpu/process_count plugins/cpu/process_cpushare plugins/currency/bitcoin/slush_hashrate_ plugins/currency/bitcoin/slush_reward_ -plugins/currency/ethereum/ethermine_hashrate_ plugins/currency/nanopool/nanopool_ plugins/currency/zcash/zcash_flypool_hashrate_ plugins/cyrus/cyrus-imapd From ab3a58375616ff2cd814ba3920b174e8270fdf0f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:29:57 +0200 Subject: [PATCH 499/695] Plugin icecast2_: fix code style issues reported by flake8 --- plugins/icecast/icecast2_ | 175 +++++++++++---------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/plugins/icecast/icecast2_ b/plugins/icecast/icecast2_ index 91cd00fa..754c8122 100755 --- a/plugins/icecast/icecast2_ +++ b/plugins/icecast/icecast2_ @@ -1,6 +1,12 @@ #! /usr/bin/python # -*- coding: iso-8859-1 -*- +import os +import sys +import urllib2 +from xml.dom import minidom + + # Hostname of Icecast server # Just canonical name, no http:// nor ending / host = "foo.bar.com" @@ -21,14 +27,13 @@ oggbitrates = [56, 128, 172] # well the total number of listeners for any configured stream. # For each stream with multiple bitrates, create one # icecast2_streamname -# If the name contains a "-" exchange it with a "_", and the script will change it back for you. This is to satisfy internal requirements of Munin. +# If the name contains a "-" exchange it with a "_", and the script will change it back for you. +# This is to satisfy internal requirements of Munin. # For each streamname, the plugin will check for the configured bitrates # Expecting the mountpoints to be on the form of # /streamname_ for mp3 # /streamname_.ogg for Ogg/Vorbis -import urllib2, os.path, time, sys -from xml.dom import minidom def hent_XML(): auth_handler = urllib2.HTTPBasicAuthHandler() @@ -45,106 +50,108 @@ def hent_XML(): xmldoc = minidom.parseString(xml) xmldoc = xmldoc.firstChild - #Totalt antall lyttere + # Totalt antall lyttere total_lyttere = xmldoc.getElementsByTagName("clients")[0].firstChild.nodeValue - #Totalt antall kilder + # Totalt antall kilder total_kilder = xmldoc.getElementsByTagName("sources")[0].firstChild.nodeValue - #Status for enkelt strøm + # Status for enkelt strøm sources = xmldoc.getElementsByTagName("source") sourcelist = {} for source in sources: mount = source.getAttribute("mount") listeners = source.getElementsByTagName("listeners")[0].firstChild.nodeValue name = source.getElementsByTagName("server_name")[0].firstChild.nodeValue - mount = mount.replace("-", "_") + mount = mount.replace("-", "_") sourcelist[mount[1:]] = (listeners, name) sourcename = sys.argv[0].split("/")[-1][len("icecast2_"):] if len(sys.argv) == 1: - sys.argv.append("") + sys.argv.append("") if sys.argv[1] == "autoconf": - print "yes" + print "yes" elif sys.argv[1] == "config": - if sourcename == "total": - print "graph_title Totalt antall lyttere" + if sourcename == "total": + print "graph_title Totalt antall lyttere" print "graph_vlabel lyttere" - print "graph_category streaming" - print "totallyttere.label Totalt antall lyttere" - print "totalkilder.label Totalt antall kilder" - chanlist = {} - for a, b, filelist in os.walk("/etc/munin/plugins"): - for file in filelist: - if file.find("icecast2_") != -1: - channelname = file[len("icecast2_"):] - if channelname != "total" and chanlist.has_key(channelname) != 1: - chanlist[channelname] = 0 - chanlist = chanlist.keys() - chanlist.sort() - for chan in chanlist: - graphtitle = "" - for key in sourcelist.keys(): - if key.find(chan) != -1: - l, graphtitle = sourcelist[key] - break - if graphtitle == "": - graphtitle = chan - print "%s.label %s" % (chan, graphtitle) + print "graph_category streaming" + print "totallyttere.label Totalt antall lyttere" + print "totalkilder.label Totalt antall kilder" + chanlist = {} + for a, b, filelist in os.walk("/etc/munin/plugins"): + for file in filelist: + if file.find("icecast2_") != -1: + channelname = file[len("icecast2_"):] + if channelname != "total" and channelname not in chanlist: + chanlist[channelname] = 0 + chanlist = chanlist.keys() + chanlist.sort() + for chan in chanlist: + graphtitle = "" + for key in sourcelist.keys(): + if key.find(chan) != -1: + l, graphtitle = sourcelist[key] + break + if graphtitle == "": + graphtitle = chan + print "%s.label %s" % (chan, graphtitle) - else: - sumstring = "" - graphtitle = "" - for key in sourcelist.keys(): - if key.find(sourcename) != -1: - l, graphtitle = sourcelist[key] - break - if graphtitle == "": - graphtitle = sourcename - print "graph_title %s" % graphtitle - print "graph_vlabel lyttere" - print "graph_category streaming" - for bitrate in mp3bitrates: - print "%s_%s.label %s-%s" % (sourcename, bitrate, "/" + sourcename.replace("_", "-"), bitrate) - sumstring += "%s_%s " % (sourcename, bitrate) - print "%s_%s.critical -0.5:" % (sourcename, bitrate) - for bitrate in oggbitrates: - print "%s_%s_ogg.label %s-%s.ogg" % (sourcename, bitrate, "/" + sourcename.replace("_", "-"), bitrate) - print "%s_%s_ogg.critical -0.5:" % (sourcename, bitrate) - sumstring += "%s_%s_ogg " % (sourcename, bitrate) - print "%slyttere.label Totalt antall lyttere" % sourcename + else: + sumstring = "" + graphtitle = "" + for key in sourcelist.keys(): + if key.find(sourcename) != -1: + l, graphtitle = sourcelist[key] + break + if graphtitle == "": + graphtitle = sourcename + print "graph_title %s" % graphtitle + print "graph_vlabel lyttere" + print "graph_category streaming" + for bitrate in mp3bitrates: + print "%s_%s.label %s-%s" % (sourcename, bitrate, + "/" + sourcename.replace("_", "-"), bitrate) + sumstring += "%s_%s " % (sourcename, bitrate) + print "%s_%s.critical -0.5:" % (sourcename, bitrate) + for bitrate in oggbitrates: + print "%s_%s_ogg.label %s-%s.ogg" % (sourcename, bitrate, + "/" + sourcename.replace("_", "-"), bitrate) + print "%s_%s_ogg.critical -0.5:" % (sourcename, bitrate) + sumstring += "%s_%s_ogg " % (sourcename, bitrate) + print "%slyttere.label Totalt antall lyttere" % sourcename print "%slyttere.sum %s" % (sourcename, sumstring) elif sys.argv[1] != "config": - if sourcename == "total": - print "totallyttere.value %s" % total_lyttere - print "totalkilder.value %s" % total_kilder - statslist = {} - for a, b, filelist in os.walk("/etc/munin/plugins"): - for file in filelist: - if file.find("icecast2_") != -1: - channelname = file[len("icecast2_"):] - if channelname != "total" and statslist.has_key(channelname) != 1: - statslist[channelname] = 0 + if sourcename == "total": + print "totallyttere.value %s" % total_lyttere + print "totalkilder.value %s" % total_kilder + statslist = {} + for a, b, filelist in os.walk("/etc/munin/plugins"): + for file in filelist: + if file.find("icecast2_") != -1: + channelname = file[len("icecast2_"):] + if channelname != "total" and channelname not in statslist: + statslist[channelname] = 0 - for source in sourcelist: - listeners, name = sourcelist[source] - if not statslist.has_key(source[:source.rfind("_")]): - statslist[source[:source.rfind("_")]] = 0 - statslist[source[:source.rfind("_")]] += int(listeners) - for stat in statslist: - print "%s.value %s" % (stat, statslist[stat]) - else: - for bitrate in mp3bitrates: - if sourcelist.has_key("%s_%s" % (sourcename, bitrate)): - listeners = sourcelist["%s_%s" % (sourcename, bitrate)][0] - print listeners - else: - listeners = -1 - print "%s_%s.value %s" % (sourcename, bitrate, listeners) - for bitrate in oggbitrates: - if sourcelist.has_key("%s_%s.ogg" % (sourcename, bitrate)): - listeners = sourcelist["%s_%s.ogg" % (sourcename, bitrate)][0] - else: - listeners = -1 - print "%s_%s_ogg.value %s" % (sourcename, bitrate, listeners) + for source in sourcelist: + listeners, name = sourcelist[source] + if source[:source.rfind("_")] not in statslist: + statslist[source[:source.rfind("_")]] = 0 + statslist[source[:source.rfind("_")]] += int(listeners) + for stat in statslist: + print "%s.value %s" % (stat, statslist[stat]) + else: + for bitrate in mp3bitrates: + if ("%s_%s" % (sourcename, bitrate)) in sourcelist: + listeners = sourcelist["%s_%s" % (sourcename, bitrate)][0] + print listeners + else: + listeners = -1 + print "%s_%s.value %s" % (sourcename, bitrate, listeners) + for bitrate in oggbitrates: + if ("%s_%s.ogg" % (sourcename, bitrate)) in sourcelist: + listeners = sourcelist["%s_%s.ogg" % (sourcename, bitrate)][0] + else: + listeners = -1 + print "%s_%s_ogg.value %s" % (sourcename, bitrate, listeners) else: print sys.argv[1] diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index cd0d0d11..616e192a 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -157,7 +157,6 @@ plugins/http/wget_page plugins/i2p/i2p_ plugins/icecast/icecast_ plugins/icecast/icecast2 -plugins/icecast/icecast2_ plugins/icecast/icecast2_all plugins/icecast/icecast2_stats_ plugins/imapproxy/imapproxy_multi From 8b24b649603ff5254637e2463a19892a5368329e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:36:00 +0200 Subject: [PATCH 500/695] Plugin icecast2_all: fix code style issues reported by flake8 --- plugins/icecast/icecast2_all | 40 +++++++++++++--------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/plugins/icecast/icecast2_all b/plugins/icecast/icecast2_all index 21549d58..d9745e8b 100755 --- a/plugins/icecast/icecast2_all +++ b/plugins/icecast/icecast2_all @@ -1,5 +1,15 @@ #! /usr/bin/python # -*- coding: iso-8859-1 -*- +""" +This plugin shows the statistics of every source currently connected to the Icecast2 server. +See the Icecast2_ plugin for specific mountpoint plugin. +""" + + +import sys +import urllib2 +from xml.dom import minidom + # Hostname of Icecast server # Just canonical name, no http:// nor ending / @@ -9,10 +19,6 @@ username = "admin" password = "" realm = "Icecast2 Server" -# This plugin shows the statistics of every source currently connected to the Icecast2 server. See the Icecast2_ plugin for specific mountpoint plugin. - -import urllib2, os.path, time, sys -from xml.dom import minidom def hent_XML(): auth_handler = urllib2.HTTPBasicAuthHandler() @@ -29,41 +35,41 @@ def hent_XML(): xmldoc = minidom.parseString(xml) xmldoc = xmldoc.firstChild - #Totalt antall lyttere + # Totalt antall lyttere total_lyttere = xmldoc.getElementsByTagName("clients")[0].firstChild.nodeValue - #Totalt antall kilder + # Totalt antall kilder total_kilder = xmldoc.getElementsByTagName("sources")[0].firstChild.nodeValue - #Status for enkelt strøm + # Status for enkelt strøm sources = xmldoc.getElementsByTagName("source") sourcelist = {} for source in sources: mount = source.getAttribute("mount") listeners = source.getElementsByTagName("listeners")[0].firstChild.nodeValue name = source.getElementsByTagName("server_name")[0].firstChild.nodeValue - mount = mount.replace("-", "_").replace(".", "_") + mount = mount.replace("-", "_").replace(".", "_") sourcelist[mount[1:]] = (listeners, name) if len(sys.argv) > 0 and sys.argv[1] == "autoconf": - print "yes" + print "yes" elif len(sys.argv) == 1 or sys.argv[1] != "config": print "totallyttere.value %s" % total_lyttere print "totalkilder.value %s" % total_kilder - sourcesort = sourcelist.keys() - sourcesort.sort() + sourcesort = sourcelist.keys() + sourcesort.sort() for source in sourcesort: listeners, name = sourcelist[source] print "%s.value %s" % (source, listeners) elif sys.argv[1] == "config": print "graph_title Total number of listeners" print "graph_vlabel listeners" - print "graph_category streaming" + print "graph_category streaming" print "totallyttere.label Total number of listeners" print "totalkilder.label Totalt number of sources" - sourcesort = sourcelist.keys() - sourcesort.sort() - for source in sourcesort: - listeners, name = sourcelist[source] - print "%s.label %s" % (source, "/" + source) + sourcesort = sourcelist.keys() + sourcesort.sort() + for source in sourcesort: + listeners, name = sourcelist[source] + print "%s.label %s" % (source, "/" + source) else: print sys.argv[1] diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 616e192a..cc9f14b2 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -157,7 +157,6 @@ plugins/http/wget_page plugins/i2p/i2p_ plugins/icecast/icecast_ plugins/icecast/icecast2 -plugins/icecast/icecast2_all plugins/icecast/icecast2_stats_ plugins/imapproxy/imapproxy_multi plugins/ip6/ip6_ From 5b8adb5844d2c15784474584f8610a877863e691 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 00:42:45 +0200 Subject: [PATCH 501/695] Plugin lighttpd_: fix code style issues reported by flake8 --- plugins/lighttpd/lighttpd_ | 123 +++++++++++++-------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 75 insertions(+), 49 deletions(-) diff --git a/plugins/lighttpd/lighttpd_ b/plugins/lighttpd/lighttpd_ index 838bfa13..4985ce34 100755 --- a/plugins/lighttpd/lighttpd_ +++ b/plugins/lighttpd/lighttpd_ @@ -1,73 +1,100 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # vim: set fileencoding=utf-8 -# -# Munin plugin to monitor lighttpd web-server. -# -# Copyright Igor Borodikhin -# -# License : GPLv3 -# -# Configuration parameters: -# env.status_url - url of lighty's server-status (optional, default is http://127.0.0.1/server-status) -# env.username - username to provide if status_url requires authentication (optional, default - no authentication) -# env.password - password to provide if status_url requires authentication (optional, default - no authentication) -# env.auth_type - the authentication mechanism to use -- either 'basic' (default) or 'digest'. -# -# Note: If HTTP authentication is required you should specify both username and password. -# -# ## Installation -# Copy file to directory /usr/share/munin/plugins/ -# Because this plugin has suggest capability the last step is to run -# # munin-node-configure --suggest --shell | sh -x -# -#%# family=contrib -#%# capabilities=autoconf suggest +""" + +=head1 NAME + +Munin plugin to monitor lighttpd web-server. + + +=head1 CONFIGURATION + +Configuration parameters: + + [lighttpd_] + env.status_url - url of lighty's server-status + (optional, default is http://127.0.0.1/server-status) + env.username - username to provide if status_url requires authentication + (optional, default - no authentication) + env.password - password to provide if status_url requires authentication + (optional, default - no authentication) + env.auth_type - the authentication mechanism to use -- either 'basic' (default) or 'digest'. + +Note: If HTTP authentication is required you should specify both username and password. + + +=head1 INSTALLTION + +Copy file to directory /usr/share/munin/plugins/ +Because this plugin has "suggest" capability the last step is to run + + munin-node-configure --suggest --shell | sh -x + + +=head1 AUTHOR + +Copyright Igor Borodikhin + + +=head1 LICENSE + +GPLv3 + + +=head1 MAGIC MARKERS + #%# family=contrib + #%# capabilities=autoconf suggest +""" + +import os +import sys +import urllib2 -import os, sys, urllib2 program = sys.argv[0] graph_type = program[program.rfind("_")+1:] graph_types = { - "accesses" : [ + "accesses": [ { - "title" : "Total accesses", - "type" : "COUNTER", - "args" : "--base 1000 -l 0", - "fields" : ["accesses"] + "title": "Total accesses", + "type": "COUNTER", + "args": "--base 1000 -l 0", + "fields": ["accesses"] } ], - "kbytes" : [ + "kbytes": [ { - "title" : "Total kBytes", - "type" : "COUNTER", - "args" : "--base 1024 -l 0", - "fields" : ["kbytes"] + "title": "Total kBytes", + "type": "COUNTER", + "args": "--base 1024 -l 0", + "fields": ["kbytes"] } ], - "uptime" : [ + "uptime": [ { - "title" : "Uptime", - "type" : "GAUGE", - "args" : "--base 1000 -l 0", - "fields" : ["uptime"] + "title": "Uptime", + "type": "GAUGE", + "args": "--base 1000 -l 0", + "fields": ["uptime"] } ], - "status" : [ + "status": [ { - "title" : "Status", - "type" : "GAUGE", - "args" : "--base 1000 -l 0", - "fields" : ["busy", "idle"] + "title": "Status", + "type": "GAUGE", + "args": "--base 1000 -l 0", + "fields": ["busy", "idle"] } ] } + if len(sys.argv) == 2 and sys.argv[1] == "autoconf": print "yes" elif len(sys.argv) == 2 and sys.argv[1] == "config": if graph_type not in graph_types.keys(): - raise Exception("Unknown graph type '%s'"%graph_type) + raise Exception("Unknown graph type '%s'" % graph_type) params = graph_types[graph_type] for item in params: print "graph_title %s" % item["title"] @@ -96,10 +123,10 @@ else: data = {} for line in info.split("\n"): try: - (title, value) = line.split(": ") - data[title] = value - except Exception: - pass + (title, value) = line.split(": ") + data[title] = value + except ValueError: + pass if graph_type == "accesses": print "accesses.value %s" % data["Total Accesses"] diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index cc9f14b2..bbde9c63 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -175,7 +175,6 @@ plugins/libvirt/kvm_io plugins/libvirt/kvm_mem plugins/libvirt/kvm_net plugins/libvirt/munin-libvirtpy -plugins/lighttpd/lighttpd_ plugins/logins/logins plugins/logs/service_events plugins/lxc/lxc_cpu From 69602b1bbf80c4376ce35cf2b5b4532193379205 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 01:23:32 +0200 Subject: [PATCH 502/695] Plugin arris-tg3442: describe content of the base64 encoded cookie The cookie content probably depends on the router model. --- plugins/router/arris-tg3442 | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 index 088b50e5..4f696888 100755 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -73,6 +73,19 @@ import sys import os +""" +The CREDENTIAL_COOKIE below equals the following: +base64.encodebytes(b'{ "unique":"280oaPSLiF", "family":"852", "modelname":"TG2492LG-85", ' + '"name":"technician", "tech":true, "moca":0, "wifi":5, "conType":"WAN", ' + '"gwWan":"f", "DefPasswdChanged":"YES" }').decode() +""" +CREDENTIAL_COOKIE = """eyAidW5pcXVlIjoiMjgwb2FQU0xpRiIsICJmYW1pbHkiOiI4NTIiLCAibW9kZWxuYW1lIjoiVEcy +NDkyTEctODUiLCAibmFtZSI6InRlY2huaWNpYW4iLCAidGVjaCI6dHJ1ZSwgIm1vY2EiOjAsICJ3 +aWZpIjo1LCAiY29uVHlwZSI6IldBTiIsICJnd1dhbiI6ImYiLCAiRGVmUGFzc3dkQ2hhbmdlZCI6 +IllFUyIgfQ== +""" + + def login(session, url, username, password): """login to """ # get login page @@ -139,13 +152,7 @@ def login(session, url, username, password): "Referer": f"{url}/" }) # set credentials cookie - session.cookies.set( - "credential", - "eyAidW5pcXVlIjoiMjgwb2FQU0xpRiIsICJmYW1pbHkiOiI4NTIiLCAibW9kZWxuYW1lIjoiV" - "EcyNDkyTEctODUiLCAibmFtZSI6InRlY2huaWNpYW4iLCAidGVjaCI6dHJ1ZSwgIm1vY2EiOj" - "AsICJ3aWZpIjo1LCAiY29uVHlwZSI6IldBTiIsICJnd1dhbiI6ImYiLCAiRGVmUGFzc3dkQ2h" - "hbmdlZCI6IllFUyIgfQ==" - ) + session.cookies.set("credential", CREDENTIAL_COOKIE) # set session r = session.post(f"{url}/php/ajaxSet_Session.php") From c586f34d4b76693d3a1b7f33c18309c3c1e242b9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 01:27:42 +0200 Subject: [PATCH 503/695] Plugin arris-tg3442: fix code style issues reported by flake8 --- plugins/router/arris-tg3442 | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 index 4f696888..94563a32 100755 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -105,7 +105,7 @@ def login(session, url, username, password): iterations=1000, dklen=128/8 ) - secret = { "Password": password, "Nonce": current_session_id } + secret = {"Password": password, "Nonce": current_session_id} plaintext = bytes(json.dumps(secret).encode("ascii")) associated_data = "loginPassword" # initialize cipher @@ -168,7 +168,7 @@ def docsis_status(session): downstream_data = json.loads(json_downstream_data) upstream_data = json.loads(json_upstream_data) # convert lock status to numeric values - for d in [ upstream_data, downstream_data ]: + for d in [upstream_data, downstream_data]: for c in d: if c['LockStatus'] == "ACTIVE" or c['LockStatus'] == "Locked": c['LockStatus'] = 1 @@ -242,24 +242,21 @@ if __name__ == "__main__": # process all graphs for g in graph_descriptions: # graph config - print( - f"multigraph docsis_{g['name']}\n" - f"graph_title {g['title']}\n" \ - f"graph_category network\n" \ - f"graph_vlabel {g['vlabel']}\n" \ - f"graph_info {g['info']}\n" \ - f"graph_scale no\n" - ) + print(f"multigraph docsis_{g['name']}") + print(f"graph_title {g['title']}") + print(f"graph_category network") + print(f"graph_vlabel {g['vlabel']}") + print(f"graph_info {g['info']}") + print(f"graph_scale no") # channels for c in g['data']: # only use channels with PowerLevel if not c['PowerLevel']: continue - print( - f"channel_{c['ChannelID']}.label {c['ChannelID']} ({c['Frequency']} MHz)\n" - f"channel_{c['ChannelID']}.info Channel type: {c['ChannelType']}, Modulation: {c['Modulation']}" - ) + info_text = f"Channel type: {c['ChannelType']}, Modulation: {c['Modulation']}" + print(f"channel_{c['ChannelID']}.label {c['ChannelID']} ({c['Frequency']} MHz)") + print(f"channel_{c['ChannelID']}.info {info_text}") # output values ? else: From ee68a404b5119df5cfa228b293cc81cd33770f89 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 14 Aug 2019 04:05:28 +0200 Subject: [PATCH 504/695] Plugin w1_: add executable flag and tolerate shellcheck failure Hopefully the issue will be resolved via #1011. --- plugins/raspberry-pi/w1_ | 0 t/test-exception-wrapper.expected-failures | 1 + 2 files changed, 1 insertion(+) mode change 100644 => 100755 plugins/raspberry-pi/w1_ diff --git a/plugins/raspberry-pi/w1_ b/plugins/raspberry-pi/w1_ old mode 100644 new mode 100755 diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index bbde9c63..4c3ded00 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -373,6 +373,7 @@ plugins/radiator/radiator_acct_ppm plugins/radiator/radiator_auth_lag plugins/radiator/radiator_auth_ppm plugins/raspberry-pi/cpu_freq_1sec +plugins/raspberry-pi/w1_ plugins/requesttracker/rt_ticket_loadtime plugins/rethinkdb/rethinkdb_node_io plugins/riak/riak_fsm_time_95 From d0139a588d3745409ef43991d083586c44ae5ae1 Mon Sep 17 00:00:00 2001 From: d0m84 Date: Fri, 26 Jul 2019 22:22:49 +0200 Subject: [PATCH 505/695] Update ntp_packets With Debian 10 the command "ntpq -c iostats -c sysstats" produces one empty line. The additional if condition tackles this. Traceback (most recent call last): File "/etc/munin/plugins/ntp_packets", line 91, in stats[line.split(':')[0]] = int(line.split(':')[1]) IndexError: list index out of range --- plugins/ntp/ntp_packets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ntp/ntp_packets b/plugins/ntp/ntp_packets index 1fb19b05..f452926c 100755 --- a/plugins/ntp/ntp_packets +++ b/plugins/ntp/ntp_packets @@ -87,7 +87,8 @@ stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], universal_newlines=True).splitlines() for line in stats_output: - stats[line.split(':')[0]] = int(line.split(':')[1]) + if len(line.split(':')) == 2: + stats[line.split(':')[0]] = int(line.split(':')[1]) print('received.value ' + str(stats['received packets'])) print('sent.value ' + str(stats['packets sent'])) From 4ee40c82279c625577f0315503292e3255448547 Mon Sep 17 00:00:00 2001 From: d0m84 Date: Wed, 14 Aug 2019 08:24:11 +0200 Subject: [PATCH 506/695] Update ntp_packets --- plugins/ntp/ntp_packets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/ntp/ntp_packets b/plugins/ntp/ntp_packets index f452926c..9251d2da 100755 --- a/plugins/ntp/ntp_packets +++ b/plugins/ntp/ntp_packets @@ -86,6 +86,8 @@ stats = dict() stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], universal_newlines=True).splitlines() +# Split the cmd output into key/value pairs +# Lines that can't be splitted into 2 individual elements by delimiter ':' will be skipped for line in stats_output: if len(line.split(':')) == 2: stats[line.split(':')[0]] = int(line.split(':')[1]) From 735689d47a5367e85a0b5f4366455cce3409f7e7 Mon Sep 17 00:00:00 2001 From: rantal Date: Fri, 16 Aug 2019 21:06:26 +0300 Subject: [PATCH 507/695] btrfs_subvol_usage: fix "btrfs qgroup show" output format Switch "btrfs qgroup show" for raw output format, because by default it's human readable form and can't be read by scripts. --- plugins/disk/btrfs_subvol_usage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/btrfs_subvol_usage b/plugins/disk/btrfs_subvol_usage index cba66d98..d58e0977 100755 --- a/plugins/disk/btrfs_subvol_usage +++ b/plugins/disk/btrfs_subvol_usage @@ -62,7 +62,7 @@ while (my $line = ) { close SVS; # get sizes from quota -open(QGS, "btrfs qgroup show @fsroot |") +open(QGS, "btrfs qgroup show --raw @fsroot |") or die("Failed to run 'btrfs qgroup show': " . $!); while (my $line = ) { chomp $line; From 748cf3b4ee0dd3c5b02c4450f1b8fd5a06f291db Mon Sep 17 00:00:00 2001 From: Marc SCHAEFER Date: Mon, 19 Aug 2019 15:07:16 +0200 Subject: [PATCH 508/695] - grep -EF is invalid - SYSFS: buster has an existing /sys/fs/cgroup/systemd/lxc/$guest_name/tasks, which does not contain anything useful, so checking /sys/fs/cgroup/cpuacct/lxc/$guest_name/tasks first. - spurious blank space for lxc_net and lxc_ram in here document --- plugins/lxc/lxc_guests | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/lxc/lxc_guests b/plugins/lxc/lxc_guests index 0b201a3d..97608d4d 100755 --- a/plugins/lxc/lxc_guests +++ b/plugins/lxc/lxc_guests @@ -121,8 +121,8 @@ lxc_netdev() { if [ -f "$lxcpath/$guest_name/config" ]; then # lxc 3 vs < 3 - (grep -EF '^lxc.net.0.veth.pair' "$lxcpath/$guest_name/config" 2>/dev/null \ - || grep -EF '^lxc.network.veth.pair' "$lxcpath/$guest_name/config" + (grep -E '^lxc.net.0.veth.pair' "$lxcpath/$guest_name/config" 2>/dev/null \ + || grep -E '^lxc.network.veth.pair' "$lxcpath/$guest_name/config" ) | awk '{print $NF;}' fi } @@ -152,8 +152,8 @@ lxc_count_processes () { for SYSFS in \ "/sys/fs/cgroup/$guest_name/tasks" \ "/sys/fs/cgroup/lxc/$guest_name/tasks" \ - "/sys/fs/cgroup/systemd/lxc/$guest_name/tasks" \ "/sys/fs/cgroup/cpuacct/lxc/$guest_name/tasks" \ + "/sys/fs/cgroup/systemd/lxc/$guest_name/tasks" \ "/sys/fs/cgroup/cpuacct/sysdefault/lxc/$guest_name/tasks" do if [ -e "$SYSFS" ]; then @@ -372,8 +372,8 @@ do_fetch() { fi cat < Date: Fri, 30 Aug 2019 15:54:27 +0200 Subject: [PATCH 509/695] This typo ensures that you understand the script before using it :-) --- plugins/postfix/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postfix/postfix_stats b/plugins/postfix/postfix_stats index d3f51fbc..3552e098 100755 --- a/plugins/postfix/postfix_stats +++ b/plugins/postfix/postfix_stats @@ -76,7 +76,7 @@ MAIL_LOG="${logfiles:-/var/log/mail.log /var/log/mail.log.1}" FILTER_HOSTNAME=$(basename "$0" | sed -r 's/postfix_stats_?//') # shellcheck disable=SC2154 -PFLOGSUMM="${pflogsum}" +PFLOGSUMM="${pflogsumm}" [ -z "$PFLOGSUMM" ] && PFLOGSUMM="$(which pflogsumm pflogsumm.pl | head -1)" # Fields (Array to avoid code duplication) must be space separated From c84659ad22a6c524a2df761ee62b6b69d718dbcc Mon Sep 17 00:00:00 2001 From: Martin Schobert Date: Sat, 31 Aug 2019 00:56:36 +0200 Subject: [PATCH 510/695] Cosmetic fix for s/pflogsum/pflogsumm/ - not functional in autoconf section --- plugins/postfix/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postfix/postfix_stats b/plugins/postfix/postfix_stats index 3552e098..5a901cf7 100755 --- a/plugins/postfix/postfix_stats +++ b/plugins/postfix/postfix_stats @@ -89,7 +89,7 @@ if [ "$1" = 'autoconf' ]; then # Check if pflogsumm exist if [ -z "${PFLOGSUMM}" ] then - echo 'no (pflogsum not found in your system)' + echo 'no (pflogsumm not found in your system)' else echo 'yes' fi From 4e7c45fdc99e854458c04cb7260bc5a86068482c Mon Sep 17 00:00:00 2001 From: RenWal Date: Mon, 2 Sep 2019 11:34:39 +0200 Subject: [PATCH 511/695] Fix typos in certificate_file_expiry Typos broke the sample configuration --- plugins/ssl/certificate_file_expiry | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/ssl/certificate_file_expiry b/plugins/ssl/certificate_file_expiry index ef20deb0..741c9212 100755 --- a/plugins/ssl/certificate_file_expiry +++ b/plugins/ssl/certificate_file_expiry @@ -2,24 +2,24 @@ : << =cut =head1 NAME -certficate_file_expiry - check the certificate validity of your certfificates +certificate_file_expiry - check the certificate validity of your certificates = head1 CONFIGURATION Installing: Add list of your certificates prefixed by the type in munin plugin-conf.d For openvpn ca.crt and crl.pem -[certficate_file_expiry] +[certificate_file_expiry] user root env.CERTS crl:/etc/openvpn/easy-rsa/keys/crl.pem x509:/etc/openvpn/easy-rsa/keys/ca.crt For letsencrypt certficates -[certficate_file_expiry] +[certificate_file_expiry] user root env.CERTS x509:/etc/letsencrypt/live/domain1.example.com/cert.pem x509:/etc/letsencrypt/live/domain2.example.com/cert.pem Warning and Critical levels can also be configured with env variables like this -[certficate_file_expiry] +[certificate_file_expiry] ... # warn when certificate will be invalid within 5 days env.warning 5: From 3cd15a822007d87b124e0495b81647f5d3738a64 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 5 Sep 2019 19:20:03 +0300 Subject: [PATCH 512/695] improved ext2, ext3, ext4 filesystems search --- plugins/disk/e2 | 54 +++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/plugins/disk/e2 b/plugins/disk/e2 index a9080055..c1377aa0 100755 --- a/plugins/disk/e2 +++ b/plugins/disk/e2 @@ -46,11 +46,10 @@ print_details() { } lse2() { - for name in {h,s}d{a..z}{1..16}; do - if [ -b /dev/$name ]; then - LANG=C dumpe2fs /dev/$name 2>/dev/null | print_details "$name" - fi - done + while read -r line ; do + value=( $line ); + LANG=C dumpe2fs -h "${value[1]}" 2>/dev/null | print_details "${value[0]}" + done <<< "$(lsblk -o NAME,PATH,FSTYPE -r | grep ext[2-4] | uniq )" } @@ -78,6 +77,7 @@ plugin=${self%%_*} if [ -n "${self//[!_]/}" ]; then blkdev=${self#*_} blkdevAppend=" ($blkdev)" + blkdevpath=$(lsblk -o PATH,NAME -r | grep "$blkdev" | uniq | cut -d' ' -f1) else blkdev= blkdevAppend= @@ -108,30 +108,31 @@ if [ "$1" = "config" ]; then if [ -z "$blkdev" ]; then lse2 else - LANG=C dumpe2fs /dev/$blkdev 2>/dev/null | print_details "$blkdev" + LANG=C dumpe2fs -h "$blkdevpath" 2>/dev/null | print_details "$blkdev" fi | \ while read name mounts maxmounts last interval writes; do [ -z "$blkdev" ] && blkdevAppend2=" ($name)" || blkdevAppend2= + name_esc=${name//-/_} case "$plugin" in mounts) - echo "${name}_mounts.label Mount count$blkdevAppend2" - echo "${name}_mounts.draw LINE2" - echo "${name}_maxmounts.label Max. mount count$blkdevAppend2" - echo "${name}_maxmounts.draw LINE1" + echo "${name_esc}_mounts.label Mount count$blkdevAppend2" + echo "${name_esc}_mounts.draw LINE2" + echo "${name_esc}_maxmounts.label Max. mount count$blkdevAppend2" + echo "${name_esc}_maxmounts.draw LINE1" ;; checked) - echo "${name}_last.label Since checked$blkdevAppend2" - echo "${name}_last.draw LINE2" - echo "${name}_last.cdef ${name}_last,86400,/" - echo "${name}_interval.label Check interval$blkdevAppend2" - echo "${name}_interval.draw LINE1" - echo "${name}_interval.cdef ${name}_interval,86400,/" + echo "${name_esc}_last.label Since checked$blkdevAppend2" + echo "${name_esc}_last.draw LINE2" + echo "${name_esc}_last.cdef ${name_esc}_last,86400,/" + echo "${name_esc}_interval.label Check interval$blkdevAppend2" + echo "${name_esc}_interval.draw LINE1" + echo "${name_esc}_interval.cdef ${name_esc}_interval,86400,/" ;; writes) - echo "${name}_writes.label Lifetime writes$blkdevAppend2" - echo "${name}_writes.type GAUGE" - echo "${name}_writes.draw LINE2" - echo "${name}_writes.cdef ${name}_writes,1073741824,*" + echo "${name_esc}_writes.label Lifetime writes$blkdevAppend2" + echo "${name_esc}_writes.type GAUGE" + echo "${name_esc}_writes.draw LINE2" + echo "${name_esc}_writes.cdef ${name_esc}_writes,1073741824,*" ;; esac done @@ -146,22 +147,23 @@ fi if [ -z "$blkdev" ]; then lse2 else - LANG=C dumpe2fs /dev/$blkdev 2>/dev/null | print_details "$blkdev" + LANG=C dumpe2fs -h "$blkdevpath" 2>/dev/null | print_details "$blkdev" fi | \ while read name mounts maxmounts last interval writes; do + name_esc=${name//-/_} case "$plugin" in mounts) - echo "${name}_mounts.value $mounts" - echo "${name}_maxmounts.value $maxmounts" + echo "${name_esc}_mounts.value $mounts" + echo "${name_esc}_maxmounts.value $maxmounts" ;; checked) now=`date +%s` past=$(( now - last )) - echo "${name}_last.value $past" - echo "${name}_interval.value $interval" + echo "${name_esc}_last.value $past" + echo "${name_esc}_interval.value $interval" ;; writes) - echo "${name}_writes.value $writes" + echo "${name_esc}_writes.value $writes" ;; esac done From 7fff067ffad6bbb7f87bd8709f351f7bbe5d561b Mon Sep 17 00:00:00 2001 From: Christopher Eagan Date: Fri, 6 Sep 2019 10:21:52 -0400 Subject: [PATCH 513/695] Add Missing Bracket Adds the missing bracket `[` to ensure that brackets show up after groups when hosts are grouped together. --- templates/munstrap/templates/munin-overview.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/munstrap/templates/munin-overview.tmpl b/templates/munstrap/templates/munin-overview.tmpl index 670747d1..0da303c0 100644 --- a/templates/munstrap/templates/munin-overview.tmpl +++ b/templates/munstrap/templates/munin-overview.tmpl @@ -53,7 +53,7 @@
  1. class="last"> "> - + [ //comparison-day.html">day //comparison-week.html">week //comparison-month.html">month From 3aa213d4976788d012ed155f7e2894a3cfd0a7cd Mon Sep 17 00:00:00 2001 From: ruliane Date: Tue, 17 Sep 2019 19:37:37 +0200 Subject: [PATCH 514/695] Add support for StartTLS in ssl-certificate-expiry (#1018) * Add support for StartTLS in ssl-certificate-expiry Added support for StartTLS in ssl-certificate-expiry Use env.services foo.example.net_25_smtp to enable StartTLS on a SMTP server. --- plugins/ssl/ssl-certificate-expiry | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index d2d56f30..9a3e8142 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -11,7 +11,12 @@ ssl-certificate-expiry - Plugin to monitor Certificate expiration on multiple se =head1 CONFIGURATION [ssl-certificate-expiry] - env.services www.service.tld blah.example.net_PORT + env.services www.service.tld blah.example.net_PORT foo.example.net_PORT_STARTTLS + +PORT is the TCP port number +STARTTLS is passed to openssl as "-starttls" argument. Useful for services like SMTP or IMAP implementing StartTLS. + Current known values are ftp, imap, pop3 and smtp + PORT is mandatory if STARTTLS is used. To set warning and critical levels do like this: @@ -29,6 +34,7 @@ For example: ssl-certificate-expiry_www.example.org_443 ssl-certificate-expiry_192.0.2.42_636 ssl-certificate-expiry_2001:0DB8::badc:0fee_485 + ssl-certificate-expiry_mail.example.net_25_smtp =head2 Cron setup @@ -102,12 +108,18 @@ parse_valid_days_from_certificate() { print_expire_days() { local host="$1" local port="$2" + local starttls="$3" # Wrap IPv6 addresses in square brackets echo "$host" | grep -q ':' && host="[$host]" + local s_client_args= + [ -n "$starttls" ] && s_client_args="-starttls $starttls" + + # shellcheck disable=SC2086 echo "" | openssl s_client -CApath /etc/ssl/certs \ - -servername "$host" -connect "${host}:${port}" 2>/dev/null \ + -servername "$host" -connect "${host}:${port}" \ + $s_client_args 2>/dev/null \ | parse_valid_days_from_certificate } @@ -116,12 +128,13 @@ main() { if echo "$service" | grep -q "_"; then host=$(echo "$service" | cut -f 1 -d "_") port=$(echo "$service" | cut -f 2 -d "_") + starttls=$(echo "$service" | cut -f 3 -d "_") else host=$service port=443 fi fieldname="$(clean_fieldname "$service")" - valid_days=$(print_expire_days "$host" "$port") + valid_days=$(print_expire_days "$host" "$port" "$starttls") [ -z "$valid_days" ] && valid_days="U" printf "%s.value %s\\n" "$fieldname" "$valid_days" echo "${fieldname}.extinfo Last checked: $(date)" From c49207419ef21b10c191876a637f54b415dd950c Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 12 Sep 2019 23:10:25 +1000 Subject: [PATCH 515/695] [isp/internode_usage] Code cleanup Also make the daily graph more flexible, by always reporting the second last value (presumably the previous, but sometimes the API lags a bit, and actively requesting by date result in never reporting some late data). Signed-off-by: Olivier Mehani --- plugins/isp/internode_usage | 99 ++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index f1f8c3db..f257a320 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -1,4 +1,4 @@ -#!/bin/sh -u +#!/bin/sh -eu # -*- sh -*- : << =cut @@ -45,7 +45,9 @@ SPDX-License-Identifier: GPL-3.0-or-later # shellcheck disable=SC1090 . "${MUNIN_LIBDIR:-.}/plugins/plugin.sh" +CURL_ARGS='-s' if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + CURL_ARGS='-v' set -x fi @@ -67,47 +69,73 @@ if [ -z "${internode_api_url:-}" ]; then fi xpath_extract() { - xpath="$1" - xpath -q -n -e "${xpath}" | \ - sed 's/<\([^>]*\)>\([^<]*\)<[^>]*>/\2/' + # shellcheck disable=SC2039 + local xpath="$1" + # shellcheck disable=SC2039 + local node="$(xpath -q -n -e "${xpath}")" \ + || { echo "error extracting ${xpath}" >&2; false; } + echo "${node}" | sed 's/<\([^>]*\)>\([^<]*\)<[^>]*>/\2/;s^N/A^U^' } xpath_extract_attribute() { - xpath="$1" - xpath -q -n -e "${xpath}" | \ - sed 's/.*="\([^"]\+\)".*/\1/' + # shellcheck disable=SC2039 + local xpath="$1" + # shellcheck disable=SC2039 + local node="$(xpath -q -n -e "${xpath}")" \ + || { echo "error extracting attribute at ${xpath}" >&2; false; } + echo "${node}" | sed 's/.*="\([^"]\+\)".*/\1/' } fetch() { # shellcheck disable=SC2154 - curl -u "${internode_api_login}:${internode_api_password}" -s "$@" + curl -u "${internode_api_login}:${internode_api_password}" -f ${CURL_ARGS} "$@" \ + || { echo "error fetching ${*} for user ${internode_api_login}" >&2; false; } } get_data() { - SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service")" - SERVICE_USERNAME="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/username")" - SERVICE_QUOTA="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/quota")" - SERVICE_PLAN="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan")" - SERVICE_ROLLOVER="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/rollover")" - SERVICE_INTERVAL="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan-interval" | sed 's/ly$//')" + SERVICE_USERNAME='n/a' + SERVICE_QUOTA='n/a' + SERVICE_PLAN='n/a' + SERVICE_ROLLOVER='n/a' + IDEAL_USAGE='' + USAGE_CRITICAL='' + SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service" \ + || true)" + if [ -n "${SERVICE_XML}" ]; then + SERVICE_USERNAME="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/username")" + SERVICE_QUOTA="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/quota")" + SERVICE_PLAN="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan")" + SERVICE_ROLLOVER="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/rollover")" + SERVICE_INTERVAL="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan-interval" | sed 's/ly$//')" - HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history")" - TODAY_TIMESTAMP="$(date +%s)" - DAILY_DATE="$(date +%Y-%m-%d -d yesterday)" - DAILY_TIMESTAMP="$(date -d "${DAILY_DATE} $(date +%H:%M:%S)" +%s)" - DAILY_USAGE="$(echo "${HISTORY_XML}" | xpath_extract "internode/api/usagelist/usage[@day=\"${DAILY_DATE}\"]/traffic")" + TODAY="$(date +%s)" + FIRST_DAY="$(date +%s --date "${SERVICE_ROLLOVER} -1 ${SERVICE_INTERVAL}")" + LAST_DAY="$(date +%s --date "${SERVICE_ROLLOVER}")" + BILLING_PERIOD="(${LAST_DAY}-${FIRST_DAY})" + IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${TODAY})/${BILLING_PERIOD})" | bc -ql)" - USAGE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/usage")" - SERVICE_USAGE="$(echo "${USAGE_XML}" | xpath_extract "internode/api/traffic")" + USAGE_CRITICAL="${SERVICE_QUOTA}" + fi - USAGE_CRITICAL="${SERVICE_QUOTA}" - USAGE_WARNING="$(echo "${SERVICE_QUOTA}*.75" | bc -ql)" + DAILY_TIMESTAMP=N + DAILY_USAGE=U + HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history" \ + || true)" + if [ -n "${HISTORY_XML}" ]; then + DAILY_USAGE="$(echo "${HISTORY_XML}" | xpath_extract "internode/api/usagelist/usage[last()-1]/traffic")" + DAILY_DATE="$(echo "${HISTORY_XML}" | xpath_extract_attribute "internode/api/usagelist/usage[last()-1]/@day")" + DAILY_TIMESTAMP="$(date -d "${DAILY_DATE} $(date +%H:%M:%S)" +%s \ + || echo N)" + fi - TODAY="$(date +%s)" - FIRST_DAY="$(date +%s --date "${SERVICE_ROLLOVER} -1 ${SERVICE_INTERVAL}")" - LAST_DAY="$(date +%s --date "${SERVICE_ROLLOVER}")" - BILLING_PERIOD="(${LAST_DAY}-${FIRST_DAY})" - IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${TODAY})/${BILLING_PERIOD})" | bc -ql)" + SERVICE_USAGE='U' + USAGE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/usage" \ + || true)" + if [ -n "${USAGE_XML}" ]; then + SERVICE_USAGE="$(echo "${USAGE_XML}" | xpath_extract "internode/api/traffic")" + + + fi } graph_config() { @@ -178,14 +206,14 @@ graph_data() { echo "multigraph internode_usage_${SERVICE_ID}${graph}" case "${graph}" in .current) - echo "hourly_rate.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}" + echo "hourly_rate.value ${SERVICE_USAGE:-U}" ;; .daily) echo "daily_rate.value ${DAILY_TIMESTAMP}:${DAILY_USAGE:-U}" ;; *) - echo "usage.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}" - echo "ideal.value ${TODAY_TIMESTAMP}:${IDEAL_USAGE:-U}" + echo "usage.value ${SERVICE_USAGE:-U}" + echo "ideal.value ${IDEAL_USAGE:-U}" ;; esac echo @@ -214,11 +242,16 @@ main() { # Determine the service ID from the name of the symlink SERVICE_ID="$(echo "${0}" | sed -n 's/^.*internode_usage_//p')" if [ -z "${SERVICE_ID}" ]; then - API_XML="$(fetch "${internode_api_url}")" # Otherwise, get the first service in the list + API_XML="$(fetch "${internode_api_url}" \ + || true)" + if [ -z "${API_XML}" ]; then + echo "unable to determine service ID for user ${internode_api_login}" >&2 + exit 1 + fi SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")" fi get_data -main ${1:-} +main "${1:-}" From e5af569b4f2c006fbdd58939a9578ed2be1502bd Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 27 Sep 2019 21:06:12 +1000 Subject: [PATCH 516/695] [isp/internode_usage] Add support for host_name Signed-off-by: Olivier Mehani --- plugins/isp/internode_usage | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index f257a320..ac500ddc 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -15,6 +15,19 @@ The ideal usage is also used as an updated warning limit. env.internode_api_login LOGIN env.internode_api_password PASSWORD +You can display the graph on another host (e.g., the actual router) than the +one running munin. To do so, first configure the plugin to use a different +hostname. + + env.host_name router + +Then configure munin (in /etc/munin/munin-conf or /etc/munin/munin-conf.d), to +support a new host. + + [example.net;router] + address 127.0.0.1 + use_node_name no + An optional 'env.internode_api_url' can be used, but should not be needed. It will default to https://customer-webtools-api.internode.on.net/api/v1.5. @@ -222,6 +235,9 @@ graph_data() { main() { case ${1:-} in config) + if [ ! -z "${host_name:-}" ]; then + echo "host_name ${host_name}" + fi graph_config graph_config usage graph_config current From f6c627bd56c7aaa92045ee9d3891d5360f572a51 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 29 Sep 2019 20:39:31 +1000 Subject: [PATCH 517/695] [isp/internode] Fix (some) issues with DIRTYCONFIG We need to set the date for all plugins, otherwise the lagging `daily` one is assumed for all, and confuses spoolfetch and other time-based sanity checks. Signed-off-by: Olivier Mehani --- plugins/isp/internode_usage | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index ac500ddc..01c465cf 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -106,6 +106,7 @@ fetch() { } get_data() { + CURRENT_TIMESTAMP="$(date +%s)" SERVICE_USERNAME='n/a' SERVICE_QUOTA='n/a' SERVICE_PLAN='n/a' @@ -121,11 +122,10 @@ get_data() { SERVICE_ROLLOVER="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/rollover")" SERVICE_INTERVAL="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan-interval" | sed 's/ly$//')" - TODAY="$(date +%s)" FIRST_DAY="$(date +%s --date "${SERVICE_ROLLOVER} -1 ${SERVICE_INTERVAL}")" LAST_DAY="$(date +%s --date "${SERVICE_ROLLOVER}")" BILLING_PERIOD="(${LAST_DAY}-${FIRST_DAY})" - IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${TODAY})/${BILLING_PERIOD})" | bc -ql)" + IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${CURRENT_TIMESTAMP})/${BILLING_PERIOD})" | bc -ql)" USAGE_CRITICAL="${SERVICE_QUOTA}" fi @@ -219,14 +219,14 @@ graph_data() { echo "multigraph internode_usage_${SERVICE_ID}${graph}" case "${graph}" in .current) - echo "hourly_rate.value ${SERVICE_USAGE:-U}" + echo "hourly_rate.value ${CURRENT_TIMESTAMP}:${SERVICE_USAGE:-U}" ;; .daily) echo "daily_rate.value ${DAILY_TIMESTAMP}:${DAILY_USAGE:-U}" ;; *) - echo "usage.value ${SERVICE_USAGE:-U}" - echo "ideal.value ${IDEAL_USAGE:-U}" + echo "usage.value ${CURRENT_TIMESTAMP}:${SERVICE_USAGE:-U}" + echo "ideal.value ${CURRENT_TIMESTAMP}:${IDEAL_USAGE:-U}" ;; esac echo @@ -240,8 +240,8 @@ main() { fi graph_config graph_config usage - graph_config current graph_config daily + graph_config current if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then main fi @@ -249,8 +249,8 @@ main() { *) graph_data graph_data usage - graph_data current graph_data daily + graph_data current ;; esac } From 81bf32a5a53bea095cfc42e694891661d7752722 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 25 Sep 2019 22:16:16 +1000 Subject: [PATCH 518/695] [network/upnpc_] Cleanup shell code and centralise call to upnpc Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 75 +++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index c50037c6..c29022e8 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -u # -*- sh -*- : << =cut @@ -16,7 +16,7 @@ router using UPnP. It can monitor the following aspects, and plot them as separa =head1 APPLICABLE SYSTEMS -Linux systems with upnpc installed. +Linux systems with upnpc installed (miniupnpc package). =head1 CONFIGURATION @@ -24,11 +24,13 @@ None needed. =head1 AUTHOR -Olivier Mehani +Olivier Mehani + +Copyright (C) 2016,2019 Olivier Mehani =head1 LICENSE -SPDX-License-Identifier: GPL-2.0-or-later +SPDX-License-Identifier: GPL-3.0-or-later =head1 MAGIC MARKERS @@ -37,21 +39,38 @@ SPDX-License-Identifier: GPL-2.0-or-later =cut +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +if ! command -v upnpc >/dev/null; then + echo "upnpc not found (miniupnpc package)" >&2 + exit 1 +fi + +PLUGIN_NAME="$(basename "${0}")" +MODE="$(echo "${PLUGIN_NAME}" | sed 's/.*_//')" +DATA="$(upnpc -s)" +SUPPORTED_MODES=$( + echo "${DATA}" | sed -n " \ + s/.*Bytes.*/traffic/p; \ + s/.*MaxBitRate.*/bitrate/p; \ + s/.*Packets.*/pkts/p; \ + s/.*uptime=.*/uptime/p; \ + ") + autoconf() { - which upnpc >/dev/null && upnpc -s >/dev/null 2>&1 && echo yes || echo "no (No upnpc or no UPnP router)" + test -n "${DATA}" && echo yes || echo "no (No UPnP router detected)" } suggest () { - upnpc -s | sed -n " \ - s/.*uptime=.*/uptime/p; \ - s/.*MaxBitRate.*/bitrate/p; \ - s/.*Bytes.*/traffic/p; \ - s/.*Packets.*/pkts/p; \ - " + for mode in ${SUPPORTED_MODES}; do + echo "${mode}" + done } config () { - case $1 in + case ${1} in "uptime") cat << EOF graph_title Uplink connection uptime @@ -71,11 +90,7 @@ 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 @@ -114,35 +129,35 @@ down.graph no up.negative down EOF ;; - "*") - echo "$0: unknown mode '$1'" >&2 + *) + echo "unknown mode '${1}'" >&2 exit 1 + ;; esac } fetch () { - case $1 in + case "${1}" in "uptime") - upnpc -s | sed -n "s/.*uptime=\([0-9]\+\)s.*/uptime.value \1/p" + echo "${DATA}" | 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" + echo "${DATA}" | 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" + echo ${DATA} | 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 "${DATA}" | 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 + *) + echo "unknown mode '${1}'" >&2 exit 1 + ;; esac } -mode=`echo $0 | sed 's/.*_//'` - -case $1 in +case ${1:-} in "autoconf") autoconf ;; @@ -150,9 +165,9 @@ case $1 in suggest ;; "config") - config $mode + config "${MODE}" ;; *) - fetch $mode + fetch "${MODE}" ;; esac From 5382722fdb6906dda9353e922a918eec5a92005b Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 25 Sep 2019 22:17:07 +1000 Subject: [PATCH 519/695] [network/upnpc_] Add dirtyconfig support Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index c29022e8..21fb2e3a 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -166,6 +166,9 @@ case ${1:-} in ;; "config") config "${MODE}" + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then + fetch "${MODE}" + fi ;; *) fetch "${MODE}" From 3212af2eed3eea703da431645239e3d3acb741be Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 25 Sep 2019 22:17:54 +1000 Subject: [PATCH 520/695] [network/upnpc_traffic] Show max bitrate Deprecate bitrate mode in suggest, but keep support for it so as not to break existing installations. Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 21fb2e3a..fd9ea754 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -20,7 +20,11 @@ Linux systems with upnpc installed (miniupnpc package). =head1 CONFIGURATION -None needed. +If you do not want to show the link maximum bitrates, add the following +plugin-configuration: + + [upnpc*] + env.traffic_remove_max true =head1 AUTHOR @@ -54,7 +58,6 @@ DATA="$(upnpc -s)" SUPPORTED_MODES=$( echo "${DATA}" | sed -n " \ s/.*Bytes.*/traffic/p; \ - s/.*MaxBitRate.*/bitrate/p; \ s/.*Packets.*/pkts/p; \ s/.*uptime=.*/uptime/p; \ ") @@ -85,7 +88,7 @@ EOF ;; "bitrate") cat << EOF -graph_title Uplink bitrate +graph_title [DEPRECATED] Uplink bitrate graph_args --base 1000 -l 0 graph_category network graph_vlabel bitrate down (-) / up (+) @@ -99,15 +102,27 @@ EOF # ${graph_period} is not a shell variable cat << 'EOF' graph_title Uplink traffic -graph_args --base 1024 -l 0 +graph_args --base 1000 -l 0 graph_category network graph_vlabel bytes in (-) / out (+) per ${graph_period} -down.label Bps +EOF + if [ "${traffic_remove_max:-false}" != 'true' ]; then + cat << EOF +maxdown.label bps (max) +maxup.label bps (max) +maxdown.graph no +maxup.negative maxdown +EOF + fi + cat << EOF +down.label bps down.type DERIVE down.min 0 -up.label Bps +down.cdef down,8,* +up.label bps up.type DERIVE up.min 0 +up.cdef up,8,* down.graph no up.negative down EOF @@ -145,7 +160,12 @@ fetch () { echo "${DATA}" | sed -n "s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/down.value \1\nup.value \2/p" ;; "traffic") - echo ${DATA} | sed -n "s/^Bytes:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + echo "${DATA}" | sed -n " + s/^Bytes:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + if [ "${traffic_remove_max:-false}" != 'true' ]; then + echo "${DATA}" | sed -n " + s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/maxdown.value \1\nmaxup.value \2/p" + fi ;; "pkts") echo "${DATA}" | sed -n "s/^Packets:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" From 076545b5f6ce70c06a5e80a5d7fb5541ea043350 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 25 Sep 2019 22:18:09 +1000 Subject: [PATCH 521/695] [network/upnpc_] Add multigraph support Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index fd9ea754..709ee76d 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -8,7 +8,8 @@ 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: +router using UPnP. It can monitor the following aspects, and plot them as +separate graphs, or a single multigraph (if linked at upnpc or upnpc_multi: * 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; @@ -66,10 +67,12 @@ autoconf() { test -n "${DATA}" && echo yes || echo "no (No UPnP router detected)" } + suggest () { for mode in ${SUPPORTED_MODES}; do echo "${mode}" done + echo "multi" } config () { @@ -144,6 +147,14 @@ down.graph no up.negative down EOF ;; + "multi"|"upnpc") + echo "multigraph ${PLUGIN_NAME}" + config "traffic" + for mode in ${SUPPORTED_MODES}; do + echo "multigraph ${PLUGIN_NAME}.${mode}" + config "${mode}" + done + ;; *) echo "unknown mode '${1}'" >&2 exit 1 @@ -170,6 +181,14 @@ fetch () { "pkts") echo "${DATA}" | sed -n "s/^Packets:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" ;; + "multi"|"upnpc") + echo "multigraph ${PLUGIN_NAME}" + fetch "traffic" + for mode in ${SUPPORTED_MODES}; do + echo "multigraph ${PLUGIN_NAME}.${mode}" + fetch "${mode}" + done + ;; *) echo "unknown mode '${1}'" >&2 exit 1 From 01edaf0086b8b4b7a75d8232ec5f4dd541f32699 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 25 Sep 2019 22:35:14 +1000 Subject: [PATCH 522/695] [network/upnpc] Add hostname in graph titles Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 709ee76d..17454b45 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -62,6 +62,7 @@ SUPPORTED_MODES=$( s/.*Packets.*/pkts/p; \ s/.*uptime=.*/uptime/p; \ ") +HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") autoconf() { test -n "${DATA}" && echo yes || echo "no (No UPnP router detected)" @@ -79,7 +80,7 @@ config () { case ${1} in "uptime") cat << EOF -graph_title Uplink connection uptime +graph_title Uplink connection uptime (${HOST}) graph_args -l 0 graph_category network graph_scale no @@ -91,7 +92,7 @@ EOF ;; "bitrate") cat << EOF -graph_title [DEPRECATED] Uplink bitrate +graph_title [DEPRECATED] Uplink bitrate (${HOST}) graph_args --base 1000 -l 0 graph_category network graph_vlabel bitrate down (-) / up (+) @@ -102,12 +103,10 @@ up.negative down EOF ;; "traffic") - # ${graph_period} is not a shell variable - cat << 'EOF' -graph_title Uplink traffic + cat << EOF +graph_title Uplink traffic (${HOST}) graph_args --base 1000 -l 0 graph_category network -graph_vlabel bytes in (-) / out (+) per ${graph_period} EOF if [ "${traffic_remove_max:-false}" != 'true' ]; then cat << EOF @@ -132,11 +131,15 @@ EOF ;; "pkts") # ${graph_period} is not a shell variable - cat << 'EOF' -graph_title Uplink packets + cat << EOF +graph_title Uplink packets (${HOST}) graph_args --base 1000 -l 0 graph_category network -graph_vlabel packets in (-) / out (+) per ${graph_period} +EOF + # ${graph_period} is not a shell variable + # shellcheck disable=SC2016 + echo 'graph_vlabel packets in (-) / out (+) per ${graph_period}' + cat << EOF down.label pps down.type DERIVE down.min 0 From 282b32b0e54a3bdfb19928f2b87f939fbedcb2bb Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 27 Sep 2019 21:01:57 +1000 Subject: [PATCH 523/695] [network/upnpc_] Add support for host_name Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 17454b45..386a69b5 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -27,6 +27,19 @@ plugin-configuration: [upnpc*] env.traffic_remove_max true +You can display the graph on another host (e.g., the actual router) than the +one running upnpc. To do so, first configure the plugin to use a different +hostname. + + env.host_name router + +Then configure munin (in /etc/munin/munin-conf or /etc/munin/munin-conf.d), to +support a new host. + + [example.net;router] + address 127.0.0.1 + use_node_name no + =head1 AUTHOR Olivier Mehani @@ -62,7 +75,17 @@ SUPPORTED_MODES=$( s/.*Packets.*/pkts/p; \ s/.*uptime=.*/uptime/p; \ ") -HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") + +HOST=${host_name:-} +HOST_TITLE="" +HOST_NAME="host_name ${HOST}" +if [ -z "${HOST}" ]; then + HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") + # Only add the host name to the title if autodetected + HOST_TITLE=" ($HOST)" + # ...but not as a separate host + HOST_NAME="" +fi autoconf() { test -n "${DATA}" && echo yes || echo "no (No UPnP router detected)" @@ -80,7 +103,7 @@ config () { case ${1} in "uptime") cat << EOF -graph_title Uplink connection uptime (${HOST}) +graph_title Uplink connection uptime${HOST_TITLE} graph_args -l 0 graph_category network graph_scale no @@ -88,11 +111,12 @@ graph_vlabel uptime in hours uptime.label uptime uptime.draw AREA uptime.cdef uptime,3600,/ +${HOST_NAME} EOF ;; "bitrate") cat << EOF -graph_title [DEPRECATED] Uplink bitrate (${HOST}) +graph_title [DEPRECATED] Uplink bitrate${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network graph_vlabel bitrate down (-) / up (+) @@ -100,11 +124,12 @@ down.label bps up.label bps down.graph no up.negative down +${HOST_NAME} EOF ;; "traffic") cat << EOF -graph_title Uplink traffic (${HOST}) +graph_title Uplink traffic${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network EOF @@ -127,12 +152,13 @@ up.min 0 up.cdef up,8,* down.graph no up.negative down +${HOST_NAME} EOF ;; "pkts") # ${graph_period} is not a shell variable cat << EOF -graph_title Uplink packets (${HOST}) +graph_title Uplink packets${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network EOF @@ -148,9 +174,13 @@ up.type DERIVE up.min 0 down.graph no up.negative down +${HOST_NAME} EOF ;; "multi"|"upnpc") + echo "${HOST_NAME}" + # Don't repeat HOST_NAME in sub-configs + HOST_NAME="" echo "multigraph ${PLUGIN_NAME}" config "traffic" for mode in ${SUPPORTED_MODES}; do From 0b4725d69d48060ba517bf13117b46669bbe69db Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 29 Sep 2019 23:35:14 +1000 Subject: [PATCH 524/695] [ssl/ssl-certificate-expiry] Fix unset starttls parameter Signed-off-by: Olivier Mehani --- plugins/ssl/ssl-certificate-expiry | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index 9a3e8142..c2bb7cf2 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -132,6 +132,7 @@ main() { else host=$service port=443 + starttls="" fi fieldname="$(clean_fieldname "$service")" valid_days=$(print_expire_days "$host" "$port" "$starttls") From a8f1eaf388384acef1842dfb73a0c2333d06b7d3 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 30 Sep 2019 15:26:12 +1000 Subject: [PATCH 525/695] [isp/internode_usage] Make titles more groupable Signed-off-by: Olivier Mehani --- plugins/isp/internode_usage | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index 01c465cf..739242c8 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -161,7 +161,7 @@ graph_config() { case "$graph" in .current) - echo "graph_title Hourly rate for ${SERVICE_USERNAME}" + echo "graph_title Uplink usage rate for ${SERVICE_USERNAME} (hourly)" echo 'graph_category network' # ${graph_period} is not a shell variable # shellcheck disable=SC2016 @@ -176,7 +176,7 @@ graph_config() { ;; .daily) - echo "graph_title Previous-day usage for ${SERVICE_USERNAME}" + echo "graph_title Uplink usage rate for ${SERVICE_USERNAME} (daily)" echo 'graph_category network' # ${graph_period} is not a shell variable # shellcheck disable=SC2016 From cf747932301037c236898da3fb3727792bafa0f5 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 1 Oct 2019 21:17:47 +1000 Subject: [PATCH 526/695] [router/upnpc] Fix autoconf Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 386a69b5..171fb532 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -61,34 +61,36 @@ if [ "${MUNIN_DEBUG:-0}" = 1 ]; then set -x fi -if ! command -v upnpc >/dev/null; then - echo "upnpc not found (miniupnpc package)" >&2 - exit 1 -fi - PLUGIN_NAME="$(basename "${0}")" MODE="$(echo "${PLUGIN_NAME}" | sed 's/.*_//')" -DATA="$(upnpc -s)" -SUPPORTED_MODES=$( +# If called without a mode, default to multigraph +[ "$MODE" = "upnpc" ] && MODE="multi" + +get_data() { + if ! command -v upnpc >/dev/null; then + echo "upnpc not found (miniupnpc package)" >&2 + exit 1 + fi + + upnpc -s +} + +get_supported_modes() { + DATA=$1 echo "${DATA}" | sed -n " \ s/.*Bytes.*/traffic/p; \ s/.*Packets.*/pkts/p; \ s/.*uptime=.*/uptime/p; \ - ") - -HOST=${host_name:-} -HOST_TITLE="" -HOST_NAME="host_name ${HOST}" -if [ -z "${HOST}" ]; then - HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") - # Only add the host name to the title if autodetected - HOST_TITLE=" ($HOST)" - # ...but not as a separate host - HOST_NAME="" -fi + " +} autoconf() { - test -n "${DATA}" && echo yes || echo "no (No UPnP router detected)" + if ! command -v upnpc >/dev/null; then + echo "no (upnpc not found [miniupnpc package])" + return + fi + upnpc -s 2>/dev/null | grep -q 'List.*devices.*found' && echo yes \ + || echo "no (No UPnP router detected)" } @@ -177,7 +179,7 @@ up.negative down ${HOST_NAME} EOF ;; - "multi"|"upnpc") + "multi") echo "${HOST_NAME}" # Don't repeat HOST_NAME in sub-configs HOST_NAME="" @@ -229,10 +231,29 @@ fetch () { esac } +if [ "${1:-}" = "autoconf" ]; then + autoconf + exit 0 +fi + +# do data-based detection here, rather than in +# config() as we don't want to do this multiple times +# when the function calls itself for multigraphs +DATA=$(get_data) +SUPPORTED_MODES=$(get_supported_modes "${DATA}") + +HOST=${host_name:-} +HOST_TITLE="" +HOST_NAME="host_name ${HOST}" +if [ -z "${HOST}" ]; then + HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") + # Only add the host name to the title if autodetected + HOST_TITLE=" ($HOST)" + # ...but not as a separate host + HOST_NAME="" +fi + case ${1:-} in - "autoconf") - autoconf - ;; "suggest") suggest ;; From cfa5206e99fb2692f102530f9c5f1630192ec5d1 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 2 Oct 2019 20:20:33 +1000 Subject: [PATCH 527/695] [backup/fresh-backups] move from system/ Signed-off-by: Olivier Mehani --- plugins/backup/fresh-backups | 82 +++++++++++++++++++++++++++++++++++ plugins/system/fresh-backups | 83 +----------------------------------- 2 files changed, 83 insertions(+), 82 deletions(-) create mode 100755 plugins/backup/fresh-backups mode change 100755 => 120000 plugins/system/fresh-backups diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups new file mode 100755 index 00000000..efde7930 --- /dev/null +++ b/plugins/backup/fresh-backups @@ -0,0 +1,82 @@ +#!/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. + +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 + +=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 backup +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/system/fresh-backups b/plugins/system/fresh-backups deleted file mode 100755 index efde7930..00000000 --- a/plugins/system/fresh-backups +++ /dev/null @@ -1,82 +0,0 @@ -#!/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. - -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 - -=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 backup -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/system/fresh-backups b/plugins/system/fresh-backups new file mode 120000 index 00000000..72776f8c --- /dev/null +++ b/plugins/system/fresh-backups @@ -0,0 +1 @@ +../backup/fresh-backups \ No newline at end of file From 089324342103e40b3d0e8c4e89082fd96d784a77 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 2 Oct 2019 20:21:58 +1000 Subject: [PATCH 528/695] [backup/fresh-backups] Rework limits Signed-off-by: Olivier Mehani --- plugins/backup/fresh-backups | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index efde7930..43b9ba5d 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -u : << =cut @@ -51,18 +51,22 @@ GPLv2 =cut +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi # 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]. +# How old backups should be considered as non-young 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` +# and warnings below `backup_number*lifetime - 1` CRIT=${backup_number:-1} -WARN=$((CRIT*LIFETIME)) +# We should have at least LIFETIME-1 complete backups when the new one is in progress +WARN=$((CRIT*(LIFETIME-1))) # The situation is critical if there are no young files, the backup is down. -case $1 in +case ${1:-} in config) cat << EOF graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} @@ -77,6 +81,6 @@ EOF esac printf "freshcount.value " -find "${BACKUP_DIR}" -name "${ARCHIVE_PATTERN}" -a -mtime "-${LIFETIME}" | wc -l +find "${BACKUP_DIR}" -name "${ARCHIVE_PATTERN}" -mmin "-$(((LIFETIME+1)*60*24))" | wc -l printf "freshcount.extinfo " du -sh "${BACKUP_DIR}" From 196051d3411c0f18c738e2bd477f39b8c655f272 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 2 Oct 2019 21:20:39 +1000 Subject: [PATCH 529/695] [isp/internode] Cache initial API response The API tends to be unstable and often fail with a 500, which makes the plugin disappear. Keeping a cache allows to prevent some flickering of the graph. This allows us to further split config data-fetching from live data-fetching, thus removing the need to support DIRTYCONFIG. Signed-off-by: Olivier Mehani --- plugins/isp/internode_usage | 73 +++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index 739242c8..a467d9db 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -35,6 +35,13 @@ If multiple services are available, the plugin will automatically pick the first service from the list. To monitor other services, the plugin can be used multiple times, by symlinking it as 'internode_usage_SERVICEID'. +=head1 CACHING + +As the API is sometimes flakey, the initial service information is cached +locally, with a one-hour lifetime, before hitting the base API again. However, +if hitting the API to refresh the cache fails, the stale cache is used anyway, +to have a better chance of getting the data out nonetheless. + =head1 CAVEATS * The hourly rate are a bit spikey in the -day view, as the API seems to update @@ -105,7 +112,45 @@ fetch() { || { echo "error fetching ${*} for user ${internode_api_login}" >&2; false; } } -get_data() { +get_cached_api() { + # shellcheck disable=SC2039 + local url=${1} + # shellcheck disable=SC2039 + local name=${2} + # shellcheck disable=SC2039 + local api_data='' + # shellcheck disable=SC2039 + local cachefile="${MUNIN_PLUGSTATE}/$(basename "${0}").${name}.cache" + if [ -n "$(find "${cachefile}" -mmin -1440 2>/dev/null)" ]; then + api_data=$(cat "${cachefile}") + else + api_data="$(fetch "${url}" \ + || true)" + + if [ -n "${api_data}" ]; then + echo "${api_data}" > ${cachefile} + else + echo "using ${name} info from stale cache ${cachefile}" >&2 + api_data=$(cat "${cachefile}") + fi + fi + echo "${api_data}" +} + +get_service_data() { + # Determine the service ID from the name of the symlink + SERVICE_ID="$(echo "${0}" | sed -n 's/^.*internode_usage_//p')" + if [ -z "${SERVICE_ID}" ]; then + # Otherwise, get the first service in the list + API_XML="$(get_cached_api ${internode_api_url} API_XML)" + if [ -z "${API_XML}" ]; then + echo "unable to determine service ID for user ${internode_api_login}" >&2 + exit 1 + fi + SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")" + fi + + CURRENT_TIMESTAMP="$(date +%s)" SERVICE_USERNAME='n/a' SERVICE_QUOTA='n/a' @@ -113,7 +158,7 @@ get_data() { SERVICE_ROLLOVER='n/a' IDEAL_USAGE='' USAGE_CRITICAL='' - SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service" \ + SERVICE_XML="$(get_cached_api "${internode_api_url}/${SERVICE_ID}/service" SERVICE_XML \ || true)" if [ -n "${SERVICE_XML}" ]; then SERVICE_USERNAME="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/username")" @@ -130,6 +175,9 @@ get_data() { USAGE_CRITICAL="${SERVICE_QUOTA}" fi +} + +get_data() { DAILY_TIMESTAMP=N DAILY_USAGE=U HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history" \ @@ -235,18 +283,16 @@ graph_data() { main() { case ${1:-} in config) - if [ ! -z "${host_name:-}" ]; then + if [ -n "${host_name:-}" ]; then echo "host_name ${host_name}" fi graph_config graph_config usage graph_config daily graph_config current - if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then - main - fi ;; *) + get_data graph_data graph_data usage graph_data daily @@ -255,19 +301,6 @@ main() { esac } -# Determine the service ID from the name of the symlink -SERVICE_ID="$(echo "${0}" | sed -n 's/^.*internode_usage_//p')" -if [ -z "${SERVICE_ID}" ]; then - # Otherwise, get the first service in the list - API_XML="$(fetch "${internode_api_url}" \ - || true)" - if [ -z "${API_XML}" ]; then - echo "unable to determine service ID for user ${internode_api_login}" >&2 - exit 1 - fi - SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")" -fi - -get_data +get_service_data main "${1:-}" From 374cdde94107e99b97fb201e3636c3b714ed46b7 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 3 Oct 2019 17:34:07 +0200 Subject: [PATCH 530/695] Remove "upnpc_" from list of expected code style test failures It was probably fixed around 81bf32a5. --- t/test-exception-wrapper.expected-failures | 1 - 1 file changed, 1 deletion(-) diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 4c3ded00..7614025d 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -257,7 +257,6 @@ plugins/network/traffic plugins/network/traffic_ipt plugins/network/transmission plugins/network/umts_sig -plugins/network/upnpc_ plugins/network/vnstat plugins/network/vnstat_ plugins/nfs/nfsv4 From 3716783a4ba92e09d0dab50759d324af2976e253 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 3 Oct 2019 17:36:04 +0200 Subject: [PATCH 531/695] test-exception-wrapper: mention removal of a file from the list of expected failures --- t/test-exception-wrapper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/test-exception-wrapper b/t/test-exception-wrapper index 08e854d3..e667542e 100755 --- a/t/test-exception-wrapper +++ b/t/test-exception-wrapper @@ -50,6 +50,6 @@ elif [ "$has_failed" = "1" ]; then echo >&2 "ERROR: the script '$SCRIPT_FILENAME' should pass the test, but it failed" exit 4 else - echo >&2 "ERROR: the script '$SCRIPT_FILENAME' was expected to fail the test, but it passed" + echo >&2 "ERROR: the script '$SCRIPT_FILENAME' was expected to fail the test, but it succeeded. Please remove this filename from the list of exepected failures ($EXPECTED_FAILURES_LIST_FILENAME)." exit 5 fi From e926acaf5f0b06e28adff0cdd62d063ccafa1057 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 9 Oct 2019 15:54:29 +1100 Subject: [PATCH 532/695] Fix titles of similar plugins so they are better grouped in category views Signed-off-by: Olivier Mehani --- plugins/backup/fresh-backups | 6 +++--- plugins/isp/internode_usage | 12 ++++++++---- plugins/network/hostsdeny | 3 ++- plugins/router/freebox | 8 ++++---- plugins/ssh/hostdenied | 3 ++- plugins/ssh/sshd_log | 3 ++- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index 43b9ba5d..c81a6152 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -69,11 +69,11 @@ WARN=$((CRIT*(LIFETIME-1))) case ${1:-} in config) cat << EOF -graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} -graph_vlabel number +graph_title Fresh backups +graph_info Number of fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} graph_args -l 0 graph_category backup -freshcount.label number +freshcount.label files fresher than ${LIFETIME}d freshcount.critical ${CRIT}: freshcount.warning ${WARN}: EOF diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index a467d9db..93f51054 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -209,7 +209,8 @@ graph_config() { case "$graph" in .current) - echo "graph_title Uplink usage rate for ${SERVICE_USERNAME} (hourly)" + echo "graph_title Uplink usage rate (hourly)" + echo "graph_info Username: ${SERVICE_USERNAME}; Service ID: ${SERVICE_ID}; Plan: ${SERVICE_PLAN}" echo 'graph_category network' # ${graph_period} is not a shell variable # shellcheck disable=SC2016 @@ -224,7 +225,9 @@ graph_config() { ;; .daily) - echo "graph_title Uplink usage rate for ${SERVICE_USERNAME} (daily)" + echo "graph_title Uplink usage rate (daily)" + echo "graph_info Username: ${SERVICE_USERNAME}; Service ID: ${SERVICE_ID}; Plan: ${SERVICE_PLAN}" + echo "graph_info Uplink usage rate (daily)" echo 'graph_category network' # ${graph_period} is not a shell variable # shellcheck disable=SC2016 @@ -237,14 +240,15 @@ graph_config() { ;; *) #.usage) - echo "graph_title Uplink usage for ${SERVICE_USERNAME}" + echo "graph_title Uplink usage" + echo "graph_info Username: ${SERVICE_USERNAME}; Service ID: ${SERVICE_ID}; Plan: ${SERVICE_PLAN}" echo 'graph_category network' echo 'graph_vlabel bytes' echo 'graph_period hour' echo "usage.label Total usage" echo "usage.draw AREA" - echo "usage.extinfo ${SERVICE_PLAN}; rollover: ${SERVICE_ROLLOVER}" + echo "ideal.extinfo Quota rollover: ${SERVICE_ROLLOVER}" echo "ideal.label Ideal usage" echo "ideal.draw LINE" echo "ideal.colour FFA500" diff --git a/plugins/network/hostsdeny b/plugins/network/hostsdeny index 2d4e7af3..1fdaab46 100755 --- a/plugins/network/hostsdeny +++ b/plugins/network/hostsdeny @@ -32,7 +32,8 @@ fi if [ "$1" = "config" ]; then - echo 'graph_title Hosts denied sshd access in /etc/hosts.deny' + echo 'graph_title Hosts denied sshd access' + echo 'graph_info Hosts denied sshd access in /etc/hosts.deny' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Hosts denied ' echo 'graph_category system' diff --git a/plugins/router/freebox b/plugins/router/freebox index 8d395b1a..edfe6848 100755 --- a/plugins/router/freebox +++ b/plugins/router/freebox @@ -364,23 +364,23 @@ graph_config() { echo 'redirect_udp.label UDP redirections' ;; *) - echo 'graph_title Freebox connection' + echo 'graph_title Uplink traffic' echo 'graph_category network' echo 'graph_vlabel bits per second in (+) / out (-)' echo 'graph_order main_atm_down main_atm main_wan_down main_wan' - # XXX: parameters duplicated from traffic + # XXX: summary data similar to (more detailed) traffic echo 'main_wan_down.label WAN down' echo 'main_wan_down.graph no' echo 'main_wan_down.cdef main_wan_down,8000,*' - echo 'main_wan.label WAN' + echo 'main_wan.label bps' echo 'main_wan.negative main_wan_down' echo 'main_wan.cdef main_wan,8000,*' echo 'main_atm_down.label ATM down' echo 'main_atm_down.graph no' echo 'main_atm_down.cdef main_atm_down,1000,*' - echo 'main_atm.label ATM sync' + echo 'main_atm.label bps (max)' echo 'main_atm.negative main_atm_down' echo 'main_atm.cdef main_atm,1000,*' ;; diff --git a/plugins/ssh/hostdenied b/plugins/ssh/hostdenied index 611c632e..a0e4854e 100755 --- a/plugins/ssh/hostdenied +++ b/plugins/ssh/hostdenied @@ -58,7 +58,8 @@ run_autoconf() { run_config() { cat << EOF -graph_title denied sshd access in $DENY +graph_title Hosts denied sshd access +graph_info Hosts denied sshd access in $DENY graph_args --base 1000 -l 0 graph_vlabel Hosts denied graph_category security diff --git a/plugins/ssh/sshd_log b/plugins/ssh/sshd_log index f9b19341..deb9a063 100755 --- a/plugins/ssh/sshd_log +++ b/plugins/ssh/sshd_log @@ -106,7 +106,8 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - echo 'graph_title SSHD login stats from' "$LOG" + echo 'graph_title SSHD login stats' + echo 'graph_info SSHD login stats from' "$LOG" echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel logins' echo 'graph_category' security From cfe070ea38c11b32fbd001f72d6600bc47c64922 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 9 Oct 2019 23:15:37 +1100 Subject: [PATCH 533/695] [network/hostsdeny] Refactor to support any blocked service The HostDenied field is renamed to ssh. To preserve existing data, # cd /var/lib/munin/example.net # mv hostname-hostsdeny-HostsDenied-g.rrd hostname-hostsdeny-sshd-g.rrd This refactor incidentally also fixes a bug where empty or commented-out lines where also counted. Signed-off-by: Olivier Mehani --- plugins/network/hostsdeny | 43 ++++++++++++++++------ t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/plugins/network/hostsdeny b/plugins/network/hostsdeny index 1fdaab46..48d7eee1 100755 --- a/plugins/network/hostsdeny +++ b/plugins/network/hostsdeny @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh -eu # # Plugin to monitor the number of hosts in /etc/hosts.deny # that are deined access to sshd @@ -6,6 +6,9 @@ # Based on denyhosts plugin by tjansson (2009) # # Copyright (C) 2009 Kåre Hartvig Jensen (kaare.hartvig.jensen@gmail.com) +# Copyright (C) 2019 Olivier Mehani +# +# SPDX-License-Identifier: GPL-3.0-or-later # # 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 @@ -20,25 +23,43 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +LOG=${LOG:-/etc/hosts.deny} -if [ "$1" = "autoconf" ]; then - if [ -r "$LOG" ]; then +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +if [ "${1:-}" = "autoconf" ]; then + if [ -r "${LOG}" ]; then echo yes else - echo no + echo "no (${LOG} not readable or non-existent)" fi exit 0 fi -if [ "$1" = "config" ]; then +COUNTS=$(sed -n 's/^\([^#]\+\):.*/\1/p' "${LOG}" \ + | sort \ + | uniq -c \ + | sed "s/^.*\s\([0-9]\+\)\s\(.*\)/\2.value \1/" + ) - echo 'graph_title Hosts denied sshd access' - echo 'graph_info Hosts denied sshd access in /etc/hosts.deny' +if [ "${1:-}" = "config" ]; then + + echo 'graph_title Hosts denied access' + echo "graph_info Hosts denied access in ${LOG}" echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Hosts denied ' - echo 'graph_category system' - echo 'HostsDenied.label Hosts denied' - exit 0 + echo 'graph_category security' + # Assume we always have SSH + echo 'sshd.label sshd' + echo 'sshd.draw AREA' + echo "${COUNTS}" \ + | sed '/ssh/d; # skip ssh + s/^\([^\.]\+\)\..*/\1.label \1\n\1.draw STACK/' + if [ "${MUNIN_DIRTYCONFIG:-0}" != 1 ]; then + exit 0 + fi fi -echo HostsDenied.value `cat /etc/hosts.deny | grep sshd | wc -l` +echo "${COUNTS}" diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 7614025d..575854cb 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -223,7 +223,6 @@ plugins/network/ethtool_ plugins/network/fwbuilder_ plugins/network/hfsc plugins/network/hfsc_sep -plugins/network/hostsdeny plugins/network/host_traffic plugins/network/if1sec_ plugins/network/ifem_ From 6ca9f65af962b01d905a37ab5c32e604e7ea4f96 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 10 Oct 2019 12:17:33 +1100 Subject: [PATCH 534/695] [transmission] Show counts as stacked areas Signed-off-by: Olivier Mehani --- plugins/network/transmission | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/plugins/network/transmission b/plugins/network/transmission index ea3d15ad..f82092b5 100755 --- a/plugins/network/transmission +++ b/plugins/network/transmission @@ -95,13 +95,13 @@ graph_category network graph_info This graph shows the throughput for Transmission torrents down.label throughput down.type COUNTER -down.draw LINE1 +down.draw AREA down.min 0 down.graph no -up.label sent +up.label Bps up.negative down up.type COUNTER -up.draw LINE1 +up.draw AREA up.min 0 multigraph ${plugin_name}_activity @@ -110,16 +110,12 @@ graph_vlabel torrents graph_args --base 1000 graph_category network graph_info This graph shows the number of Transmission torrents -total.label total -total.draw AREA -total.min 0 -total.colour AFE3FF active.label active active.draw AREA active.min 0 active.colour 77FF6F paused.label paused -paused.draw LINE1 +paused.draw STACK paused.min 0 paused.colour 8F8F8F """) @@ -152,11 +148,6 @@ def fetch(): def print_values_activity(stats): print "multigraph {plugin_name}_activity".format(plugin_name=plugin_name) - try: - print "total.value %s" % stats.torrentCount - except: - print "total.value U" - try: print "active.value %s" % stats.activeTorrentCount except: From 118457fa444157e46790e7640a9de9e18816f4e2 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 10 Oct 2019 12:17:45 +1100 Subject: [PATCH 535/695] [sickbeard_] Show counts as stacked areas Signed-off-by: Olivier Mehani --- plugins/sickbeard/sickbeard_episodes | 9 ++++++--- plugins/sickbeard/sickbeard_shows | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes index e4f38a24..4670d75d 100755 --- a/plugins/sickbeard/sickbeard_episodes +++ b/plugins/sickbeard/sickbeard_episodes @@ -46,9 +46,12 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') graph_title Episodes graph_vlabel Episodes graph_category tv -down.label Downloaded -snatched.label Snatched total.label Total +total.draw AREA +down.label Downloaded +down.draw AREA +snatched.label Snatched +snatched.draw STACK EOC ; @@ -60,9 +63,9 @@ my $req = $get->get($sURL); my $json = JSON::Any->jsonToObj($req->content()); if ($json->{result} eq 'success') { + print "total.value $json->{data}->{ep_total}\n"; 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 { print "$json->{message}\n"; diff --git a/plugins/sickbeard/sickbeard_shows b/plugins/sickbeard/sickbeard_shows index ed755ac5..4b3db267 100755 --- a/plugins/sickbeard/sickbeard_shows +++ b/plugins/sickbeard/sickbeard_shows @@ -46,8 +46,10 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') graph_title Shows graph_vlabel Shows graph_category tv -active.label Active total.label Total +total.draw AREA +active.label Active +active.draw AREA EOC ; @@ -59,8 +61,8 @@ my $req = $get->get($sURL); my $json = JSON::Any->jsonToObj($req->content()); if ($json->{result} eq 'success') { - print "active.value $json->{data}->{shows_active}\n"; print "total.value $json->{data}->{shows_total}\n"; + print "active.value $json->{data}->{shows_active}\n"; exit 0; } else { print "$json->{message}\n"; From b97762097b8b6301d3d1d75cce431e74e9fbfdce Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 10 Oct 2019 12:18:10 +1100 Subject: [PATCH 536/695] [sickbeard] Add multigraph plugin doing both shows and episodes Signed-off-by: Olivier Mehani --- plugins/sickbeard/sickbeard | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100755 plugins/sickbeard/sickbeard diff --git a/plugins/sickbeard/sickbeard b/plugins/sickbeard/sickbeard new file mode 100755 index 00000000..d3a764f9 --- /dev/null +++ b/plugins/sickbeard/sickbeard @@ -0,0 +1,94 @@ +#!/usr/bin/perl +# +# Munin plugins for Sick-Beard +# +# Copyright (C) 2012 - Blauwbek +# Copyright (C) 2012 - Thiago +# Copyright (C) 2019 - Olivier Mehani , for the +# multigraph support and better error handling +# +# Sick-Beard : http://sickbeard.com/ +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Requires: JSON::Any +# LWP::UserAgent +# +# Configuration example +# [sickbeard*] +# env.host http://host:port/ +# env.api apikey +# + +use strict; +use JSON::Any; +use LWP::UserAgent; + +#defines +my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8081/"; +my $API = exists $ENV{'api'} ? $ENV{'api'} : ""; +my $URL = $HOST."/api/".$API."/?cmd=shows.stats"; +my $sURL = sprintf $URL; + +#config output +if(defined $ARGV[0] && $ARGV[0] eq 'config') +{ + print <new; +my $req = $get->get($sURL); +if (!$req->is_success) { + die $req->status_line; +} +my $json = JSON::Any->jsonToObj($req->content()); + +if ($json->{result} eq 'success') { + print "multigraph sickbeard_episodes\n"; + print "total.value $json->{data}->{ep_total}\n"; + print "down.value $json->{data}->{ep_downloaded}\n"; + print "snatched.value $json->{data}->{ep_snatched}\n"; + + print "multigraph sickbeard_shows\n"; + print "total.value $json->{data}->{shows_total}\n"; + print "active.value $json->{data}->{shows_active}\n"; + exit 0; +} else { + die "$json->{message}"; +} From 95ebf419fe8880db362093485bf20bd868231575 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 10 Oct 2019 12:19:00 +1100 Subject: [PATCH 537/695] [fresh-backups] Show backup counts as stacked areas by age Signed-off-by: Olivier Mehani --- plugins/backup/fresh-backups | 52 ++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index c81a6152..febe4809 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -1,4 +1,4 @@ -#!/bin/sh -u +#!/bin/bash -u : << =cut @@ -39,11 +39,11 @@ An example configuration snippet for backup-manager [0] follows. =head1 AUTHOR -Olivier Mehani +Copyright (C) 2016,2019 Olivier Mehani =head1 LICENSE -GPLv2 +SPDX-License-Identifier: GPL-3.0-or-later =head1 MAGIC MARKERS @@ -51,6 +51,9 @@ GPLv2 =cut +# Bash is needed for this array to work +COLOUR=(00FF00 24DA00 48B600 6D9100 916D00 B64800 DA2400 FF0000) # green to red + if [ "${MUNIN_DEBUG:-0}" = 1 ]; then set -x fi @@ -73,14 +76,47 @@ graph_title Fresh backups graph_info Number of fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} graph_args -l 0 graph_category backup -freshcount.label files fresher than ${LIFETIME}d +age0.label today +age0.draw AREA +age0.colour ${COLOUR[0]} +age0.critical : +age0.warning : +EOF +for AGE in $(seq 1 "${LIFETIME}"); do + cat << EOF +age${AGE}.label older than $((AGE*24))h +age${AGE}.draw STACK +age${AGE}.colour ${COLOUR[$AGE]} +age${AGE}.warning : +age${AGE}.critical : +EOF +done +cat << EOF +freshcount.label ${ARCHIVE_PATTERN} files fresher than ${LIFETIME}d freshcount.critical ${CRIT}: freshcount.warning ${WARN}: +freshcount.colour 0080FF EOF exit 0;; esac -printf "freshcount.value " -find "${BACKUP_DIR}" -name "${ARCHIVE_PATTERN}" -mmin "-$(((LIFETIME+1)*60*24))" | wc -l -printf "freshcount.extinfo " -du -sh "${BACKUP_DIR}" +for AGE in $(seq 0 "${LIFETIME}"); do + FILES=$(find "${BACKUP_DIR}" \ + -name "${ARCHIVE_PATTERN}" \ + -mmin "-$(((AGE+1)*60*24))" \ + -not -mmin "-$(((AGE)*60*24))" \ + ) + COUNT="$(echo "${FILES}" \ + | wc -l)" + echo "age${AGE}.value $((COUNT))" + # shellcheck disable=SC2086 + echo "age${AGE}.extinfo $(echo ${FILES} | sort | sed "s^${BACKUP_DIR}^^g")" +done + +COUNT=$(find "${BACKUP_DIR}" \ + -name "${ARCHIVE_PATTERN}" \ + -mmin "-$(((LIFETIME+1)*60*24))" \ + | wc -l) +# The last count is also our total count +echo "freshcount.value ${COUNT}" +echo "freshcount.extinfo $(du -sh "${BACKUP_DIR}")" From 5587f77cf5cbb0e2dacc530467052f919f82f7de Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 11 Oct 2019 21:06:03 +1100 Subject: [PATCH 538/695] [sickbeard] Add perldoc header Signed-off-by: Olivier Mehani --- plugins/sickbeard/sickbeard | 95 ++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/plugins/sickbeard/sickbeard b/plugins/sickbeard/sickbeard index d3a764f9..5c3d968d 100755 --- a/plugins/sickbeard/sickbeard +++ b/plugins/sickbeard/sickbeard @@ -1,37 +1,66 @@ #!/usr/bin/perl -# -# Munin plugins for Sick-Beard -# -# Copyright (C) 2012 - Blauwbek -# Copyright (C) 2012 - Thiago -# Copyright (C) 2019 - Olivier Mehani , for the -# multigraph support and better error handling -# -# Sick-Beard : http://sickbeard.com/ -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Requires: JSON::Any -# LWP::UserAgent -# -# Configuration example -# [sickbeard*] -# env.host http://host:port/ -# env.api apikey -# +# -*- perl -*- + +=head1 NAME + +sickbeard - Munin multigraph plugin for Sick-Beard and forks (e.g., +SickChill) + +=head1 DESCRIPTION + +This plugin uses the Sick-Beard API (also implemented by forks such as +SickChill) to collect and report on the numbers of shows (total and +active), and episodes (downloaded, snatched, and all) currently tracked +by the app. + +It is a drop-in replacement for sickbeard_shows and sickbeard_episodes, +reusing the vast majority of the code, and providing the same graphs and +data series. + +Sick-Beard : http://sickbeard.com/ + +SickChill : https://sickchill.github.io/ + +=head1 REQUIREMENTS + +JSON::Any, LWP::UserAgent + +=head1 CONFIGURATION + +You need to specify the host/port of the Sick-Beard as well as API key +to use. + + [sickbeard*] + env.host http://host:port/ + env.api apikey + +=head1 AUTHORS + +Copyright (C) 2012 - Blauwbek + +Copyright (C) 2012 - Thiago + +Copyright (C) 2019 - Olivier Mehani , for the +multigraph support and better error handling + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + +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 . + +=cut use strict; use JSON::Any; From 3abb60e3e71fa26546f03699646bbc2bc20712f8 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 11 Oct 2019 23:29:56 +1100 Subject: [PATCH 539/695] [debsecan] Don't include suite in main title Signed-off-by: Olivier Mehani --- plugins/debian/debsecan_ | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/debian/debsecan_ b/plugins/debian/debsecan_ index 7ef84e1d..ab743004 100755 --- a/plugins/debian/debsecan_ +++ b/plugins/debian/debsecan_ @@ -84,7 +84,8 @@ esac if [ "$1" = "config" ] ; then cat < Date: Mon, 14 Oct 2019 00:49:26 +1100 Subject: [PATCH 540/695] [network/upnpc_] Add missing vlabel Signed-off-by: Olivier Mehani --- plugins/network/upnpc_ | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/network/upnpc_ b/plugins/network/upnpc_ index 171fb532..36ce6fbd 100755 --- a/plugins/network/upnpc_ +++ b/plugins/network/upnpc_ @@ -134,6 +134,7 @@ EOF graph_title Uplink traffic${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network +graph_vlabel bits per second in (-) / out (+) EOF if [ "${traffic_remove_max:-false}" != 'true' ]; then cat << EOF From 6a47ad0aed3db6ca2e87eef34d8ae1ac29536158 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 15 Oct 2019 20:32:59 +1100 Subject: [PATCH 541/695] [fresh-backups] Reverse order of series so new entries are on top Signed-off-by: Olivier Mehani --- .../example-graphs/fresh_backups-week.png | Bin 0 -> 28371 bytes plugins/backup/fresh-backups | 21 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 plugins/backup/example-graphs/fresh_backups-week.png diff --git a/plugins/backup/example-graphs/fresh_backups-week.png b/plugins/backup/example-graphs/fresh_backups-week.png new file mode 100644 index 0000000000000000000000000000000000000000..384474e89ec5801797984efb67a8d43fa6f8f93b GIT binary patch literal 28371 zcmZU41z1#3*Y42WC|%NubjMK2fJldQiG+05&gFy5kg;&y=p6UC(UY=Bc{yb19FzOm@7%rNLRb&V&%RK%3OYW!o={JTB zC5907M%8R7vh0L*h(aKXEz)lSJ((LdwgggjWM(`<8SJQEw3RV~(Ij)R@5S7k`)iqO zC+kCb(I(m}ScK5BJL4=3rq=(9ZgPncF zeROfL)wDBoa&mHFVj?^|JSYembTzm1;J!IPf!q0(Wqg08s;ps?QWR;+xuNxHHR8vQ z=LO>;cC*zV`V+4g&%MMm{qNT3m=qq4!z3s6F7_r%nl=*k!y_V)vg_&)I|W&OyM+aj za*X%ihO#7wclk%^2r&=IL_Cj|TYYb7y|)==v#YAky2*^8>#>k+X}HtwL?N&s@w?+r z(DC1oFaErPfp4tdym|Bf{Th`+^T~vUiZwrF-Q1wqe&8hrWlVW1e$bBqV3Uwnk5@YD zmi%t(o!0m%2bUfnJc8&C8@4j*>+2uywj__5&xY<1OG`5|*E5z`_rNE^zkUJ26!E(g zUR#K!mGHhjs7o8%TxjxeSZwwhOcj{7q`|A+%p$u*V%0 ze{2wwf>EQZ^?1G#J3Bj{#Q-P(vE;wB;yCZrO9LQ`RVA~ysOWvBJpzGvJaF*u9~>3vwQ!9T|l%Q;99gf6=1k%lyKlaF*rkgyD zb{RCaw2<288(eU(vH2;L92|~z#tR%8ck+S9f;-Axcg9LfOKWQdOSFsre0*CFW5&Xt8&6v~?+LIh1~~)=PyO`a0ri%3GF34NA%=7BMlO z!}*3kfBt|>YV?YVir$qPq@<((yVKv_pVC>~(sExjWt1jpHyi%sMaUJSeSviACn?7{ z04G%zgQA|t^dLf1r}e(r(^Kb3e~PG{^z`)ES-qtGKa-R0z`g_H#i#cw_>+6MYJ}DQVfB*m;j0%YYwjgIuN+nbL8o*0kRaN{0nJE6j z&d#V6e_>(ayc|9qn3{?T7{VaXIDXC=fMK?S^xAaA`!pekeB*Hu5(Z=Qp>&b5)|=ha zjeeVtAFK83tgYFT-a0us0Xt@3VBqKXkf#t2zTM}mn_HNkzG^x0B|s(+d#U|vK_%iS zM2sHud6-cd!c*M!XSi2K&>P?L07XSLOAg{#0D#^EXf7~o1jAMdZ?-StnfK}XulxJ^qZY*CLEVy(re@yM9^zom#N%)t&?Eq)9^8u>WmA0Y;%S3Vo5y38%r*xAG!EhJMS!F%EG&$S7GoJ9 z%2{9un@Nrm?f(1T)zwu1;*x@bwRw3=41X`v;Y^8&iVD|yQmg3_ZJX>Y#*qN}S6p#% zaeDgte7wA%?3R{{FX_;no;haha^<4ZA~II?Z-tRa$#3(2#k2Y!cVN)a&;a(juy7~C zW03(fx3<>3H<~6RBg3dHDVHsXp6z+$(i)%_Tfx-rOTHfJ>SJTy6d$jL{Y&-hIGLG006JPzQzI=(vr>n7{u*TRJAr+!)-H|PRHi+S zJ)^X|yu7C7tFlHyLc;z+QP-y2si?^om(%NL_RBH+Fn)UIQl z>APwPM99cgq_1g&9i3uw+Bc)Ol$cVCiRmKI0oF&ok!jZ zs1p(j2*`MojwR>_%CIqjcel>2)?n|nwHIB-mGnxq@t}FzI42YhO7f&P!BcW>U_JWT zxjEU{dVE^aBK8W~5LFrIGn=gr3_5dS8uc@;o1KEGVqHG#F(#s0N}IMRon;bED4jUB zY6i@`X+cRzX<=bOK|w*f_ML&#Yx%UuIeQ%IctAX{u?%TZ??<0?VVp=q zPYSlXp-=)6&L|{2XCmY=&?` zUFwC|S!~dNfg{Ld=T`u!(|M|Zjdq#QU^8IdPP|(P#E;tnGvfmo@6b^6AbLT-W14k` zFFjn=U0hrYbS;$!tXlnBqvjAR(||>la)FJlt?kE;V>_R zSn_^H<;A;FS7GWb|A%W|!~zB=44CZ>NW_LSL_nsssK3)8*8!eog`&~uyUbA5Q zrEVoEh~p_v_}^GjjwH9fG?@M~AOS80hCufbP)u6d+{}#byLXmVGx#*3UHY{&#B6j? zSM=b|u;14qA-rxhRD!lvR#w@@T*j^9e0*n7LW?F~!^E_07Lf z`GTI8PKnES2j`I3$|`duc_@hi3@nl{BPvGD`@W+_EdyrZdSqfn>RI>c(0x4M&v zr=O}#Z@%{CGfL6{m?q3p>)8Tg2BN*il=}{W&yxmWXd7@Mx-Ql`cg~sYbAi4Sy&vMi zf+yZr>)JY+*|>C0cDbB%gH6IuaYlFlO6FO+Lu-IVv338&TBwJ>5bS>WOr@31UkS4y zgb+9OdOI^S6R>B?f(wxE*DuK~^b@j@Yq~ee!dW|KX zot2eUOpL16sc|zIjTFL2ANhpZ=F?m)-o#z{(ZK9^_pHb-m`c5jyE+_oGy#T>^?zUP zDFq`Tq^DchIPDvNxKh=e5TV8GEe>W$W;J2#EHcpd!7?t6rymaCbp@x;+JiF5p%-30 zJ;*Z(QN*8#AsVFJZlSBaqlg8Mr5lC;;D31x1Az&k)c5Q0DxCVYk^Ksos?>iWyHd+1 z6@c#{8z9`+yTfWEj@}D#O>=Yta)Lu;{$WBS)8gg(@#EQ<9^a+mt?S~`U$4uprgA@3 z##1sAGDmM_P)2W3MDOg0ukz&s!q4u=Xs3f95VuNv)DdfpjQ|><9X4=Lx+vG9zo>o& z#wRdu;^GyoE})&CRcKNa>*-}ROZ)70gni#(LsHU!kMnOQv$*HH{OF)y$L+nnQjV$w zttZ;ozS=dsWj7r?X=MrVux&x?QzGUmr^9&>`wabmN)h6CV$Q57voRG>x_MoE=s!jP zUU=wC`8(6k(-5%?<+MR`(0|fE1C&1-qNqO+?u>-0U`jw4pex6qWqeH4sTF4m^G#sh zvsWP@|Jy>~FNl@?rUB+B6+_o{L#>=|KO^D8JZwqxfk*#A!f^@puk>F%{jVk8yYL1% zHZ72G8nAJ4TFl6o`t)iw$ToTaS_;Rc3&W6~M@L$<`uT*Fk8ad%qKB19K5pV;&ACm9 zwqhL9l$>)5J`N<~eNQBXd>IOowBP&<5&v80#&QCNC)kDAluIZU#?q(!F@e5PxUr_@ z35fSU;TH|4va(V~Fm4Op@Z3Wlfe);WbcL^>Hm?2l+U@B4UdZUCM=@&W*B^6a#YO1Sf2GnLK7cjrlpRR_QMnoJgPENRv{qI)4ogr9AdveY=|E`Sy7q|S9 zJW6ho_Sc%#f28qWy<6_^2^6Bk1D)9YspS;;k0Q%Z?i?I^t2@UjjiPS!F!(A4;5&d` z(&7%yR;aX?&tc{9Y9WJbc+krPS)U788uJ;Tw}JR_`LZl6_zeFu|Me}tWh5c{PXSKn z-_UQ5OAzGLfW%~EivBb%*hk5YPfblNEje(L*9%%@G9drd>8MZYxTxx%BY~az-(`_$ zcMaZJ9a4M!XKw&cba`=+GPv2=3XWsaP*<`rKW-+Muy z)qc@IlqR8+DphZaa{TAfpVnJ(cs-B5#E#kU$-Ch`7@&(O%YEY|_uCJr$FV=L95BCamWfzt-_;E*k>efHyfpX<5S{D!uCJLEr*CR-NvD^0{HPgcFRIOCX@V({yS~kHF9lx;%0ZCtc8)P zo~`s@EE=pxZqWkELxaOiI%;T$BQPldks@(xqHachC|9H}cPSUJ!Y%i+Czy zgXi#dADnr|osPDBNF27`XPx=nHN36YGoI1o zb|^}po|(5Pelf^xrL#92!XsXz=Pa5WLJ+9NwK)3BPsNeIPr^6aVed=%E4qV?B$3nf zKYDP%UQx&pH#KYP$?F(ZyMnuHAEJ>w z6<8}{yWK{3P@JkUs)~`mHjk<>MLUQVU<2EHTAjpGhD3i9e={)z+Il_W683;&-)Ud) z-ng#!7me)H;Tx69K<-(h2o?IY=zBwZ!C|R3pRPua@Er)o&%~GdKvD@XOUy9%qXA0Y zB4(jw&hFFi58!zYdY-zS2-YnoFwe^Gkwi?)9LGkQL_aH=W|h~@1M!6lhm9u6d4)Ph z$aU-Di^6w@VO3uj0CsVTix@PraTfBjrYQ=O)!A!pZD8;j? z3?=i(k}ilnV2tBBVtAUpc<#~>i(Zitl*)VV-hd45p;>c7{;X1e;40isZu5RIRM-G* zZY0pi{GcnoQ(8q@42Bmkk(3BqoLsXo#v011OWz5ZjLa~tW_+3@CFrdW(?4y*?*{fl zr^&r!!5-i)j4REtN_LrEA?#l{o$KE&ixn|A&}o9vq&jk0Wgq)%+ZGdr{Kj!xnd6BF ze|AY0EvM{Ou{vbt@yD7PY^2PMl}mmOY+@@TvWZLY^~ow}`FZDAv4p(tPGH_FB|@vA z!G3%@N1JJ_C+QR@b;dRcLk|$JnG{FNyAVj66EZdlIWLIiI8{ratj?kdeAuoM6>&?G zRTbxyh2}=6gwgOjb?Oa#{0zricO)Wnptj4q!*0u44Hhe=b%*NdTk@G6lB2Z52}Wr;b-mRQHTrjJb?9{gX>#{Wy@{f>VD$!cJMs- z+|3$*7u{qh9Z>@@@`MBrIV2BSEWW~cRR(*S4pR=BCIox4MIY8Rpvawv_kQ@S;(B6T zhe9;O<>|NgHIRzVe4F99IZ5FlO*h#?zho5)KE9C|h0%%ziXy_7w>6?j&qud_?f%j_uA^ z^&0JLk}!*Hwd&YTxXDUOze94TPG+lCK?HUvmcjQ@qIY`=?cl?_D`c_Ws|oj(X-ngJ z{h94b>8A*&T`f)JU2Bi)YA^dgP=W+$lsuGNh8~75klu-x%V+!aHfd{D-)s|JoqBP* zcQjudV<1nGgK(Uv&_Jqf!W-?grF2F_UImz#vTSppEr-_m6k3yR66W&bv95@F%V2WS z&A~^N0i$1D`~IG_zB@Tb9OnxN-IwBC_4j=Qyv#?2+|C50!$$_?2=p8hmb|mkzuCd7 z&v|k7An}SPO+83b@)#1+39U;T*x#RId-{=_eBlHq*(UIn#(EWnLaRH+dz&{0AvSj^ zq&vH!)ajLLhfgE$2^b2PT)>o54e7KZyP`nI$q4u{kE2DyCiez|(%jr!eTSAmCft}~ zEmR8BEe-;>c|ZTyEYJ9Pif3~l_=B-Sh$zoj;P#6In3#QrG=HLtM;wykC);BgbHZ-J>u9Ddk zMNMaV>&w4_{XO)Dr$Ig~mFVkNIE?D* zYCs@YS62Y>TV)x%(TB{S>3a_HjMOKqt~y!@;{UMCXm}E6OIlW^MUZ7g#*g@@KY#yx zg|Se{9O`XINVfJ4x!BX!75R!i?06CqZ`4K|Vtz4-PfJJh?mIwfUlE^TnFO!d*RdwM zRr-1XZ2IzL9G7o^7a^7Clx(H&mKcI>Sonim!7(=`>5S>>M znJ9z<%WrusK?U8uyVd#$v0DRj`F*#am8Fj|sCqVIJBx$16C&}cbBg$1XmkK7w({;Ow&W`i?8-9sr&aKUZL5TG#U>KtHEQD0>dhx<{- z2P!K)V#_Ku<)T{K{v}7^4~knKDnw!tV(7;Q%*oeeo@?_X|D$_0 zZt_C0FES?A0~GUBwXzqg?EIX}ve(|f-!IAd=gP`s`%3+AYo<@|dJ9g^e14Yp<1Ip> zjf2pNuG^aipfyf)+Xhw~`*M6BQ*Qgea2bKHxx#RZ#}<<%Sxdym-JOxLfW|iYU`ajd*hc(>U&(O@04DvpRp+z`^VGTDHS( z^@6?ktckh6?Bz&HRj*517T^6ZZDHf9&CLG8gl3P9`dYZz@Y14d3BF)S4;j&vTm8ae z1R2-v#SMVIJf_TV4NNdN53YW#?F@;E_z5u2C4dkDswwnohYO46+-uuI5h!;ANowhR z0nzZL^jCybBG>XCNt~!lc&;Qgw8Wz)THm>6T0jG3ctd^QDhg0x$w^5`85wqUbGJ91=nQ&W<(MNa5euRPLXnO5Aa3^> z=l(q;sU1-yt&<&^^U+0mLFd!h36|AH{Ld#5KS;~=Mq>TGkv6;0(2hO9=oBhbKN+24 zCur)c(beDXzGYxDFWnn4Qyz=(h(Q|1&7E+ouB`=_hh0}u+)f}b+eZ7T`&parC0q1j z5~V%$!r^iq#OWDI5eewK(L3({uFaR<`dF;)2@LL*x0EtuOotaEhbL*06b>6gGHQ-G zSNy>w%0Vms3W(nU!U|Ko_9Cq{QA#kkYZRzqph}N?@&NGF#3uZG1QL<&195j~scgfw z!CS;1cS!k$Yu02ocaZ&tM&RPOLex}FMym}+1Bpo&UBlpl`Gw2I}MB zSSk=>63J2$^V2W&&++s$Y-BFe9V)vUY&b7jsbPFd2Tn^5Nk*CT*WwX4I^ru`sj2u3 zXq(D|MScM6xzaa6crg_aoY*ulHbY%)E!%?IX3;nFDReTjl^TVYe*zZgpXN2Se?#c+ z=$t_LZ8C+*g3&>YBG5aA7p$Sm!XErbIM6*MY?ka^^ku$owhv0Ymw_V~-mp-;)3 z8bI{*=M=eGx|-QFGv)uM7r+ouETF*0G}VF<;Kq5iqjkI^cXf@M^Ef7=Po!9@=kTaMv26`zecAWm3N;m(6CDi{;J8 zt2IrW53Whn;VJUJai4Sx0@wwdJTAq~h%{0Ro}&yu}kQP0expfRVEA6GgV%Wp-uoGAXE-D~QwKXwrc z?;WKrGqz_?rB)*Dv{3IEt?@PNbbk1Xry8n{?#hrq+(!-Zkx$}m%^9XtRgg&#PL16u z$9+jSR1z4E8W9-8=NK|C)&rZXN+cb9Zi6CO1@7 z*}d9UOeR0M7WL0wm2tveFoYBq1~#^tFx`v-wLzc=|Lxm10Re$;-}cK5TcPKHC>W*9 zr*A+pvWQY%GXAbvf=JWtMI=pV1{#d%9mha0EB9nAkd1jF0G)h1fqMJrlvIv6OXAjQ zG_T$x9PStIzfQlv+Z}al+Vg+0!u})Ig^o#ZVcHcNW#8wk*SUN62kbLSl>NcqKFk{~ zSDn9$gv_~pu#E%5m;5fp=CbO2Yu?!>g$=t6C%&6+!^zBjez=52uH{4@_(-lHKUH#P z!Vxmo(sXHc7|`?%A55w8`g&qz&3|Z}_lNAv$;#}&JquQ+IKfvVsvBsVtu&D0ftF7^ zXJ=>6y?re$3yjLPDEmku9&Th|{vvK@1sU9FxJpzqxT zH>qCo^14Sg4MFTR5y&lz8?6`q6Hl>ja;q1Y8D!}w&)W za!|~%zxS%`A1X;wnY$hhSe)g^hLtviw%wI~7t{$3#1^OE z_iU;2{dO<4Hi$JF?VzF5jZRh$TT59#Z@pP#Xd;op0+mXn-u@{DZAf>6K(A}k-NU_X zy=;8MXiP@bj<=UvV%aY8@IX)dz1)q-v0zNMhcRsrb#$O*I}1zG$(x8KM7wIYpagi( z*Cf(uhIXTEy5GP-agIq^-P_JV;qvE!ldb$c6uk)S{%5|k6(2^}8YnI_@Q$#xE8M06iIIQnChAWqdVNu4!vpJ^ z%|YGdS4vE~L&&{aUm_AXtj#NP7+INULJMyJ1wyyK`4iSg9x)$c3 z+3k(bxqe-<>V^X{IWIKJ6%o?uF!{J4kQhi%rWEeMEuAor^18;)f9l}H`-7E-d#H=O zeA?dS*H5k^13Zb@3eSH8=Zea2XtFALi1+?!wB#ke#j3WEA|Ts z8(PKmR;`vJ|HOYiR=Khq%++${!-?E8#2FT`^S)wHso}%+gm!`# z7F9xRU+DhyY(V4daQdy)<%`x9;X)WODGkJz`XyRO*XX0m&?jkfr|MWU6;xOtdEf$8 zbu53heO0iay(jjm4p6b}=o-Zzrw}+cwROFltplCXN=7OXr}XVdcCUKbz1r$b*9Mtn zpCjMKSwdaO?WqHBeJDWW{2k};4-s&!Ex51SQGKJqKrI9Od0zoY^+!XsFLajahoq<2 z+cqE@*S$^36# z;d2GI(+SwNtN^vm02H(!ePLBmb1KE>&DTz+xy41(pL_#Tf3qhe1X_m?aI6W>;=|?` z034`ZhXd=PItRy_qMm!A_K)PdEY0qYvs^oMW`D5K5ruo0345xqre4aI2GeHUJwgN? z%#o9?j!;Mo_jEk1SGymongzorUJiH+HBLYi78R7AgHnbGknlo!1a;bur2?TD@jfe2oJunHGPv2z*qJ z@&A+LEm;wy?i^PDj%z%}_%Co7?wMpk<5v`r>ZP$V%b4+-J(b@hg;M4=XJ*5Kqh+56 zFn>eNq@;NJn(|sPe*S_~P5uCZB$k7kG{AJeztAT`R?hdxhaqC(*;8uL)YW&_RubF6 z!d94@5F369ZwHl%+`{YW3}{>lAbn79jEg$XYw!9v25VS1MY#5$F(GgGrW_ zl>vPn0H^%U$*F>Hp^2zxbWjXIr5+u3m@v>#hjo_}m;QS0{R>}&!C{8Agug8lf5`iF zC)dpglQxfL31!qmGUHd))WDQ91c?pX@U+3qd~Z$O6lsmxy@=j9vkAiyiDNbLn)Xo* z^{YDD+MlG$HXdfM@X%?E&Cv*C8&zVlLDyP*8~+MJYOjd;2LRt_A7um zg^Y|WBG-rP^jWBBm&dRN=4cC*eK?aY-AaPCi3hS>UIbhLyAh+ER;)OUuV>Aw2_y2x zq^`gBOg4aYtV}*LjJbbk+g(2%yors(Nj9#DmWdrPR zd)vy!21t7-;1RPE6A6ilKrbID9i6d}k>4f9BV)oc=Jh#t{A0c zAW!m=fUKga0Tug|6;yOgoifn(7av%78e<~q*{H{IK*xGys z)0(v}@nP*gVNz|amQi)QbQo=^?t$#*1cEr1q-WQLNzZI`H}SgCEb42w*~wwa#Gb_N zs9MqY0RdHXK(WhbbL?<+2KHSVKvE7|V!26Su*9Fqv7<4RUPD}g2it55z%4*)X11Jb zNLk7=Vz(DRg{)t1CnhFN8TlNLwOzfIlj{Nc9ByxKEr&B-0*q=1aMw_bU$m)bB!%a8 z3;FR|JO6#ZtE6?bjlmfe6(Tw$)G?Bq&NrVV@nQVK^`|~F8p4|EZJ@Av9H_|ZpZpC} z1xi*{t|}}H&5;+vg3iBD1~*-5K6~3|bo_dL`I*3y)5~)HA66ZyGqWNKmKZJ@Gizk~ zlbEYd7p|?UW^8#=KmoaIpmy%a(H7uOpv~bqJ3H1Nbb^p|c#@G7u3}m6v8XN`kI;wR zUrD3Oy0@KZJm_s-n%|4l*|`Mc6Xf?4N*Q7ZsNZ8aos9}_8rMmAH3+I7cR*?HOaKvZ zdG$aPtxehJN^*ieLEt}1;W;@ga2R;^@Nj5&xD9B{1aNIW(qgmF^7Yy3aaZbjzd!S_ zF|XMfV$0xo!R5X^=xmVJa2-Mp@%N&oU7T~fgRI&VzP6^mN01zEoe6}sv0CwndU3

    Ec3@zD(Ri{Rbdm#2)>A`>h9(Z+U<9UjDET(vyb(4heq z^0#MhHSY6&?E!Rh0EHWXZyp&}kp#fy!OF}W__onwkt%iXXE;0qReymRJ}t30pLa z$ZOP2p)s>!`GrV9FP*|-6GjdbO82&10*96$@J9R*41M)i4~_VT zOzg&e##0@qqo^zEv+^BbOjw%&a%zAhv4*k*pHh=1poI7QI0-pAo*dPK0 ze2jfk1?+vrd>~^t5st>!ZW3`{v#M}CV)4eP{a~QB8oGSIeGeGsj4U*8_y?wqC%Ipl z=jB8f#C)32#^>_J-6DYB)2J$`X?V}D?BCn>A+y7>9AR4H-#Exm|N25$FDF6Pc|M1Q zIWOg)sZ*jilz2Auy2}Iez6EHZAi$RSVp2l~-4iQVeGqFB1Om5U#Kv5L{m(AS!Q=GQ zJ)ctHB`+O$z`~V&eTz8sZwqP(J@DD$`fwTPw9s_%=M#i?N!uTaoQf^li5Sc0&IS}QK-%DL;FlOE46Nk0L`a^9g# zb$vFv{2>DGO6CE|AO9OJ?P(~dL_H^m3~qgqFg{G6qBz$I>IzaHnXM%QGu$yo$f2i_#i+ihKq$vq{p<4y&^{qbjSm|6*ZzzW5YybDTCa8-ZzjRtyn|_I z!X=}iBMs)hD+WbG{!_?;))$|nke&D`iW3zdgj4nP^I7pYsJ>j8#|LkaoF{+Zx_c&1 zz6SzdycULxG zkTeL&%wkpJ<@O6WFEkZJyGbW|+ro7ahrfH?6trq+HG0sNX*d}Q8B=ksocw7?^W`K% zn4KQ$HonN9r6AZz+`1#4j!tsq0dh;n3$>~k_gsC%5Lp)VQ~~ITS5}S&dWj5KGY<>B zIqW9y^S_i~KXO%BuP&ai)VCHhEN)bwRge)ePVDtvGvdw*)Ud8pZ+GQtmNA~a-fj>Y zCLt3okHf1<9c)2KO0eLG6j_KDZM>iVUH`M_q3Elu1f$Sa`0wdX5O)jDpG|D{CPu#e zy1Y5+P6S++83z$#9ClYWs_Fw#)7013*V6hrW1Fam7}6g#WY4=Csw+f-Ex9@h?T}7q zNKq-Ajk?Pg9c5Oe!!pfM#tv(XN=iG|XaMt9e{E%NN3r8xVGDE|xZ{U{&)M@c&mu}U zvd>0A*^F{s5K?(sh!8)&?Q}U(XE{LhU!QGp8PpSk00*!KzPLTqQ2A!;WUPL&_w!LP zjLHT(R3#6~t+VotI-@zY;T1MN>UD`(ak=P|a+4?Zu?)Mb+Z};%wo=dJPQ22wMO$<% z2CM`SbN8#>Ea zNsEo!0Ny9-apA}hB4!l>K`6`ij^_CLE#*UBJkw~L_~v8TK(~w)`S9X)lyviAd5Q5C zhd;l?npOY3>Lr|g9!V{n0C&nQQu9%I9>Ck*aH@c+#IF(N)^fo#7`#M!7Tnd7=bfH?$4T{<9gyZ`_%RxVc0aB^cc*O@ z&cl>3ghNBGNC_#LfH*);2NE9(a#12aB33!DynD*0<K9*k6`hAwTFtfWaRs> z%W_m2#E>E5RQH&&CB)n6(^};aPAY-DMCnX_fr(1-N2hpy9ktN=Xk1I_W84uY*6sY; z#$xdOE3YXxLkZ&_fx~@uWp9P|+?y`EeJw2Vw>j0^z^Ooc>*EKvasvzrera6FIX~Ug z1H{XBcgEVgw;i`;sg)|f?U>OOYt;F7*tbOM|N3!I)$oR&#!q9s%WGn6P$D$CVm1dc z&Q>x`HsQU#(iw0$dcMb;^&UjXcl)EH(C>*}VD%CwW5MhmMwaxKQvDvKpWHrHbT7+k zDz2__4TQ>yEIjYEMf|xI-Q!%@DJ(-R4SS?hXhQ7(9wR;;CoX#UTO2CY%u$ni(&`!t z2wiEvzA8ArZReySq1;6}aP}Iiz%R*{fXU$hvb|Yf<0;nYm1Qb#Q0z?M6uGCJ+$${l zbtKYenza#4Ly>F8EecB8eH5V)gCVlr+h?2aneGykwC=7HI_{w501X_lPRmw` zmeHf4`c@Q-K7a{w0vDwH9s0!c*;y(rUCU&j4SPC_4dD7SObN860*Rh1e3gOnk0&5S zn7Z#u58Ln&zv1T3d9mtF9_oC$GU=w89>bt&7d#4qs3J*(6V1IEz;L-W>>-u(!+m&> z*NX&;D&sE9gPPH!gU&;MLVFBQ<&$O9j&EmzaPJ1?>6Og1+x;;4@ zzTn;J&pp}WC)tZv<8T1elzHK9{QS<5^mWy zM<98~7rr?gmdxULkAANQ^iQy_TBTFAN_O+4PgyV}NQDT`(o53WQy;kN_@2oS3P<-q z`G53iQZG2zocI=9ova;P#DmtO;JT)8S14`*El3_AMM%W}RQoMsp5PV;?VRM4F*B|( zVy_>M7N}$?D=7{3_rsUM|7B|V9iL0sBe;=f^WJKRW-$x9WtT#q;YoZ;$|4Yoo{S-s zz5YZVxM~$guDwPr=2VT}NyFK88$CE)b5341YHUb@GXM|7eKgxM#WO(FOk*5XL;*1~ zVRKFO6%L$|lNXb&J*PV=sJ5W0qcL`?djVtl)e|;T>3Xz%-~YTyD&R70&Mry0j^p!T zMy2RSIS#>~C7I%-h`DGZ8;+I)9MFIu{%;*0kja}G1zND^8=r$wgEY7y^BP}Yc$dyv zFbZdR#`a{(T!YJ2lCj^f=E1>1fay;G(&^u zr~^F-HKQbL+|b2@3H2wM22kkF>UpE++&m8Q5#W*Gu2W&SR~%jag6w6NJazsfqj zI`1z!fP~yRxK1q^`rluiKjUb->gK}6#`e44GX`>NR(c91O+>Uc_L**w28y0hTC5tS zHJFml?3*o^5@g2KHrFn7z3n&bNa?!24##Wvo6Ork33kc!J=ui2JuFIzg7w}|ZE3t> zDCa61=8B|Go;ts>Vt@Hx7&hV?FAKLiZ~XQLMyozF)YWA&)-nNm3yKrTX&7;>rPL&Z zE?J-^m~>rRZDy+p0)NE27E*vPV?%bgR93<;&LyR4G?DrgQU}363 zM7^W4IZKKs!g?y#5*iy(@U;#ueF67XD~T=L_{v^brpw?m0DD9_P}WE$G7Fyjk8-hX zIUatZrAs9F8~_-RNfC~mJ@}4S=*om!OxN)z&ArF7?<=ANAWGu+ECL7Ws9Ub=Au33( znjhGEziYHOmtOVeYSh#g-0Z|317s(}w8KnKf(z|w%jRZ!0eb-<(e#0m_KdE|gaZNL z8a!~ljslCB+=+V7U>YbpI*oBD_E15pK7Q|2r5-#P{Y@(=s{Dpeiu%B#Qoa|1k$uuP znR18=_Wmk|P6AB+#5NFCLG{<$0j&kVu`F^*w8fVt!cV$bdcRf(d{B$#d-0b71ag!h z^6cTdpiAD!Tu-}@Y7+Cgn0V544P%GC1&0b9?zp&kn1dKUhJ~c^N2|8|y$oLj&>kMC ziOZK68FfT^SPSN2&x>*p8|W*)#N1KD2A>Nas2icSeX4IuI?AkHbmCt3fVNZ}WeT&w zF=c4MUk-Fd)r&Guew8Qf2=JhrA=^ODeLI@N9K^PUGJqa@;H()67WYTDqvG0z%@!38 zg9|B{{V&VDJW3SrHB>$jaQb^iA%4j9+m{Z?vmv$b$L=gH7*>`4s??_i% zvudI%bfBS%zQ?oB-a*G#p%(-6_U8Qj2ma}V>7ruu?x3D$qoQM6#=~hai-qCn3}|(td6qV?Q}Z{di_q$+HP&V( z8oJ+#+>pxSRwjuyvuZyg1_>}Jk*ysc&}JSO^NZeFLKc&YPD0#?;(NJ^i;S&YGAv)d z?BAP0b~VkqZ#;UITFRwN=@igZdX&s5}=^NaGNfu{1L|CEZwonJDjH zhVN$keQ&hyNjDj1k`2(b+@HkN%l-E0f|!}j?_4whxhAAtCUUF-AW*{N8A*598GrJV z1ORO{Ms2<9%}&*ml9RMzPW+C$NC^^t+|jU27(4^t>x$5*u1APg-5UyO59q#nunKmn zo*JydjexPmh)1(1R051g>+Rv~po8E03LRim*T-7ULiMMRKnkhmH)u8zUVlewmMui(&B0AtoG7{QBc5+ph` z`Ucpf6+h6dBmd^jm=(WX32+!dn|7h81L`X5#1CRJ3D(eT$zzRC>H>_46Te8;n-gKm zv*D?;y~XRPx#$ex)C+Eu1h5}B*Bk_JHs)6qb|FsuE2s6H(KE~vZ8{rY&aYymeb2YQ zz^%n^w&8!%rLpnx-Y6J~b>h zilAx(NQnFh!&DnRHkzDb)Gd{38a>Fn!Mp2sE%gVgOzZ#?=4@jhDX5GNrZqF!b!r$c zTRwPO9KI3_o06da=Stq2RLwkGl=(j)BTE~!pm04xH~3_nCG3B3P95CJTzx?pr} zJ`9xI{2u!5=sK1VlMEQ&ubiu`piE6!n@|cdr0IISxk?ykGIcTVaa){n-hxTBujYU<_D(vhnud-yYjv9*&vxQ~$VFgNvr*fINSdd);GDsur>ofnxf^v6mj0IVyI zeIEdK!OqoWp*XV^cHxMIybedAH;QM%p!>NdRPb)+1W7q#Ow|y=jUnN^D_U7qrII0f zG4C>L&7TS!tN~mp(4+w@eER~o42zGhbnGJ9RHpvI-BpEYC0vaUkm=@&TSm+F!_^N!1FOaXHkr`VYdH zuj{@f6Kq^kJ+pukmv_YiAti9ohxrFAUoi;V^|etrra1`@^XH-ZV4-N1`!Z8NZO3iS ze8m8Nw&@-g2)yjYHBR7~)c_xK8aBC>Qb(ixwkJ{rwk%gGMILbc&<&=v&=ivCM0EFb^}(ps z=N!e0UJ77!TqUe|k7I{e%)ohb9b#a;I*7cOJR%x{BIi}PJTHs;))q=cS#P z;bXo|(^a0ePuG61ekYdMg*RmV26bbhB1a_q{H!l~!#U!xQ3f+YjY!>l{^3xQ{>{){ z{f0;L2!+{jq)SLGm0Pkok0YN9C|ig{Ya`UXHSWd=Z8H7I=ha!0EOcyCs8rrLG+CQ! zR+>*qmt_}g3^;GmxL(XBVd~D>mw&E)NZ}H&cSj5gSk+S&Qw%lceg6z2i$RA)H(5B9 zWkfI4mJz*|wO^IczfoEl+<9;Xd+YJLd|f7+Ttu5B$=Ci^A-~>9mrpaa`kx(ic0=po z!#k(N_S}Vg`Q!ZDz}zFpIYJi)94P+~;mDqIq*4 z;CNrQ9Osi~9HBl3$3)>KbnPl~fae>dJ#9Uq^JG-!ne4sm)@rUE|-68pX>gl65L z22Hr(@T5+!>SaIXPbKn{q1?Y*il-V2weKb8f1k5|^lge#*C+WzVIp~b3pj-L*F6mZ zarq$73?C8_($)0}KH3MQXP^=vvJOqz3eF#`N)-ITVfe}cF9LumOwd90!agdvk(YY- z0OyYY8}H=G(ZwU3O1Zj?YKYyQEksk&OHfA>+_N13Uiv?cop(Id@Bjagm6@HrS0$m6 zBzqkx6d6hO&dzphp{z&vjRQAqZi4!9GP$7F9JATj8`~CZTzn|Y-7AeKnF_hQoI z-lg+7u_Y%)wq8jfQUXMi%f`az!&S|oP$W>}L517cx}JAJ0!^v)!epXJw;-vbN3gH8 zQ^O%D#jA>+L1#xaVz38|7U~z$XBV5;?KERxfe2l*8Fqsl6z{wa6S6WZ_xFJ z7CfNaoB#n;HYz(Si9zuN#r%YJd419k%UyR|wAKCH)_<3rMeO1Vmj-3(GfwR2 zvVIcGynZpg_dTJ)b2-@-+gz=-#~1twhdc|4_AQ>f)$IIO@ApDeez6zSBswHcEsHTHFRBrEr?eh5E3B(oJZP-SfvSKVe>A2Xb; zaG9b;6zUh@`(QdfS93a%iwZ;tX{up&3A@207d=VtB3vkS3wd`K&7NI(tTwbaF7RK< zh9c!}$hT&HFK%mGI$cgw;iQ|yPd0s1w!f>0b8b7G3no&Z zPVVdXs8HB=#@`6r8^2iIkAJV=%TW|dkddJ2G|O-%j*Dg9#-xxpZkvj#$L^o|h_I;T z^h5cy@U!l!+wxP~`Oo(MH)=h6uc__lUS6k^z}d{3vpAcP9@o$CuHv)e*k3t38T)?9 zD}jFJPIFIrq5R{+E^=J58tL)K)P16z`HVcQ1ocEM2Hq zcXM`BEB;3xtZNvSk{)?nQPQ}%%vs1eIeLwC zSKP<-`}-A6<=_+(n-2z>(T(oYV__FJ%}2>~U{}-M_G?~FIxZT0YS=UN7R@VORaQ&~ z@&r`ersSQMl6l|ZO(QEeYjK)Yi&*4>{^qWV_j0<*Q$B7@I#I-@3tW0GO&5A zK4H^1C!Z0uiTRK+rDqDog2k8zD1k<`#q|~^(pj=}%q)6t(7&?GC7!B8<94wX zVb2^-%Nh>T4T7PA-1BPL_2J_CV^dSm6usB4OoQ?NfvKmw61QDL%0ulD7+v!Sb6>(_ zjz11v&bW-eys`OMDlV?c0<|~^vA3nf2SiKF2L8JC9X%k*!A3Q+m3^%p2ZX&)KMxJc zLxl#HT=WY4=RBo@SF=h#{aqby9Gb~h4iWUt4(!~M_|$fS_beB(w3`M0!~=23%TGvW z=QDXNCE~#8^9(o5PquY*4AgkjN${zH281IDPI9?9$aMNGPh30VMBypxU-*jNojwb= z3J^c+|E@9WwwE0@;^ULVWS)ooeKQHTi;N~}A8WEAZs}N)S@5T888R+R5-7Np6Ci{J zr{U3}@9a4L!Wxq4mT2ir)Y{wLd>$811y%FB zstoc!#B{UDtjFQT>j8hI)F-iLzCy9&i?fu})!W8z(#P~lO#?L~njyrF?`^zOYry5K zBz}ISJ7ktL(IyK)U#bz;zBxac$j92tvKx%b;4xCHV2i)uPePC0ow6(~Lz zIW{z@{X8~)v_0|)cdziCjtqBEF4VsMT_YVg-g_P{tYc+yR?~ld2fEm@B?A1_NyK|U z+;0E`^<~JLrbEt2o3Ghl0@xnTuwbQ>E zGUHa(E?>+I=0clenbmBWH3q$>CHv3op6y8;t>WfEW&J7QP^)S}b=FgEMv9G0p1TNc zB^G^M?AT~fk;fkTZIa0$4vZY5Zd!-MbxSe81HU9d6+M5OBUabw`Sd!*A({bJ;&m|s z#E)ARx1DBN*zpYo5(P7E5A$;1oR@37 zr*U9I)SHVKGZgq4RCeNc(y+gWuD|t1`pW3U#}C3Hc%0uohIy>| z(4G!~09+vXg};C8!KefOymR=AYzR@$mhP%sEN{he)Z(%~qh|PE_A#%Ar-obW^o)0$ zD;_#%51jo~G(XE9rrC;;(Y?W*W}8yEwQG!#@A4yGnEa4cBW1)R(2cZozEbp#QPCTd zA{H&ht(mNS!7}&nY%zpSIi79X#Jt;x!1x`mRV1jl9W>;2bS#<=`**e@`ToaF@O2{n z)2YRp9%hpL$4(rf%ErhMpAFwQ;dKDf%HEx#8GLrWiurCQ8|X8JIk3%HSSMdW{|yf~ za2VqPU6^p0FLKXy>(2f&;ao`nG=u^b-fw|~w2Z3>h`$6~CCxvXhw-Cc$=PkbsrQ64 z20gLlNArC*%6i~ik+$Z>d|4Akeaj<4x4&=Br{(#dOee^fMA=FH7Lhg|MfIS%ts z`^Bjtf<>B5c{j#*w5+o{x2#b-_oP?aL^5jX>+I~h)QcJZq}Y}>_Ra(M4m=g}XTwQJ zX6)49ChaC_$@17V7?BLlM`)GxDrH4{MznzQ$T&2jlT#YK?(jx~!k#$G$?yFyi|x%> z^-*j&Z_{c-5V+gAD<@mmod-_1UyJPx0SC4>vB0b&9XghBV&+7Q&}^_C zS1Jpk_ivfHjMoS4*}b2Qjn=3OIsFr9OyI4-Zk(kFQ<~}rh8ivQKA##7cFCcy}T)e!x+V}Z$4)c08 zcHKwPdAwlAVIlt&m7l+VrPG+cscFf;f{<344}2ALhifI6RgPV89-kkZR^OG``;kB$ zNrc$)`vZH;jB^8HV+ZRU+|agNYg}BrQd3w}WvD0zFb=W$E&9!ckJdd*B8Hhf+z33| zs`!4bu_*9mFh!M<4&qcL!i8iR%VU+?awtPI251^{HH*cYmy=%6Tz_8cwLIDwB(Hk= z=g*(eWfGB@FwBT;StR&@<>#;*MV8zxjWDspc+bsc1j#{PJ#>5SuWb3%{q~VbWNC1E{PwZ+wA#Gb2BpyMkLa*mIW_oiMso+gzSPwjQ7$LV`|UcPieiB1m6XX>~Vxa68Iw0<*R!r}|_zZz7RMKfit8t$D3ZLOqwJ(7^H0>haMm_c6ZW ztlkgzqbnh3BOxQ^DU2AD&R$@l+MMe%Ewg*}5hX1x4Q1JZN1GXo9!o81Z`QK_1M%|R zyLS*`!*}8QrfwKmSY&^M`WrEGpY5f)?Z}=_r?23Dm=W~w!@v-$X8E-1!IL#|i+4-w zp9wcGE%VFEvi{pkJB`PGD49jy%l%$hSU+EmS@3}62{(W%?aon|)&d$zEmvQIX zWl6f#fY&}V^Y>@;LnIf4=?`PW%gL03u1V!mlsv<$^m9gRaF(gzT##D1qDA_U3j^Nr_IfF#KWSN z3D}od{c$;Wm-xf&Z66!O+&-#PO;FPfY?m_dzffLQ1Ulo8fvL6J#g{i52q#TSDhmsH ztS*+9l|k(uz_ZjUgweESEH;vA{F!b<$fz|0Ilp9;X*$RA>9_V)Im2k=f3O)NCE z7Z-~PDcyOh{ouhOY%XyHY+*bXzvbrT0aYovd!tF*ac|?Nwk&QC$|d8>Q@X64zob#U z4Ydr9$CiOWcNWpw*7n^o>}wd{zueqf2-jh>2}$B@MW4oVwL{UKwph$TwCQ1XX6E#8 z;Nb~}tsSSk((8KEvsMV$&A z7(G2bo6@^?=Wtqo23W^`6a!lG$)@6z#0dt z+EvSTs;hgn#Osii2?_{^-2E6n$zZkdiVqd4&@>fInfMLa6LykaQbIzaza&}A^otC( z+7;Vun-S3dcJaMY5pb%m1s&Dt7p6wR5w5Tq$*3y3`S}u1)1XrX z0Vo9id(v8Nj^kD9AalTuVD(oOwQz;T=#L7oG{|lvo(oL*&ZWcHOaTQBjEA$6)5&Uq zg6?OjsZfuidYkclJJZnn?~aVB_T{5TM}eleCcAKoAZFmzs1N|D?CsmP`o+d?h8%#Z z1{H1=#n6@fT9zlsjc1Dop{cr3L9Zy z;%mJ=)8<#tb!X2L;CTVFu~!_*M`51Uy3duQ$@p%7w^>>DL1Y#euKNydSm?Vi#AQ2Kw!i6rl)7g&okOR66K1Y3!{;P8FQ;PMt~Qe zot*`$B+y}o7dhnv>!?uKyG@kMBZ^*L6~l|NK5LmhKc&5v40UxAOiH*}Sl~SdPo6A# z-$JC^s3=sX?NJ(E8{I5;YA7|!Rt+erntAg3w*w*toC3BbGM-D;$i##i0nPweVqnq0 zF9LE;LzkLJVj0QhYlZtg6oc3S7AF|gLv-|AXGsO}yURYS8IY79*m{nA?41r6wbut|>;a^7V z^Vzyzo&wJef4enjO8v<4e&eg-V5GH+7iPRM zFDkGrx5-UFTZA4~c-m+DpC^t-JJVh&4zcK~{If<5It=+;S#id{e+_kAau={!DK#G#VXU%tfC zBEz48zURAbLfEusMAc>nuCb5+-Mm>=_rkrsm7r78P)Jx=H|Tn zF5N;(1@GQ1LBAt#L?J*T%gWB?tDPvmb%yD@(TmhnaBR`h(eS#D_xQ{}r9AX`k?|&K zJaS=sb4NI#tJUH|3pR6%u#Pz3&nb#FFf4bIE~oa=KMub%fi5q<49X=?(0HLw;dtr$ zr4NQTImmhX((n(gmOoIhQDQ76-M9vEFNobh*42ply}BwUEKET|)7R5;Lt8svdwshq zW`hG<^L<_2Ql~M=XX%`t4f>pbTHc z$Dr@BvUmjrN!+TL$GhNN@9uUF`et4^X5U8e%vtQ2y3}Pd39bB1a1anI7xRnsBqb-8 z7+2FrU-11#6vNbtTU}fntMwLZ5g#d6Nv<>$x%hGkLJA}q(DCkyFyWtO-Hogy^l6J5 zH(O1{;bNAlM?3Kx5Z%xJFDK(=^)F1AA!`QUGfaHq==$rkc+r|2sLNI z(_pRCo5auA?Ogw6CJq@=wXtY&kK0C|zrR1UI-ctjh9~I5@Vko9J|TLhu97o$LxBD@ zTX;=P4Gh}Ln-M{t(&+%!y$)N=4&`Z1O&rU5^NZX}OkQi#iuEQz`&;(0YGHa(E1gys zquV~ZVwJ-CfvdVM94|F9d&Y#MDMFd*S;MR#wR8K=NcO%s6Srp;a@nk4 zt2qcVC&<$EkW}-sJRc{9RXYE3!Z? zo|&;ovVQpR;r-RCkZ=6!Z?~7nt^ffvR_!4SWVsP1E9+xgoi}%kinf973jlKK?*PND zll!R*@rHqEfOYA2<3996*1)pw*P$Wni!Y5$3Zc)0lm?9G8bY4n7>J~gN>=^SGfNuP z&~FL~s{C*cuC8l9Q=FZdalP*C6naM(k9&Nhn@_6*c}jD}SU-}u8C3uGPYd3^cLkT? zqbYpoI-dm&9lLbOyexB8(8NOtA0>dR4oaxr=NDO}@6u?~_ADgp!|obm@N(BH|J zQ_z3^_89{V_+}CmCqX|G0%6It=IIlzK34MXU0uL|QJk&8?p*7F z(~G+_%{%yC04nhzSk3j+CcF`uilZnOk(+L=ADl>-hf-nTPpZ zri^p+k3ow7xb>%D>?OF-a5nsZBi_>=-8&*7s(O8fXQ zxDPI(mV%{~wY|FyRui(*tgNip5fP;^v=)j4%Zn+vwDH>Qx$xnK+GOV$KKVOGsRVwq zg#jX$;>ezZuZ2iHBVhYwugx`30U(yUze3FH!5U#6PD@D$m!58**78K8cWPIX_>ix8 zW`3gTa0bUzN%QB_qbe#Tb+Qlt4z}6&5JB4G!|BDf3)72aQLezNgEFU`)hT)N=6c5g z6$FCX3V%jG`0nm51p0E;hDkVok$(rCM}t%0^oH2I3Mv$512Kv-gr2%Z9E6{?t3JR- z{#myB)%pY}=^rl2U1`GhVZ}nCd6mH03F-#w41xw~Mmww&N1Xr8O1;lI;c<_=uW3KY`?$ZY;x}v}Ic?cJ{0!LrjlN$j zK0uhRldDq>19~m90eczNAoa<|DoX4!cBZ7}L%?yZ0$U2H4y}?wiHV+YB6wHym>y3i z*5fTLarU$Y8R3OA^8ijDZ8!N|N1YfXtvhO<9?eo@mw{6eu!{=Em-(vSu>m$7vXsT; zWpE%xg@p(6=y|=Bz+8v^qYs9?1%at$~)q@X{f198UyRENRtywlr@D3y(eaFp*O^?t$PAdzIv+- z9%d|t+?2F^BCE-MjR(bGpEH|Hboj3BzIZ4%f4mG z6Cg!#Dw~&nhfJd!SnR;e->VgaX**KP^o^*muLn8k_8G=B?i{dZF?8Xz_(?_(*Zlo> zYWi#b=;#RegYo)KkO?5;IL?ZQv}R=Ug~L)lCoH_y2PqghZTRsa>ME$jeH>Zdz8y1h z<#H`i;3>q~1$13Wi%ltjk8Q22q|(1Yd?;etfU?T4-zdAl6AlrRJfsK^2iL~gl^YEo zb%Rv^sVCqMZkT_bD5K&q`bymyk)BDA(1uNePEt^82;5tfEt^!#)gdXK4sEFSHSr>d zP^Qr|HXeuL2*Pi{;S7oA45Y7J`EltMD`VCbzLSr%XyC#&4RV;2nlhP-ss@kaiDp1!_x`WFxsAmIgVV*Oq9{|-LW z{$xE6+E@0!E%iW1QFE^MbeI_l z^VV{&?{{)ycIt?Sb~1a?Af-Ve+zuqpYwtSZ`%ln>A~roo94i3U2Qvuc2o|TUwbivD z1n)4wSOq|>ht_ME?;|4x7`rtp!+ZDG;q~Z_;+FO`<$DHtz&sdeNH&XQ2jP*5i}1-R zZm=aTE)G-<2*$gu0Mqj^A|e6^;POYimOV-5m6u-5Ia2Ztj*jkwwjm@S5T(NR`tr`! z7UY2tVScQt+JzMFA`8{lU6o#sAUm-q6V9g{lGFvlNmTis^;^$(AMEdMuT1oa-wX^4 zymjlTRzCWVaA3%{YgDnHNSIrfHI2P$>i))^at(3JVkY5{k=(L=gJ$AZHa5j~OHWMA ziCu$<9r$NX-`shXkr5vRvdG#UkTTqMQyYgI%1ug!?Ig%})877gMS#5EI~DNi0h|J{ zv?Sv?jX1gtq8Xs!A)h>T>XcV)!2Z?(teQE~hwknl&tVd5d+F%tq`Y!DfaLe9KiXOtV2f=x6Q>P46{I-N8KL@jRSJn-OE)?9 z!*x6|BECdfC;|N6DMm*1+^?X>3=I$G=jCN5b6dE~c1>fo6!xVf+WrcsDypggIl+n~ z!V9{E!}Y$FWoKrY`0)n_^#E#?X#|y1@bqbyk@t#Ox2h)0R=4WhJn`j z@HLc!+VSzpmH`;~*RDI+{g)pa-HJH545wEJC0s8Yg774=#r%UIA3r~NtzY$1L-@$r{?yjts_Uqd@lM_F+6RcX5-T{eRJn%d&~ulH3 z<*%&aJeNLcjyUeXT>{=R=r9Hymrh_6)s^J;Gpvv%$;6etshelKh2l!A5-iNYA?k^4QEtbQ;x!t=3O!?ly|s z5!QF*w&OudzzU2&)ayK2_PEh2!qtryw-ys!@i0b&LJORE@GulZg*P4_rCNW7KhPOi zNTz~lQ2|>%5$15-1{A*Txr}>6>Q-NRXF+niJqApd`7$>dZ?$pU!^8`R<(I;v!wMp` zjmdaNzg+luMk?eg8_PplHU<@YJv&Z{da90%1kgJ8!sEE*kB$yjZwP>3`MjW@puN2v z=FbvGQ6vq}FUM&8DjwmkU?D)Wmoy$Jk!fbg-OnH34R?lFT>N%E$r{3lMyoD*x#lFu zimoRJVLgb;jsC#;hY%j*O_K&xHP{X_GqZ#3Wr*HdOt|H`)05jKjuee!!F_>eURzs( zjf<@<5A7p{8IjR8#X;qrkT@`#^)J#}5?WdrU`{Nll`Q#^d&r9@ij=XBo6LX;x(pUq zD@`iI_PUX$Qkhm|+u1-xvLcYB=D!KjlYh~oqNY|O^Yr!}9vpNWtI!mnPlOE!E2}e! z-R6(ZGex&0KBQw~dvc!v)bc-YiU35qA^Ap$8f}~VR7K<1u+s;mZkPY5tOZmTWYMrq zbUsnz>Yvjqwr(|2^L`Pr(c7MD@u4*M+lvkfA^3ZTND;@T_af0jkUV~R+z9sGOnjPq zT<>&R5ktAJPSqNLSPksRMbUm;#DRd+sx1to4qIzFZ#eWv_!7E|FR2FwHQp3@D4ni` zzHsf@fnZ7Z{2A}v5qV=l*!|?^Tg%pZB{gf1}1(t(G&d3i#IInQsUyH;qG{k)Fd_y~pt_GMrb@|pVJ z_;{Ti9O0lm^8CdMI0Yvcwq7F4p|)86one#sXE-|u^ny7f;0c!`nPEy`T`+Z zBDGf|_j&wrYeIBOJ(rvtrXkMYap;L(cGI{H+}T^s&MR=}hJ7q~zJA;N$mZ(vR&eSD z1S)WjOmU=uM(ze^@b-?5X+T;9=sIn#^t!-pH_D*5{?h&RdqWYXXhC7&Cz~Z| z=nrtzi+Rf0nrouQ&j$V~G`hv=V%JaiQ9E;Smd`eL*V2N8Jwixobk8ut0Xh6fD-sBn zQ)Zkece!8Qw7K|_R!Jz~abI#{m(@jW1_`Aze0*@&N&rA1!@|NqfAbu-_yFO}S=~Hw zDoe@K32jyu78VYU)`1VOBi3aFx1>e}sZ*@fng4s$H+LM$jqWCagyKU2r?8Ncl0t;* zBEiNQK~6z&ku4V14pobQpdcLH^)As>ULBQCqb^{<(mqicoZL>;a)yy2x^3olo2@vD z(9M+ouU|uHLA!E+Lzos8AY9ci#N~eVA}|{a@s!{pQ&LvO`N`%lB3eF*c$rp}I9;Wg znT0By__(-V=#3^V4uk8DRkHO9fu|7~7KT+Mo%V*cOsuG)65eFyTI3WK8VYj%^mJy= zDg(TS;y2aRqi$?0EP&NF^Y=f1jRtc2mv@9ge$&byaFnba^Z#H4Kx{}>%F4^JVQ^-R z8rm&thYGQFa_t=vEisJM`SZQF2av3Idwav_S!7(7fsC8b70&H?T1ZGE_bXVm(*gq1 z`I9hc?$yRww_uY1B_k?IeZT=H)u2#^pFSxlD~B11=nE&JxZ;R{4MJp;VXa^Z`=sF` uobq46;lJ0)RW_h*Regej!~e&EtLtIzYQYlt43;jP9TU43= literal 0 HcmV?d00001 diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index febe4809..d952c515 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -76,19 +76,18 @@ graph_title Fresh backups graph_info Number of fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} graph_args -l 0 graph_category backup -age0.label today -age0.draw AREA -age0.colour ${COLOUR[0]} -age0.critical : -age0.warning : EOF -for AGE in $(seq 1 "${LIFETIME}"); do +for AGE in $(seq "${LIFETIME}" -1 0); do + if [ "${AGE}" = 0 ]; then + echo "age${AGE}.label today" + else + echo "age${AGE}.label older than $((AGE*24))h" + fi cat << EOF -age${AGE}.label older than $((AGE*24))h -age${AGE}.draw STACK +age${AGE}.draw AREASTACK age${AGE}.colour ${COLOUR[$AGE]} -age${AGE}.warning : -age${AGE}.critical : +age${AGE}.warning +age${AGE}.critical EOF done cat << EOF @@ -100,7 +99,7 @@ EOF exit 0;; esac -for AGE in $(seq 0 "${LIFETIME}"); do +for AGE in $(seq "${LIFETIME}" -1 0); do FILES=$(find "${BACKUP_DIR}" \ -name "${ARCHIVE_PATTERN}" \ -mmin "-$(((AGE+1)*60*24))" \ From f8801b87a0734e003d39502bd2c39d0ee2cbd236 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 23 Oct 2019 14:51:11 +1100 Subject: [PATCH 542/695] [hostdenied] Reverse order of series so new entries are on top Signed-off-by: Olivier Mehani --- .../ssh/example-graphs/hostdenied-week.png | Bin 0 -> 29497 bytes plugins/ssh/hostdenied | 37 +++++++++--------- t/test-exception-wrapper.expected-failures | 1 - 3 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 plugins/ssh/example-graphs/hostdenied-week.png diff --git a/plugins/ssh/example-graphs/hostdenied-week.png b/plugins/ssh/example-graphs/hostdenied-week.png new file mode 100644 index 0000000000000000000000000000000000000000..d7366d3b0c25a717912e9ace6cfd206f35eb6703 GIT binary patch literal 29497 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 /dev/null && RUN="yes" # only run when grep and denyhost are present + RUN="no (denyhosts not found)" + command -v denyhosts > /dev/null && RUN="yes" # only run when denyhosts is present echo "$RUN" } run_config() { -cat << EOF + cat << EOF graph_title Hosts denied sshd access graph_info Hosts denied sshd access in $DENY graph_args --base 1000 -l 0 graph_vlabel Hosts denied graph_category security -age0.label added last 24h -age0.draw AREA -age0.colour ${COLOUR[0]} EOF -for AGE in {1..7}; do -cat << EOF -age${AGE}.label older than $((AGE*24))h -age${AGE}.draw STACK +for AGE in {7..0}; do + if [ "${AGE}" = 0 ]; then + echo "age${AGE}.label added last 24h" + else + echo "age${AGE}.label older than $((AGE*24))h" + fi + cat << EOF +age${AGE}.draw AREASTACK age${AGE}.colour ${COLOUR[$AGE]} EOF done @@ -81,17 +82,17 @@ run_fetch() { TOTAL=0 NOW=$(date +%s) sed -n 's/^\# DenyHosts: //;s/ | .*//gp' $DENY | # strip all but date - while read DATE; do + while read -r DATE; do echo $(((NOW - $(date -d "$DATE" +%s))/86400)) # calculate rule age - done > $STATEFILE # rather than going through temp file, the age could be - for AGE in {0..6} ; do # used to increment an array element with that index. - COUNT="$(grep -c "^$AGE$" $STATEFILE)" # That'd save grepping for counting from temp file. + done > "$STATEFILE" # rather than going through temp file, the age could be + for AGE in {6..0} ; do # used to increment an array element with that index. + COUNT="$(grep -c "^$AGE$" "$STATEFILE")" # That'd save grepping for counting from temp file. echo "age${AGE}.value $COUNT" # produce values for all but oldest ((TOTAL+=COUNT)) done - echo "age7.value $(($(grep -c . $STATEFILE)-TOTAL))" # all non-printed are older - rm $STATEFILE + echo "age7.value $(($(grep -c . "$STATEFILE")-TOTAL))" # all non-printed are older + rm "$STATEFILE" } -run_${1:-"fetch"} +run_"${1:-fetch}" exit 0 diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 575854cb..e82e1965 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -417,7 +417,6 @@ plugins/spamassasin/sa-learn plugins/sphinx/sphinx_documents plugins/squid/squid_efficiency plugins/squid/squid_times -plugins/ssh/hostdenied plugins/ssh/openssh-denyhosts plugins/ssh/sshd_log plugins/streaming/packetship_ From bf83802da3149ad1708c0cf3223123eb9fe3bec8 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 27 Oct 2019 23:36:41 +0100 Subject: [PATCH 543/695] Remove obsolete bebox plugins The plugins beboxsync, beboxstats and beboxstats.expect are obsolete. The ISP that sold the Bebox [Be*] was acquired by another provider more than five years ago so it is very unlikely that anybody is using these plugins any more. The plugin tg585v7_ is a suitable replacement as the Bebox and TG585 are both made by Technicolor [formerly Thompson] and run on the same platform. tg585v7_ also seems to be much more comprehensive than the bebox plugins. Closes: #1023 Thanks, troffasky --- plugins/router/beboxstats | 62 -------------------------------- plugins/router/beboxstats.expect | 25 ------------- plugins/router/beboxsync | 50 -------------------------- 3 files changed, 137 deletions(-) delete mode 100755 plugins/router/beboxstats delete mode 100755 plugins/router/beboxstats.expect delete mode 100755 plugins/router/beboxsync diff --git a/plugins/router/beboxstats b/plugins/router/beboxstats deleted file mode 100755 index 5f539724..00000000 --- a/plugins/router/beboxstats +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/perl -w - -use strict; - -my ($Args) = @ARGV; - -my $expecter = "/path/to/beboxstats.expect"; - -if ($Args) { - - # work out line to grab - if ($Args eq 'autoconf') { - # Check the expect script that polls the router exists - unless ( -e $expecter ) { - print "no (Can't find expect script. Check value of \$expecter: $expecter)\n"; - } else { - print "yes\n"; - } - - } elsif ($Args eq 'config') { # print out plugin parameters - printf(" -graph_title bebox line stats -graph_vlabel deciBels -graph_category network -graph_info This graph shows the various line parameters -attenuationdownstream.label Downstream Attenuation -attenuationupstream.label Upstream Attenuation -margindownstream.label Downstream Noise Margin -marginupstream.label Upstream Noise Margin -outputpowerdownstream.label Downstream Output Power -outputpowerupstream.label Upstream Output Power -margindownstream.type GAUGE -outputpowerupstream.type GAUGE -attenuationdownstream.type GAUGE -marginupstream.type GAUGE -outputpowerdownstream.type GAUGE -attenuationupstream.type GAUGE - "); - # .label is the Key on the graph - } else { - printf("Usage: $0 - No arguments: print line stats - autoconf: print 'yes' - config: print config info for Munin\n"); - } - -} else { - # if no arguments, just fetch the data and print it out - -my @insplitted = split(' ', `$expecter | grep dB`); - -print "margindownstream.value $insplitted[3]\n"; -print "marginupstream.value $insplitted[4]\n"; - -print "attenuationdownstream.value $insplitted[8]\n"; -print "attenuationupstream.value $insplitted[9]\n"; - -print "outputpowerdownstream.value $insplitted[13]\n"; -print "outputpowerupstream.value $insplitted[14]\n"; -} - - diff --git a/plugins/router/beboxstats.expect b/plugins/router/beboxstats.expect deleted file mode 100755 index c990f271..00000000 --- a/plugins/router/beboxstats.expect +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/expect -f - -# script to log on to a BeBox router [ST Speedtouch 780] and gather line stats - -# set timeout for response from router to 30 seconds -set timeout 30 -set router "host.or.ip.of.router" -set port "23" -set username "Administrator" -set password "routerpassword" - -# telnet to $router on $port -spawn telnet $router $port - -expect "Username :" -send "$username\r" - -expect "Password :" -send "$password\r" - -expect "}=>" -send "adsl info expand=enabled\r" - -expect "}=>" -send "exit\r" diff --git a/plugins/router/beboxsync b/plugins/router/beboxsync deleted file mode 100755 index 369ec8f6..00000000 --- a/plugins/router/beboxsync +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/perl -w - -# (C) Alex Dekker -# License is GPL - -use strict; - -my ($Args) = @ARGV; - -my $expecter = "/path/to/beboxstats.expect"; - -if ($Args) { - - # work out line to grab - if ($Args eq 'autoconf') { - # Check the expect script that polls the router exists - unless ( -e $expecter ) { - print "no (Can't find expect script. Check value of \$expecter: $expecter)\n"; - } else { - print "yes\n"; - } - } elsif ($Args eq 'config') { # print out plugin parameters - printf(" -graph_title bebox sync stats -graph_vlabel ATM kbps -graph_category network -graph_info This graph shows line sync speed -syncdownstream.label Downstream Sync Speed -syncupstream.label Upstream Sync Speed -syncdownstream.type GAUGE -syncupstream.type GAUGE - "); - # .label is the Key on the graph - } else { - printf("Usage: $0 - No arguments: print line stats - autoconf: print 'yes' - config: print config info for Munin\n"); - } - -} else { - # if no arguments, just fetch the data and print it out - -my @insplitted = split(' ', `$expecter | grep stream`); - -print "syncdownstream.value $insplitted[3]\n"; -print "syncupstream.value $insplitted[7]\n"; -} - - From f003e1bb64e046bc9d19e07cf80bc81cf8322f92 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 29 Oct 2019 03:58:44 +0100 Subject: [PATCH 544/695] Remove plugin "arp" The "arp_" plugin is probably a better alternative: it uses modern linux tools (iproute2) instead of the "arp" tool. Closes: #1024 --- plugins/arp/arp | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100755 plugins/arp/arp diff --git a/plugins/arp/arp b/plugins/arp/arp deleted file mode 100755 index 0dee771d..00000000 --- a/plugins/arp/arp +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -# -# Plugin to monitor total ARP entries -# -# Parameters understood: -# -# config (required) -# autoconf (optional) -# -# Made by Sven Hartge (sven AT svenhartge DOT de) -# - -#%# family=contrib -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - # Search for arp - if which arp >/dev/null; then - echo yes - else - echo "no (missing 'arp' executable)" - fi - exit 0 -fi - - - -if [ "$1" = "config" ]; then - echo 'graph_title ARP entries' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel Entries' - echo 'graph_category network' - echo 'graph_scale no' - echo 'graph_info This graph shows the number of ARP entries registered by the system.' - echo 'entries.label ARP entries' - echo 'entries.draw LINE2' - echo 'entries.type GAUGE' - echo 'entries.info Number of ARP entries' - exit 0 -fi - -arp -an | awk 'BEGIN { regex="";} { if (!match($4,regex)) { a[$4] }} END{for(i in a){n++};print "entries.value " n}' From ac2850809562b5daf4f79b4a9ac654137b1bc172 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Sat, 9 Nov 2019 19:32:53 +0100 Subject: [PATCH 545/695] arris-tg3442: Remove newlines from cookie --- plugins/router/arris-tg3442 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 index 94563a32..c51d5d30 100755 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -79,11 +79,10 @@ base64.encodebytes(b'{ "unique":"280oaPSLiF", "family":"852", "modelname":"TG249 '"name":"technician", "tech":true, "moca":0, "wifi":5, "conType":"WAN", ' '"gwWan":"f", "DefPasswdChanged":"YES" }').decode() """ -CREDENTIAL_COOKIE = """eyAidW5pcXVlIjoiMjgwb2FQU0xpRiIsICJmYW1pbHkiOiI4NTIiLCAibW9kZWxuYW1lIjoiVEcy -NDkyTEctODUiLCAibmFtZSI6InRlY2huaWNpYW4iLCAidGVjaCI6dHJ1ZSwgIm1vY2EiOjAsICJ3 -aWZpIjo1LCAiY29uVHlwZSI6IldBTiIsICJnd1dhbiI6ImYiLCAiRGVmUGFzc3dkQ2hhbmdlZCI6 -IllFUyIgfQ== -""" +CREDENTIAL_COOKIE = "eyAidW5pcXVlIjoiMjgwb2FQU0xpRiIsICJmYW1pbHkiOiI4NTIiLCAibW9kZWxuYW1lIjoiVEcy"\ + "NDkyTEctODUiLCAibmFtZSI6InRlY2huaWNpYW4iLCAidGVjaCI6dHJ1ZSwgIm1vY2EiOjAsICJ3"\ + "aWZpIjo1LCAiY29uVHlwZSI6IldBTiIsICJnd1dhbiI6ImYiLCAiRGVmUGFzc3dkQ2hhbmdlZCI6"\ + "IllFUyIgfQ==" def login(session, url, username, password): From 8fc3d29dd61a3e1c45edfa7138496c680cabea01 Mon Sep 17 00:00:00 2001 From: Alban Date: Thu, 12 Dec 2019 13:08:37 +0100 Subject: [PATCH 546/695] Update mongo_lag Fix mongo_lag plugin and add auth feature --- plugins/mongodb/mongo_lag | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index 3e6de845..52db6274 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -7,9 +7,11 @@ replication lag for all connected members. munin-node.conf: - [mongodb_lag] + [mongo_lag] env.host 127.0.0.1 env.port 27017 + env.username user + env.password P@55w0rd :author: Stefan Andersen :license: The Beer Ware License (Revision 42) @@ -25,8 +27,15 @@ import pymongo def _get_members(): host = os.environ.get('host', '127.0.0.1') port = os.environ.get('port', 27017) - conn = pymongo.MongoClient(host,port) - repl_status = conn.admin.command("replSetGetStatus") + username = os.environ.get('username', '') + password = os.environ.get('password', '') + conn = pymongo.MongoClient(host,int(port)) + if username: + connAuth = conn['admin'] + connAuth.authenticate(username, password) + repl_status = conn.admin.command("replSetGetStatus") + else: + repl_status = conn.admin.command("replSetGetStatus") members = {} for member in repl_status['members']: From 923e134dd090081eac57eb3722b6fc094615db46 Mon Sep 17 00:00:00 2001 From: Alban Date: Thu, 12 Dec 2019 13:11:33 +0100 Subject: [PATCH 547/695] Update mongo_lag --- plugins/mongodb/mongo_lag | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index 52db6274..67292ac6 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -33,9 +33,8 @@ def _get_members(): if username: connAuth = conn['admin'] connAuth.authenticate(username, password) - repl_status = conn.admin.command("replSetGetStatus") - else: - repl_status = conn.admin.command("replSetGetStatus") + + repl_status = conn.admin.command("replSetGetStatus") members = {} for member in repl_status['members']: From 1357b8189ec36e725f720428c61501702afb27b6 Mon Sep 17 00:00:00 2001 From: Alban Date: Fri, 13 Dec 2019 09:27:30 +0100 Subject: [PATCH 548/695] Update mongo_lag --- 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 67292ac6..681798ff 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -29,7 +29,7 @@ def _get_members(): port = os.environ.get('port', 27017) username = os.environ.get('username', '') password = os.environ.get('password', '') - conn = pymongo.MongoClient(host,int(port)) + conn = pymongo.MongoClient(host, int(port)) if username: connAuth = conn['admin'] connAuth.authenticate(username, password) From 1fc177ce9a78f28d18ad2e08ff4545a241aa0976 Mon Sep 17 00:00:00 2001 From: Hannes Erven Date: Sun, 8 Dec 2019 23:30:55 +0100 Subject: [PATCH 549/695] multi_tcp_ping: enable service_check by default By default, Net::Ping will report a good attempt even when the remote end returns "connection refused". By enabling service_check, the connection must have been established. See https://perldoc.perl.org/Net/Ping.html#Functions --- plugins/ping/multi_tcp_ping | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ping/multi_tcp_ping b/plugins/ping/multi_tcp_ping index cd07de4e..ee88033d 100755 --- a/plugins/ping/multi_tcp_ping +++ b/plugins/ping/multi_tcp_ping @@ -105,6 +105,7 @@ sub ping_host { $p=Net::Ping->new("tcp", $defaults{timeout}); $p->hires(); + $p->service_check(1); $p->{port_num} = $host->[1] || $defaults{port}; ($ret, $time, $ip) = $p->ping($host->[0]); From 6abc523a58df6fa62f8ce5c7b1ffa379ccbe480e Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sat, 7 Dec 2019 22:41:20 -0800 Subject: [PATCH 550/695] add plugin for Technicolor TC8715D cable modem stats --- plugins/router/technicolor_tc8715d | 233 +++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 plugins/router/technicolor_tc8715d diff --git a/plugins/router/technicolor_tc8715d b/plugins/router/technicolor_tc8715d new file mode 100755 index 00000000..49a6c927 --- /dev/null +++ b/plugins/router/technicolor_tc8715d @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 + +""" +=pod + +=encoding utf8 + +=head1 NAME + +technicolor_tc8715d - Munin plugin for graphing statistics from +Technicolor TC8715D cable modems. + +=head1 DESCRIPTION + +The following values are graphed: + +=over + +=item * + +upstream and downstream power levels + +=item * + +downstream signal to noise ratio + +=item * + +downstream signal statistics (codeword counts) + +=back + +=head1 USAGE + +Install as you would with any Munin plugin. No configuration is +needed. Requires the multigraph and dirtyconfig capabilities available +in munin 2.0 and newer. + +=head1 NOTES + +Developed and tested with Python 3.7.3, Technicolor TC8715D cable +modem hardware revision 1.1, software image name +TC8715D-01.EF.04.38.00-180405-S-FF9-D.img, advanced services +2.6.30-1.0.11mp1-g24a0ad5-dirty. + +=head1 DEVELOPMENT + +The latest version of this plugin can be found in the munin contrib +repository at https://github.com/munin-monitoring/contrib. Issues +with this plugin may be reported there. Patches accepted through the +normal github process of forking the repository and submitting a +pull request with your commits. + +=head1 AUTHOR + +Copyright © 2019 Kenyon Ralph + +=head1 LICENSE + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +=cut + +""" + +import html.parser +import urllib.request +import sys + + +class TechnicolorHTMLParser(html.parser.HTMLParser): + def __init__(self): + html.parser.HTMLParser.__init__(self) + self.signaldatapage = list() + + # Number of downstream channels. + self.downstream_channels = 0 + + # Number of upstream channels. + self.upstream_channels = 0 + + self.downstream_SNRs = list() + self.downstream_powers = list() + self.upstream_powers = list() + self.unerrored_codewords = list() + self.correctable_codewords = list() + self.uncorrectable_codewords = list() + + def handle_data(self, data): + data = data.strip() + if data != "": + self.signaldatapage.append(data) + + def process(self): + # Delete the junk before the statistics start. This list + # should start with 'Downstream' after this deletion. + del self.signaldatapage[0:119] + + index_positions = [i for i, x in enumerate(self.signaldatapage) if x == "Index"] + lock_status_positions = [ + i for i, x in enumerate(self.signaldatapage) if x == "Lock Status" + ] + self.downstream_channels = lock_status_positions[0] - index_positions[0] - 1 + self.upstream_channels = lock_status_positions[1] - index_positions[1] - 1 + + self.downstream_SNRs = self.signaldatapage[ + 6 + 3 * self.downstream_channels : 22 + 3 * self.downstream_channels + ] + self.downstream_SNRs = [x.split()[0] for x in self.downstream_SNRs] + + self.downstream_powers = self.signaldatapage[ + 7 + 4 * self.downstream_channels : 23 + 4 * self.downstream_channels + ] + self.downstream_powers = [x.split()[0] for x in self.downstream_powers] + + self.upstream_powers = self.signaldatapage[ + 15 + + 6 * self.downstream_channels + + 4 * self.upstream_channels : 15 + + 6 * self.downstream_channels + + 5 * self.upstream_channels + ] + self.upstream_powers = [x.split()[0] for x in self.upstream_powers] + + self.unerrored_codewords = self.signaldatapage[ + 19 + + 6 * self.downstream_channels + + 7 * self.upstream_channels : 19 + + 7 * self.downstream_channels + + 7 * self.upstream_channels + ] + + self.correctable_codewords = self.signaldatapage[ + 20 + + 7 * self.downstream_channels + + 7 * self.upstream_channels : 20 + + 8 * self.downstream_channels + + 7 * self.upstream_channels + ] + + self.uncorrectable_codewords = self.signaldatapage[ + 21 + + 8 * self.downstream_channels + + 7 * self.upstream_channels : 21 + + 9 * self.downstream_channels + + 7 * self.upstream_channels + ] + + +if len(sys.argv) != 2 or sys.argv[1] != "config": + print( + "Error: plugin designed for the dirtyconfig protocol, must be run with the config argument" + ) + sys.exit(1) + +parser = TechnicolorHTMLParser() + +for line in urllib.request.urlopen("http://192.168.100.1/vendor_network.asp"): + parser.feed(line.decode()) + +parser.process() + +print( + """multigraph technicolor_tc8715d_power +graph_title Technicolor TC8715D Cable Modem Power +graph_vlabel Signal Strength (dBmV) +graph_info This graph shows the channel power values reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""ds_power_{i+1}.label Channel {i+1} Downstream Power +ds_power_{i+1}.type GAUGE +ds_power_{i+1}.value {parser.downstream_powers[i]}""" + ) + +for i in range(parser.upstream_channels): + print( + f"""us_power_{i+1}.label Channel {i+1} Upstream Power +us_power_{i+1}.type GAUGE +us_power_{i+1}.value {parser.upstream_powers[i]}""" + ) + +print( + """multigraph technicolor_tc8715d_snr +graph_title Technicolor TC8715D Cable Modem SNR +graph_vlabel Signal-to-Noise Ratio (dB) +graph_info This graph shows the downstream signal-to-noise ratio reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""snr_{i+1}.label Channel {i+1} SNR +snr_{i+1}.type GAUGE +snr_{i+1}.value {parser.downstream_SNRs[i]}""" + ) + +print( + """multigraph technicolor_tc8715d_codewords +graph_title Technicolor TC8715D Cable Modem Codewords +graph_vlabel Codewords/${graph_period} +graph_info This graph shows the downstream codeword rates reported by a Technicolor TC8715D cable modem. +graph_category network""" +) + +for i in range(parser.downstream_channels): + print( + f"""unerr_{i+1}.label Channel {i+1} Unerrored Codewords +unerr_{i+1}.type DERIVE +unerr_{i+1}.min 0 +unerr_{i+1}.value {parser.unerrored_codewords[i]} +corr_{i+1}.label Channel {i+1} Correctable Codewords +corr_{i+1}.type DERIVE +corr_{i+1}.min 0 +corr_{i+1}.value {parser.correctable_codewords[i]} +uncorr_{i+1}.label Channel {i+1} Uncorrectable Codewords +uncorr_{i+1}.type DERIVE +uncorr_{i+1}.min 0 +uncorr_{i+1}.value {parser.uncorrectable_codewords[i]}""" + ) From 73f885e382d5a1f9ef9efaf299b60b211350b093 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Dec 2019 00:23:09 +0100 Subject: [PATCH 551/695] flake8 configuration: allow whitespace around colons --- .flake8 | 1 + 1 file changed, 1 insertion(+) diff --git a/.flake8 b/.flake8 index 61d90815..4551085d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,3 @@ [flake8] +ignore = W503, E203 max-line-length = 99 From 7063330e031922c63012990543e917a5d85cd25d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Dec 2019 00:25:39 +0100 Subject: [PATCH 552/695] Fix python style issues reported by flake8 Additionally some python2-only "print" statements are now compatible with python3. --- plugins/bacula/bacula_job | 6 +-- plugins/bacula/bacula_sd | 2 +- plugins/currency/bitcoin/bitcoind_ | 2 +- plugins/currency/bitcoin/btcguild_hashrate_ | 20 ++++----- plugins/lighttpd/lighttpd_ | 26 +++++------ plugins/nginx/nginx_upstream_multi_ | 2 +- plugins/percona/percona_ | 50 ++++++++++----------- plugins/prosody/prosody_ | 4 +- plugins/router/arris-tg3442 | 2 +- plugins/router/technicolor_tc8715d | 4 +- plugins/solr/solr4_ | 6 +-- plugins/sphinx/sphindex_ | 2 +- plugins/synology/snmp__synology | 24 +++++----- plugins/torrent/deluge_ | 43 +++++++++--------- plugins/weather/weather_ | 8 ++-- plugins/weather/weather_press_ | 2 +- plugins/weather/weather_temp_ | 2 +- 17 files changed, 102 insertions(+), 103 deletions(-) diff --git a/plugins/bacula/bacula_job b/plugins/bacula/bacula_job index 6b8d7122..af030bd7 100755 --- a/plugins/bacula/bacula_job +++ b/plugins/bacula/bacula_job @@ -100,7 +100,7 @@ def parse(clients): # Get the current bytes if line.endswith(" is running."): - bytes_count_text = input_lines[pos+2].split()[1].split("=")[1].replace(",", "") + bytes_count_text = input_lines[pos + 2].split()[1].split("=")[1].replace(",", "") try: # python2 bytes_count = long(bytes_count_text) @@ -126,8 +126,8 @@ def print_config(): print("graph_category backup") print("graph_order", " ".join(fd[1] for fd in clients)) print() - if ((os.getenv("report_hostname") is not None) and - (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): + if ((os.getenv("report_hostname") is not None) + and (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): print("host_name", hostname) for client in clients: print("%s.label %s" % (client[1], client[0])) diff --git a/plugins/bacula/bacula_sd b/plugins/bacula/bacula_sd index b05ddae3..f623c4a9 100755 --- a/plugins/bacula/bacula_sd +++ b/plugins/bacula/bacula_sd @@ -91,7 +91,7 @@ def parse(): # Get the current bytes if input_lines[pos].endswith("is mounted with:"): - bytes_count_text = input_lines[pos+5].split()[1].split("=")[1].replace(",", "") + bytes_count_text = input_lines[pos + 5].split()[1].split("=")[1].replace(",", "") try: bytes_count = long(bytes_count_text) except NameError: diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 43373360..6a7bccb8 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -236,7 +236,7 @@ class Proxy: 'method': self.method, 'params': args, 'id': self.id, - } + } request = urllib.request.Request(self.service.url, json.dumps(data).encode()) if self.service.username: auth_string = '%s:%s' % (self.service.username, self.service.password) diff --git a/plugins/currency/bitcoin/btcguild_hashrate_ b/plugins/currency/bitcoin/btcguild_hashrate_ index edae22ea..c60db5a3 100755 --- a/plugins/currency/bitcoin/btcguild_hashrate_ +++ b/plugins/currency/bitcoin/btcguild_hashrate_ @@ -7,11 +7,11 @@ import urllib2 import json URL = 'https://www.btcguild.com/api.php?api_key=' -API_KEY = sys.argv[0][(sys.argv[0].rfind('_')+1):] +API_KEY = sys.argv[0][(sys.argv[0].rfind('_') + 1):] STATS = URL + API_KEY -print STATS +print(STATS) command = '' if len(sys.argv) > 1: @@ -25,16 +25,16 @@ workers = mining_stats['workers'] if command == 'config': - print "graph_title BTCGuild Hashrate" - print "graph_args --upper-limit 3000 -l 0" - print "graph_vlabel MHash/s" - print "graph_category htc" + print("graph_title BTCGuild Hashrate") + print("graph_args --upper-limit 3000 -l 0") + print("graph_vlabel MHash/s") + print("graph_category htc") for worker in workers: label = workers[worker]['worker_name'] - print label + ".label " + label + print(label + ".label " + label) sys.exit(0) for worker in workers: - hash_rate = workers[worker]['hash_rate'] - label = workers[worker]['worker_name'] - print label + ".value %d" % int(hash_rate) + hash_rate = workers[worker]['hash_rate'] + label = workers[worker]['worker_name'] + print(label + ".value %d" % int(hash_rate)) diff --git a/plugins/lighttpd/lighttpd_ b/plugins/lighttpd/lighttpd_ index 4985ce34..37efbc27 100755 --- a/plugins/lighttpd/lighttpd_ +++ b/plugins/lighttpd/lighttpd_ @@ -53,7 +53,7 @@ import urllib2 program = sys.argv[0] -graph_type = program[program.rfind("_")+1:] +graph_type = program[program.rfind("_") + 1:] graph_types = { "accesses": [ { @@ -91,21 +91,21 @@ graph_types = { if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": if graph_type not in graph_types.keys(): raise Exception("Unknown graph type '%s'" % graph_type) params = graph_types[graph_type] for item in params: - print "graph_title %s" % item["title"] - print "graph_category webserver" + print("graph_title %s" % item["title"]) + print("graph_category webserver") for field in item["fields"]: - print "%s.label %s" % (field, field) - print "%s.type %s" % (field, item["type"]) - print "graph_args %s" % item["args"] + print("%s.label %s" % (field, field)) + print("%s.type %s" % (field, item["type"])) + print("graph_args %s" % item["args"]) elif len(sys.argv) == 2 and sys.argv[1] == "suggest": for item in graph_types.keys(): - print item + print(item) else: status_url = os.environ.get('status_url', 'http://127.0.0.1/server-status') @@ -129,11 +129,11 @@ else: pass if graph_type == "accesses": - print "accesses.value %s" % data["Total Accesses"] + print("accesses.value %s" % data["Total Accesses"]) elif graph_type == "kbytes": - print "kbytes.value %s" % data["Total kBytes"] + print("kbytes.value %s" % data["Total kBytes"]) elif graph_type == "uptime": - print "uptime.value %s" % str(float(data["Uptime"])/86400) + print("uptime.value %s" % str(float(data["Uptime"]) / 86400)) elif graph_type == "status": - print "busy.value %s" % data["BusyServers"] - print "idle.value %s" % data["IdleServers"] + print("busy.value %s" % data["BusyServers"]) + print("idle.value %s" % data["IdleServers"]) diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index 95b2ba48..a1ee02f0 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -54,7 +54,7 @@ import time # How we've been called progName = sys.argv[0] -progName = progName[progName.rfind("/")+1:] +progName = progName[progName.rfind("/") + 1:] # Where to store plugin state diff --git a/plugins/percona/percona_ b/plugins/percona/percona_ index 6282f09f..4992b550 100755 --- a/plugins/percona/percona_ +++ b/plugins/percona/percona_ @@ -53,31 +53,31 @@ filterwarnings('ignore', category=MySQLdb.Warning) program_name = os.path.basename(__file__) variables = { - 'percona_queues': { - 'label': 'Queue sizes', - 'vlabel': 'size', - 'fields': ['wsrep_local_recv_queue', 'wsrep_local_send_queue'] - }, - 'percona_flow': { - 'label': 'Flow control', - 'vlabel': '', - 'fields': ['wsrep_flow_control_sent', 'wsrep_flow_control_recv'] - }, - 'percona_transactions': { - 'label': 'Transactions in and out', - 'vlabel': 'transactions', - 'fields': ['wsrep_replicated', 'wsrep_received'] - }, - 'percona_transactions_bytes': { - 'label': 'Transactions in and out in bytes', - 'vlabel': 'bytes', - 'fields': ['wsrep_replicated_bytes', 'wsrep_received_bytes'] - }, - 'percona_replication': { - 'label': 'Replication conflicts', - 'vlabel': 'conflicts', - 'fields': ['wsrep_local_cert_failures', 'wsrep_local_bf_aborts'], - } + 'percona_queues': { + 'label': 'Queue sizes', + 'vlabel': 'size', + 'fields': ['wsrep_local_recv_queue', 'wsrep_local_send_queue'] + }, + 'percona_flow': { + 'label': 'Flow control', + 'vlabel': '', + 'fields': ['wsrep_flow_control_sent', 'wsrep_flow_control_recv'] + }, + 'percona_transactions': { + 'label': 'Transactions in and out', + 'vlabel': 'transactions', + 'fields': ['wsrep_replicated', 'wsrep_received'] + }, + 'percona_transactions_bytes': { + 'label': 'Transactions in and out in bytes', + 'vlabel': 'bytes', + 'fields': ['wsrep_replicated_bytes', 'wsrep_received_bytes'] + }, + 'percona_replication': { + 'label': 'Replication conflicts', + 'vlabel': 'conflicts', + 'fields': ['wsrep_local_cert_failures', 'wsrep_local_bf_aborts'], + } } # Parse environment variables diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 4cab0d6a..53aa3bed 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -139,8 +139,8 @@ def main(): telnet.write("server:uptime()\n") telnet_response = telnet.read_until("minutes (", 5) parsed_info = uptime_re.findall(telnet_response) - uptime_value = (float(parsed_info[0]) + float(parsed_info[1])/24 - + float(parsed_info[2])/60/24) + uptime_value = (float(parsed_info[0]) + float(parsed_info[1]) / 24 + + float(parsed_info[2]) / 60 / 24) print("uptime.value %s" % (uptime_value)) telnet.write("quit\n") diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 index c51d5d30..a232ef29 100755 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -102,7 +102,7 @@ def login(session, url, username, password): bytes(password.encode("ascii")), salt, iterations=1000, - dklen=128/8 + dklen=128 / 8 ) secret = {"Password": password, "Nonce": current_session_id} plaintext = bytes(json.dumps(secret).encode("ascii")) diff --git a/plugins/router/technicolor_tc8715d b/plugins/router/technicolor_tc8715d index 49a6c927..c2473d9f 100755 --- a/plugins/router/technicolor_tc8715d +++ b/plugins/router/technicolor_tc8715d @@ -197,7 +197,7 @@ print( """multigraph technicolor_tc8715d_snr graph_title Technicolor TC8715D Cable Modem SNR graph_vlabel Signal-to-Noise Ratio (dB) -graph_info This graph shows the downstream signal-to-noise ratio reported by a Technicolor TC8715D cable modem. +graph_info Downstream signal-to-noise ratio reported by a Technicolor TC8715D cable modem. graph_category network""" ) @@ -212,7 +212,7 @@ print( """multigraph technicolor_tc8715d_codewords graph_title Technicolor TC8715D Cable Modem Codewords graph_vlabel Codewords/${graph_period} -graph_info This graph shows the downstream codeword rates reported by a Technicolor TC8715D cable modem. +graph_info Downstream codeword rates reported by a Technicolor TC8715D cable modem. graph_category network""" ) diff --git a/plugins/solr/solr4_ b/plugins/solr/solr4_ index 5f0637ff..b41c3741 100755 --- a/plugins/solr/solr4_ +++ b/plugins/solr/solr4_ @@ -77,7 +77,7 @@ def parse_params(): data = params['core'].rsplit('_', 1) handler = data.pop() params['params'] = { - 'handler': os.environ.get('qpshandler_%s' % handler, 'standard') + 'handler': os.environ.get('qpshandler_%s' % handler, 'standard'), } if not data: params['core'] = '' @@ -416,7 +416,7 @@ class SolrMuninGraph: handler=self.params['params']['handler'], core=self.params['core'], cores_qps_cdefs='%s,%s' % (','.join(map(lambda x: 'qps_%s' % x, cores)), - ','.join(['+'] * (len(cores)-1))), + ','.join(['+'] * (len(cores) - 1))), gorder=','.join(cores) ) @@ -512,4 +512,4 @@ if __name__ == '__main__': SOLR_URL = '/' + SOLR_URL mb = SolrMuninGraph(SOLR_HOST_PORT, SOLR_URL, params) if hasattr(mb, params['op']): - print(getattr(mb, params['op'])(params['type'])) + print(getattr(mb, params['op'])(params['type'])) diff --git a/plugins/sphinx/sphindex_ b/plugins/sphinx/sphindex_ index 7c37f2c4..34c6d4c8 100755 --- a/plugins/sphinx/sphindex_ +++ b/plugins/sphinx/sphindex_ @@ -42,7 +42,7 @@ import sphinxsearch prog_name = sys.argv[0] -index_name = prog_name[prog_name.find("_")+1:] +index_name = prog_name[prog_name.find("_") + 1:] if len(sys.argv) == 2 and sys.argv[1] == "autoconf": print("yes") diff --git a/plugins/synology/snmp__synology b/plugins/synology/snmp__synology index b81dee6a..835ac385 100755 --- a/plugins/synology/snmp__synology +++ b/plugins/synology/snmp__synology @@ -91,7 +91,7 @@ class SynologySNMPClient(object): if not oid.startswith(disk_table): continue - disk_id = oid[oid.rindex('.')+1:] + disk_id = oid[oid.rindex('.') + 1:] values = devices.get(disk_id, [None, None, None]) if oid.startswith(disktable_id): @@ -119,21 +119,21 @@ class SynologySNMPClient(object): return int(varBindTable[0][1]) def print_config(self): - print """multigraph synology_hdd_temperatures + print("""multigraph synology_hdd_temperatures host_name {hostname} graph_title HDD temperatures on {hostname} graph_vlabel Temperature in °C graph_args --base 1000 graph_category sensors -graph_info HDD temperatures on {hostname}""".format(hostname=self.hostname) +graph_info HDD temperatures on {hostname}""".format(hostname=self.hostname)) for id, name, model, temp in self._get_disks(): - print """disk{disk_id}.info Temperature of {name} ({model}) + print("""disk{disk_id}.info Temperature of {name} ({model}) disk{disk_id}.label {name} ({model}) disk{disk_id}.type GAUGE -disk{disk_id}.min 0""".format(disk_id=id, name=name, model=model) +disk{disk_id}.min 0""".format(disk_id=id, name=name, model=model)) - print """multigraph synology_sys_temperature + print("""multigraph synology_sys_temperature host_name {hostname} graph_title System temperatures of {hostname} graph_vlabel Temperature in °C @@ -144,15 +144,15 @@ sys_temp.info System temperature sys_temp.label Temperature sys_temp.type GAUGE sys_temp.min 0 -""".format(hostname=self.hostname) +""".format(hostname=self.hostname)) def execute(self): - print """multigraph synology_hdd_temperatures""" + print("""multigraph synology_hdd_temperatures""") for id, name, model, temp in self._get_disks(): - print """disk{disk_id}.value {temp}""".format(disk_id=id, temp=temp) + print("""disk{disk_id}.value {temp}""".format(disk_id=id, temp=temp)) - print """multigraph synology_sys_temperature""" - print "sys_temp.value {temp}".format(temp=self._get_sys_temperature()) + print("""multigraph synology_sys_temperature""") + print("sys_temp.value {temp}".format(temp=self._get_sys_temperature())) host = None @@ -175,7 +175,7 @@ except Exception as ex: if "snmpconf" in sys.argv[1:]: - print "require 1.3.6.1.4.1.6574.2.1.1" + print("require 1.3.6.1.4.1.6574.2.1.1") sys.exit(0) else: if not (host and port and community): diff --git a/plugins/torrent/deluge_ b/plugins/torrent/deluge_ index 6812ae21..0394d63f 100755 --- a/plugins/torrent/deluge_ +++ b/plugins/torrent/deluge_ @@ -121,25 +121,25 @@ log = logging.getLogger("delugeStats") log.setLevel(logging.WARNING) conf = { - 'host': os.getenv('host', '127.0.0.1'), - 'port': int(os.getenv('port', '58846')), - 'username': os.getenv('username', ''), - 'password': os.getenv('password', '') + 'host': os.getenv('host', '127.0.0.1'), + 'port': int(os.getenv('port', '58846')), + 'username': os.getenv('username', ''), + 'password': os.getenv('password', '') } names_for_munin = { - 'numConnections': 'numConnections', - 'payloadUploadRate': 'payloadUploadRate', - 'overheadUploadRate': 'overheadUploadRate', - 'payloadDownloadRate': 'payloadDownloadRate', - 'overheadDownloadRate': 'overheadDownloadRate', - 'state.Seeding': 'seeding', - 'state.Downloading': 'downloading', - 'state.Paused': 'paused', - 'state.Error': 'error', - 'state.Queued': 'queued', - 'state.Checking': 'checking', - 'state.Other': 'other' + 'numConnections': 'numConnections', + 'payloadUploadRate': 'payloadUploadRate', + 'overheadUploadRate': 'overheadUploadRate', + 'payloadDownloadRate': 'payloadDownloadRate', + 'overheadDownloadRate': 'overheadDownloadRate', + 'state.Seeding': 'seeding', + 'state.Downloading': 'downloading', + 'state.Paused': 'paused', + 'state.Error': 'error', + 'state.Queued': 'queued', + 'state.Checking': 'checking', + 'state.Other': 'other' } torrent_states = ["Downloading", @@ -300,8 +300,8 @@ def print_config(mode): "graph_info This graph shows the number of connections used by Deluge Torrent") print(names_for_munin["numConnections"] + ".label connections") print(names_for_munin["numConnections"] + ".min 0") - print(names_for_munin["numConnections"] + - ".info The number of connections used by Deluge Torrent") + print(names_for_munin["numConnections"] + + ".info The number of connections used by Deluge Torrent") elif mode == "bandwidth": print("graph_title Bandwidth usage") print("graph_order payloadDownloadRate overheadDownloadRate payloadUploadRate " @@ -353,13 +353,12 @@ def print_config(mode): print("graph_period second") for state_name in torrent_states: - print(names_for_munin["state." + - state_name] + ".label " + state_name) + print(names_for_munin["state." + state_name] + ".label " + state_name) print(names_for_munin["state." + state_name] + ".draw AREASTACK") print(names_for_munin["state." + state_name] + ".type GAUGE") print(names_for_munin["state." + state_name] + ".min 0") - print(names_for_munin["state." + state_name] + - ".info Number of torrents in the '" + state_name + "' state") + print(names_for_munin["state." + state_name] + + ".info Number of torrents in the '" + state_name + "' state") def fetch_info(mode): diff --git a/plugins/weather/weather_ b/plugins/weather/weather_ index ab8ebc39..d4807063 100755 --- a/plugins/weather/weather_ +++ b/plugins/weather/weather_ @@ -3,7 +3,7 @@ import os import re import sys -import urllib +from urllib.request import urlopen url = 'http://www.weather.com/weather/today/%s' @@ -19,7 +19,7 @@ if not code: elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - u = urllib.urlopen(url % code) + u = urlopen(url % code) txt = u.read() u.close() @@ -39,7 +39,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": print('graph_args --base 1000 -l 0') else: - u = urllib.urlopen(url % code) + u = urlopen(url % code) txt = u.read() u.close() @@ -53,7 +53,7 @@ else: if len(TMP_F_list): TMP_F = TMP_F_list[0] - TMP_C = (int(TMP_F) - 32) * 5/9 + TMP_C = (int(TMP_F) - 32) * 5 / 9 else: sys.exit(1) diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index 0682998b..56569c66 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -21,7 +21,7 @@ url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' re_hpa = re.compile(r'Pressure.*\((\d+) hPa\)') -code = sys.argv[0][(sys.argv[0].rfind('_')+1):] +code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] if not code: sys.exit(1) elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index dd07b1db..a23840fa 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -21,7 +21,7 @@ url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' re_C = re.compile(r'Temperature:.*\((-?\d+\.?\d?) C\)') re_DewC = re.compile(r'Dew.*\((-?\d+\.?\d?) C\)') -code = sys.argv[0][(sys.argv[0].rfind('_')+1):] +code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] if not code: From 86bee016372293c4e8eceb536006334f1973c69b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 18 Dec 2019 00:27:02 +0100 Subject: [PATCH 553/695] Port some python2 plugins to python3 --- plugins/bacula/bacula_job | 2 +- plugins/bacula/bacula_sd | 2 +- plugins/prosody/prosody_ | 3 +-- plugins/sphinx/sphindex_ | 3 +-- plugins/synology/snmp__synology | 3 +-- plugins/weather/weather_press_ | 6 +++--- plugins/weather/weather_temp_ | 6 +++--- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/bacula/bacula_job b/plugins/bacula/bacula_job index af030bd7..65d6e8ff 100755 --- a/plugins/bacula/bacula_job +++ b/plugins/bacula/bacula_job @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # Copyright (C) 2009 Andreas Thienemann # diff --git a/plugins/bacula/bacula_sd b/plugins/bacula/bacula_sd index f623c4a9..545db656 100755 --- a/plugins/bacula/bacula_sd +++ b/plugins/bacula/bacula_sd @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # Copyright (C) 2009 Andreas Thienemann # diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 53aa3bed..7aa288f5 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # Copyright (c) 2010 Christoph Heer (Christoph.Heer@googlemail.com) # # Permission is hereby granted, free of charge, to any person obtaining a diff --git a/plugins/sphinx/sphindex_ b/plugins/sphinx/sphindex_ index 34c6d4c8..73c3ef17 100755 --- a/plugins/sphinx/sphindex_ +++ b/plugins/sphinx/sphindex_ @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # vim: set fileencoding=utf-8 # # Munin plugin to show number of documents in Sphinx index diff --git a/plugins/synology/snmp__synology b/plugins/synology/snmp__synology index 835ac385..dda582d8 100755 --- a/plugins/synology/snmp__synology +++ b/plugins/synology/snmp__synology @@ -1,5 +1,4 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # Copyright (C) 2014 Johann Schmitz # diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index 56569c66..12cc9936 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) @@ -13,7 +13,7 @@ Linux users might need to adjust the shebang. """ import sys -import urllib +from urllib.request import urlopen import re url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' @@ -36,7 +36,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": print('graph_args --base 1000 -l 850 -u 1050 --rigid') print('graph_scale no') else: - u = urllib.urlopen(url % code) + u = urlopen(url % code) txt = u.read() u.close() diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index a23840fa..2d28b436 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) @@ -13,7 +13,7 @@ Linux users might need to adjust the shebang. """ import sys -import urllib +from urllib.request import urlopen import re url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' @@ -36,7 +36,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": print('dewpoint.label Dew Point') print('graph_args --base 1000 -l 0') else: - u = urllib.urlopen(url % code) + u = urlopen(url % code) txt = u.read() u.close() From e3664c39d8301441684c397bf65d8f2944dcc84f Mon Sep 17 00:00:00 2001 From: Lucas Hoffmann Date: Thu, 19 Dec 2019 14:47:03 +0100 Subject: [PATCH 554/695] Fix state saving in loggrepx_ plugin The pattern under which the data was saved was different than the pattern under the data was loaded on the next run. The reason being an undefined variable that evaluates to the empty string. The result was that the number of matching lines reported was for the whole logfile instead of the time since the last run. --- plugins/logs/loggrepx_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/logs/loggrepx_ b/plugins/logs/loggrepx_ index cf156f00..49cb7393 100755 --- a/plugins/logs/loggrepx_ +++ b/plugins/logs/loggrepx_ @@ -150,7 +150,7 @@ function fetch() { echo "$var_prefix.value $matches" # Push onto next state - nextstate+=("${var}_lines=$curlines") + nextstate+=("${var_prefix}_lines=$curlines") done 3< <(echo "$LOGFILES") # Write state to munin statefile From d4188712d96635665e79efd7de695868ec8a7176 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Tue, 31 Dec 2019 16:30:20 -0500 Subject: [PATCH 555/695] create new plugin for graphing PuppetDB's usage of JVM Heapspace PuppetDB requires a pretty high amount of memory allocated to it. If you don't give enough memory space to the JVM, PuppetDB might crash when it has fully used the available max memory. This plugin should be useful for better tuning up your instance(s) of PuppetDB. Currently, it's only compatible with PuppetDB 6.x since it's requesting data from the API as described for that version: https://puppet.com/docs/puppetdb/6.0/api/metrics/v1/mbeans.html#jvm-metrics To add support for more versions, one would need to consult the same documentation for different versions and to adapt URLs used and the code that's obtaining the data from JSON if the layout of the responses is different. --- .../puppet/example-graphs/puppetdb-day.png | Bin 0 -> 26471 bytes plugins/puppet/puppetdb | 153 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 plugins/puppet/example-graphs/puppetdb-day.png create mode 100755 plugins/puppet/puppetdb diff --git a/plugins/puppet/example-graphs/puppetdb-day.png b/plugins/puppet/example-graphs/puppetdb-day.png new file mode 100644 index 0000000000000000000000000000000000000000..c731238fcd78015e15538dc92a584871062404ba GIT binary patch literal 26471 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 1 and sys.argv[1] == 'autoconf': + try: + dummy = rest_request(mbeans_url, timeout) + except Exception as e: + print(f"no ({e})") + exit(0) + + print("yes") + exit(0) + + if len(sys.argv) > 1 and sys.argv[1] == 'config': + config() + exit(0) + + fetch_field_values(mbeans_url, timeout) From 6a8fbf3bd3267612a51d1dc767cfc29807ceae48 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Thu, 2 Jan 2020 14:40:20 -0500 Subject: [PATCH 556/695] change category of puppet plugins to "other" using the "devel" category is a bit weird. there's currently no good category to place those into so let's use "other" instead. --- plugins/puppet/puppet_runtime | 2 +- plugins/puppet/puppetdb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/puppet/puppet_runtime b/plugins/puppet/puppet_runtime index b2cdebeb..11598776 100755 --- a/plugins/puppet/puppet_runtime +++ b/plugins/puppet/puppet_runtime @@ -29,7 +29,7 @@ end case ARGV[0] when 'config' - puts "graph_category devel" + puts "graph_category other" puts "graph_args --base 1000 -l 0" puts "graph_scale no" puts "graph_title puppet catalog run time" diff --git a/plugins/puppet/puppetdb b/plugins/puppet/puppetdb index 6c0414e4..7eb1352a 100755 --- a/plugins/puppet/puppetdb +++ b/plugins/puppet/puppetdb @@ -75,9 +75,7 @@ def config(): print("graph_vlabel Bytes") print("graph_info This graph shows how much memory from the JVM " + "Heapspace is being used by PuppetDB") - # This is super weird, but currently puppet graphs are in this - # category so let's place puppetdb graphs in the same category - print("graph_category devel") + print("graph_category other") print("graph_order jvm_mem_max jvm_mem_committed jvm_mem_used") # Fields From 70f565c5037eba91e5dcfe50d4a21b9200eed8ae Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sun, 19 Jan 2020 19:40:29 -0500 Subject: [PATCH 557/695] puppetdb: implement cert validation and client cert auth It is very common for PuppetDB installs to run on a different host than the puppetmaster. In such cases, a certificate file is normally used to establish an encrypted communication to the server. The most common setup for this server certificate is to use a certificate that was signed by the puppetmaster's CA, so one would want to verify the server cert against this same CA (it should be present on puppet clients). Moreover, to ensure that *only* puppet clients can communicate with PuppetDB, a pair of client certificat/key files are usually used to authenticate clients. --- plugins/puppet/puppetdb | 63 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/plugins/puppet/puppetdb b/plugins/puppet/puppetdb index 7eb1352a..a4d55aa4 100755 --- a/plugins/puppet/puppetdb +++ b/plugins/puppet/puppetdb @@ -21,11 +21,33 @@ Plugin configuration parameters: Time in seconds (int) to wait for a result when querying the REST API. By default, wait for 2 seconds + * ca: + Path to the Certificate Authority used for verifying a cert received from + the PuppetDB server during an https connection. This can be useful if the + cert used by PuppetDB was signed by the puppetmaster's CA. This option is + not necessary if a plaintext connection is used (e.g. if pdburl starts + with 'http://'). + + * cert: + Path to the TLS certificate file used for establishing client + communication for an https connection. This option should be paired with + the `key` option. This option is not necessary if a plaintext connection + is used (e.g. if pdburl starts with 'http://'). + + * key: + Path to the TLS private key used for establishing client communication for + an https connection. This option should be paired with the `cert` option. + This option is not necessary if a plaintext connection is used (e.g. if + pdburl starts with 'http://'). + Example: [puppetdb] env.pdburl https://puppetdb.example.com:8080/metrics/v1/mbeans env.timeout 5 + env.ca /etc/puppetboard/ca.pem + env.cert /etc/puppetboard/client_cert.pem + env.key /etc/puppetboard/client_key.pem =head1 DEPENDENCIES @@ -54,14 +76,21 @@ class WrongStatusCode(Exception): pass -def rest_request(url, timeout): +def rest_request(url, timeout, ca, key_pair): """Make a GET request to URL. We expect a 200 response. This function will let exceptions from requests raise through to indicate request failure. If response code is not 200, it will raise a WrongStatusCode exception. """ headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} - resp = requests.get(url, headers=headers, timeout=timeout) + + ssl_options = {} + if ca: + ssl_options['verify'] = ca + if key_pair: + ssl_options['cert'] = key_pair + + resp = requests.get(url, headers=headers, timeout=timeout, **ssl_options) if resp.status_code != 200: err = f"GET Request to '{url}' returned code {resp.status_code}; expected 200." # noqa: E501 raise WrongStatusCode(err) @@ -93,12 +122,12 @@ def config(): print("jvm_mem_used.draw AREA") -def fetch_field_values(mbeans_url, timeout): +def fetch_field_values(mbeans_url, timeout, ca, key_pair): """Get values from PuppetDB and print them out.""" memory_url = f"{mbeans_url}/java.lang:type=Memory" try: - mem_req = rest_request(memory_url, timeout) + mem_req = rest_request(memory_url, timeout, ca, key_pair) except Exception as e: print(f"HTTP Request did not complete successfully: {e}", file=sys.stderr) @@ -134,9 +163,31 @@ if __name__ == '__main__': print(f"Invalid value for timeout: {e}", file=sys.stderr) exit(1) + ca = os.environ.get('ca', None) + if ca: + if not os.path.exists(ca): + print(f"CA file '{ca}' not found.", file=sys.stderr) + exit(1) + + cert = os.environ.get('cert', None) + key = os.environ.get('key', None) + if cert or key: + if cert and key: + if not os.path.exists(cert): + print(f"Certificate file '{cert}' not found.", file=sys.stderr) + exit(1) + if not os.path.exists(key): + print(f"Key file '{key}' not found.", file=sys.stderr) + exit(1) + else: + print("Only one of 'cert' and 'key' supplied. " + "Both are needed for client authentication.", + file=sys.stderr) + exit(1) + if len(sys.argv) > 1 and sys.argv[1] == 'autoconf': try: - dummy = rest_request(mbeans_url, timeout) + dummy = rest_request(mbeans_url, timeout, ca, (cert, key)) except Exception as e: print(f"no ({e})") exit(0) @@ -148,4 +199,4 @@ if __name__ == '__main__': config() exit(0) - fetch_field_values(mbeans_url, timeout) + fetch_field_values(mbeans_url, timeout, ca, (cert, key)) From 292cfb955ae6c3c902df73f42f7e5f320f93fc92 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 25 Jan 2020 01:22:11 +0100 Subject: [PATCH 558/695] ssl-certificate-expiry: add support for FreeBSD's "date" Thanks to oz42 Closes: #1038 --- plugins/ssl/ssl-certificate-expiry | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index c2bb7cf2..055af6f5 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -94,9 +94,15 @@ parse_valid_days_from_certificate() { valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ | grep "^notAfter=" | cut -f 2 -d "=") if [ -n "$valid_until_string" ]; then - valid_until_epoch=$(date --date="$valid_until_string" +%s) - if [ -n "$valid_until_epoch" ]; then + # FreeBSD requires special arguments for "date" + if uname | grep -q ^FreeBSD; then + valid_until_epoch=$(date -j -f '%b %e %T %Y %Z' "$valid_until_string" +%s) + now_epoch=$(date -j +%s) + else + valid_until_epoch=$(date --date="$valid_until_string" +%s) now_epoch=$(date +%s) + fi + if [ -n "$valid_until_epoch" ]; then # calculate the number of days left echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }' fi From 0a4891fcef2de8651e94c1ae8d447850f245b428 Mon Sep 17 00:00:00 2001 From: Alban Date: Sat, 25 Jan 2020 01:28:33 +0100 Subject: [PATCH 559/695] Add new mongodb plugins * mongo_cpu_ram * mongodb_conn * mongodb_cpu * mongodb_ram --- plugins/mongodb/mongodb_conn | 79 ++++++++++++++++++++++++++++++++++++ plugins/mongodb/mongodb_cpu | 77 +++++++++++++++++++++++++++++++++++ plugins/mongodb/mongodb_ram | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100755 plugins/mongodb/mongodb_conn create mode 100755 plugins/mongodb/mongodb_cpu create mode 100755 plugins/mongodb/mongodb_ram diff --git a/plugins/mongodb/mongodb_conn b/plugins/mongodb/mongodb_conn new file mode 100755 index 00000000..5772ea5c --- /dev/null +++ b/plugins/mongodb/mongodb_conn @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +=head1 NAME + MongoDB Connection Count Plugin + +=head1 APPLICABLE SYSTEMS + + MongoDB 3.X and 4.X with pymongo installed. + +=head1 CONFIGURATION + + munin-node.conf + defauts for host is 127.0.0.1 and port 27017 + and will work without being defined : + + [mongodb_conn] + env.host 127.0.0.1 + env.port 27017 + env.username user + env.password P@55w0rd + +=head1 AUTHOR + + Alban Espie-Guillon + based on Stefan Andersen work. + +=head1 LICENSE + The Beer Ware License (Revision 42) + wrote this file. As long + as you retain this notice you can do whatever you want + with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. +""" +import os +import sys +import pymongo + + +def _get_connections(): + host = os.environ.get('host', '127.0.0.1') + port = os.environ.get('port', 27017) + username = os.environ.get('username', '') + password = os.environ.get('password', '') + conn = pymongo.MongoClient(host, int(port)) + if username: + connAuth = conn['admin'] + connAuth.authenticate(username, password) + + """ cli : db.serverStatus().connections """ + conn_status = conn.admin.command("serverStatus")['connections'] + return conn_status + + +def run(): + connections = _get_connections() + for connection, value in connections.items(): + print(connection + ".value", value) + + +def config(): + print(""" +graph_title MongoDB Connections Count +graph_vlabel Connections count +graph_category db +graph_args --base 1000 -l 0 +current.label current +current.draw AREASTACK +available.label available +available.draw AREASTACK +active.label active +active.draw AREASTACK +""") + + +if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "config": + config() + else: + run() diff --git a/plugins/mongodb/mongodb_cpu b/plugins/mongodb/mongodb_cpu new file mode 100755 index 00000000..a2fa08de --- /dev/null +++ b/plugins/mongodb/mongodb_cpu @@ -0,0 +1,77 @@ +#!/usr/bin/perl -w + +# Author : Alban Espie-Guillon +# Based on http://github.com/perusio/nginx-munin Author: António P. P. Almeida + +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# Except as contained in this notice, the name(s) of the above copyright +# holders shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written authorization. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +=head1 NAME + +mongodb_cpu - Munin plugin to show CPU used by mongodb. + +=encoding utf8 + +=head1 APPLICABLE SYSTEMS + +Any mongodb host + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +1.0 + +=head1 BUGS + +None known + +=head1 AUTHOR + +Based on a script by António P. P. Almeida . Modified by Alban Espie-Guillon + +=head1 LICENSE + +MIT + +=cut + +## Munin config method. +if (exists $ARGV[0] and $ARGV[0] eq "config") { + print "graph_title Mongodb CPU usage\n"; + print "graph_vlabel CPU\n"; + print "graph_category db\n"; + print "graph_args --base 1000 -r --lower-limit 0\n"; + print "graph_scale no\n"; + print "graph_period second\n"; + print "cpu.label CPU\n"; + print "cpu.type GAUGE\n"; + print "cpu.draw AREASTACK\n"; + print "cpu.min 0\n"; + exit 0; +} else { + my $c = `ps u -p \$(pidof mongod) | awk 'NR > 1 {print \$3}'`; + print "cpu.value $c"; +} diff --git a/plugins/mongodb/mongodb_ram b/plugins/mongodb/mongodb_ram new file mode 100755 index 00000000..ff00861e --- /dev/null +++ b/plugins/mongodb/mongodb_ram @@ -0,0 +1,75 @@ +#!/usr/bin/perl -w + +# Author : Alban Espie-Guillon +# Based on http://github.com/perusio/nginx-munin Author: António P. P. Almeida + +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# Except as contained in this notice, the name(s) of the above copyright +# holders shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written authorization. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +=head1 NAME + +mongodb_ram - Munin plugin to show RAM used by mongodb. + +=encoding utf8 + +=head1 APPLICABLE SYSTEMS + +Any mongodb host + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +1.0 + +=head1 BUGS + +None known + +=head1 AUTHOR + +Based on a script by António P. P. Almeida . Modified by Alban Espie-Guillon + +=head1 LICENSE + +MIT + +=cut + +## Munin config method. +if (exists $ARGV[0] and $ARGV[0] eq "config") { + print "graph_title Mongodb RAM usage\n"; + print "graph_vlabel RAM\n"; + print "graph_category db\n"; + print "graph_args --base 1024 -l 0 --upper-limit 4138442752\n"; + print "ram.label RAM\n"; + print "ram.type GAUGE\n"; + print "ram.draw AREASTACK\n"; + print "ram.min 0\n"; + exit 0; +} else { + my $m = `ps u -p \$(pidof mongod) | awk 'NR > 1 {nm += \$5} END {print nm*1024}'`; + print "ram.value $m"; +} From 2e16d2f8457794f791acc1e9e4404380439db71d Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 28 Jan 2020 09:44:26 +1100 Subject: [PATCH 560/695] Templates: name in title (#1019) * [original] Put host name first in * [munstrap] Put host name or service (NAME) first in <title> Useful when many tabs are open. Signed-off-by: Olivier Mehani <shtrom@ssji.net> --- templates/munstrap/templates/partial/head.tmpl | 4 +++- templates/official/partial/head.tmpl | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/templates/munstrap/templates/partial/head.tmpl b/templates/munstrap/templates/partial/head.tmpl index 0cefcf46..46db4e43 100644 --- a/templates/munstrap/templates/partial/head.tmpl +++ b/templates/munstrap/templates/partial/head.tmpl @@ -1,7 +1,9 @@ <!DOCTYPE html> <html lang="en"> <head> - <title>Munin <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"><TMPL_ELSE>Munin</TMPL_IF></TMPL_LOOP> + + <TMPL_IF NAME="NAME"><TMPL_VAR ESCAPE="HTML" NAME="NAME"> (</TMPL_IF>Munin <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"></TMPL_IF></TMPL_LOOP><TMPL_IF NAME="NAME">)</TMPL_IF> + diff --git a/templates/official/partial/head.tmpl b/templates/official/partial/head.tmpl index 1a7c2342..ae4e9b54 100644 --- a/templates/official/partial/head.tmpl +++ b/templates/official/partial/head.tmpl @@ -12,7 +12,9 @@ - <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"><TMPL_ELSE>Munin</TMPL_IF></TMPL_LOOP> + + <TMPL_IF NAME="NAME"><TMPL_VAR ESCAPE="HTML" NAME="NAME"> (</TMPL_IF>Munin <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"></TMPL_LOOP><TMPL_IF NAME="NAME">)</TMPL_IF> + /static/favicon.ico" /> From 065d4f129c3bf5ff78f26adcea82ee2b184f3db1 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 1 Feb 2020 16:44:44 -0500 Subject: [PATCH 561/695] relayd: avoid matching table defs within comments if a table is commented out, you currently get an entry for the hosts that the entry contains but you'll get NaN for those hosts all the time if they are not defined elsewhere. to avoid that we anchor our match to the beginning of lines and permit leading spaces since some folks might like to indent lines. --- plugins/relayd/relayd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/relayd/relayd b/plugins/relayd/relayd index aef563ae..bddbbdcf 100755 --- a/plugins/relayd/relayd +++ b/plugins/relayd/relayd @@ -68,7 +68,7 @@ my $cmd = (defined($ARGV[0])) ? $ARGV[0] : ''; my @hosts = (); open(my $conf, "<", $configfile) or die "can't open $configfile: $!"; my $content = join("", <$conf>); -while ( $content =~ /table\s*<([^>]+)>\s*{([^}]+)}/g) { +while ( $content =~ /^\s*table\s*<([^>]+)>\s*{([^}]+)}/mg) { my $hosts = $2; print "table: $1, " if $Munin::Plugin::DEBUG; $hosts =~ s/#.*$//mg; # comments From daba178eef55184ea3a566761f72686b18c3a4c1 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 1 Feb 2020 16:46:26 -0500 Subject: [PATCH 562/695] relayd: permit commas between hosts in tables relayd.conf(5) mentions the following: Each table must contain at least one host address; multiple hosts are separated by newline, comma, or whitespace. So the addition of commas as separators should now cover all possible cases. Without this, hosts separated by commas in a table will show up with a trailing comma in their host name and will probably not get their stats correctly assigned when grabbing information from relayctl. --- plugins/relayd/relayd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/relayd/relayd b/plugins/relayd/relayd index bddbbdcf..e43400c2 100755 --- a/plugins/relayd/relayd +++ b/plugins/relayd/relayd @@ -74,7 +74,7 @@ while ( $content =~ /^\s*table\s*<([^>]+)>\s*{([^}]+)}/mg) { $hosts =~ s/#.*$//mg; # comments $hosts =~ s/^\s+//mg; # trim spaces before lines print "hosts: $hosts\n" if $Munin::Plugin::DEBUG; - push @hosts , split /\s+/, $hosts; + push @hosts , split /,?\s+/, $hosts; } if ($cmd eq 'config') { From 7784ddf4d8ca11cabc14ddf1a6f04334a6b9141a Mon Sep 17 00:00:00 2001 From: Doctor Date: Sun, 2 Feb 2020 21:01:46 +0100 Subject: [PATCH 563/695] Fix prosody plugin by encoding telnetlib commands --- plugins/prosody/prosody_ | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 7aa288f5..4a4b9552 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -56,20 +56,20 @@ def main(): else: connection_count_re = re.compile(r"Total:\s(\d+)\s") telnet = telnetlib.Telnet(host, port) - telnet.write("c2s:show_secure()\n") - telnet_response = telnet.read_until("secure client connections", 5) - parsed_info = connection_count_re.findall(telnet_response) + telnet.write(b"c2s:show_secure()\n") + telnet_response = telnet.read_until(b"secure client connections", 5) + parsed_info = connection_count_re.findall(telnet_response.decode('ascii')) secure_client_connections = int(parsed_info[0]) print("secure_client_connections.value %s" % secure_client_connections) - telnet.write("c2s:show_insecure()\n") - telnet_response = telnet.read_until("insecure client connections", 5) - parsed_info = connection_count_re.findall(telnet_response) + telnet.write(b"c2s:show_insecure()\n") + telnet_response = telnet.read_until(b"insecure client connections", 5) + parsed_info = connection_count_re.findall(telnet_response.decode('ascii')) insecure_client_connections = int(parsed_info[0]) print("insecure_client_connections.value %s" % insecure_client_connections) all_client_connections = secure_client_connections + insecure_client_connections print("all_client_connections.value %s" % (all_client_connections)) - telnet.write("quit\n") + telnet.write(b"quit\n") elif wildcard == "s2s": if mode == "config": @@ -84,12 +84,12 @@ def main(): else: server_connections_re = re.compile(r"(\d+) outgoing, (\d+)") telnet = telnetlib.Telnet(host, port) - telnet.write("s2s:show()\n") - telnet_response = telnet.read_until("connections", 5) - parsed_info = server_connections_re.findall(telnet_response) + telnet.write(b"s2s:show()\n") + telnet_response = telnet.read_until(b"connections", 5) + parsed_info = server_connections_re.findall(telnet_response.decode('ascii')) print("outgoing_connections.value %s" % (parsed_info[0][0])) print("incoming_connections.value %s" % (parsed_info[0][1])) - telnet.write("quit\n") + telnet.write(b"quit\n") elif wildcard == "presence": if mode == "config": @@ -105,17 +105,17 @@ def main(): sys.exit(0) else: - client_presence_re = re.compile(r"[-\]] (.*?)\(\d+\)") + client_presence_re = re.compile("[\]] (.*?)\(\d+\)") telnet = telnetlib.Telnet(host, port) - telnet.write("c2s:show()\n") - telnet_response = telnet.read_until("clients", 5) - parsed_info = client_presence_re.findall(telnet_response) + telnet.write(b"c2s:show()\n") + telnet_response = telnet.read_until(b"clients", 5) + parsed_info = client_presence_re.findall(telnet_response.decode('ascii')) print("available.value %s" % parsed_info.count("available")) print("chat.value %s" % (parsed_info.count("chat"))) print("away.value %s" % (parsed_info.count("away"))) print("xa.value %s" % (parsed_info.count("xa"))) print("dnd.value %s" % (parsed_info.count("dnd"))) - telnet.write("quit\n") + telnet.write(b"quit\n") elif wildcard == "uptime": if mode == "config": @@ -135,13 +135,13 @@ def main(): else: uptime_re = re.compile(r"\d+") telnet = telnetlib.Telnet(host, port) - telnet.write("server:uptime()\n") - telnet_response = telnet.read_until("minutes (", 5) - parsed_info = uptime_re.findall(telnet_response) + telnet.write(b"server:uptime()\n") + telnet_response = telnet.read_until(b"minutes (", 5) + parsed_info = uptime_re.findall(telnet_response.decode('ascii')) uptime_value = (float(parsed_info[0]) + float(parsed_info[1]) / 24 + float(parsed_info[2]) / 60 / 24) print("uptime.value %s" % (uptime_value)) - telnet.write("quit\n") + telnet.write(b"quit\n") elif wildcard == "users": if mode == "config": From 60782f9d72de1fcfb07b9b359aeb08a324b1d014 Mon Sep 17 00:00:00 2001 From: kozakana Date: Wed, 5 Feb 2020 19:48:19 +0900 Subject: [PATCH 564/695] Plugin wordpress2: support PHP7 --- plugins/wordpress/wordpress2 | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/wordpress/wordpress2 b/plugins/wordpress/wordpress2 index f219225d..58fb9113 100755 --- a/plugins/wordpress/wordpress2 +++ b/plugins/wordpress/wordpress2 @@ -109,13 +109,26 @@ $comments = 0; $pingbacks = 0; // GET DATA -mysql_connect($d["host"], $d["user"], $d["pass"]) or die("Error: Failed to connect to the MySQL database!"); -mysql_select_db($d["dbnm"]) or die("Error: Failed to select database!"); +if(function_exists('mysql_connect')) { + mysql_connect($d["host"], $d["user"], $d["pass"]) or die("Error: Failed to connect to the MySQL database!"); + mysql_select_db($d["dbnm"]) or die("Error: Failed to select database!"); -$users = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."users;"), 0); -$posts = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."posts WHERE post_status='publish' AND post_password='' AND post_type='post';"), 0); -$comments = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='';"), 0); -$pingbacks = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='pingback';"), 0); + $users = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."users;"), 0); + $posts = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."posts WHERE post_status='publish' AND post_password='' AND post_type='post';"), 0); + $comments = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='';"), 0); + $pingbacks = mysql_result(mysql_query("SELECT COUNT(*) FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='pingback';"), 0); +}else{ + $mysqli = new mysqli($d["host"], $d["user"], $d["pass"], $d["dbnm"]); + if ($mysqli->connect_errno) { + die("Error: Failed to connect to the MySQL database!"); + } + + $users = $mysqli->query("SELECT COUNT(*) AS cnt FROM ".$d["tbpf"]."users;")->fetch_object()->cnt; + $posts = $mysqli->query("SELECT COUNT(*) AS cnt FROM ".$d["tbpf"]."posts WHERE post_status='publish' AND post_password='' AND post_type='post';")->fetch_object()->cnt; + $comments = $mysqli->query("SELECT COUNT(*) AS cnt FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='';")->fetch_object()->cnt; + $pingbacks = $mysqli->query("SELECT COUNT(*) AS cnt FROM ".$d["tbpf"]."comments WHERE comment_approved='1' AND comment_type='pingback';")->fetch_object()->cnt; + $mysqli->close(); +} // OUTPUT DATA echo "users.value ".$users ."\n"; From 52144bc2771f3d74d94fcf9a831ccc79cc61ddf3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 6 Feb 2020 00:53:44 +0100 Subject: [PATCH 565/695] ssl-certificate-expiry: remove hardcoded CA directory path This should allow the plugin to be usable on all platforms. --- plugins/ssl/ssl-certificate-expiry | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index 055af6f5..c757c8a9 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -123,7 +123,7 @@ print_expire_days() { [ -n "$starttls" ] && s_client_args="-starttls $starttls" # shellcheck disable=SC2086 - echo "" | openssl s_client -CApath /etc/ssl/certs \ + echo "" | openssl s_client \ -servername "$host" -connect "${host}:${port}" \ $s_client_args 2>/dev/null \ | parse_valid_days_from_certificate From 276169a6c937ecc322859e24f8e48ca366691ee3 Mon Sep 17 00:00:00 2001 From: Alban Date: Thu, 13 Feb 2020 16:11:16 +0100 Subject: [PATCH 566/695] Update mongodb plugins * Update mongodb plugins * fix category * chmod +x * flake8-CI * flake8 adjustments for mongo_collection_ and env python3 --- plugins/mongodb/mongo_btree | 22 ++++- plugins/mongodb/mongo_collection_ | 135 +++++++++++++++--------------- plugins/mongodb/mongo_conn | 22 ++++- plugins/mongodb/mongo_lag | 42 ++++++---- plugins/mongodb/mongo_lock | 22 ++++- plugins/mongodb/mongo_mem | 84 +++++++++++++------ plugins/mongodb/mongo_ops | 90 ++++++++++++++------ plugins/mongodb/mongodb_conn | 4 +- plugins/mongodb/mongodb_docs | 85 +++++++++++++++++++ 9 files changed, 360 insertions(+), 146 deletions(-) create mode 100755 plugins/mongodb/mongodb_docs diff --git a/plugins/mongodb/mongo_btree b/plugins/mongodb/mongo_btree index bddd972a..ea27faaa 100755 --- a/plugins/mongodb/mongo_btree +++ b/plugins/mongodb/mongo_btree @@ -1,6 +1,22 @@ -#!/usr/bin/python +#!/usr/bin/env python +""" +=head1 NAME + MongoDB btree Plugin -## GENERATED FILE - DO NOT EDIT +=head1 APPLICABLE SYSTEMS + + Works until MongoDB 2.7. The "indexCounters" field was removed in 2.8 version. + +=head1 CONFIGURATION + + [mongo_btree] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Doc added by Alban Espie-Guillon +""" import urllib2 import sys @@ -50,5 +66,3 @@ if __name__ == "__main__": doConfig() else: doData() - - diff --git a/plugins/mongodb/mongo_collection_ b/plugins/mongodb/mongo_collection_ index 229435fe..37d939e7 100755 --- a/plugins/mongodb/mongo_collection_ +++ b/plugins/mongodb/mongo_collection_ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim: set sts=4 sw=4 encoding=utf-8 @@ -29,8 +29,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#%# family=auto -#%# capabilities=suggest autoconf +# #%# family=auto +# #%# capabilities=suggest autoconf import pymongo @@ -40,7 +40,7 @@ settings_host = '127.0.0.1' settings_port = 27017 # mongodb_uri will override host and port settings_mongodb_uri = '' -settings_db = 'mydb' +settings_db = 'admin' settings_user = '' settings_password = '' settings_ignoredb = {} @@ -88,98 +88,97 @@ typeIndex['indexsize']['category'] = 'db' def getCollstats(graphtype): - if settings_mongodb_uri: - con = pymongo.MongoClient(settings_mongodb_uri) - else: - 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'] - db.authenticate(settings_user, settings_password) + if settings_user: + db = con[settings_db] + db.authenticate(settings_user, settings_password) - stats_tmp = {} - for dbname in con.database_names(): - if dbname in settings_ignoredb: - continue - db = con[dbname] - for coll in db.collection_names(): - if coll.startswith('system.'): - continue - stats = db.command("collstats", coll) - collname = dbname + "_" + coll.replace('.', '_') - if not stats_tmp.has_key(collname): - stats_tmp[collname] = {} - stats_tmp[collname]['value'] = 0 - stats_tmp[collname]['dbname'] = dbname - if typeIndex[graphtype]['index'] in stats: - stats_tmp[collname]['value'] += long(stats[typeIndex[graphtype]['index']]) + stats_tmp = {} + for dbname in con.list_database_names(): + if dbname in settings_ignoredb: + continue + db = con[dbname] + for coll in db.list_collection_names(): + if coll.startswith('system.'): + continue + stats = db.command("collstats", coll) + collname = dbname + "_" + coll.replace('.', '_') + if collname not in stats_tmp: + stats_tmp[collname] = {} + stats_tmp[collname]['value'] = 0 + stats_tmp[collname]['dbname'] = dbname + if typeIndex[graphtype]['index'] in stats: + stats_tmp[collname]['value'] += int(stats[typeIndex[graphtype]['index']]) + + con.close() + + for collname, item in sorted(stats_tmp.items()): + yield ("%s" % collname, item['value'], item['dbname']) - con.close() - - for collname, item in sorted(stats_tmp.items()): - yield ("%s" % collname, item['value'], item['dbname']) - - -def doData(base,graphtype): +def doData(base, graphtype): lastdb = "" for coll, stats, db in sorted(getCollstats(graphtype), key=itemgetter(2)): if lastdb != db: - print "multigraph " + base + "_" + graphtype + "_" + db + print("multigraph " + base + "_" + graphtype + "_" + db) lastdb = db - print "%s_%s.value %s" % (graphtype, coll, stats) + print("%s_%s.value %s" % (graphtype, coll, stats)) -def doConfig(base,graphtype): +def doConfig(base, graphtype): lastdb = "" - for k,v,d in sorted(getCollstats(graphtype), key=itemgetter(2)): + for k, v, d in sorted(getCollstats(graphtype), key=itemgetter(2)): if lastdb != d: - print "multigraph " + base + "_" + graphtype + "_" + d + print("multigraph " + base + "_" + graphtype + "_" + d) lastdb = d -# print "graph_total total" - print "graph_title MongoDB " + typeIndex[graphtype]['title'] + " for database " + d - print "graph_args --base " + typeIndex[graphtype]['base'] + " " + typeIndex[graphtype]['scale'] - print "graph_vlabel " + typeIndex[graphtype]['yaxis'] - print "graph_category db" - print "%s_%s.label %s" % (graphtype, k, k) - print "%s_%s.min 0" % (graphtype, k) - print "%s_%s.draw LINE1" % (graphtype, k) + print("graph_title MongoDB " + typeIndex[graphtype]['title'] + " for database " + d) + print("graph_args --base " + typeIndex[graphtype]['base'] + " " + typeIndex[graphtype]['scale']) + print("graph_vlabel " + typeIndex[graphtype]['yaxis']) + print("graph_category db") + print("%s_%s.label %s" % (graphtype, k, k)) + print("%s_%s.min 0" % (graphtype, k)) + print("%s_%s.draw LINE1" % (graphtype, k)) + def doSuggest(): - print "keys" - for k in typeIndex.keys(): - print k + print("keys") + for k in typeIndex.keys(): + print(k) if __name__ == "__main__": - from sys import argv,exit - from os import environ,path + from sys import argv, exit + from os import environ, path import re # Could be done by a for loop # but i think if's are faster - if 'HOST' in environ: - settings_host = environ['HOST'] - if 'PORT' in environ: - settings_port = environ['PORT'] - if 'DB' in environ: - settings_db = environ['DB'] - if 'MONGO_USER' in environ: - settings_user = environ['MONGO_USER'] - if 'PASSWORD' in environ: - settings_password = environ['PASSWORD'] - if 'IGNOREDB' in environ: - settings_ignoredb = environ['IGNOREDB'].split(',') + if 'host' in environ: + settings_host = environ['host'] + if 'port' in environ: + settings_port = environ['port'] + if 'db' in environ: + settings_db = environ['db'] + if 'username' in environ: + settings_user = environ['username'] + if 'password' in environ: + settings_password = environ['password'] + if 'ignoredb' in environ: + settings_ignoredb = environ['ignoredb'].split(',') m = re.search('^(.*)_([a-zA-Z0-9]*)$', path.basename(argv[0])) if len(argv) < 2: - doData(m.group(1),m.group(2)) + doData(m.group(1), m.group(2)) elif argv[1] == "config": - doConfig(m.group(1),m.group(2)) + doConfig(m.group(1), m.group(2)) elif argv[1] == "autoconf": - print "yes" + print("yes") elif argv[1] == "suggest": doSuggest() else: - print "invalid argument" + print("invalid argument") exit(1) diff --git a/plugins/mongodb/mongo_conn b/plugins/mongodb/mongo_conn index 62324c65..727c2cda 100755 --- a/plugins/mongodb/mongo_conn +++ b/plugins/mongodb/mongo_conn @@ -1,6 +1,22 @@ -#!/usr/bin/python +#!/usr/bin/env python +""" +=head1 NAME + MongoDB connections Plugin -## GENERATED FILE - DO NOT EDIT +=head1 APPLICABLE SYSTEMS + + Works until MongoDB 3.6. The httpinterface was later removed. + +=head1 CONFIGURATION + + [mongo_lock] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Doc added by Alban Espie-Guillon +""" import urllib2 import sys @@ -40,5 +56,3 @@ if __name__ == "__main__": doConfig() else: doData() - - diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index 681798ff..8a44541c 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -1,24 +1,38 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ +=head1 NAME MongoDB Replication Lag - ~~~~~~~~~~~~~~~~~~~~~~~ Connects to a single mongo instance and retrieve replication lag for all connected members. - munin-node.conf: +=head1 APPLICABLE SYSTEMS + + MongoDB 3.X and 4.X with pymongo installed. + +=head1 CONFIGURATION + + munin-node.conf + defauts for host is 127.0.0.1 and port 27017 + and will work without being defined : + [mongo_lag] env.host 127.0.0.1 env.port 27017 env.username user env.password P@55w0rd - :author: Stefan Andersen - :license: The Beer Ware License (Revision 42) - wrote this file. As long - as you retain this notice you can do whatever you want - with this stuff. If we meet some day, and you think - this stuff is worth it, you can buy me a beer in return. +=head1 AUTHOR + + Stefan Andersen + Updated by Alban Espie-Guillon + +=head1 LICENSE + The Beer Ware License (Revision 42) + wrote this file. As long + as you retain this notice you can do whatever you want + with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. """ import os import sys @@ -33,7 +47,7 @@ def _get_members(): if username: connAuth = conn['admin'] connAuth.authenticate(username, password) - + repl_status = conn.admin.command("replSetGetStatus") members = {} @@ -51,17 +65,17 @@ def run(): for member in members: lag = (primary_optime - members[member]['optimeDate']).seconds - print "{0}.value {1}".format(member, lag) + print("{0}.value {1}".format(member, lag)) def config(): - print """graph_title MongoDB replication lag + print("""graph_title MongoDB replication lag graph_args --base 1000 graph_vlabel Replication lag (seconds) graph_category db -""" +""") for member in _get_members(): - print "{0}.label {0}".format(member) + print("{0}.label {0}".format(member)) if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == "config": diff --git a/plugins/mongodb/mongo_lock b/plugins/mongodb/mongo_lock index ef490d7d..9bd84791 100755 --- a/plugins/mongodb/mongo_lock +++ b/plugins/mongodb/mongo_lock @@ -1,6 +1,22 @@ -#!/usr/bin/python +#!/usr/bin/env python +""" +=head1 NAME + MongoDB lock Plugin -## GENERATED FILE - DO NOT EDIT +=head1 APPLICABLE SYSTEMS + + MongoDB 2.X. The "lockTime" field was removed in later versions. + +=head1 CONFIGURATION + + [mongo_lock] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Doc added by Alban Espie-Guillon +""" import urllib2 import sys @@ -47,5 +63,3 @@ if __name__ == "__main__": doConfig() else: doData() - - diff --git a/plugins/mongodb/mongo_mem b/plugins/mongodb/mongo_mem index 8f720602..6daeb7ad 100755 --- a/plugins/mongodb/mongo_mem +++ b/plugins/mongodb/mongo_mem @@ -1,49 +1,83 @@ -#!/usr/bin/python +#!/usr/bin/env python3 +""" +=head1 NAME + MongoDB mem Plugin -## GENERATED FILE - DO NOT EDIT +=head1 APPLICABLE SYSTEMS -import urllib2 + MongoDB 3.X and 4.X with pymongo installed. + +=head1 CONFIGURATION + + munin-node.conf + defauts for host is 127.0.0.1 and port 27017 + and will work without being defined : + + [mongo_mem] + env.host 127.0.0.1 + env.port 27017 + env.username user + env.password P@55w0rd + env.db dbname + + or + + [mongodb_mem] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Updated by Alban Espie-Guillon +""" import sys - -try: - import json -except ImportError: - import simplejson as json - +import os +import pymongo def getServerStatus(): - raw = urllib2.urlopen( "http://127.0.0.1:28017/_status" ).read() - return json.loads( raw )["serverStatus"] + if 'MONGO_DB_URI' in os.environ: + c = pymongo.MongoClient(os.environ['MONGO_DB_URI']) + + elif 'username' and 'password' in os.environ: + host = os.environ.get('host', '127.0.0.1') + port = os.environ.get('port', 27017) + username = os.environ.get('username', '') + password = os.environ.get('password', '') + c = pymongo.MongoClient(host, int(port)) + if username: + cAuth = c['admin'] + cAuth.authenticate(username, password) + + else: + c = pymongo.MongoClient() + + return c.admin.command('serverStatus', workingSet=True) def ok(s): return s == "resident" or s == "virtual" or s == "mapped" def doData(): - for k,v in getServerStatus()["mem"].iteritems(): + for k,v in getServerStatus()["mem"].items(): if ok(k): print( str(k) + ".value " + str(v * 1024 * 1024) ) -def doConfig(): - print "graph_title MongoDB memory usage" - print "graph_args --base 1024 -l 0 --vertical-label Bytes" - print "graph_category db" +def doConfig(): + print(""" +graph_title MongoDB memory usage +graph_args --base 1024 -l 0 --vertical-label Bytes +graph_category mongodb +""") for k in getServerStatus()["mem"]: if ok( k ): - print k + ".label " + k - print k + ".draw LINE1" - - - - - + print(k + ".label " + k) + print(k + ".draw LINE1") if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "config": doConfig() else: doData() - - diff --git a/plugins/mongodb/mongo_ops b/plugins/mongodb/mongo_ops index 15f6b8b9..216fe270 100755 --- a/plugins/mongodb/mongo_ops +++ b/plugins/mongodb/mongo_ops @@ -1,45 +1,85 @@ -#!/usr/bin/python +#!/usr/bin/env python3 +""" +=head1 NAME + MongoDB ops Plugin -## GENERATED FILE - DO NOT EDIT +=head1 APPLICABLE SYSTEMS -import urllib2 + MongoDB 3.X and 4.X with pymongo installed. + +=head1 CONFIGURATION + + munin-node.conf + defauts for host is 127.0.0.1 and port 27017 + and will work without being defined : + + [mongodb_ops] + env.host 127.0.0.1 + env.port 27017 + env.username user + env.password P@55w0rd + env.db dbname + + or + + [mongodb_ops] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Updated by Alban Espie-Guillon +""" import sys - -try: - import json -except ImportError: - import simplejson as json - +import os +import pymongo def getServerStatus(): - raw = urllib2.urlopen( "http://127.0.0.1:28017/_status" ).read() - return json.loads( raw )["serverStatus"] + if 'MONGO_DB_URI' in os.environ: + c = pymongo.MongoClient(os.environ['MONGO_DB_URI']) + + elif 'username' and 'password' in os.environ: + host = os.environ.get('host', '127.0.0.1') + port = os.environ.get('port', 27017) + username = os.environ.get('username', '') + password = os.environ.get('password', '') + c = pymongo.MongoClient(host, int(port)) + if username: + cAuth = c['admin'] + cAuth.authenticate(username, password) + + else: + c = pymongo.MongoClient() + + return c.admin.command('serverStatus', workingSet=True) def doData(): ss = getServerStatus() - for k,v in ss["opcounters"].iteritems(): + for k,v in ss["opcounters"].items(): print( str(k) + ".value " + str(v) ) -def doConfig(): - print "graph_title MongoDB ops" - print "graph_args --base 1000 -l 0" - print "graph_vlabel ops / ${graph_period}" - print "graph_category db" - print "graph_total total" +def doConfig(): + print(""" +graph_title MongoDB ops +graph_args --base 1000 -l 0 +graph_vlabel ops / ${graph_period} +graph_category db +graph_total total +""") for k in getServerStatus()["opcounters"]: - print k + ".label " + k - print k + ".min 0" - print k + ".type COUNTER" - print k + ".max 500000" - print k + ".draw LINE1" + print(k + ".label " + k) + print(k + ".min 0") + print(k + ".type COUNTER") + print(k + ".max 500000") + print(k + ".draw LINE1") + if __name__ == "__main__": + if len(sys.argv) > 1 and sys.argv[1] == "config": doConfig() else: doData() - - diff --git a/plugins/mongodb/mongodb_conn b/plugins/mongodb/mongodb_conn index 5772ea5c..831b7cda 100755 --- a/plugins/mongodb/mongodb_conn +++ b/plugins/mongodb/mongodb_conn @@ -53,8 +53,8 @@ def _get_connections(): def run(): connections = _get_connections() - for connection, value in connections.items(): - print(connection + ".value", value) + for c, v in connections.items(): + print(str(c) + ".value " + str(v)) def config(): diff --git a/plugins/mongodb/mongodb_docs b/plugins/mongodb/mongodb_docs new file mode 100755 index 00000000..c1eb201a --- /dev/null +++ b/plugins/mongodb/mongodb_docs @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +=head1 NAME + MongoDB docs Plugin + +=head1 APPLICABLE SYSTEMS + + MongoDB 3.X and 4.X with pymongo installed. + +=head1 CONFIGURATION + + munin-node.conf + defauts for host is 127.0.0.1 and port 27017 + and will work without being defined : + + [mongodb_docs] + env.host 127.0.0.1 + env.port 27017 + env.username user + env.password P@55w0rd + env.db dbname + + or + + [mongodb_docs] + env.MONGO_DB_URI mongodb://user:password@host:port/dbname + +=head1 AUTHOR + + Original script there : https://github.com/comerford/mongo-munin + Updated by Alban Espie-Guillon +""" +import sys +import os +import pymongo + + +def getServerStatus(): + if 'MONGO_DB_URI' in os.environ: + c = pymongo.MongoClient(os.environ['MONGO_DB_URI']) + + elif 'username' and 'password' in os.environ: + host = os.environ.get('host', '127.0.0.1') + port = os.environ.get('port', 27017) + username = os.environ.get('username', '') + password = os.environ.get('password', '') + c = pymongo.MongoClient(host, int(port)) + if username: + cAuth = c['admin'] + cAuth.authenticate(username, password) + + else: + c = pymongo.MongoClient() + + return c.admin.command('serverStatus', workingSet=True) + + +def doData(): + ss = getServerStatus() + for k, v in ss["metrics"]["document"].items(): + print(str(k) + ".value " + str(v)) + + +def doConfig(): + print(""" +graph_title MongoDB documents +graph_args --base 1000 -l 0 +graph_vlabel documents +graph_category db +""") + + for k in getServerStatus()["metrics"]["document"]: + print(k + ".label " + k) + print(k + ".min 0") + print(k + ".type COUNTER") + print(k + ".max 500000") + print(k + ".draw LINE1") + + +if __name__ == "__main__": + + if len(sys.argv) > 1 and sys.argv[1] == "config": + doConfig() + else: + doData() From 937cb1d030fb4dd27df6192d0edb0de0def6b169 Mon Sep 17 00:00:00 2001 From: Doctor Date: Sat, 8 Feb 2020 15:05:52 +0100 Subject: [PATCH 567/695] Rewrite Docker plugin Fix: - CPU - Memory Add: - Containers number - Containers status - Images number - Volumes number --- plugins/docker/docker_ | 190 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100755 plugins/docker/docker_ diff --git a/plugins/docker/docker_ b/plugins/docker/docker_ new file mode 100755 index 00000000..61551901 --- /dev/null +++ b/plugins/docker/docker_ @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +""" +=head1 NAME + +docker_ - Docker wildcard-plugin to monitor a L host. + +This wildcard plugin provides at the moment only the suffixes C, C, C, +C, C and C. + +=head1 INSTALLATION + +- Copy this plugin in your munin plugins directory +- Install Python3 "docker" package + +=over 2 + + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_containers + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_cpu + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_images + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_memory + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_status + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_volumes + +=back + +After the installation you need to restart your munin-node: + +=over 2 + + systemctl restart munin-node + +=back + +=head1 CONFIGURATION + +This plugin need to run as root, you need to create a file named docker placed in the +directory /etc/munin/plugin-conf.d/ with the following config (you can also use +Docker environment variables here as described in +https://docs.docker.com/compose/reference/envvars/): + +=over 2 + + [docker_*] + user root + env.DOCKER_HOST unix://var/run/docker.sock + +=back +""" + +import os +import sys +import docker + + +def print_containers_status(client): + running = 0 + paused = 0 + created = 0 + restarting = 0 + removing = 0 + exited = 0 + dead = 0 + for container in client.containers.list(): + if container.status == 'running': + running += 1 + elif container.status == 'paused': + paused += 1 + elif container.status == 'created': + created += 1 + elif container.status == 'restarting': + restarting += 1 + elif container.status == 'removing': + removing += 1 + elif container.status == 'exited': + exited += 1 + elif container.status == 'dead': + dead += 1 + print('running.value', running) + print('paused.value', paused) + print('created.value', created) + print('restarting.value', restarting) + print('removing.value', removing) + print('exited.value', exited) + print('dead.value', dead) + + +def print_containers_cpu(client): + for container in client.containers.list(): + stats = container.stats(stream=False) + cpu_count = len(stats["cpu_stats"]["cpu_usage"]["percpu_usage"]) + cpu_percent = 0.0 + cpu_delta = float(stats["cpu_stats"]["cpu_usage"]["total_usage"]) \ + - float(stats["precpu_stats"]["cpu_usage"]["total_usage"]) + system_delta = float(stats["cpu_stats"]["system_cpu_usage"]) \ + - float(stats["precpu_stats"]["system_cpu_usage"]) + if system_delta > 0.0: + cpu_percent = cpu_delta / system_delta * 100.0 * cpu_count + print(container.name + '.value', cpu_percent) + + +def print_containers_memory(client): + for container in client.containers.list(): + stats = container.stats(stream=False)['memory_stats'] + print(container.name + '.value', stats['stats']['total_rss']) + + +def main(): + try: + mode = sys.argv[1] + except IndexError: + mode = "" + wildcard = sys.argv[0].split("docker_")[1].split("_")[0] + + if mode == "suggest": + print("containers") + print("cpu") + print("images") + print("memory") + print("status") + print("volumes") + + client = docker.from_env() + + if wildcard == "status": + if mode == "config": + print("graph_title Docker status") + print("graph_vlabel containers") + print("graph_category virtualization") + print("running.label RUNNING") + print("paused.label PAUSED") + print("created.label CREATED") + print("restarting.label RESTARTING") + print("removing.label REMOVING") + print("exited.label EXITED") + print("dead.label DEAD") + else: + print_containers_status(client) + elif wildcard == "containers": + if mode == "config": + print("graph_title Docker containers") + print("graph_vlabel containers") + print("graph_category virtualization") + print("containers_quantity.label Containers") + else: + print('containers_quantity.value', len(client.containers.list())) + elif wildcard == "images": + if mode == "config": + print("graph_title Docker images") + print("graph_vlabel images") + print("graph_category virtualization") + print("images_quantity.label Images") + else: + print('images_quantity.value', len(client.images.list())) + elif wildcard == "volumes": + if mode == "config": + print("graph_title Docker volumes") + print("graph_vlabel volumes") + print("graph_category virtualization") + print("volumes_quantity.label Volumes") + else: + print('volumes_quantity.value', len(client.volumes.list())) + elif wildcard == "cpu": + if mode == "config": + graphlimit = str(os.cpu_count() * 100) + print("graph_title Docker containers CPU usage") + print("graph_args --base 1000 -r --lower-limit 0 --upper-limit " + graphlimit) + print("graph_scale no") + print("graph_period second") + print("graph_vlabel CPU usage (%)") + print("graph_category virtualization") + print("graph_info This graph shows docker container CPU usage.") + for container in client.containers.list(): + print("{}.label {}".format(container.name, container.name)) + else: + print_containers_cpu(client) + elif wildcard == "memory": + if mode == "config": + print("graph_title Docker containers memory usage") + print("graph_args --base 1024 -l 0") + print("graph_vlabel Bytes") + print("graph_category virtualization") + print("graph_info This graph shows docker container memory usage.") + for container in client.containers.list(): + print("{}.label {}".format(container.name, container.name)) + else: + print_containers_memory(client) + + +if __name__ == '__main__': + main() From 06cfee068930d47c2b26109d773323a6c53f9dbf Mon Sep 17 00:00:00 2001 From: Terry Burton Date: Mon, 17 Feb 2020 12:40:39 +0000 Subject: [PATCH 568/695] Plugin freeradius_sqlippool_: New plugin to show IP pool utilisation Uses the generic Perl DBI interface, requiring the user to specify a driver and provide the corresponding DBD module --- plugins/freeradius/freeradius_sqlippools_ | 203 ++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100755 plugins/freeradius/freeradius_sqlippools_ diff --git a/plugins/freeradius/freeradius_sqlippools_ b/plugins/freeradius/freeradius_sqlippools_ new file mode 100755 index 00000000..bfaca5b7 --- /dev/null +++ b/plugins/freeradius/freeradius_sqlippools_ @@ -0,0 +1,203 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +use strict; + +=head1 NAME + +freeradius_sqlippools_ - Plugin to monitor IP pool utilisation + +=head1 CONFIGURATION + +This is a wildcard plugin to support fetching the status of multiple sqlippool +instances. + +It can also be linked directly (as with a non-wildcard plugin) to present a +combined graph showing the percentage utilisation of all pools. + +It is likely that a common configuration will apply to all plugins but this +doesn't have to be so: + + [freeradius_sqlippools_*] + env.fr_driver mysql + env.fr_host 192.0.2.1 + env.fr_port 3306 + env.fr_db radius + env.fr_user radius + env.fr_pass radpass + +fr_driver is the name of the Perl DBI driver used in the DSN connection string. +The corresponding DBD module for the driver must be installed. + +You should omit fr_pass and specify fr_passfile to avoid placing the password +in a plugin configuration file that is world accessible, e.g.: + + [freeradius_sqlippools_mypool] + user radmonitor + group radmonitor + env.fr_driver Pg + env.fr_host 192.0.2.2 + env.fr_port 5432 + env.fr_db radius + env.fr_user radmonitor + env.fr_passfile /home/radmonitor/db_pass.txt + +=head1 AUTHORS + +Original Author: Network RADIUS + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + + +use File::Basename; +use Storable qw(lock_store lock_retrieve); +use DBI; + +use constant STATEFILE => "$ENV{MUNIN_PLUGSTATE}/freeradius_sqlippools.state"; + +use constant SQL => <<'EOF'; +SELECT + DISTINCT pool_name AS pool_name, + COUNT(id) OVER (PARTITION BY pool_name) AS total, + SUM(CASE WHEN expiry_time > NOW() THEN 1 ELSE 0 END) OVER (PARTITION BY pool_name) AS used +FROM radippool +EOF + +my $script = basename($0); +(my $instance) = $script =~ /freeradius_sqlippools_(.+)/; +my $command = $ARGV[0] || 'show'; + +autoconf() if $command eq 'autoconf'; +suggest() if $command eq 'suggest'; +config_instance($instance) if $command eq 'config' && defined $instance; +config_combined() if $command eq 'config' && !defined $instance; +show_instance($instance) if $command eq 'show' && defined $instance; +show_combined() if $command eq 'show' && !defined $instance; + +exit; + + +sub autoconf { + my $results; + eval { + $results = get_pools(1); + }; + if ($results) { + print "yes\n"; + } else { + print "no (Failed to read pool status from database)\n"; + } +} + +sub suggest { + my $pools = get_pools(1); + return unless defined $pools; + print "$_\n" foreach keys %{$pools}; +} + +sub config_instance { + my $instance = shift; + + print <{$instance}; + print "total.value $pool->{total}\n"; + print "used.value $pool->{used}\n"; +} + +sub show_combined { + my $pools = get_pools(); + foreach (keys %{$pools}) { + my $util = $pools->{$_}->{used} * 100 / $pools->{$_}->{total}; + print "$_.value $util\n"; + } +} + +sub get_pools { + + my $no_cache = shift; # Ensure that caching doesn't interfere with reconfiguration + + # Read results from the cache unless stale or told not to + if (!$no_cache && -e STATEFILE && -M STATEFILE < 60/86400) { + return lock_retrieve(STATEFILE); + } + + my $driver = $ENV{'fr_driver'} || 'mysql'; + my $host = $ENV{'fr_host'} || '127.0.0.1'; + my $port = $ENV{'fr_port'} || '3306'; + my $db = $ENV{'fr_db'} || 'radius'; + my $user = $ENV{'fr_user'} || 'radius'; + my $pass = $ENV{'fr_pass'} || 'radpass'; + my $passfile = $ENV{'fr_passfile'}; + + # Read password from a file + if (!defined $pass && defined $passfile) { + open (my $FH, '<', $passfile) || die "Failed to open fr_passfile: $passfile"; + $pass = <$FH>; + chomp $pass; + close $FH; + } + + my $dsn; + if ($driver eq 'Oracle') { + $dsn = "DBI:$driver:$db"; + } else { + $dsn = "DBI:$driver:database=$db;host=$host"; + } + $dsn .= ";port=$port" if $port; + + # Read the results by running our query against the database + my $dbh = DBI->connect($dsn, $user, $pass, { RaiseError => 1, PrintError => 0, AutoCommit => 1 }); + my $sth = $dbh->prepare(SQL); + $sth->execute(); + my $results=$sth->fetchall_hashref('pool_name'); + $sth->finish(); + $dbh->disconnect(); + + # Cache the results + if (!$no_cache && $results) { + lock_store($results,STATEFILE); + } + + return $results; + +} + +# vim:syntax=perl From 561da1a3f10ab676a029116509cfe0b2ece59088 Mon Sep 17 00:00:00 2001 From: corbolais Date: Fri, 28 Feb 2020 18:02:03 +0100 Subject: [PATCH 569/695] typo: retransmited -> retransmitted Signed-off-by: corbolais --- plugins/network/netstat_s_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/netstat_s_ b/plugins/network/netstat_s_ index 39bbbba4..cd16e627 100755 --- a/plugins/network/netstat_s_ +++ b/plugins/network/netstat_s_ @@ -166,7 +166,7 @@ def graphs_for(protocol) # It could be reasonable to add more elements as warning and critical values. [ /(\d+) segments send out$/, [ [ :segments, 'total' ] ] ], - [ /(\d+) segments retransmited$/, [ [ :segments, 'retransmitted' ] ] ] + [ /(\d+) segments retransmitted$/, [ [ :segments, 'retransmitted' ] ] ] ]), Graph.new('received', protocol, [ From 8713eb372211d2ed79a7c645671db1f54261a629 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:12:57 +0100 Subject: [PATCH 570/695] Fix spelling mistakes in comments and descriptions Thanks, codespell! --- plugins/apt/deb_packages/deb_packages.py | 6 ++-- .../assp/assp-envelope-recipient-statistics | 2 +- plugins/assp/assp-general-runtime-information | 2 +- plugins/assp/assp-message-statistics | 2 +- plugins/assp/assp-smtp-connection-statistics | 2 +- plugins/assp/assp-smtp-handler-statistics | 2 +- plugins/audit/portaudit | 2 +- plugins/backup/fresh-backups | 2 +- plugins/bacula/bacula_sd | 2 +- plugins/battery/acpi_batt_ | 2 +- plugins/battery/acpi_sys_batt_ | 2 +- plugins/battery/batteries | 28 +++++++++---------- plugins/bind/bind95_ | 2 +- plugins/bind/bind_ | 2 +- plugins/chrony/chrony | 2 +- .../currency/zcash/zcash_flypool_hashrate_ | 2 +- plugins/disk/du | 2 +- plugins/disk/du-2 | 4 +-- plugins/disk/hp_temp | 2 +- plugins/disk/hpasmcli2_ | 2 +- plugins/disk/quota2percent_ | 10 +++---- plugins/dovecot/dovecot1 | 4 +-- plugins/dspam/dspam_ | 2 +- plugins/emc/emc_vnx_block_lun_perfdata | 2 +- plugins/emc/emc_vnx_file_ | 2 +- plugins/ftp/proftpd | 6 ++-- plugins/ftp/pure-ftpd-bw | 4 +-- plugins/google/googlecode | 2 +- plugins/gpu/nvidia_smi_ | 2 +- plugins/groupwise/snmp__gwia_bytes_ | 2 +- plugins/groupwise/snmp__gwia_msgs_ | 2 +- plugins/groupwise/snmp__gwmta_msgs_ | 2 +- plugins/groupwise/snmp__gwpoa_ | 2 +- plugins/hdfs/hadoop-dfs-plugin | 2 +- plugins/horde/horde | 2 +- plugins/http/wget_page | 2 +- plugins/icecast/icecast_ | 2 +- plugins/ilias/ilias_session | 2 +- plugins/jvm/jvm_sun_memory | 2 +- plugins/libvirt/kvm_cpu | 4 +-- plugins/libvirt/kvm_io | 4 +-- plugins/libvirt/kvm_mem | 4 +-- plugins/libvirt/kvm_net | 4 +-- plugins/libvirt/libvirt | 4 +-- plugins/lighttpd/lighttpd_ | 2 +- plugins/load/load_extended | 4 +-- plugins/mailman/mailman_subscribers | 2 +- plugins/mediawiki/mediawiki | 6 ++-- plugins/memcached/memcached_multi_ | 4 +-- plugins/memory/multimemory | 2 +- plugins/minecraft/minecraft-users-ram_ | 2 +- plugins/mongodb/mongo_lag | 2 +- plugins/mongodb/mongo_mem | 2 +- plugins/mongodb/mongo_ops | 2 +- plugins/mongodb/mongodb_conn | 2 +- plugins/mongodb/mongodb_docs | 2 +- plugins/munin/healthcheck_process | 4 +-- plugins/mysql/mysql_connections | 2 +- plugins/mysql/mysql_qcache | 2 +- plugins/mysql/mysql_qcache_mem | 4 +-- plugins/mysql/mysql_size_ | 4 +-- plugins/mysql/mysql_size_all | 4 +-- plugins/nagios/nagios_multi_ | 2 +- plugins/netapp/snmp__netapp_cifs2 | 2 +- plugins/netapp/snmp__netapp_cifscalls | 2 +- plugins/netapp/snmp__netapp_cpu2 | 2 +- plugins/netapp/snmp__netapp_diskusage2_ | 2 +- plugins/netapp/snmp__netapp_diskutil | 2 +- plugins/netapp/snmp__netapp_ndmp | 2 +- plugins/netapp/snmp__netapp_net | 2 +- plugins/netapp/snmp__netapp_nfs3calls | 2 +- plugins/netapp/snmp__netapp_ops | 2 +- plugins/netapp/snmp__netapp_sis | 2 +- plugins/network/ddclient | 6 ++-- plugins/network/hostsdeny | 2 +- plugins/network/if | 8 +++--- plugins/network/if1sec-c.c | 2 +- plugins/network/linux_if/linux_if | 6 ++-- plugins/network/multibandwidth | 4 +-- plugins/network/netatalk3 | 4 +-- plugins/network/nsd3 | 2 +- plugins/network/vnstat_ | 2 +- plugins/nfs/nfsv4 | 2 +- plugins/nginx/nginx_vhost_traffic | 2 +- plugins/ntp/ntp_packets | 2 +- plugins/openvz/openvzcpu | 2 +- plugins/openvz/vpsmem | 2 +- plugins/oracle/oracle__database_hitratio | 2 +- plugins/oracle/oracle_sysstat | 6 ++-- plugins/other/hookbox | 2 +- plugins/other/listeners | 4 +-- plugins/other/s9y | 2 +- plugins/pf/pf_bytes | 2 +- plugins/pf/pf_openbsd | 2 +- plugins/pf/pf_packets | 2 +- plugins/pf/pf_states | 2 +- plugins/php/eaccelerator | 6 ++-- plugins/php/php-cgi | 2 +- plugins/php/php_time_execution | 2 +- plugins/poseidon/snmp__poseidon-sensors | 2 +- plugins/postfix/postfix_mailstats_ | 2 +- plugins/postfix/postfwd2 | 14 +++++----- plugins/postfix/postgrey-new | 2 +- plugins/postgresql/pg__connections | 2 +- plugins/power/currentcost | 4 +-- plugins/power/nutups2_ | 2 +- plugins/power/upsmonpro_ | 2 +- plugins/prosody/prosody_ | 2 +- plugins/qmail/spamdyke | 2 +- plugins/radiator/radiator_acct_lag | 2 +- plugins/radiator/radiator_auth_lag | 2 +- plugins/radiator/radiator_auth_ppm | 2 +- plugins/rethinkdb/rethinkdb_node_io | 4 +-- plugins/router/ag241-adsl | 6 ++-- .../d-link-dir-655-router-statistics-plugin | 2 +- plugins/router/freeboxuptime | 2 +- plugins/router/tg585v7__ | 2 +- plugins/samba/samba_locked | 4 +-- plugins/samba/samba_users | 4 +-- .../san/emc_comprehensive/emc_comprehensive | 2 +- plugins/sensors/sequoia_websens | 2 +- plugins/sensors/voltcraft_tcm220_ | 4 +-- plugins/shoutcast/shoutcast2_multi | 2 +- plugins/smf/smf_errors | 2 +- plugins/snmp/snmp__airport | 2 +- plugins/snmp/snmp__cyberpower | 2 +- plugins/snmp/snmp__fn | 4 +-- plugins/snmp/snmp__if_combined | 2 +- plugins/solaris/fsstat_act_ | 4 +-- plugins/solaris/fsstat_bytes | 6 ++-- plugins/solaris/io_disk | 2 +- plugins/squid/squid | 4 +-- plugins/ssh/sshd_invalid_countries | 2 +- plugins/ssh/sshd_invalid_countries_ruby | 2 +- plugins/ssl/certificate_file_expiry | 2 +- plugins/synology/snmp__synology_temperature | 2 +- plugins/synology/snmp__synology_ups | 4 +-- plugins/system/top-memory | 2 +- plugins/thecus/snmp__thecus_fans | 4 +-- plugins/tor/tor-bandwidth-usage | 2 +- plugins/tor/tor_ | 2 +- plugins/ubiquiti/unifi_api | 24 ++++++++-------- plugins/vdr/vdr_ | 2 +- plugins/vmware/esx_ | 2 +- plugins/vmware/esxcli_env_ | 2 +- plugins/vmware/esxi | 2 +- plugins/voip/murmur_users | 2 +- .../voip/murmurice_host_port_id_description | 6 ++-- plugins/weather/buienradar_ | 2 +- plugins/wifi/ath9k_ | 2 +- plugins/wifi/wifi_signal | 2 +- plugins/xen/xen-cpu | 2 +- plugins/xen/xen-multi | 2 +- plugins/xen/xen_cpu_v2 | 2 +- .../zfs/zfs-stats-for-freebsd-arc-efficiency | 2 +- .../zfs/zfs-stats-for-freebsd-arc-utilization | 2 +- ...stats-for-freebsd-cache-hits-by-cache-list | 2 +- ...-stats-for-freebsd-cache-hits-by-data-type | 2 +- .../zfs/zfs-stats-for-freebsd-dmu-prefetch | 2 +- plugins/zfs/zfs_usage_ | 6 ++-- plugins/zfs/zpool_capacity | 2 +- plugins/zope/zope_cache_parameters | 2 +- 162 files changed, 248 insertions(+), 248 deletions(-) diff --git a/plugins/apt/deb_packages/deb_packages.py b/plugins/apt/deb_packages/deb_packages.py index 931d1d8f..f2c21eb6 100755 --- a/plugins/apt/deb_packages/deb_packages.py +++ b/plugins/apt/deb_packages/deb_packages.py @@ -84,7 +84,7 @@ def Property(func): class Apt(object): """ - lazy helperclass i need in this statisticprogram, which have alle the apt_pkg stuff + lazy helperclass i need in this statisticprogram, which has all the apt_pkg stuff """ def __init__(self): @@ -575,7 +575,7 @@ class PacketHandler(object): def adder(self, package, packageStat, *args, **kwords): """ - take the package and add it tho the packageStat dictionary in defined way + take the package and add it to the packageStat dictionary in defined way """ pass @@ -868,7 +868,7 @@ deb_packages - plugin to monitor update resources and pending packages on Debian This plugin has checked on Debian - Wheezy and squeeze. If you want to use it on older installations, tell me whether it works or which errors you had. It -shoud run past python-apt 0.7 and python 2.5. +should run past python-apt 0.7 and python 2.5. =head1 DESCRIPTION diff --git a/plugins/assp/assp-envelope-recipient-statistics b/plugins/assp/assp-envelope-recipient-statistics index d5062bb2..d09d90e6 100755 --- a/plugins/assp/assp-envelope-recipient-statistics +++ b/plugins/assp/assp-envelope-recipient-statistics @@ -30,7 +30,7 @@ my $version = "1.0"; # UA Version my $agentname = "$pluginname Munin Plugin V$version"; # UA String my $url = "http://localhost:55553/"; # (defaults to localhost) my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my @content = (); # the content we retrieve from $response my %index = ( # for Version 2 'from' => 40, # <-- index frame from ( a tweak for other ASSP Versions ) 'to' => 63 # <-- index frame to ( "" ) diff --git a/plugins/assp/assp-general-runtime-information b/plugins/assp/assp-general-runtime-information index a8535373..889f86c8 100755 --- a/plugins/assp/assp-general-runtime-information +++ b/plugins/assp/assp-general-runtime-information @@ -30,7 +30,7 @@ my $version = "1.0"; # UA Version my $agentname = "$pluginname Munin Plugin V$version"; # UA String my $url = "http://localhost:55553/"; # (defaults to localhost) my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my @content = (); # the content we retrieve from $response my %index = ( # for Version 2 'from' => 2, # <-- index frame from ( a tweak for other ASSP Versions ) 'to' => 4 # <-- index frame to ( "" ) diff --git a/plugins/assp/assp-message-statistics b/plugins/assp/assp-message-statistics index 92b1c667..dd13b6c7 100755 --- a/plugins/assp/assp-message-statistics +++ b/plugins/assp/assp-message-statistics @@ -30,7 +30,7 @@ my $version = "1.0"; # UA Version my $agentname = "$pluginname Munin Plugin V$version"; # UA String my $url = "http://localhost:55553/"; # (defaults to localhost) my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my @content = (); # the content we retrieve from $response my %index = ( # for Version 2 'from' => 66, # <-- index frame from ( a tweak for other ASSP Versions ) 'to' => 100 # <-- index frame to ( "" ) diff --git a/plugins/assp/assp-smtp-connection-statistics b/plugins/assp/assp-smtp-connection-statistics index a41f9f21..266ceaa0 100755 --- a/plugins/assp/assp-smtp-connection-statistics +++ b/plugins/assp/assp-smtp-connection-statistics @@ -30,7 +30,7 @@ my $version = "1.0"; # UA Version my $agentname = "$pluginname Munin Plugin V$version"; # UA String my $url = "http://localhost:55553/"; # (defaults to localhost) my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my @content = (); # the content we retrieve from $response my %index = ( # for Version 2 'from' => 25, # <-- index frame from ( a tweak for other ASSP Versions ) 'to' => 38 # <-- index frame to ( "" ) diff --git a/plugins/assp/assp-smtp-handler-statistics b/plugins/assp/assp-smtp-handler-statistics index 3041b481..981fec05 100755 --- a/plugins/assp/assp-smtp-handler-statistics +++ b/plugins/assp/assp-smtp-handler-statistics @@ -30,7 +30,7 @@ my $version = "1.0"; # UA Version my $agentname = "$pluginname Munin Plugin V$version"; # UA String my $url = "http://localhost:55553/"; # (defaults to localhost) my $response = 0; # the server output -my @content = (); # the content we're retrive from $response +my @content = (); # the content we retrieve from $response my %index = ( # for Version 2 'from' => 4, # <-- index frame from ( a tweak for other ASSP Versions ) 'to' => 5 # <-- index frame to ( "" ) diff --git a/plugins/audit/portaudit b/plugins/audit/portaudit index f7cced66..e4b63ceb 100755 --- a/plugins/audit/portaudit +++ b/plugins/audit/portaudit @@ -7,7 +7,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index d952c515..3d2d7a42 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -27,7 +27,7 @@ daily archives. 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. +limits, the relevant notifications will be triggered. An example configuration snippet for backup-manager [0] follows. diff --git a/plugins/bacula/bacula_sd b/plugins/bacula/bacula_sd index 545db656..7d1fc023 100755 --- a/plugins/bacula/bacula_sd +++ b/plugins/bacula/bacula_sd @@ -110,7 +110,7 @@ def print_config(): print("graph_args --base 1024 -l 0") print("graph_scale yes") print("graph_info Bacula Storage Daemon througput measurement based on written bytes. " - "This may be somewhat inacurate whenever a tape is changed.") + "This may be somewhat inaccurate whenever a tape is changed.") print("graph_category backup") print("graph_order", " ".join([dev[2] for dev in devstats])) print() diff --git a/plugins/battery/acpi_batt_ b/plugins/battery/acpi_batt_ index 3e625809..dfb3adb1 100755 --- a/plugins/battery/acpi_batt_ +++ b/plugins/battery/acpi_batt_ @@ -7,7 +7,7 @@ acpi_batt_ Munin plugin to monitor the (note|net)book battery states through pro =head1 APPLICABLE SYSTEMS -Notebooks and netbooks with avialable /proc/acpi/battery +Notebooks and netbooks with available /proc/acpi/battery =head1 CONFIGURATION diff --git a/plugins/battery/acpi_sys_batt_ b/plugins/battery/acpi_sys_batt_ index 40c8d127..9f91f0ca 100755 --- a/plugins/battery/acpi_sys_batt_ +++ b/plugins/battery/acpi_sys_batt_ @@ -7,7 +7,7 @@ acpi_sys_batt_ Munin plugin to monitor the (note|net)book battery states through =head1 APPLICABLE SYSTEMS -Notebooks and netbooks with avialable /sys/class/power_supply +Notebooks and netbooks with available /sys/class/power_supply =head1 CONFIGURATION diff --git a/plugins/battery/batteries b/plugins/battery/batteries index 05aa480c..c8352fe5 100755 --- a/plugins/battery/batteries +++ b/plugins/battery/batteries @@ -7,7 +7,7 @@ batteries Munin plugin to monitor the battery states through procfs and sysfs =head1 APPLICABLE SYSTEMS -Systems with avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx +Systems with available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx =head1 CONFIGURATION @@ -16,19 +16,19 @@ none =head1 INTERPRETATION The plugin shows: - Design capacity -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Last full capacity -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Design capacity low -> avialable only if avialable /proc/acpi/battery/BATx - Design capacity warning -> avialable only if avialable /proc/acpi/battery/BATx - Capacity granularity 1 -> avialable only if avialable /proc/acpi/battery/BATx - Capacity granularity 2 -> avialable only if avialable /proc/acpi/battery/BATx - Remaining capacity -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Present rate -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Percentage Current/design voltage -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Percentage Current/full capacity -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Percentage Full/design capacity -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Design voltage -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx - Present voltage -> avialable if avialable /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Design capacity -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Last full capacity -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Design capacity low -> available only if available /proc/acpi/battery/BATx + Design capacity warning -> available only if available /proc/acpi/battery/BATx + Capacity granularity 1 -> available only if available /proc/acpi/battery/BATx + Capacity granularity 2 -> available only if available /proc/acpi/battery/BATx + Remaining capacity -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Present rate -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Percentage Current/design voltage -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Percentage Current/full capacity -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Percentage Full/design capacity -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Design voltage -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx + Present voltage -> available if available /proc/acpi/battery/BATx or /sys/class/power_supply/BATx =head1 MAGIC MARKERS diff --git a/plugins/bind/bind95_ b/plugins/bind/bind95_ index edad0815..1d589df0 100755 --- a/plugins/bind/bind95_ +++ b/plugins/bind/bind95_ @@ -1,7 +1,7 @@ #!/usr/bin/perl # # Copyright Jean-Samuel Reynaud -# Licenced under GPL v2 +# Licensed under GPL v2 # # We use this script to produce graph with munin for dns requests # This script must have his name start with bind95_ diff --git a/plugins/bind/bind_ b/plugins/bind/bind_ index 6f7f5098..bf2b8fe4 100755 --- a/plugins/bind/bind_ +++ b/plugins/bind/bind_ @@ -1,7 +1,7 @@ #!/usr/bin/perl # # Copyright Jean-Samuel Reynaud -# Licenced under GPL v2 +# Licensed under GPL v2 # # We use this script to produce graph with munin for dns requests # This script must have his name start with bind_ diff --git a/plugins/chrony/chrony b/plugins/chrony/chrony index 314860f6..0554e91f 100755 --- a/plugins/chrony/chrony +++ b/plugins/chrony/chrony @@ -34,7 +34,7 @@ Revision 0.3 2014/02/16 zjttoefs reduce forking by using awk do not limit output precision add stratum monitoring - detect slow/fast time or freqency and adjust sign of value accordingly + detect slow/fast time or frequency and adjust sign of value accordingly remove commented out code Revision 0.4 2016/11/10 Lars Kruse diff --git a/plugins/currency/zcash/zcash_flypool_hashrate_ b/plugins/currency/zcash/zcash_flypool_hashrate_ index a93c0acc..e13a95a2 100755 --- a/plugins/currency/zcash/zcash_flypool_hashrate_ +++ b/plugins/currency/zcash/zcash_flypool_hashrate_ @@ -87,7 +87,7 @@ hash_rate = "U" try: mining_stats = json.loads(mining_stats_raw.read().decode("utf-8")) except ValueError: - print("Failed to parse JSON responce.", file=sys.stderr) + print("Failed to parse JSON response.", file=sys.stderr) else: try: worker = mining_stats['data'] diff --git a/plugins/disk/du b/plugins/disk/du index 0d886146..547a2d68 100755 --- a/plugins/disk/du +++ b/plugins/disk/du @@ -47,7 +47,7 @@ ID=1; # - initial release, # ################################################################# -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index b38f5550..e2689a5b 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -121,7 +121,7 @@ daemonize(); # ## -### PUBLIC FONCTIONS +### PUBLIC FUNCTIONS ############################################################################### ## Used to create the fork sub daemonize { @@ -185,7 +185,7 @@ sub munin_exit_fail { # ## -### INTERNALS FONCTIONS +### INTERNALS FUNCTIONS ############################################################################### sub __munin_exit { my $exitcode = shift; diff --git a/plugins/disk/hp_temp b/plugins/disk/hp_temp index 373231b9..baa850a4 100755 --- a/plugins/disk/hp_temp +++ b/plugins/disk/hp_temp @@ -26,7 +26,7 @@ if ($mode eq 'autoconf') if ($mode eq 'config') { - # headers for the temperatur + # headers for the temperature print "graph_title HP Temperature sensors in Celsius\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel degrees Celsius\n"; diff --git a/plugins/disk/hpasmcli2_ b/plugins/disk/hpasmcli2_ index f0d6bb0d..f27c48e9 100755 --- a/plugins/disk/hpasmcli2_ +++ b/plugins/disk/hpasmcli2_ @@ -3,7 +3,7 @@ # Plugin to monitor Proliant server health status using hpasmcli. # # Config variables: -# user root -- requrired by hpasmcli +# user root -- required by hpasmcli # env.hpasmcli -- path to hpasmcli executable (optional) # env.degree -- Unit of temperatures (C or F / default value is C) # diff --git a/plugins/disk/quota2percent_ b/plugins/disk/quota2percent_ index 91e16315..b8784d50 100755 --- a/plugins/disk/quota2percent_ +++ b/plugins/disk/quota2percent_ @@ -111,7 +111,7 @@ GPLv2 (L) # Preparation section # ################################################### -# Load munin's shell libary +# Load munin's shell library . "$MUNIN_LIBDIR/plugins/plugin.sh" # if any fetch from munin-node file @@ -172,7 +172,7 @@ GPLv2 (L) de) echo "graph_title Quota-Hard-Limit von $Id" echo "graph_vlabel Nutzung in % Hardlimit" - echo "graph_info Die Grafik zeigt die Belegung des durch Quota reglementierten Speicherplatzes für alle regulären Nutzer (UID >= $Min_UID) in Prozent des Hardlimits." + echo "graph_info Die Grafik zeigt die Belegung des durch Quota reglementierten Speicherplatzes für all regulären Nutzer (UID >= $Min_UID) in Prozent des Hardlimits." Total_txt="Su. aller Nutzer" Total_info="Inklusive Systemnutzer (UID < $Min_UID)" ;; @@ -208,10 +208,10 @@ GPLv2 (L) # Determine the currently processing UID Cur_UID="$(id -u "$User")" - # skip if actual user a system user und low_uid ist set to never + # skip if actual user a system user und low_uid is set to never [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue - # No graph for none human uid if low_uid ist set to no + # No graph for none human uid if low_uid is set to no [ "$Cur_UID" -lt "$Min_UID" ] && echo "$Fieldname.graph $Low_UID" # configure the user lines @@ -238,7 +238,7 @@ GPLv2 (L) Quota=( ${Quotas[$i]} ) Fieldname="$(clean_fieldname "${Quota[0]}")" - # skip if actual user a system user und low_uid ist set to never + # skip if actual user a system user und low_uid is set to never [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue # write the result zu munin diff --git a/plugins/dovecot/dovecot1 b/plugins/dovecot/dovecot1 index 62dee459..83f4d897 100755 --- a/plugins/dovecot/dovecot1 +++ b/plugins/dovecot/dovecot1 @@ -29,7 +29,7 @@ if ( $logfile =~ /dovecot/ ) { $dovelogfile = 1 ; } -# Use an overrided $PATH for all external programs if needed +# Use an overridden $PATH for all external programs if needed $DOVEADM = "doveadm"; if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { @@ -152,7 +152,7 @@ if (! -f $logfile and ! -f $rotlogfile) { exit 0; } -# dit kan beter maar twee calls zijn toch nodig als we niet zelf aggegreren +# dit kan beter maar twee calls zijn toch nodig also we niet zelf aggegreren # suggestie: doveadm who -1 | awk '{print $1" "$2" "$4}' | sort | uniq -c $connectedimap = `$DOVEADM -f flow who | grep imap | wc -l`; $connectedpop3 = `$DOVEADM -f flow who | grep pop3 | wc -l`; diff --git a/plugins/dspam/dspam_ b/plugins/dspam/dspam_ index b301373e..5f7de09a 100755 --- a/plugins/dspam/dspam_ +++ b/plugins/dspam/dspam_ @@ -89,7 +89,7 @@ The plugin supports the following graph types: display the increase of processed messages over time. relprocessed - Shows the same data as dspam_absprocessed_, but as - messages per minute instead of ever-growing asolute + messages per minute instead of ever-growing absolute values. processed - Shows the same data as dspam_absprocessed_, but as diff --git a/plugins/emc/emc_vnx_block_lun_perfdata b/plugins/emc/emc_vnx_block_lun_perfdata index 5520c6e3..a37f2ea1 100755 --- a/plugins/emc/emc_vnx_block_lun_perfdata +++ b/plugins/emc/emc_vnx_block_lun_perfdata @@ -182,7 +182,7 @@ check_conf_and_set_vars () { echo "No control station addresses ('cs_addr' environment variable)!" return 1 fi - #Choosing Cotrol Station. Code have to be "10" + #Choosing Control Station. Code have to be "10" for CS in $cs_addr; do if [[ "10" = "$(ssh_check_cmd "$CS")" ]]; then PRIMARY_CS=$CS diff --git a/plugins/emc/emc_vnx_file_ b/plugins/emc/emc_vnx_file_ index 32a1c26a..c18b5b2b 100755 --- a/plugins/emc/emc_vnx_file_ +++ b/plugins/emc/emc_vnx_file_ @@ -160,7 +160,7 @@ check_conf () { return 1 fi - #Choosing Cotrol Station. Code have to be "10" + #Choosing Control Station. Code have to be "10" for CS in $cs_addr; do if [[ "10" = "$(ssh_check_cmd "$CS")" ]]; then PRIMARY_CS=$CS diff --git a/plugins/ftp/proftpd b/plugins/ftp/proftpd index f02c49fd..dbfcbf53 100755 --- a/plugins/ftp/proftpd +++ b/plugins/ftp/proftpd @@ -8,7 +8,7 @@ if [ "$1" = 'config' ]; then echo "graph_title Serveur FTP" echo "graph_category network" echo "graph_vlabel Stats Proftpd" - echo "succes.label Login succes" + echo "succes.label Login success" echo "succes.draw AREA" echo "failed.label Login failed" echo "failed.draw AREA" @@ -16,10 +16,10 @@ fi LOGFILE=${LOGFILE:-"/var/log/proftpd/proftpd.log"} -succes=$(grep -c "successful" "$LOGFILE" ) +success=$(grep -c "successful" "$LOGFILE") failed=$(grep -c "Login failed" "$LOGFILE" ) -echo "succes.value $succes" +echo "succes.value $success" echo "failed.value $failed" exit 0 diff --git a/plugins/ftp/pure-ftpd-bw b/plugins/ftp/pure-ftpd-bw index c0c18110..6d707894 100755 --- a/plugins/ftp/pure-ftpd-bw +++ b/plugins/ftp/pure-ftpd-bw @@ -53,7 +53,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Pure Ftpd Bandwidth' echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel Datas sent / received' + echo 'graph_vlabel Data sent / received' echo 'graph_category network' echo 'dl.label Bytes downloaded' echo 'ul.label Bytes uploaded' @@ -73,7 +73,7 @@ if [ -f $TMP1 ]; then echo "ul.value ${ul}" rm $TMP1 else - echo "cant write temp file" + echo "can't write temp file" exit 1 fi diff --git a/plugins/google/googlecode b/plugins/google/googlecode index 7a419572..50687f90 100755 --- a/plugins/google/googlecode +++ b/plugins/google/googlecode @@ -71,7 +71,7 @@ if [ "$1" = "config" ]; then exit 0 fi -# Pring number of downloads. +# Print number of downloads. j=0 for (( i = 1 ; i < $Nfiles ; i=i+5 )) diff --git a/plugins/gpu/nvidia_smi_ b/plugins/gpu/nvidia_smi_ index 052dda07..6f6c910d 100755 --- a/plugins/gpu/nvidia_smi_ +++ b/plugins/gpu/nvidia_smi_ @@ -17,7 +17,7 @@ # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # diff --git a/plugins/groupwise/snmp__gwia_bytes_ b/plugins/groupwise/snmp__gwia_bytes_ index 1f750992..07f74593 100755 --- a/plugins/groupwise/snmp__gwia_bytes_ +++ b/plugins/groupwise/snmp__gwia_bytes_ @@ -55,7 +55,7 @@ # ...will monitor a single GWIA object on host foo.example.com. # # Parameters -# community - Specify wich community string to use (Default: public) +# community - Specify which community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) # pos - Specify which table Object to read (Default: Read from link in servicedir, diff --git a/plugins/groupwise/snmp__gwia_msgs_ b/plugins/groupwise/snmp__gwia_msgs_ index 14a8c4fd..f1c1a9fe 100755 --- a/plugins/groupwise/snmp__gwia_msgs_ +++ b/plugins/groupwise/snmp__gwia_msgs_ @@ -59,7 +59,7 @@ # ...will monitor a single GWIA object on host foo.example.com. # # Parameters -# community - Specify wich community string to use (Default: public) +# community - Specify which community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) # pos - Specify which table Object to read (Default: Read from link in servicedir, diff --git a/plugins/groupwise/snmp__gwmta_msgs_ b/plugins/groupwise/snmp__gwmta_msgs_ index 184af277..5f74ae52 100755 --- a/plugins/groupwise/snmp__gwmta_msgs_ +++ b/plugins/groupwise/snmp__gwmta_msgs_ @@ -56,7 +56,7 @@ # ...will monitor a single GWMTA object on host foo.example.com. # # Parameters -# community - Specify wich community string to use (Default: public) +# community - Specify which community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) # pos - Specify which table Object to read (Default: Read from link in servicedir, diff --git a/plugins/groupwise/snmp__gwpoa_ b/plugins/groupwise/snmp__gwpoa_ index e17ca0c4..304cc272 100755 --- a/plugins/groupwise/snmp__gwpoa_ +++ b/plugins/groupwise/snmp__gwpoa_ @@ -63,7 +63,7 @@ # Configuration: # -------------- # Parameters -# community - Specify wich community string to use (Default: public) +# community - Specify which community string to use (Default: public) # port - Specify which port to read from (Default: 161) # host - Specify which host to monitor (Default: Read from link in servicedir) # pos - Specify which table Object to read (Default: Read from link in servicedir, diff --git a/plugins/hdfs/hadoop-dfs-plugin b/plugins/hdfs/hadoop-dfs-plugin index 8cb4c7b7..f1ef35fd 100755 --- a/plugins/hdfs/hadoop-dfs-plugin +++ b/plugins/hdfs/hadoop-dfs-plugin @@ -7,7 +7,7 @@ # # Installation notes: # Symlink this file to hadoop_hdfs_block or hadoop_hdfs_capacity to get block -# or capacity informations about the DFS. +# or capacity information about the DFS. # # # Needs following minimal configuration in plugin-conf.d/munin-node: diff --git a/plugins/horde/horde b/plugins/horde/horde index 662ab046..07347612 100755 --- a/plugins/horde/horde +++ b/plugins/horde/horde @@ -15,7 +15,7 @@ # user root # # -# formely known as postfix_mail_stats by: +# formerly known as postfix_mail_stats by: # Boudewijn Ector, for Boudewijn Ector IT. # Loosely based on http://munin.projects.linpro.no/attachment/wiki/PluginCat/postfix_messages_hourly.txt # diff --git a/plugins/http/wget_page b/plugins/http/wget_page index c137ff96..b9ddb268 100755 --- a/plugins/http/wget_page +++ b/plugins/http/wget_page @@ -190,7 +190,7 @@ for name in ${names}; do [ -z "${iregex_header}" -a -z "${iregex_body}" ] && break if [ ${K} -eq 1 ]; then OIFS="${IFS}" - # we skip carrige return characters from the end of header lines + # we skip carriage return characters from the end of header lines IFS=$(echo -en "\r") inheader=0 # The "read" command reads only lines terminated by a specific diff --git a/plugins/icecast/icecast_ b/plugins/icecast/icecast_ index 6ec74fb1..0aca7253 100755 --- a/plugins/icecast/icecast_ +++ b/plugins/icecast/icecast_ @@ -51,7 +51,7 @@ pw = "yourpassword" exclude = () # Exclude these sources from calculation. This is useful to excluse special sources like -# fallback sources which doesn't expose the same informations and then break this script +# fallback sources which doesn't expose the same information and then break this script # Ever add fallback sources to this list #source_exclude = ["/fallback.mp3", "/fallback.ogg"] source_exclude = ("/fallback.mp3", "/fallback.ogg") diff --git a/plugins/ilias/ilias_session b/plugins/ilias/ilias_session index ad227ebf..c04bccc5 100755 --- a/plugins/ilias/ilias_session +++ b/plugins/ilias/ilias_session @@ -85,7 +85,7 @@ global_attr=" graph_title ILIAS session and logins graph_category cms graph_args --lower-limit 0 - graph_vlabel occurences + graph_vlabel occurrences graph_info Number of active ILIAS user sessions and logins " diff --git a/plugins/jvm/jvm_sun_memory b/plugins/jvm/jvm_sun_memory index a170561c..290c03a0 100755 --- a/plugins/jvm/jvm_sun_memory +++ b/plugins/jvm/jvm_sun_memory @@ -44,7 +44,7 @@ my $grtitle = $ENV{graphtitle} || "Sun Java"; # Title that appears on munin graph my $title = "$grtitle memory usage"; # Extended information that appears below munin graph -my $info = "Write som info about this graph..."; +my $info = "Write some info about this graph..."; sub analyze_record { # Match all interesting elements of a record and insert them diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index ff696fc7..f43f2902 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -63,7 +63,7 @@ def detect_kvm(): def find_vm_names(pids): '''Find and clean vm names from pids - @return a dictionnary of {pids : cleaned vm name} + @return a dictionary of {pids : cleaned vm name} ''' result = {} for pid in pids: @@ -80,7 +80,7 @@ def list_pids(): def fetch(vms): ''' Fetch values for a list of pids - @param dictionnary {kvm_pid: cleaned vm name} + @param dictionary {kvm_pid: cleaned vm name} ''' for ( pid, name ) in vms.iteritems(): ( user, system ) = open("/proc/%s/stat" % pid, 'r').readline().split(' ')[13:15] diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 0e0cc386..d1aff23d 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -56,7 +56,7 @@ def clean_vm_name(vm_name): def fetch(vms): ''' Fetch values for a list of pids - @param dictionnary {kvm_pid: cleaned vm name} + @param dictionary {kvm_pid: cleaned vm name} ''' res = {} for pid in vms: @@ -80,7 +80,7 @@ def detect_kvm(): def find_vm_names(pids): '''Find and clean vm names from pids - @return a dictionnary of {pids : cleaned vm name} + @return a dictionary of {pids : cleaned vm name} ''' result = {} for pid in pids: diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index aeb1af7e..d316bae3 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -53,7 +53,7 @@ def clean_vm_name(vm_name): def fetch(vms): ''' Fetch values for a list of pids - @param dictionnary {kvm_pid: cleaned vm name} + @param dictionary {kvm_pid: cleaned vm name} ''' res = {} for pid in vms: @@ -77,7 +77,7 @@ def detect_kvm(): def find_vm_names(pids): '''Find and clean vm names from pids - @return a dictionnary of {pids : cleaned vm name} + @return a dictionary of {pids : cleaned vm name} ''' result = {} for pid in pids: diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 6a0a8024..e9b6a711 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -106,7 +106,7 @@ def clean_vm_name(vm_name): def fetch(vms): """ Fetch values for a list of pids - @param dictionnary {kvm_pid: cleaned vm name} + @param dictionary {kvm_pid: cleaned vm name} """ for pid, vm_data in vms.items(): vm_interface_names = get_vm_network_interface_names(pid) @@ -149,7 +149,7 @@ def detect_kvm(): def find_vm_names(pids): """Find and clean vm names from pids - @return a dictionnary of {pids : cleaned vm name} + @return a dictionary of {pids : cleaned vm name} """ result = {} for pid in pids: diff --git a/plugins/libvirt/libvirt b/plugins/libvirt/libvirt index b77365dc..753f50f7 100755 --- a/plugins/libvirt/libvirt +++ b/plugins/libvirt/libvirt @@ -729,7 +729,7 @@ sub init() { print $device . "_errs.label " . $device . "\n"; print $device . "_errs.type COUNTER\n"; - print $device . "_errs.info The number of errors by " . $hash{$vm}{'name'} . " on defice " . $device . "\n"; + print $device . "_errs.info The number of errors by " . $hash{$vm}{'name'} . " on device " . $device . "\n"; print $device . "_errs.min 0\n"; print $device . "_errs.draw LINE2\n"; } @@ -1217,7 +1217,7 @@ sub read_diskstats{ $res{'major'} = $rdev >> 8; $res{'minor'} = $rdev & 0xff; - # If major numer is 253, then proceed as dm-device + # If major number is 253, then proceed as dm-device if($res{'major'} == 253){ # check that the directory /sys/block/dm-$minor/ exists with a /slaves/ sub directory diff --git a/plugins/lighttpd/lighttpd_ b/plugins/lighttpd/lighttpd_ index 37efbc27..98863983 100755 --- a/plugins/lighttpd/lighttpd_ +++ b/plugins/lighttpd/lighttpd_ @@ -24,7 +24,7 @@ Configuration parameters: Note: If HTTP authentication is required you should specify both username and password. -=head1 INSTALLTION +=head1 INSTALLATION Copy file to directory /usr/share/munin/plugins/ Because this plugin has "suggest" capability the last step is to run diff --git a/plugins/load/load_extended b/plugins/load/load_extended index f0f7314b..96af601c 100755 --- a/plugins/load/load_extended +++ b/plugins/load/load_extended @@ -40,7 +40,7 @@ # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -133,5 +133,5 @@ cut -f3 -d' ' < /proc/loadavg # Except for the Nagios-warnings (which most people don't have any need # for) and things used by installation scripts and munin-config (which # you don't need if you don't plan on submitting your plugin to the -# pacakge), and the scaling (no milliload numbers) the two versions will +# package), and the scaling (no milliload numbers) the two versions will # work identically. diff --git a/plugins/mailman/mailman_subscribers b/plugins/mailman/mailman_subscribers index 3000d4e2..9cf7db41 100755 --- a/plugins/mailman/mailman_subscribers +++ b/plugins/mailman/mailman_subscribers @@ -30,7 +30,7 @@ # # to /etc/munin/plugin-conf.d/munin-node # -# Magic markers (optinal - used by munin-config and some installation +# Magic markers (optional - used by munin-config and some installation # scripts): # #%# family=manual diff --git a/plugins/mediawiki/mediawiki b/plugins/mediawiki/mediawiki index 09e3bbb3..d6107a42 100755 --- a/plugins/mediawiki/mediawiki +++ b/plugins/mediawiki/mediawiki @@ -4,7 +4,7 @@ # Reads number of edits,views,articles,pages,users,admins and images from a Mediawiki # http://www.mediawiki.org/wiki/MediaWiki | http://munin.projects.linpro.no/wiki # by mutante of S23 | http://s23.org/wiki | greets to hundfred -# 2007-02-12 | v0.1 first version, didnt really work yet +# 2007-02-12 | v0.1 first version, didn't really work yet # 2007-02-16 | v0.2 introduced different retrieval methods, separate graphs for the different values that can be symlinked.. # What you need to config: @@ -29,7 +29,7 @@ # require_once("/home/mutante/wiki_mysql_conf.php"); -# I include the database settings from elsewhere, so i dont have to show the password in /usr/share/.. +# I include the database settings from elsewhere, so i don't have to show the password in /usr/share/.. # I also set "[mediawiki] user mutante" in plugin-conf.d/ so that my user can read the config # alternatively set them in here like: @@ -41,7 +41,7 @@ ## B - URL reading # These methods all retrieve the Special:Statistics?action=raw URL from Mediawiki via the webserver -# This is the preferred method to get accurate stats currently, because Mediawiki doesnt use site_stats correctly atm +# This is the preferred method to get accurate stats currently, because Mediawiki does not use site_stats correctly atm # getmethod="curl" # uses curl via libcurl from PHP, should be fastest but you need the lib installed. if it works, use this. diff --git a/plugins/memcached/memcached_multi_ b/plugins/memcached/memcached_multi_ index 5683b399..36048acc 100755 --- a/plugins/memcached/memcached_multi_ +++ b/plugins/memcached/memcached_multi_ @@ -133,7 +133,7 @@ my $leitime = $ENV{leitime} || -1; # This gives us the ability to specify which commands we want to display on the # command graph. Allowing finer control since some environments don't leverage # every command possible in memcached. -# Options: get set delete incr decr cas touch flush +# Options: get set delete incr decr case touch flush my $commands = $ENV{cmds} || "get set delete incr decr touch"; # This hash contains the information contained in two memcache commands @@ -488,7 +488,7 @@ if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { which can be specified. Note we only specify the root graphs for the multigraphs, since the rest of the subgraphs will appear "behind" the root graphs. It also attempts to connect to the memcached service to - verify it is infact running. + verify it is in fact running. =cut diff --git a/plugins/memory/multimemory b/plugins/memory/multimemory index 1227e7fb..906b7b55 100755 --- a/plugins/memory/multimemory +++ b/plugins/memory/multimemory @@ -20,7 +20,7 @@ There is no default configuration. This is an example: env.os freebsd env.names apache2 mysqld php-cgi -Set env.os to freebsd if you are running this script on a machine which doesnt have +Set env.os to freebsd if you are running this script on a machine which does not have GNU sed installed (FreeBSD / OpenBSD / Solaris ...), else set it to linux. The names are used to grep with directly, after cleaning. So, this plugin diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ index 1de3aaa7..fda6e67c 100755 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -4,7 +4,7 @@ =head1 NAME -minecraft-users-ram_ - monitor ressource usage of a local minecraft server +minecraft-users-ram_ - monitor resource usage of a local minecraft server =head1 APPLICABLE SYSTEMS diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index 8a44541c..d07667eb 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -13,7 +13,7 @@ =head1 CONFIGURATION munin-node.conf - defauts for host is 127.0.0.1 and port 27017 + defaults for host is 127.0.0.1 and port 27017 and will work without being defined : [mongo_lag] diff --git a/plugins/mongodb/mongo_mem b/plugins/mongodb/mongo_mem index 6daeb7ad..672a89c2 100755 --- a/plugins/mongodb/mongo_mem +++ b/plugins/mongodb/mongo_mem @@ -10,7 +10,7 @@ =head1 CONFIGURATION munin-node.conf - defauts for host is 127.0.0.1 and port 27017 + defaults for host is 127.0.0.1 and port 27017 and will work without being defined : [mongo_mem] diff --git a/plugins/mongodb/mongo_ops b/plugins/mongodb/mongo_ops index 216fe270..1247fa60 100755 --- a/plugins/mongodb/mongo_ops +++ b/plugins/mongodb/mongo_ops @@ -10,7 +10,7 @@ =head1 CONFIGURATION munin-node.conf - defauts for host is 127.0.0.1 and port 27017 + defaults for host is 127.0.0.1 and port 27017 and will work without being defined : [mongodb_ops] diff --git a/plugins/mongodb/mongodb_conn b/plugins/mongodb/mongodb_conn index 831b7cda..f0643adf 100755 --- a/plugins/mongodb/mongodb_conn +++ b/plugins/mongodb/mongodb_conn @@ -10,7 +10,7 @@ =head1 CONFIGURATION munin-node.conf - defauts for host is 127.0.0.1 and port 27017 + defaults for host is 127.0.0.1 and port 27017 and will work without being defined : [mongodb_conn] diff --git a/plugins/mongodb/mongodb_docs b/plugins/mongodb/mongodb_docs index c1eb201a..43541055 100755 --- a/plugins/mongodb/mongodb_docs +++ b/plugins/mongodb/mongodb_docs @@ -10,7 +10,7 @@ =head1 CONFIGURATION munin-node.conf - defauts for host is 127.0.0.1 and port 27017 + defaults for host is 127.0.0.1 and port 27017 and will work without being defined : [mongodb_docs] diff --git a/plugins/munin/healthcheck_process b/plugins/munin/healthcheck_process index 5c3a160d..685938bc 100755 --- a/plugins/munin/healthcheck_process +++ b/plugins/munin/healthcheck_process @@ -15,14 +15,14 @@ #env.process_1 httpd #--------------------------------------------------- # -#chcek two process +#check two process #--------------------------------------------------- #[healthcheck_process] #env.process_1 httpd #env.process_2 samba #--------------------------------------------------- # -#chcek three process +#check three process #--------------------------------------------------- #[healthcheck_process] #env.process_1 httpd diff --git a/plugins/mysql/mysql_connections b/plugins/mysql/mysql_connections index f25f2b3e..38991504 100755 --- a/plugins/mysql/mysql_connections +++ b/plugins/mysql/mysql_connections @@ -83,7 +83,7 @@ sub poll_variables { my $expression = shift; my $ret = 0; open(SERVICE, "$command |") - or die("Coult not execute '$command': $!"); + or die("Could not execute '$command': $!"); while () { my ($field, $value) = (m/(\w+).*?(\d+(?:\.\d+)?)/); next unless ($field); diff --git a/plugins/mysql/mysql_qcache b/plugins/mysql/mysql_qcache index a8e9fa88..269a3e3b 100755 --- a/plugins/mysql/mysql_qcache +++ b/plugins/mysql/mysql_qcache @@ -59,7 +59,7 @@ if ($arg eq 'config') { open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { my ($k, $v) = (m/(\w+).*?(\d+(?:\.\d+)?)/); diff --git a/plugins/mysql/mysql_qcache_mem b/plugins/mysql/mysql_qcache_mem index cd5ec1d2..2d11cfed 100755 --- a/plugins/mysql/mysql_qcache_mem +++ b/plugins/mysql/mysql_qcache_mem @@ -62,7 +62,7 @@ if ($arg eq 'config') { my ($free, $used) = (0,0); open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { my ($k, $v) = (m/(\w+).*?(\d+(?:\.\d+)?)/); @@ -75,7 +75,7 @@ while () { close(SERVICE); open(SERVICE, "$COMMANDSIZE |") - or die("Coult not execute '$COMMANDSIZE': $!"); + or die("Could not execute '$COMMANDSIZE': $!"); while () { my ($k, $v) = (m/(\w+).*?(\d+(?:\.\d+)?)/); diff --git a/plugins/mysql/mysql_size_ b/plugins/mysql/mysql_size_ index 7d934faa..4c5eeb6b 100755 --- a/plugins/mysql/mysql_size_ +++ b/plugins/mysql/mysql_size_ @@ -72,7 +72,7 @@ my (@infos,$info,$i_data,$i_index); my $COMMAND = "$MYSQLADMIN $ENV{mysqlopts} $db -e 'show table status;' | grep 'Data'"; open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { (@infos) = split; @@ -95,7 +95,7 @@ foreach $info (@infos) { $COMMAND = "$MYSQLADMIN $ENV{mysqlopts} $db -e 'show table status;' | cut -f $i_data,$i_index | grep -v leng"; open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { (m/(\d+).*?(\d+(?:\.\d+)?)/); diff --git a/plugins/mysql/mysql_size_all b/plugins/mysql/mysql_size_all index c5a39f84..f8a4de9f 100755 --- a/plugins/mysql/mysql_size_all +++ b/plugins/mysql/mysql_size_all @@ -76,7 +76,7 @@ foreach my $db (getDBList()) { $COMMAND = "$MYSQLADMIN $ENV{mysqlopts} $db -e 'show table status;' | head -n 1"; open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { (@infos) = split; @@ -100,7 +100,7 @@ foreach my $db (getDBList()) { $COMMAND = "$MYSQLADMIN $ENV{mysqlopts} $db -e 'show table status;' | cut -f $i_data,$i_index | grep -v leng"; open(SERVICE, "$COMMAND |") - or die("Coult not execute '$COMMAND': $!"); + or die("Could not execute '$COMMAND': $!"); while () { (m/(\d+).*?(\d+(?:\.\d+)?)/); diff --git a/plugins/nagios/nagios_multi_ b/plugins/nagios/nagios_multi_ index c34d99ec..cb45d85a 100755 --- a/plugins/nagios/nagios_multi_ +++ b/plugins/nagios/nagios_multi_ @@ -76,7 +76,7 @@ if (basename($0) !~ /^nagios_multi_/) { exit 1; } -# tell munin about our multigraph capabilties +# tell munin about our multigraph capabilities need_multigraph(); # import binary information or use default setting diff --git a/plugins/netapp/snmp__netapp_cifs2 b/plugins/netapp/snmp__netapp_cifs2 index 40814761..140fa48f 100755 --- a/plugins/netapp/snmp__netapp_cifs2 +++ b/plugins/netapp/snmp__netapp_cifs2 @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_cifscalls b/plugins/netapp/snmp__netapp_cifscalls index 5ff673d3..a1d862b3 100755 --- a/plugins/netapp/snmp__netapp_cifscalls +++ b/plugins/netapp/snmp__netapp_cifscalls @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_cpu2 b/plugins/netapp/snmp__netapp_cpu2 index 651a2077..b1a577af 100755 --- a/plugins/netapp/snmp__netapp_cpu2 +++ b/plugins/netapp/snmp__netapp_cpu2 @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_diskusage2_ b/plugins/netapp/snmp__netapp_diskusage2_ index 4f76d021..fe0e5312 100755 --- a/plugins/netapp/snmp__netapp_diskusage2_ +++ b/plugins/netapp/snmp__netapp_diskusage2_ @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_diskutil b/plugins/netapp/snmp__netapp_diskutil index f6a19c57..4efada8b 100755 --- a/plugins/netapp/snmp__netapp_diskutil +++ b/plugins/netapp/snmp__netapp_diskutil @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_ndmp b/plugins/netapp/snmp__netapp_ndmp index e37b28b4..24275f07 100755 --- a/plugins/netapp/snmp__netapp_ndmp +++ b/plugins/netapp/snmp__netapp_ndmp @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_net b/plugins/netapp/snmp__netapp_net index b2ab602b..09935dcc 100755 --- a/plugins/netapp/snmp__netapp_net +++ b/plugins/netapp/snmp__netapp_net @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_nfs3calls b/plugins/netapp/snmp__netapp_nfs3calls index 3f29fcf2..c85130d9 100755 --- a/plugins/netapp/snmp__netapp_nfs3calls +++ b/plugins/netapp/snmp__netapp_nfs3calls @@ -12,7 +12,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_ops b/plugins/netapp/snmp__netapp_ops index a443749d..3ffd15d1 100755 --- a/plugins/netapp/snmp__netapp_ops +++ b/plugins/netapp/snmp__netapp_ops @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/netapp/snmp__netapp_sis b/plugins/netapp/snmp__netapp_sis index 640c2f39..07dbff10 100755 --- a/plugins/netapp/snmp__netapp_sis +++ b/plugins/netapp/snmp__netapp_sis @@ -13,7 +13,7 @@ with SNMP agent daemon activated. See na_snmp(8) for details. =head1 CONFIGURATION -Unfortunately, SNMPv3 is not fully supported on all NetApp equipments. +Unfortunately, SNMPv3 is not fully supported on all NetApp equipment. For this reason, this plugin will use SNMPv2 by default, which is insecure because it doesn't encrypt the community string. diff --git a/plugins/network/ddclient b/plugins/network/ddclient index b77cd67f..bb3ca78a 100755 --- a/plugins/network/ddclient +++ b/plugins/network/ddclient @@ -47,7 +47,7 @@ fi # Nur fuer Testzwecke kann das - For testing only you can -# Zeitfenster vergroessert werden resize the reference periode +# Zeitfenster vergroessert werden resize the reference period if [ "${1//[^[:digit:]]}" != "" ]; then factor=${1//[^[:digit:]]} else @@ -60,7 +60,7 @@ fi # Zeitfenster in Sekunden - time slot in seconds let Timeslot=60*30*$factor -# Referenzzeitpunkt berechnen - calculate the reference periode +# Referenzzeitpunkt berechnen - calculate the reference period let Ref_Timestamp=Timestamp-Timeslot # Zeitstempel der letzten Aktualisierung - timestampe of the last update @@ -75,7 +75,7 @@ fi echo "ip_change.value 0" fi -# Nur zum Testen - for testing ony +# Nur zum Testen - for testing only if [ "$factor" -gt 1 ]; then echo "======================== Nur fuer Testzwecke ======================" echo "Timestamp :" $Timestamp $(date -d "1970-01-01 UTC + $Timestamp seconds") diff --git a/plugins/network/hostsdeny b/plugins/network/hostsdeny index 48d7eee1..175b18ae 100755 --- a/plugins/network/hostsdeny +++ b/plugins/network/hostsdeny @@ -1,7 +1,7 @@ #!/bin/sh -eu # # Plugin to monitor the number of hosts in /etc/hosts.deny -# that are deined access to sshd +# that are denied access to sshd # # Based on denyhosts plugin by tjansson (2009) # diff --git a/plugins/network/if b/plugins/network/if index 3ad61159..f2a5f40a 100755 --- a/plugins/network/if +++ b/plugins/network/if @@ -26,7 +26,7 @@ Virtual interface names prefixes with '~' This plugin is configurable environment variables. env.exclude - Removing interfaces from graphs, default empty env.include - Includes interfaces into graphs, default empty -env.if_max_bps - Maximum interface bps. Avialable suffixes: k, M, G, default empty +env.if_max_bps - Maximum interface bps. Available suffixes: k, M, G, default empty env.protexct_peaks - Protect graph peaks, default 'no' env.min_packet_size - Minimal network packet size, default 20 Example: @@ -73,7 +73,7 @@ my $ifpath = '/sys/class/net'; # ----------------------------------- global ----------------- my $interfaces = {}; -# ------------------------ avialable graphs ------------------------- +# ------------------------ available graphs ------------------------- my $graphs = { 'if_bit' => @@ -178,7 +178,7 @@ my $graphs = { 'category' => 'network', 'args' => '--base 1024', - 'title' => ':if: avgerage packet size', + 'title' => ':if: average packet size', 'vlabel' => 'bytes', 'info' => 'This graph shows average packet size of the :if:' }, @@ -187,7 +187,7 @@ my $graphs = } }; -#-------------------------- avialable fields ------------------------- +#-------------------------- available fields ------------------------- # info: # 'munin' => {} - just copy fields to munin config # 'source' => - field data source diff --git a/plugins/network/if1sec-c.c b/plugins/network/if1sec-c.c index c31031ae..6cab7235 100644 --- a/plugins/network/if1sec-c.c +++ b/plugins/network/if1sec-c.c @@ -23,7 +23,7 @@ int fail(char* msg) { } /* Returns the ifname from a /proc/net/dev line - * It will return an inside pointer to line, and modifiy the end with a \0 + * It will return an inside pointer to line, and modify the end with a \0 */ char* get_ifname_from_procstatline(char* line) { char *ifname; diff --git a/plugins/network/linux_if/linux_if b/plugins/network/linux_if/linux_if index e9d6b89e..19944008 100755 --- a/plugins/network/linux_if/linux_if +++ b/plugins/network/linux_if/linux_if @@ -17,12 +17,12 @@ plugin configuration: # run plugin as root (required if you have VLAN sub-interfaces) user = root - # comma separated list of intreface patterns to exclude from monitoring + # comma separated list of interface patterns to exclude from monitoring # default: lo # example: env.exclude = lo,vnet* - # comma separated list of intreface patterns to include in monitoring + # comma separated list of interface patterns to include in monitoring # default: (empty) # example: env.include = br_* @@ -43,7 +43,7 @@ Tested on: RHEL 6.x and clones (with Python 2.6) TODO: * implement 'data loaning' between graphs, removes duplicit measures * add support for bridging -* configurable graph max based on intreface speed +* configurable graph max based on interface speed MUNIN MAGIC MARKER #%# family=manual diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth index b7ebfdc6..e1a763ac 100755 --- a/plugins/network/multibandwidth +++ b/plugins/network/multibandwidth @@ -6,7 +6,7 @@ =head1 NAME -multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. +multibandwidth - Plugin to monitor the bandwidth between localhost and several hosts. =head1 APPLICABLE SYSTEMS @@ -82,7 +82,7 @@ case $1 in echo 'graph_args --base 1024 -l 0' echo 'graph_scale yes' echo 'graph_category network' - echo 'graph_info This graph shows the bandwidth between localhost and serveral hosts' + echo 'graph_info This graph shows the bandwidth between localhost and several hosts' for host in $hosts; do fieldname="host_$(clean_fieldname "$host")" echo "$fieldname.label $host" diff --git a/plugins/network/netatalk3 b/plugins/network/netatalk3 index fc766804..96f0816c 100755 --- a/plugins/network/netatalk3 +++ b/plugins/network/netatalk3 @@ -48,7 +48,7 @@ function count_connected_users () { local afpd_bin_path=$1 afpd_procs=$(ps anx --no-headers -o uid,command |grep -E "\w+\d+*\s${afpd_bin_path}" |wc -l) # one of those processes will be always from root user, so it's not being - # used to externaly connect volumes, therefor being disconsider. + # used to externally connect volumes, therefore being ignored. echo $(echo "${afpd_procs} - 1" |bc) } @@ -119,7 +119,7 @@ EOM #### Boilerplates #################################################### # Locating AFP related files: # * Binary: Using the first result of "which" command; - # * Config: Using "afpd" with parameters to pring configuration file + # * Config: Using "afpd" with parameters to print configuration file # location; # diff --git a/plugins/network/nsd3 b/plugins/network/nsd3 index 3b2e8b42..bddb3931 100755 --- a/plugins/network/nsd3 +++ b/plugins/network/nsd3 @@ -11,7 +11,7 @@ =head1 APPLICABLE SYSTEMS - Linux or *nix system with a logging installtion of NSD v3 installed. + Linux or *nix system with a logging installation of NSD v3 installed. (http://nlnetlabs.nl/projects/nsd/) =head1 CONFIGURATION diff --git a/plugins/network/vnstat_ b/plugins/network/vnstat_ index 23388ae1..0094ca13 100755 --- a/plugins/network/vnstat_ +++ b/plugins/network/vnstat_ @@ -11,7 +11,7 @@ # # 2009.09.28 _KaszpiR_ # - quick an ddirty update to support multiple interfaces, for example symlink vnstat_ to vnstat_eth0 and vnstat_eth1 -# rember to run before that vnstat -u -i eth0 and vnstat -u -i eth1 to build databases (read manual of vnstat) +# remember to run before that vnstat -u -i eth0 and vnstat -u -i eth1 to build databases (read manual of vnstat) # other updates of this script maybe soon diff --git a/plugins/nfs/nfsv4 b/plugins/nfs/nfsv4 index 2c0392fb..e7fe68bc 100755 --- a/plugins/nfs/nfsv4 +++ b/plugins/nfs/nfsv4 @@ -28,7 +28,7 @@ # Set categories on a bunch of plugins # # Revision 1.3 2004/05/15 21:33:29 jimmyo -# "Upped" som plugins from contrib/manual to manual or auto. +# "Upped" some plugins from contrib/manual to manual or auto. # # Revision 1.2 2004/05/06 21:55:18 jimmyo # Added patch to contrib-plugin linux/nfsd, to graph rpc count (Alexandre Dupouy). diff --git a/plugins/nginx/nginx_vhost_traffic b/plugins/nginx/nginx_vhost_traffic index b512dce2..e2f710fe 100755 --- a/plugins/nginx/nginx_vhost_traffic +++ b/plugins/nginx/nginx_vhost_traffic @@ -2,7 +2,7 @@ # # Script for monitoring nginx Virtual host output traffic # -# Requierements: logtail awk +# Requirements: logtail awk # one unique access log file with $bytes_sent value for more accuracy # check http://wiki.nginx.org/NginxHttpLogModule # diff --git a/plugins/ntp/ntp_packets b/plugins/ntp/ntp_packets index 9251d2da..bf93d3e9 100755 --- a/plugins/ntp/ntp_packets +++ b/plugins/ntp/ntp_packets @@ -87,7 +87,7 @@ stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], universal_newlines=True).splitlines() # Split the cmd output into key/value pairs -# Lines that can't be splitted into 2 individual elements by delimiter ':' will be skipped +# Lines that can't be split into 2 individual elements by delimiter ':' will be skipped for line in stats_output: if len(line.split(':')) == 2: stats[line.split(':')[0]] = int(line.split(':')[1]) diff --git a/plugins/openvz/openvzcpu b/plugins/openvz/openvzcpu index 288f81ca..7df858cb 100755 --- a/plugins/openvz/openvzcpu +++ b/plugins/openvz/openvzcpu @@ -45,7 +45,7 @@ of idle and iowait times: env.drawidle 1 For kernels which have other than 100 jiffies per second (sic) n.b. this is -unlikely to be necessary - you may add the followin to the plugin-specific +unlikely to be necessary - you may add the following to the plugin-specific configuration: env.HZ 1000 diff --git a/plugins/openvz/vpsmem b/plugins/openvz/vpsmem index adc9442c..173ff31a 100755 --- a/plugins/openvz/vpsmem +++ b/plugins/openvz/vpsmem @@ -1,7 +1,7 @@ #!/bin/bash # # Monitors memory usage in openVZ or Virtuozzo -# based on http://www.huschi.net/archiv/speicher-anzeigen-mit-vzfree.html +# based on http://www.huschi.net/archive/speicher-anzeigen-mit-vzfree.html # Author: Michael Richter, http://osor.de/ # Cleaned up and translated to english by: Marian Sigler , 2010-08-13 # diff --git a/plugins/oracle/oracle__database_hitratio b/plugins/oracle/oracle__database_hitratio index fae7045a..6e3c8003 100755 --- a/plugins/oracle/oracle__database_hitratio +++ b/plugins/oracle/oracle__database_hitratio @@ -100,7 +100,7 @@ $sth_curr->execute(); my ($read_hitratio) = $sth_curr->fetchrow(); print "read_hitratio.value $read_hitratio\n"; -#libray hit ratio +#library hit ratio $sql_curr = "select sum(lc.pins)/(sum(lc.pins)+sum(lc.reloads))*100 \ from v\$librarycache lc"; diff --git a/plugins/oracle/oracle_sysstat b/plugins/oracle/oracle_sysstat index 1ea0526a..d25f87e1 100755 --- a/plugins/oracle/oracle_sysstat +++ b/plugins/oracle/oracle_sysstat @@ -83,7 +83,7 @@ example: env.exclude_module asmusage tablespace default: asmusage - Module name(s) to exclude seperated by white-space. + Module name(s) to exclude separated by white-space. By default, asmusage module is excluded because another privilege is necessary to connect ASM instance. @@ -91,7 +91,7 @@ example: env.include_module asmusage default: none - Module name(s) to include seperated by white-space. + Module name(s) to include separated by white-space. If both include_module and exclude_module are set, exclude will be ignored. @@ -518,7 +518,7 @@ global_attrs[$key]=" graph_category db graph_args --base 1000 --lower-limit 0 --rigid graph_vlabel microseconds - graph_info Oracle Wait Events - It may look wierd that Y-axis indicates 'microseconds per second'. Although number of times of wait event looks easier to understand, in many cases the number of events does not matter, but wait time become more important to analyze bottle necks. + graph_info Oracle Wait Events - It may look weird that Y-axis indicates 'microseconds per second'. Although number of times of wait event looks easier to understand, in many cases the number of events does not matter, but wait time become more important to analyze bottle necks. " data_attrs[$key]="" getfield_func[$key]=getfield_eventwait diff --git a/plugins/other/hookbox b/plugins/other/hookbox index 1ca44af3..dd257ff5 100755 --- a/plugins/other/hookbox +++ b/plugins/other/hookbox @@ -13,7 +13,7 @@ class HookboxPlugin(MuninPlugin): title = 'hookbox' args = "--base 1000" vlabel = "Y" - info = "Subscibed users" + info = "Subscribed users" scale = False def get_channels(self): diff --git a/plugins/other/listeners b/plugins/other/listeners index 235bcb08..efe31fe4 100755 --- a/plugins/other/listeners +++ b/plugins/other/listeners @@ -15,7 +15,7 @@ # yyyy/mm/dd v author changelog: -# 2008/01/15 v0.02 Lothar Schmidt added alternetive retrievers, stream name substitution +# 2008/01/15 v0.02 Lothar Schmidt added alternative retrievers, stream name substitution # 2008/01/15 v0.01 Lothar Schmidt initial version, email: l.make.a.noise.here@scarydevilmonastery.net @@ -87,7 +87,7 @@ graph_vlabel ${GRAPH} EOF for (( I=2 ; I -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and split by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_openbsd b/plugins/pf/pf_openbsd index 65fd05d1..1dc1e163 100755 --- a/plugins/pf/pf_openbsd +++ b/plugins/pf/pf_openbsd @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and split by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_packets b/plugins/pf/pf_packets index 59b30c5d..fc379996 100755 --- a/plugins/pf/pf_packets +++ b/plugins/pf/pf_packets @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and split by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/pf/pf_states b/plugins/pf/pf_states index 4d84ee0a..e15df623 100755 --- a/plugins/pf/pf_states +++ b/plugins/pf/pf_states @@ -2,7 +2,7 @@ # # OpenBSD's pf(4) monitoring for OpenBSD # 2007, Originally by Gergely Czuczy -# for FreeBSD systems. Ported and splitted by the +# for FreeBSD systems. Ported and split by the # immerda admin team admin(at)immerda.ch # this version is adapted for openbsd and is only tested on # openbsd systems. diff --git a/plugins/php/eaccelerator b/plugins/php/eaccelerator index f4f8e111..40526816 100755 --- a/plugins/php/eaccelerator +++ b/plugins/php/eaccelerator @@ -28,13 +28,13 @@ # foreach($keys as $key => $val) echo strtolower($key).".value ".$info[$key]."\n"; # ?> # -# - name that file eaccelerator_status.php, will be easier, file should be at least accesible from the address that runs this script (usually localhost) -# you can make this file accessible globally, it just displays the memor usage by eaccelerator, thats all. +# - name that file eaccelerator_status.php, will be easier, file should be at least accessible from the address that runs this script (usually localhost) +# you can make this file accessible globally, it just displays the memory usage by eaccelerator, that's all. # usually you can put it to the /var/www/ (but it depends on the server configuration etc) # - check if you can see the output of the file, for example if you placed file in the DocumentRoot then it should be available from # http://localhost/eaccelerator_status.php # if you see the plain text with values then its working ok! -# if you see the plain text and all values are zero then probalby eaccelerator is not enabled. +# if you see the plain text and all values are zero then probably eaccelerator is not enabled. # - installed wget # ################################################################# diff --git a/plugins/php/php-cgi b/plugins/php/php-cgi index c9b8009e..48a31d05 100755 --- a/plugins/php/php-cgi +++ b/plugins/php/php-cgi @@ -10,7 +10,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=manual #%# capabilities=autoconf diff --git a/plugins/php/php_time_execution b/plugins/php/php_time_execution index 8a1ce651..5564e510 100755 --- a/plugins/php/php_time_execution +++ b/plugins/php/php_time_execution @@ -7,7 +7,7 @@ # Min, Max and Avg are calculated on number of page, default 10. On high traffic site, increase this value and you get a better # stat, on low traffic site keep small value, it's must be avg number of page every 5 minutes. # -# Require read permitions for $LOG +# Require read permissions for $LOG # (set in /etc/munin/plugin-conf.d/munin-node on debian) # On busy servers you can change value type to COUNTER and set min to 0 to avoid minus peaks at logrotate # diff --git a/plugins/poseidon/snmp__poseidon-sensors b/plugins/poseidon/snmp__poseidon-sensors index aea79be5..82758480 100755 --- a/plugins/poseidon/snmp__poseidon-sensors +++ b/plugins/poseidon/snmp__poseidon-sensors @@ -26,7 +26,7 @@ #%# family=auto snmpauto contrib #%# capabilities=snmpconf -E_OK="0" # everything went allright +E_OK="0" # everything went alright E_UNKNOWN="1" # "catch all" for otherwise unhandled errors E_ARG="81" # invalid argument diff --git a/plugins/postfix/postfix_mailstats_ b/plugins/postfix/postfix_mailstats_ index 24d0029e..64e074ec 100755 --- a/plugins/postfix/postfix_mailstats_ +++ b/plugins/postfix/postfix_mailstats_ @@ -10,7 +10,7 @@ rejected by postfix Uses the last part of the symlink name for grepping the correct data from the postfix log file. The name must be syslog_name from the postfix config. -The environment settings still applay to this plugin. +The environment settings still apply to this plugin. Configuration parameters for /etc/munin/postfix_mailstats_, if you need to override the defaults below: diff --git a/plugins/postfix/postfwd2 b/plugins/postfix/postfwd2 index edae34a8..bd5379b0 100755 --- a/plugins/postfix/postfwd2 +++ b/plugins/postfix/postfwd2 @@ -25,12 +25,12 @@ This are perl regexp. If env.include is set and env.exclude is not, only the policy which name - matchs will be used. + matches will be used. If env.exclude is set and env.include is not, only the policy which name NOT - matchs will be used. - If both are set, a policy which name matchs the both regex will be used, a - policy which matchs only the exclude regexp will NOT be used and a policy - which match not the exclude regex will be used, even if it not matchs the + matches will be used. + If both are set, a policy which name matches the both regex will be used, a + policy which matches only the exclude regexp will NOT be used and a policy + which match not the exclude regex will be used, even if it not matches the include regexp. if none are set, all the policy will be used. @@ -156,7 +156,7 @@ if (!defined($ENV{path}) || !(-x $ENV{path})) { } munin_exit_fail() unless (defined($ENV{path}) && -x $ENV{path}); -##### I have to parse the output BEFORE config, since policy matchs are dependent of the postfwd --dumpstats output +##### I have to parse the output BEFORE config, since policy matches are dependent of the postfwd --dumpstats output open(DATA, $ENV{path}.' --dumpstats |') or munin_exit_fail(); my $total_requests; while(defined (my $data = )) { @@ -252,7 +252,7 @@ munin_exit_done(); # ## -### INTERNALS FONCTIONS +### INTERNALS FUNCTIONS ############################################################################### sub munin_exit_done { munin_exit(0); diff --git a/plugins/postfix/postgrey-new b/plugins/postfix/postgrey-new index 37a1dd40..35199b24 100755 --- a/plugins/postfix/postgrey-new +++ b/plugins/postfix/postgrey-new @@ -9,7 +9,7 @@ # output of postgrey. You can change that by setting env.logfile # # The state file is /var/lib/munin/plugin-state/postgrey-new.state# # This can be changed by setting env.statefile # -# Keep in mind to grant enough rigths in order to open the # +# Keep in mind to grant enough rights in order to open the # # logfiles etc. # # Parameters understood by this plugin # # # diff --git a/plugins/postgresql/pg__connections b/plugins/postgresql/pg__connections index f6ca4018..2c9e2824 100755 --- a/plugins/postgresql/pg__connections +++ b/plugins/postgresql/pg__connections @@ -63,7 +63,7 @@ if (exists $ARGV[0]) { my ($max_connections) = `$sqlCommand`; my $warning = int ($max_connections * 0.7); my $critical = int ($max_connections * 0.8); - print "graph_title PostgresSQL active connections\n"; + print "graph_title PostgreSQL active connections\n"; print "graph_args -l 0 --base 1000\n"; print "graph_vlabel Connections\n"; print "graph_category db\n"; diff --git a/plugins/power/currentcost b/plugins/power/currentcost index 41437c51..ea34f531 100755 --- a/plugins/power/currentcost +++ b/plugins/power/currentcost @@ -58,7 +58,7 @@ The configuration can be broken down into the following subsections: =item env.device -Specfies the device node where the CurrentCost monitor can be found. You may find it useful to use a udev rule to symlink this somewhere permanent. +Specifies the device node where the CurrentCost monitor can be found. You may find it useful to use a udev rule to symlink this somewhere permanent. =item env.baud @@ -100,7 +100,7 @@ The time period for which C applies. This should be of the form C =item env.standingcharge -The standing charge in hundreths of a C per month. If you do not have a standing charge, set this to 0. +The standing charge in hundredths of a C per month. If you do not have a standing charge, set this to 0. =item env.metertype diff --git a/plugins/power/nutups2_ b/plugins/power/nutups2_ index 9730d457..73d6881f 100755 --- a/plugins/power/nutups2_ +++ b/plugins/power/nutups2_ @@ -239,7 +239,7 @@ sub common_config { print $id . ".label " . $field . "\n"; print $id . ".type GAUGE\n"; - # Draw nominal values a litle thinner + # Draw nominal values a little thinner print $id . ".draw LINE1\n" if $nominal; print_range_warning($id, $key, $values); diff --git a/plugins/power/upsmonpro_ b/plugins/power/upsmonpro_ index 0008e4e2..7b7fe428 100755 --- a/plugins/power/upsmonpro_ +++ b/plugins/power/upsmonpro_ @@ -121,7 +121,7 @@ END sub run_config_load { print <<'END'; graph_title UPS Battery Load/Capacity -graph_vlabel precent% +graph_vlabel percent (%) graph_scale no graph_category sensors battery_load.label battery_load diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 4a4b9552..9f7c1821 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -97,7 +97,7 @@ def main(): print("graph_vlabel clients") print("graph_category chat") - print("available.label Avaible Clients") + print("available.label Available Clients") print("chat.label Ready for Chat Clients") print("away.label Away Clients") print("xa.label Extended Away Clients") diff --git a/plugins/qmail/spamdyke b/plugins/qmail/spamdyke index b6e58284..1868ae45 100755 --- a/plugins/qmail/spamdyke +++ b/plugins/qmail/spamdyke @@ -117,7 +117,7 @@ if [ "$1" = "config" ]; then echo 'failedauth.info The remote server attempted to authenticate but the given username and/or password were incorrect ("smtp-auth-command" or "smtp-auth-command-encryption"). ' echo 'failedauth.min 1' echo 'unknownauth.label UNKNOWN_AUTH' - echo 'unknownauth.info The remote server requested an authentication method spamdyke doesnt support. This should not happen. ' + echo 'unknownauth.info The remote server requested an authentication method spamdyke does not support. This should not happen. ' echo 'unknownauth.min 1' echo 'failedtls.label FAILED_TLS' echo 'failedtls.info The remote client attempted to start a TLS session but SSL negotiation failed.' diff --git a/plugins/radiator/radiator_acct_lag b/plugins/radiator/radiator_acct_lag index 362b53a0..217339cd 100755 --- a/plugins/radiator/radiator_acct_lag +++ b/plugins/radiator/radiator_acct_lag @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel time' echo 'graph_args -l 0' echo 'graph_category auth' - echo 'graph_info This graph displayes ACCT response lag of all radiator processes combined.' + echo 'graph_info This graph displays ACCT response lag of all radiator processes combined.' # # Count all statistics files and create labels for COUNT in `ls $STATISTICSDIR/Statistics-acct* | sed -r 's/acct([1-9]{1})\.log$/acct0\1\.log/g' | sort | awk -F"acct" '{print $2}' | sed 's/\.log//g'`; do diff --git a/plugins/radiator/radiator_auth_lag b/plugins/radiator/radiator_auth_lag index 4a14e06b..c8179286 100755 --- a/plugins/radiator/radiator_auth_lag +++ b/plugins/radiator/radiator_auth_lag @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel time' echo 'graph_args -l 0' echo 'graph_category auth' - echo 'graph_info This graph displayes AUTH response lag of all radiator processes combined.' + echo 'graph_info This graph displays AUTH response lag of all radiator processes combined.' # # Count all statistics files and create labels for COUNT in `ls $STATISTICSDIR/Statistics-auth* | sed -r 's/auth([1-9]{1})\.log$/auth0\1\.log/g' | sort | awk -F"auth" '{print $2}' | sed 's/\.log//g'`; do diff --git a/plugins/radiator/radiator_auth_ppm b/plugins/radiator/radiator_auth_ppm index e4d2cd84..46dff61d 100755 --- a/plugins/radiator/radiator_auth_ppm +++ b/plugins/radiator/radiator_auth_ppm @@ -36,7 +36,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel packets per minute' echo 'graph_args -l 0' echo 'graph_category auth' - echo 'graph_info This graph displayes AUTH packets of all radiator processes combined.' + echo 'graph_info This graph displays AUTH packets of all radiator processes combined.' # # Count all statistics files and create labels for COUNT in `ls $STATISTICSDIR/Statistics-auth* | sed -r 's/auth([1-9]{1})\.log$/auth0\1\.log/g' | sort | awk -F"auth" '{print $2}' | sed 's/\.log//g'`; do diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io index 91893568..a81f795b 100755 --- a/plugins/rethinkdb/rethinkdb_node_io +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -12,7 +12,7 @@ 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 + localhost:28015, and/or your database-server differs in name from `hostname` (short hostname), you can add rethinkdb-node-io config vars like: [rethinkdb_*] @@ -80,7 +80,7 @@ def print_config(servername): def check_autoconf() -> bool: - # this might be too easy, but gonna try. + # this might be too easy, but it is a good indication. if which("rethinkdb"): return True return False diff --git a/plugins/router/ag241-adsl b/plugins/router/ag241-adsl index 19ebcbf4..23c6174c 100755 --- a/plugins/router/ag241-adsl +++ b/plugins/router/ag241-adsl @@ -38,10 +38,10 @@ require 'net/http' host = nil port = ENV['port'] || 80 user = ENV['user'] || 'admin' -pass = ENV['pass'] || 'forhax' #dont remember what the default admin password was +pass = ENV['pass'] || 'forhax' #don't remember what the default admin password was stat = nil -# Check executeable "name" for parameter count +# Check executable "name" for parameter count params = $0.split('_') if params.size != 3 puts "Incorrect number of parameters" @@ -100,7 +100,7 @@ if response.code != '200' case response.code when '401' puts 'Probably because the username and password are incorrect' - #Looks like the modem respons with 200 when you try to access a page that doesnt exist >_> + #Looks like the modem response with 200 when you try to access a page that does not exist >_> #when '404' # puts 'Looks like the page this plugin needs isn\'t available...' # puts 'Check your modem make/model/version' diff --git a/plugins/router/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin index 7379c73d..adc0afff 100755 --- a/plugins/router/d-link-dir-655-router-statistics-plugin +++ b/plugins/router/d-link-dir-655-router-statistics-plugin @@ -44,7 +44,7 @@ def output x = agent.get(router_path) salt = x.body.match(/salt = "(.*)"/)[1] - # pad the pasword to length 16 + # pad the password to length 16 pad_size = (16 - password.length) padded_password = password + "\x01" * pad_size diff --git a/plugins/router/freeboxuptime b/plugins/router/freeboxuptime index 1ced6e0a..7626c700 100755 --- a/plugins/router/freeboxuptime +++ b/plugins/router/freeboxuptime @@ -30,7 +30,7 @@ # by using nmap on a specific tcp port, the detection is pretty fast (2-5 seconds) # with this command: nmap -O --osscan-guess -p80 remote_host # -# if you dont want nmap to query your freebox each time, set CACHE_HOURS=n +# if you don't want nmap to query your freebox each time, set CACHE_HOURS=n # to keep the uptime in cache for n hours # if not, set CACHE_HOURS=0 # diff --git a/plugins/router/tg585v7__ b/plugins/router/tg585v7__ index db438baf..20d4a838 100755 --- a/plugins/router/tg585v7__ +++ b/plugins/router/tg585v7__ @@ -800,7 +800,7 @@ EOF } else { print <session(-translate => my $temp = $session->get_single (".1.3.6.1.4.1.6574.1.2.0") || 'ERROR'; -print "Retrived uptime is '$temp'\n" if $Munin::Plugin::SNMP::DEBUG; +print "Retrieved uptime is '$temp'\n" if $Munin::Plugin::SNMP::DEBUG; print "temp.value ", $temp, "\n"; diff --git a/plugins/synology/snmp__synology_ups b/plugins/synology/snmp__synology_ups index 2fd6e3f5..c975f85b 100755 --- a/plugins/synology/snmp__synology_ups +++ b/plugins/synology/snmp__synology_ups @@ -95,8 +95,8 @@ $charge = unpack "f", reverse pack "H*", $charge; my $load = $session->get_single (".1.3.6.1.4.1.6574.4.2.12.1.0") || 'ERROR'; $load = unpack "f", reverse pack "H*", $load; -print "Retrived charge '$charge'\n" if $Munin::Plugin::SNMP::DEBUG; -print "Retrived load '$load'\n" if $Munin::Plugin::SNMP::DEBUG; +print "Retrieved charge '$charge'\n" if $Munin::Plugin::SNMP::DEBUG; +print "Retrieved load '$load'\n" if $Munin::Plugin::SNMP::DEBUG; print "charge.value ", $charge, "\n"; print "load.value ", $load, "\n"; diff --git a/plugins/system/top-memory b/plugins/system/top-memory index 85ee490e..6dd19836 100755 --- a/plugins/system/top-memory +++ b/plugins/system/top-memory @@ -8,7 +8,7 @@ # Set categories on a bunch of plugins # # Revision 1.3 2004/05/15 21:33:30 jimmyo -# "Upped" som plugins from contrib/manual to manual or auto. +# "Upped" some plugins from contrib/manual to manual or auto. # # Revision 1.2 2004/05/09 20:42:08 jimmyo # Fixed problem with sunos/memory, when memory was reported in gigabytes (SF#930964). diff --git a/plugins/thecus/snmp__thecus_fans b/plugins/thecus/snmp__thecus_fans index ec43e277..27f237b3 100755 --- a/plugins/thecus/snmp__thecus_fans +++ b/plugins/thecus/snmp__thecus_fans @@ -3,7 +3,7 @@ =head1 NAME -snmp__thecus_fans - Munin plugin to retrive fanspeed readings from a Thecus +snmp__thecus_fans - Munin plugin to retrieve fanspeed readings from a Thecus NAS device running SNMP. =head1 APPLICABLE SYSTEMS @@ -98,7 +98,7 @@ my $session = Munin::Plugin::SNMP->session(-translate => my $fan1 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.1.0") || 'U'; my $fan2 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.2.0") || 'U'; -#print "Retrived uptime is '$uptime'\n" if $DEBUG; +#print "Retrieved uptime is '$uptime'\n" if $DEBUG; print "fan1.value ", $fan1, "\n"; print "fan2.value ", $fan2, "\n"; diff --git a/plugins/tor/tor-bandwidth-usage b/plugins/tor/tor-bandwidth-usage index 5c95acf3..a5b7db11 100755 --- a/plugins/tor/tor-bandwidth-usage +++ b/plugins/tor/tor-bandwidth-usage @@ -3,7 +3,7 @@ # tor-bandwidth-usage - munin plugin to monitor Tor traffic # # To use this plugin you need the following: -# o Enable accounting on torrc configuration file (even if you dont want to limit bandwidth usage, +# o Enable accounting on torrc configuration file (even if you don't want to limit bandwidth usage, # just put a huge value for on AccountingMax) # example: # AccountingStart day 12:00 diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 0c809f89..921cd9ea 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -4,7 +4,7 @@ tor_ =head1 DESCRIPTION -Wildcard plugin that gathers some metrics from the Tor deamon +Wildcard plugin that gathers some metrics from the Tor daemon https://github.com/daftaupe/munin-tor Derived from https://github.com/mweinelt/munin-tor diff --git a/plugins/ubiquiti/unifi_api b/plugins/ubiquiti/unifi_api index 86729820..d689a75d 100755 --- a/plugins/ubiquiti/unifi_api +++ b/plugins/ubiquiti/unifi_api @@ -57,7 +57,7 @@ You will need to supply your API login details: env.site site_string -=head2 Graph Categories / Host Managment +=head2 Graph Categories / Host Management Sometimes, you need more control over where the unifi graphs appear. @@ -89,7 +89,7 @@ And, in your munin master configuration: (Something like: /etc/munin/munin.conf) [hostname.whatever.youlike] address ip.of.munin.node -Make sure you do *not* set "use_node_name" on this new host. It may be nessesary to define "host_name" +Make sure you do *not* set "use_node_name" on this new host. It may be necessary to define "host_name" in your munin-node configuration as well, if you have not already (Likely, on a multi-homed host, this has been done to keep munin-node from advertising itself as localhost) @@ -223,7 +223,7 @@ use strict; use utf8; use Munin::Plugin; -# Check dependancies +# Check dependencies my @errorCode; my $me = (split '/', $0)[-1]; @@ -234,9 +234,9 @@ if (! eval {require WWW::Curl::Easy; 1;} ) { push @errorCode, "WWW::Curl::Easy module not found"; } -# Fail on not found dependancies +# Fail on not found dependencies if ( @errorCode != 0 ) { - die "FATAL:$me: Perl dependancies not installed (", join(", " => @errorCode), ")\n"; + die "FATAL:$me: Perl dependencies not installed (", join(", " => @errorCode), ")\n"; } # Multigraph cabability is required for this plugin @@ -300,7 +300,7 @@ my $jsonOBJ = JSON->new() or die "FATAL:$me: JSON init failed!\n"; if ( !env_default_bool_true('USE_API') ) { if (! eval {require File::Slurp; File::Slurp->import(); 1; } ) { - die "Local debug unavailable, File::Slurp CPAN moduel required\n"; + die "Local debug unavailable, File::Slurp CPAN module required\n"; } foreach ( "./demo-test-files/device", "./demo-test-files/sta", "./demo-test-files/wlanconf" ) { if ( ! -f $_ ) { die "File not found: $_\n"; } @@ -438,7 +438,7 @@ sub do_config_xfer_by_radio { '--base 1000', 'Packets/${graph_period}', 'network', - 'Number of packets transfered per individual radio band' + 'Number of packets transferred per individual radio band' ); foreach my $thisDevice ( sort keys %{$Data{'device'}} ) { @@ -591,7 +591,7 @@ sub do_config_xfer_by_uplink { foreach ( "rx", "tx" ) { print $_ , "_speed.label Speedtest\n"; - print $_ , "_bytes.label Transfered\n"; + print $_ , "_bytes.label Transferred\n"; print $_ , "_speed.type GAUGE\n"; print $_ , "_bytes.type DERIVE\n"; print $_ , "_speed.min 0\n"; @@ -707,7 +707,7 @@ sub do_config_load { } sub do_config_mem { - # Provice device memory usage for each unifi device - CONFIG + # Provide device memory usage for each unifi device - CONFIG if ( !$PluginConfig{'enable_device_mem'} ) { return 0; } graph_prologue( 'unifi_device_mem', @@ -901,7 +901,7 @@ sub do_values_cpu { } sub do_values_mem { - # Provice device memory usage for each unifi device - VALUES + # Provide device memory usage for each unifi device - VALUES if ( !$PluginConfig{'enable_device_mem'} ) { return 0; } print "multigraph unifi_device_mem\n"; @@ -1192,7 +1192,7 @@ sub make_safe { } } -# Get a default from an enviromental variable - return text +# Get a default from an environmental variable - return text # # env_default(, ) sub env_default_text { @@ -1200,7 +1200,7 @@ sub env_default_text { return ( ( defined $ENV{$env_var} ) ? $ENV{$env_var} : $default ), } -# Get a default from an enviromental variable - boolean true +# Get a default from an environmental variable - boolean true # # env_default_bool_true (, ) sub env_default_bool_true { diff --git a/plugins/vdr/vdr_ b/plugins/vdr/vdr_ index 35febff1..fb92443c 100755 --- a/plugins/vdr/vdr_ +++ b/plugins/vdr/vdr_ @@ -3,7 +3,7 @@ # Version 1.2 # - kein div 0 Fehler mehr wenn der Host nicht zu erreichen ist # - Serien Timer werden nun separat gezaehlt (anzahl pro Woche) -# - im Namen kann munic conform der hostname mit angegeben werden: vdr_localhost vdr_192.168.0.2, ... (localhost ist default) +# - im Namen kann Munin-konform der Hostname mit angegeben werden: vdr_localhost vdr_192.168.0.2, ... (localhost ist default) # - Timer werden nur ignoriert wenn sie 0(inaktiv) als Status haben # - Erkennung der neuen Aufzeichnungen passte nicht mehr # diff --git a/plugins/vmware/esx_ b/plugins/vmware/esx_ index 4c4ad4b4..53af9b85 100755 --- a/plugins/vmware/esx_ +++ b/plugins/vmware/esx_ @@ -327,7 +327,7 @@ foreach $host_name (@host_names) { group => "datastore", name => "uncommitted", value => $_->uncommitted, - counter => PerfCounterInfo->new(nameInfo => ElementDescription->new(label => "Uncomitted", summary => "Additional storage space, in bytes, potentially used by the virtual machine on this datastore.\n\nAdditional space may be needed for example when lazily allocated disks grow, or storage for swap is allocated when powering on the virtual machine.\n\nIf the virtual machine is running off delta disks (for example because a snapshot was taken), then only the potential growth of the currently used delta-disks is considered.\n\n")), + counter => PerfCounterInfo->new(nameInfo => ElementDescription->new(label => "Uncommitted", summary => "Additional storage space, in bytes, potentially used by the virtual machine on this datastore.\n\nAdditional space may be needed for example when lazily allocated disks grow, or storage for swap is allocated when powering on the virtual machine.\n\nIf the virtual machine is running off delta disks (for example because a snapshot was taken), then only the potential growth of the currently used delta-disks is considered.\n\n")), vm => $vmId, instance => $uuid, unit => "Bytes" }); diff --git a/plugins/vmware/esxcli_env_ b/plugins/vmware/esxcli_env_ index 036d1ba5..273a6440 100755 --- a/plugins/vmware/esxcli_env_ +++ b/plugins/vmware/esxcli_env_ @@ -52,7 +52,7 @@ then fi ### PROCESSING ### -# Case insensetive regex +# Case insensitive regex shopt -s nocasematch # Determine sensor type diff --git a/plugins/vmware/esxi b/plugins/vmware/esxi index 0c2894a6..a4fd18e5 100755 --- a/plugins/vmware/esxi +++ b/plugins/vmware/esxi @@ -214,7 +214,7 @@ EOF fi # --------------- -# Get the informations +# Get the information get_infos if [ -z "$summary" ]; then diff --git a/plugins/voip/murmur_users b/plugins/voip/murmur_users index 23c4dd69..5c9ab348 100755 --- a/plugins/voip/murmur_users +++ b/plugins/voip/murmur_users @@ -23,7 +23,7 @@ Notice: - tested on PHP 5.2.6-3 with Suhosin-Patch 0.9.6.2 (cli) (built: Aug 21 2008 17:02:32) murmur 1.1.4 precompiled binaries from sourceforge net, all running under debian etch -- this is not the best way to get users connected to the murmur server, maybe in the beginningn of the 2009 gonna make another script +- this is not the best way to get users connected to the murmur server, maybe in the beginning of the 2009 I will make another script Known issues and limitations: - counts all users on the server not respecting different server instances diff --git a/plugins/voip/murmurice_host_port_id_description b/plugins/voip/murmurice_host_port_id_description index 8f350d46..fb8c8de2 100755 --- a/plugins/voip/murmurice_host_port_id_description +++ b/plugins/voip/murmurice_host_port_id_description @@ -21,7 +21,7 @@ Requirements: Configuration: 1. link creation -create a symlink to thie file in the following format: +create a symlink to the file in the following format: murmurice_ip_port_sid_description where ip/port correspond to the ip and port on which the mumble ice interface listens to sid is the server id we want to query (mumble can run multiple servers ) @@ -43,7 +43,7 @@ channels number = number of existing channels on server (with temoprary ones) players number = number of all connected users on server registered number = number of connected and registered users on server unregistered number = number of connected and not registered users on server -chanlinks number = linked chanels +chanlinks number = linked channels error number = this is set to 1 when there is error with communicatind with server via ice On any error script exits with status 1, otherwise it is 0. @@ -130,7 +130,7 @@ if(isset($argv[1]) && $argv[1] == "config") echo "graph_info This graph shows the number of connected users on a murmur server\n"; echo "channels.label Chans\n"; echo "players.label Total Users\n"; - echo "registered.label Registerd\n"; + echo "registered.label Registered\n"; echo "unregistered.label Unregistered\n"; echo "chanlinks.label Linked chans\n"; echo "error.label Server status error\n"; diff --git a/plugins/weather/buienradar_ b/plugins/weather/buienradar_ index 2fda3f52..686067dd 100755 --- a/plugins/weather/buienradar_ +++ b/plugins/weather/buienradar_ @@ -152,7 +152,7 @@ if($RAIN eq "yes") { print "rain.value ".$doc->findnodes($stationNode."/rege # Station codes # Static, I know I could do this dynamically, but since they don't change regularly I choose this way -# so I could present two nice rows whitout having to write a buncgh of code... :P +# so I could present two nice rows without having to write a buncgh of code... :P sub print_stations { return "Available station codes, updated last on April 4th 2011\n\n" ." [6391] Venlo (Station Arcen) [6275] Arnhem (Station Arnhem)\n" diff --git a/plugins/wifi/ath9k_ b/plugins/wifi/ath9k_ index a43d844d..69876886 100755 --- a/plugins/wifi/ath9k_ +++ b/plugins/wifi/ath9k_ @@ -333,7 +333,7 @@ class Station: yield "graph_category wireless" yield "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" all_rates = {} - # collect alle unique rates + # collect all unique rates for station in siblings: for rate, details in station._rc_stats.items(): all_rates[rate] = details diff --git a/plugins/wifi/wifi_signal b/plugins/wifi/wifi_signal index 0126320f..a06a8bd9 100755 --- a/plugins/wifi/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -23,7 +23,7 @@ do_config () { echo "graph_args --base 1000 -u 0" echo "graph_vlabel dB" echo "graph_category wireless" - echo "graph_info This graph shows the noise and singal levels of your WiFi devices" + echo "graph_info This graph shows the noise and signal levels of your WiFi devices" awk -F'[ :]*' '/:/ { print $2"_noise.label Noise "$2; diff --git a/plugins/xen/xen-cpu b/plugins/xen/xen-cpu index 76cdca29..af90a60b 100755 --- a/plugins/xen/xen-cpu +++ b/plugins/xen/xen-cpu @@ -57,7 +57,7 @@ if (defined($ARGV[0])) { ); my @domains = `$XM list`; - shift(@domains); # we dont need the header line + shift(@domains); # we don't need the header line my $cnt = "0"; foreach my $domain ( @domains ) { my ($dom,undef) = split(/\s/, $domain, 2); diff --git a/plugins/xen/xen-multi b/plugins/xen/xen-multi index ad4c6812..5de46a98 100755 --- a/plugins/xen/xen-multi +++ b/plugins/xen/xen-multi @@ -31,7 +31,7 @@ This plugin produces four different graphs: CPU usage, memory usage, disk IOs and network traffic. In each graph, all Xen domains (including dom0) have their data stacked, giving -an overall amount of ressources used. +an overall amount of resources used. NOTE: xentop always reports 0 for dom0's disk IOs and network traffic, but both graphs show its entry all the same, so each domain can keep its own color diff --git a/plugins/xen/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 index 06a4a2fc..16db4b8c 100755 --- a/plugins/xen/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -77,7 +77,7 @@ if ( defined($ARGV[0]) ) my @domains = `$XM list`; my $cnt = 0; - shift(@domains); # we dont need the header line + shift(@domains); # we don't need the header line foreach my $domain ( @domains ) { my ($dom,undef) = split(/\s/, $domain, 2); diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency index 0d9905c4..2b0cf872 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency @@ -8,7 +8,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization index 97baef57..8877899e 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization @@ -8,7 +8,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list index 5c5a406c..378bf7ae 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list @@ -8,7 +8,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type index 7070b69f..d15e5b7e 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type @@ -8,7 +8,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch index db10af62..69c1a531 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch +++ b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch @@ -8,7 +8,7 @@ # config (required) # autoconf (optional - used by munin-config) # -# Magick markers (optional - used by munin-config and som installation +# Magick markers (optional - used by munin-config and some installation # scripts): #%# family=auto #%# capabilities=autoconf diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_ index e44caf12..120aff9c 100755 --- a/plugins/zfs/zfs_usage_ +++ b/plugins/zfs/zfs_usage_ @@ -162,7 +162,7 @@ sub do_collect { my ($name, $key, $value, undef ) = (split(/\t/,$line)); # try to preserve underscores: # * duplicate existing ones - # * afterwards replace slashs with single ones + # * afterwards replace slashes with single ones ($name =~ s/_/__/g); ($name =~ s/\//_/g); $filesystems->{$name}->{$key}=$value; @@ -174,8 +174,8 @@ sub do_config_fs { my ($fs) = @_; my $fs_slash = ($fs); # try to restore underscores (see "do_collect" for the reverse operation): - # * substitute all underscores with slashs - # * afterwards transform *double* slashs back into a single underscore + # * substitute all underscores with slashes + # * afterwards transform *double* slashes back into a single underscore ($fs_slash =~ s/_/\//g); ($fs_slash =~ s/\/\//_/g); diff --git a/plugins/zfs/zpool_capacity b/plugins/zfs/zpool_capacity index 360d246e..d59080f1 100755 --- a/plugins/zfs/zpool_capacity +++ b/plugins/zfs/zpool_capacity @@ -8,7 +8,7 @@ These functions are implemented: capacity : to monitor zpool capacity % - fragmentation : to monitor zpool fragmantation % + fragmentation : to monitor zpool fragmentation % allocated : to monitor zpool allocated bytes dedup : to monitor zpool dedup and compress ratio diff --git a/plugins/zope/zope_cache_parameters b/plugins/zope/zope_cache_parameters index 5581177b..268dc7fc 100755 --- a/plugins/zope/zope_cache_parameters +++ b/plugins/zope/zope_cache_parameters @@ -13,7 +13,7 @@ url = "/munin_cache_parameters.py" if len(argv) > 1 and argv[1] == 'config': - # there is probably a better way to display this cached vs targed graph + # there is probably a better way to display this cached vs target graph # as a percentage of target possibly.. print """graph_title Zope cache parameters From f776b7e04fcba70ad76afc8978db45b826e9663a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:13:34 +0100 Subject: [PATCH 571/695] Fix spelling mistakes in variable names Thanks, codespell! --- plugins/mysql/mysql_size_ | 8 ++++---- plugins/network/if | 16 ++++++++-------- plugins/rackspace/rackspace_cdn_count.php | 4 ++-- plugins/rackspace/rackspace_cdn_size.php | 4 ++-- plugins/zope/zope_conflict_errors | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/plugins/mysql/mysql_size_ b/plugins/mysql/mysql_size_ index 4c5eeb6b..9d980255 100755 --- a/plugins/mysql/mysql_size_ +++ b/plugins/mysql/mysql_size_ @@ -48,7 +48,7 @@ my $db = $2; my $MYSQLADMIN = $ENV{mysqladmin} || "mysql"; my %WANTED = ( "Index" => "index", - "Datas" => "datas", + "Data" => "data", ); my $arg = shift(); @@ -65,7 +65,7 @@ if ($arg eq 'config') { exit; } -my $datas = 0; +my $data_count = 0; my $indexes = 0; my (@infos,$info,$i_data,$i_index); @@ -99,12 +99,12 @@ open(SERVICE, "$COMMAND |") while () { (m/(\d+).*?(\d+(?:\.\d+)?)/); - $datas += $1; + $data_count += $1; $indexes += $2; } close(SERVICE); -print("datas.value $datas\n"); +print("data.value $data_count\n"); print("index.value $indexes\n"); diff --git a/plugins/network/if b/plugins/network/if index f2a5f40a..312034aa 100755 --- a/plugins/network/if +++ b/plugins/network/if @@ -1204,36 +1204,36 @@ sub check_field_avialability #------------------------------ percent ------------------------ if($fields->{$field}{'source'}{'calculated'}{'type'} eq 'percent') { - my %avialable; + my %available; for my $a ('full', 'part') { if($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'interface') { - $avialable{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}}); + $available{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}}); } elsif($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'field') { - $avialable{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'}); + $available{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'}); } } - $fields->{$field}{'avialable'}{$if} = ($avialable{'full'} and $avialable{'part'}); + $fields->{$field}{'available'}{$if} = ($available{'full'} and $available{'part'}); } #------------------------------ division ------------------------ elsif($fields->{$field}{'source'}{'calculated'}{'type'} eq 'division') { - my %avialable; + my %available; for my $a ('dividend', 'divider') { if($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'interface') { - $avialable{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}}); + $available{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}}); } elsif($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'field') { - $avialable{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'}); + $available{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'}); } } - $fields->{$field}{'avialable'}{$if} = ($avialable{'dividend'} and $avialable{'divider'}); + $fields->{$field}{'available'}{$if} = ($available{'dividend'} and $available{'divider'}); } #------------------------------ sum ------------------------ elsif($fields->{$field}{'source'}{'calculated'}{'type'} eq 'sum') diff --git a/plugins/rackspace/rackspace_cdn_count.php b/plugins/rackspace/rackspace_cdn_count.php index 111d6590..ca2e66b5 100755 --- a/plugins/rackspace/rackspace_cdn_count.php +++ b/plugins/rackspace/rackspace_cdn_count.php @@ -17,8 +17,8 @@ $api_url='https://auth.api.rackspacecloud.com/v1.0/'; function SplitTwice($content,$first,$second) { $s1=split($first,$content); - $splitted=split($second,$s1[1]); - return trim($splitted[0]); + $tokens=split($second,$s1[1]); + return trim($tokens[0]); } diff --git a/plugins/rackspace/rackspace_cdn_size.php b/plugins/rackspace/rackspace_cdn_size.php index b3b836fd..5f50257b 100755 --- a/plugins/rackspace/rackspace_cdn_size.php +++ b/plugins/rackspace/rackspace_cdn_size.php @@ -16,8 +16,8 @@ $api_url='https://auth.api.rackspacecloud.com/v1.0/'; function SplitTwice($content,$first,$second) { $s1=split($first,$content); - $splitted=split($second,$s1[1]); - return trim($splitted[0]); + $tokens=split($second,$s1[1]); + return trim($tokens[0]); } diff --git a/plugins/zope/zope_conflict_errors b/plugins/zope/zope_conflict_errors index c2e03d35..5458d750 100755 --- a/plugins/zope/zope_conflict_errors +++ b/plugins/zope/zope_conflict_errors @@ -46,9 +46,9 @@ else: log = logs[i] error_count = 0 for line in file(log): - splitted = line.split() - if 'ConflictError' in splitted: - logdate = datetime(*time.strptime(splitted[0], date_format)[:-3]) + tokens = line.split() + if 'ConflictError' in tokens: + logdate = datetime(*time.strptime(tokens[0], date_format)[:-3]) delta = datetime.now() - logdate if delta.days >= 1: continue From 5215545c5be25c504cbee65e8c0c8642cc360765 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:14:03 +0100 Subject: [PATCH 572/695] Plugin murmur_users: fix type in pattern The output probably does not contain the misspelled string "Tiemout". --- plugins/voip/murmur_users | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/voip/murmur_users b/plugins/voip/murmur_users index 5c9ab348..b5a37b5f 100755 --- a/plugins/voip/murmur_users +++ b/plugins/voip/murmur_users @@ -123,7 +123,7 @@ for($i=count($fp);$i>(count($fp)-$limit);--$i) if(!array_key_exists($nick,$seen)){ if( strpos($msg," Connection closed")!==FALSE - || strpos($msg," Tiemout")!==FALSE + || strpos($msg," Timeout")!==FALSE ){ $seen[$nick]['online'] = 0; $offline+=1; From c7f6227a11ed4e40160eda3740e00146426a218a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:14:53 +0100 Subject: [PATCH 573/695] Plugin acpi_batt_: fix spelling mistake in pattern The pattern "batery type" is probably never emitted. --- plugins/battery/acpi_batt_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/battery/acpi_batt_ b/plugins/battery/acpi_batt_ index dfb3adb1..d88804a2 100755 --- a/plugins/battery/acpi_batt_ +++ b/plugins/battery/acpi_batt_ @@ -98,7 +98,7 @@ sub read_data { $val = "unknown"; } - elsif ( $var ne "batery type" or $var ne "serial number" or $var ne "OEM info" ) + elsif ( $var ne "battery type" or $var ne "serial number" or $var ne "OEM info" ) { @tmp = split(" " ,$val); $val = trim($tmp[0]); From 90b60cdb34f59dccfd0a356bc1cc4d3aa9e7c674 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:15:32 +0100 Subject: [PATCH 574/695] Plugin trafic_ro_24h: remove duplicate "curl" token The plugin always emitted an error message (without failing) before. --- plugins/trafic_ro/trafic_ro_24h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/trafic_ro/trafic_ro_24h b/plugins/trafic_ro/trafic_ro_24h index fe49fcf2..1c5d2eb8 100755 --- a/plugins/trafic_ro/trafic_ro_24h +++ b/plugins/trafic_ro/trafic_ro_24h @@ -13,5 +13,5 @@ if [ "$1" = "config" ]; then exit 0 fi -VISITORS="$(echo 'munin' | curl curl --silent -X POST -H 'Content-type: text/xml' -d @- http://api.trafic.ro/rest/0.01/sumar-site/$RID | xmlstarlet sel -t -m "/sumar-site/vizitatori_ultimele_24_ore" -v ".")" +VISITORS="$(echo 'munin' | curl --silent -X POST -H 'Content-type: text/xml' -d @- http://api.trafic.ro/rest/0.01/sumar-site/$RID | xmlstarlet sel -t -m "/sumar-site/vizitatori_ultimele_24_ore" -v ".")" echo "24h_visitors.value" $VISITORS; From 69ab03d647f5382b8f89e41841dde896c997e9e4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:26:38 +0100 Subject: [PATCH 575/695] Plugin mysql_size_all: remove unused variable WANTED The array should have been removed in cba9455cd030bbeeb33d6da77a883e3e2502f669. --- plugins/mysql/mysql_size_all | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/mysql/mysql_size_all b/plugins/mysql/mysql_size_all index f8a4de9f..2dcc3665 100755 --- a/plugins/mysql/mysql_size_all +++ b/plugins/mysql/mysql_size_all @@ -48,10 +48,6 @@ use strict; my $COMMAND; my $MYSQLADMIN = $ENV{mysqladmin} || "mysql"; -my %WANTED = ( "Index" => "index", - "Datas" => "datas", - ); - my $arg = shift(); if ($arg eq 'config') { From f468341a0778ad5212cf8ad0fd3cdf3dcfe2004d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:43:18 +0100 Subject: [PATCH 576/695] Plugin read_serial_temperature: port to python3 --- plugins/system/read_serial_temperature | 61 ++++++++++------------ t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/plugins/system/read_serial_temperature b/plugins/system/read_serial_temperature index dd4fcf44..8329ef9e 100755 --- a/plugins/system/read_serial_temperature +++ b/plugins/system/read_serial_temperature @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Paul Wiegmans (p.wiegmans@bonhoeffer.nl) 2009 dec 18 @@ -12,42 +12,39 @@ Linux: "/dev/usb/ttyUSB[n]" or "/dev/ttyUSB[n]" e.g. "/dev/usb/ttyUSB0" """ -import sys, serial +import sys + +import serial + # Open named port at "19200,8,N,1", 1s timeout:: - def gettemperature(): - ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) - t = 0 - while t<1: - line = ser.readline().strip() - if line: - temp = str(line.split(" ")[0]) # temperature in tenths celsius - i = len(temp)-1 - temp = temp[:i] # return only integer value (as a string) - return temp - t += 1 - ser.close() + connection = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) + # default to munin's value "unknown" + result = "U" + while True: + line = connection.readline().strip() + if line: + # temperature in tenths celsius + temp = str(line.split(" ")[0]) + # return only integer value (as a string) + result = temp[:-1] + break + connection.close() + return result + # shamelessly copied from weather_temp_ - if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - - print "yes" - + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - - print 'graph_title Temperatuur in de serverruimte' - print 'graph_vlabel temperature in C' - print 'graph_category sensors' - print 'temperature.label temperature' - print 'graph_info Dit is de temperatuur in het rek in de serverruimte B104' - print 'graph_scale no' - # lower limit 10, upper limit 50 - print 'graph_args --base 1000 -l 10 -u 50' - + print('graph_title Temperatuur in de serverruimte') + print('graph_vlabel temperature in C') + print('graph_category sensors') + print('temperature.label temperature') + print('graph_info Dit is de temperatuur in het rek in de serverruimte B104') + print('graph_scale no') + # lower limit 10, upper limit 50 + print('graph_args --base 1000 -l 10 -u 50') else: - - print 'temperature.value %s' % gettemperature() - - + print('temperature.value %s' % gettemperature()) diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index e82e1965..91a67d93 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -427,7 +427,6 @@ plugins/system/auth plugins/system/blockhosts plugins/systemd/systemd_units plugins/system/file_length_ -plugins/system/read_serial_temperature plugins/tarsnap/tarsnap plugins/tcp/tcp-retransmissions plugins/tcp/tcp-states From 07c854f276d4cc4de536ca1024b7cd3e76bca148 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:44:33 +0100 Subject: [PATCH 577/695] Plguin mysql_size_all: minor cleanup --- plugins/mysql/mysql_size_all | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/mysql/mysql_size_all b/plugins/mysql/mysql_size_all index 2dcc3665..6d5da300 100755 --- a/plugins/mysql/mysql_size_all +++ b/plugins/mysql/mysql_size_all @@ -65,8 +65,6 @@ if ($arg eq 'config') { sub getDBList; foreach my $db (getDBList()) { - my $datas = 0; - my $indexes = 0; my (@infos,$info,$i_data,$i_index); $COMMAND = "$MYSQLADMIN $ENV{mysqlopts} $db -e 'show table status;' | head -n 1"; @@ -100,16 +98,12 @@ foreach my $db (getDBList()) { while () { (m/(\d+).*?(\d+(?:\.\d+)?)/); - $datas += $1; - $indexes += $2; + $total_size += $1 + $2; } close(SERVICE); - $total_size = $datas+$indexes; } print("$db.value $total_size\n"); -# print("datas.value $datas\n"); -# print("index.value $indexes\n"); } From cdb9a4456f915e8ebd5c5eab689932af88e2469b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 02:57:40 +0100 Subject: [PATCH 578/695] Plugin xfs_frag: fix shellcheck issues --- plugins/disk/xfs_frag | 29 +++++++++++----------- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/plugins/disk/xfs_frag b/plugins/disk/xfs_frag index d41d4fcc..cb3d9843 100755 --- a/plugins/disk/xfs_frag +++ b/plugins/disk/xfs_frag @@ -32,7 +32,7 @@ Paul Saunders L =cut -declare -a ARRY +declare -a TOKENS shopt -s nocasematch case $1 in @@ -42,12 +42,12 @@ graph_title XFS fragmentation graph_vlabel Percent graph_category disk EOF - cat /etc/mtab | awk '{print $2 " " $3}' | while read LINE - do - ARRY=($LINE) - if [[ ${ARRY[1]} =~ xfs ]]; then - FIELDNAME=$(echo ${ARRY[0]} | sed 's/^[^A-Za-z_]/_/; s/[^A-Za-z0-9_]/_/g') - echo "$FIELDNAME.label ${ARRY[0]}" + awk '{print $2 " " $3}' Date: Thu, 26 Mar 2020 03:22:04 +0100 Subject: [PATCH 579/695] Plugin amavis_awk: improve variable names --- plugins/amavis/amavis_awk | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/amavis/amavis_awk b/plugins/amavis/amavis_awk index 04b6e279..7a3741fc 100755 --- a/plugins/amavis/amavis_awk +++ b/plugins/amavis/amavis_awk @@ -69,15 +69,13 @@ fi $LOGTAIL ${MAIL_LOG} $STATEFILE | \ -awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; total= 0 } +awk 'BEGIN { clean_count=0; passed_spam_count=0; blocked_spam_count=0; infected_count=0; total=0 } { - if (index($0, "Passed CLEAN")) { na++ ; total++ } - else if (index($0, "Passed SPAMMY")) { nb++ ; total++ } - else if (index($0, "Blocked SPAMMY")) { nc++ ; total++ } - else if (index($0, "INFECTED")) { nd++ ; total++ } + if (index($0, "Passed CLEAN")) { clean_count++ ; total++ } + else if (index($0, "Passed SPAMMY")) { passed_spam_count++ ; total++ } + else if (index($0, "Blocked SPAMMY")) { blocked_spam_count++ ; total++ } + else if (index($0, "INFECTED")) { infected_count++ ; total++ } } - END { print "clean.value " na"\np_spam.value " nb"\nb_spam.value " nc"\nvirus.value " nd"\ntotal.value " total }' - - + END { print "clean.value " clean_count"\np_spam.value " passed_spam_count"\nb_spam.value " blocked_spam_count"\nvirus.value " infected_count"\ntotal.value " total }' From 68980ed21f701ba79978193384b19b5df0bf72c0 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 03:36:40 +0100 Subject: [PATCH 580/695] Plugin irq: improve variable name Avoid misdetections by codespell. --- plugins/system/irq | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/system/irq b/plugins/system/irq index 6949bea1..8b4bb67e 100755 --- a/plugins/system/irq +++ b/plugins/system/irq @@ -343,10 +343,10 @@ sub prepare_graphs my $field_name = clean_fieldname(sprintf("irq_%s", $irq_name)); append_cpu_limit($limits, $gr, $irq_type, $field_name, $graph_name, $i, $irq_name); $gr->{$graph_name}{'graph'}{'order'} .= ' '.$field_name; - for my $fo (qw(label info)) + for my $this_field (qw(label info)) { - $gr->{$graph_name}{'fields'}{$field_name}{$fo} = replace($fields->{'irq'}{$fo}, ':irq:', $irq_name); - $gr->{$graph_name}{'fields'}{$field_name}{$fo} = replace($gr->{$graph_name}{'fields'}{$field_name}{$fo}, + $gr->{$graph_name}{'fields'}{$field_name}{$this_field} = replace($fields->{'irq'}{$this_field}, ':irq:', $irq_name); + $gr->{$graph_name}{'fields'}{$field_name}{$this_field} = replace($gr->{$graph_name}{'fields'}{$field_name}{$this_field}, ':irqinfo:', exists($IRQi->{'description'}{$irq_type}{$irq_name}) ? $IRQi->{'description'}{$irq_type}{$irq_name} : @@ -368,7 +368,7 @@ sub print_config { printf("multigraph %s\n", $g); for my $go (sort keys %{$config->{$g}{'graph'}}) { printf("graph_%s %s\n", $go, $config->{$g}{'graph'}{$go}); } - for my $f (sort keys %{$config->{$g}{'fields'}}) { for my $fo (sort keys %{$config->{$g}{'fields'}{$f}}) { printf("%s.%s %s\n", $f, $fo, $config->{$g}{'fields'}{$f}{$fo}); } } + for my $f (sort keys %{$config->{$g}{'fields'}}) { for my $this_field (sort keys %{$config->{$g}{'fields'}{$f}}) { printf("%s.%s %s\n", $f, $this_field, $config->{$g}{'fields'}{$f}{$this_field}); } } print "\n"; } } From 59073bff12dbfd8f31375adb8c8bf2f342fe576d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 03:37:13 +0100 Subject: [PATCH 581/695] Plugin postfix_filtered_awk: improve variable name Avoid misdetections by codespell. --- plugins/postfix/postfix_filtered_awk | 29 ++++++++++++---------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/plugins/postfix/postfix_filtered_awk b/plugins/postfix/postfix_filtered_awk index 54dd7b38..08b17d1c 100755 --- a/plugins/postfix/postfix_filtered_awk +++ b/plugins/postfix/postfix_filtered_awk @@ -94,21 +94,16 @@ if [ "$1" = "config" ]; then fi - - $LOGTAIL ${MAIL_LOG} $STATEFILE | \ -awk 'BEGIN { na= 0; nb= 0; nc= 0; nd= 0; ne= 0; nf= 0; ng= 0; nh= 0 ; st= ENVIRON["POLICY"] } - - { - if (index($0, "queued as")) { na++ } - else if (index($0, "Relay access denied")) { nb++ } - else if (index($0, "blocked using")) { nc++ } - else if (index($0, "Helo command rejected")) { nd++ } - else if (index($0, "Client host rejected")) { ne++ } - else if (index($0, "Sender address rejected")) { nf++ } - else if (index($0, "Recipient address rejected")) { ng++ } - else if (st && index($0, st)) { nh++ } - } - END { print "allowed.value " na"\nrelay.value " nb"\nrbl.value " nc"\nhelo.value " nd"\nclient.value " ne"\nsender.value " nf"\nrecipient.value " ng ; if (st) print "policy.value " nh }' - - +awk 'BEGIN { allowed_count=0; relay_denied_count=0; rbl_count=0; helo_rejected_count=0; client_rejected_count=0; sender_rejected_count=0; reciepient_rejected_count=0; policy_count=0 ; st=ENVIRON["POLICY"] } + { + if (index($0, "queued as")) { allowed_count++ } + else if (index($0, "Relay access denied")) { relay_denied_count++ } + else if (index($0, "blocked using")) { rbl_count++ } + else if (index($0, "Helo command rejected")) { helo_rejected_count++ } + else if (index($0, "Client host rejected")) { client_rejected_count++ } + else if (index($0, "Sender address rejected")) { sender_rejected_count++ } + else if (index($0, "Recipient address rejected")) { reciepient_rejected_count++ } + else if (st && index($0, st)) { policy_count++ } + } + END { print "allowed.value " allowed_count"\nrelay.value " relay_denied_count"\nrbl.value " rbl_count"\nhelo.value " helo_rejected_count"\nclient.value " client_rejected_count"\nsender.value " sender_rejected_count"\nrecipient.value " reciepient_rejected_count ; if (st) print "policy.value " policy_count }' From 4cea2f60f9d5c1fe1e401fb71000ef4c7f4ed2d6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 03:45:09 +0100 Subject: [PATCH 582/695] Plugin ts3v2_: switch to unix line endings --- plugins/teamspeak/ts3v2_ | 440 +++++++++++++++++++-------------------- 1 file changed, 220 insertions(+), 220 deletions(-) diff --git a/plugins/teamspeak/ts3v2_ b/plugins/teamspeak/ts3v2_ index 969efb94..91cb9251 100755 --- a/plugins/teamspeak/ts3v2_ +++ b/plugins/teamspeak/ts3v2_ @@ -1,220 +1,220 @@ -#!/usr/bin/perl -w -# ts3v2_ (perl) -# Munin Plugin for Teamspeak3 Servers -# displays the number of connected users on TS3 servers -# and average transferrate per second over 5 min. -# -# You can use it with symlinks for overview and explicit ids. -# Also you can configure the following variables: -# host -# port -# -# by Marc Urben, www.oxi.ch or www.oom.ch -# -# Based on Tim Wulkau's script. Thank you! -# www.wulkau.de -# -####################################################### -# 02.10.10 - v0.3 -# -now works again with 3.0.0-beta29 -# -# 18.04.10 - v0.2 -# -transfer mode added -# -# 13.04.10 - v0.1 -# -initial release -# -###################################################### - -#%# family=auto -#%# capabilities=autoconf suggest - -use strict; -use Net::Telnet; - -# CONFIG HERE! -my $hostname = $ENV{host} || "localhost"; # serveraddress -my $port = $ENV{port} || 10011; # querryport -my $user = $ENV{user} || "serveradmin"; -my $password = $ENV{password} || ""; -my $filename = "ts3v2_"; - -# SCRIPT START! -#init telnet connection -my $name = ""; my $server = ""; my @num; my $id = 0; my $i = 0; my $myserver; my $transfer; -my $telnet = new Net::Telnet(Timeout=>1, Errmode=>"return", Prompt=>"/\r/"); -if (!$telnet->open(Host=>$hostname, Port=>$port)) { - die "Server could not be reached, please check your config!"; -} - -$telnet->waitfor("/Welcome/"); - -if ($password ne "") { - $telnet->cmd("login client_login_name=".$user." client_login_password=".$password); - my $response = $telnet->getline(Timeout=>1); - if ($response !~ "error id=0 msg=ok") { - $telnet->close; - die "ServerQuery login failed: ".$response; - } -} - -#get argument -my $argument = ""; -if ($0 =~ /$filename(\w+)$/i) { - $argument = $1; -} elsif ($ARGV[0] ne "suggest" and $ARGV[0] ne "autoconf") { - die "Error: We need to know what serverid you want, so link this plugin as " - .$filename."1, ".$filename."2 or ".$filename."overview.\n"; -} - -#check for overview, transfer or single server mode -my $mode = ""; -if ($argument eq "overview" or $argument eq "transfer" or (defined $ARGV[0] and ($ARGV[0] eq "suggest" or $ARGV[0] eq "autoconf"))) { - if ($argument eq "overview") { - $mode = "o"; - } elsif($argument eq "transfer") { - $mode = "t"; - } - $telnet->cmd("serverlist"); - my $line = $telnet->getline(Timeout=>1); - my @servers = split(/\|/, $line); - foreach (@servers) { - if ($_ =~ m/virtualserver_id=(\d+) virtual/) { - push(@num, $1); - } else { - die "ERROR: server string not recognized!\n"; - } - } - $telnet->waitfor("/error id=0 msg=ok/"); -} elsif ($argument =~ /^[+-]?\d+$/ ) { - $mode = "s"; - $server = $argument; -} else { - die "ERROR: unknown plugin mode: $argument\n"; -} - -#check for config mode -if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { - print "yes"; - exit 0; -} elsif (exists $ARGV[0] and $ARGV[0] eq "suggest") { - print "overview\n"; - print "transfer\n"; - foreach (@num) { - print $_."\n"; - } - exit 0; -} elsif (exists $ARGV[0] and $ARGV[0] eq "config") { - if ($mode eq "s") { - #single server mode - $telnet->cmd("use sid=".$server); - $telnet->waitfor("/error id=0 msg=ok/"); - $telnet->cmd("serverinfo"); - my $line = $telnet->getline(Timeout=>1); - - if ($line =~ m/virtualserver_id=(\d+) virtual/) { - my $id = $1; - } else { - die "ERROR: server string not recognized!\n"; - } - if ($line =~ m/virtualserver_name=(.*) virtualserver_welcomemessage/) { - $name = $1; - $name =~ s/\\s/ /g; - } else { - die "ERROR: server string not recognized!\n"; - } - $telnet->waitfor("/error id=0 msg=ok/"); - - print "graph_title Teamspeak Users ".$name."\n"; - print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category voip\n"; - print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; - print "users.label Users\n"; - print "users.info Connected users to ".$name."\n"; - print "users.type GAUGE\n"; - - exit 0; - } elsif ($mode eq "o") { - #overview mode - print "graph_title Teamspeak Users Overview\n"; - print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category voip\n"; - print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; - - foreach (@num) { - $telnet->cmd("use sid=".$_); - $telnet->waitfor("/error id=0 msg=ok/"); - $telnet->cmd("serverinfo"); - my $line = $telnet->getline(Timeout=>1); - if ($line =~ m/virtualserver_name=(.*) virtualserver_welcomemessage/) { - $name = $1; - $name =~ s/\\s/ /g; - } else { - die "ERROR: server string not recognized!\n"; - } - $telnet->waitfor("/error id=0 msg=ok/"); - print $_.".label ".$name."\n"; - print $_.".info Users connected on ".$name."\n"; - print $_.".type GAUGE\n"; - } - exit 0; - } elsif ($mode eq "t") { - #transfer mode - print "graph_title Teamspeak Transfer Overview\n"; - print "graph_vlabel Teamspeak Transfer\n"; - print "graph_category voip\n"; - print "graph_info This graph shows the Teamspeak3 Transfer Overview\n"; - print "transfer.label ~ Transfer per second\n"; - print "transfer.info Transfer per second over 5 min\n"; - print "transfer.type DERIVE\n"; - exit 0; - } -} else { - #go go magic, go! - if ($mode eq "s") { - #single mode - $telnet->cmd("use sid=".$server); - $telnet->waitfor("/error id=0 msg=ok/"); - $telnet->cmd("serverinfo"); - - my $line = $telnet->getline(Timeout=>1); - if ($line =~ m/virtualserver_clientsonline=(\d+) /) { - print "users.value ".($1-1)."\n"; - } else { - print "users.value 0\n"; - } - $telnet->waitfor("/error id=0 msg=ok/"); - } elsif ($mode eq "o") { - #overview mode - for (@num) { - $telnet->waitfor("/Welcome/"); - $telnet->cmd("use sid=".$_); - $telnet->waitfor("/error id=0 msg=ok/"); - $telnet->cmd("serverinfo"); - - my $line = $telnet->getline(Timeout=>1); - if ($line =~ m/virtualserver_clientsonline=(\d+) /) { - print $_.".value ".($1-1)."\n"; - } else { - print $_.".value 0\n"; - } - $telnet->waitfor("/error id=0 msg=ok/"); - } - } elsif ($mode eq "t") { - $telnet->cmd("hostinfo"); - my @tsave; - my $line = $telnet->getline(Timeout=>1); - if ($line =~ m/connection_bytes_received_total=(\d+) connection_bandwidth_sent_last_second_total/) { - $transfer = $1; - print "transfer.value ".(($transfer-($transfer%300))/300)."\n"; - } else { - die "ERROR: server string not recognized!\n"; - } - $telnet->waitfor("/error id=0 msg=ok/"); - - } -} - -#close telnet connection -$telnet->close; -exit; +#!/usr/bin/perl -w +# ts3v2_ (perl) +# Munin Plugin for Teamspeak3 Servers +# displays the number of connected users on TS3 servers +# and average transferrate per second over 5 min. +# +# You can use it with symlinks for overview and explicit ids. +# Also you can configure the following variables: +# host +# port +# +# by Marc Urben, www.oxi.ch or www.oom.ch +# +# Based on Tim Wulkau's script. Thank you! +# www.wulkau.de +# +####################################################### +# 02.10.10 - v0.3 +# -now works again with 3.0.0-beta29 +# +# 18.04.10 - v0.2 +# -transfer mode added +# +# 13.04.10 - v0.1 +# -initial release +# +###################################################### + +#%# family=auto +#%# capabilities=autoconf suggest + +use strict; +use Net::Telnet; + +# CONFIG HERE! +my $hostname = $ENV{host} || "localhost"; # serveraddress +my $port = $ENV{port} || 10011; # querryport +my $user = $ENV{user} || "serveradmin"; +my $password = $ENV{password} || ""; +my $filename = "ts3v2_"; + +# SCRIPT START! +#init telnet connection +my $name = ""; my $server = ""; my @num; my $id = 0; my $i = 0; my $myserver; my $transfer; +my $telnet = new Net::Telnet(Timeout=>1, Errmode=>"return", Prompt=>"/\r/"); +if (!$telnet->open(Host=>$hostname, Port=>$port)) { + die "Server could not be reached, please check your config!"; +} + +$telnet->waitfor("/Welcome/"); + +if ($password ne "") { + $telnet->cmd("login client_login_name=".$user." client_login_password=".$password); + my $response = $telnet->getline(Timeout=>1); + if ($response !~ "error id=0 msg=ok") { + $telnet->close; + die "ServerQuery login failed: ".$response; + } +} + +#get argument +my $argument = ""; +if ($0 =~ /$filename(\w+)$/i) { + $argument = $1; +} elsif ($ARGV[0] ne "suggest" and $ARGV[0] ne "autoconf") { + die "Error: We need to know what serverid you want, so link this plugin as " + .$filename."1, ".$filename."2 or ".$filename."overview.\n"; +} + +#check for overview, transfer or single server mode +my $mode = ""; +if ($argument eq "overview" or $argument eq "transfer" or (defined $ARGV[0] and ($ARGV[0] eq "suggest" or $ARGV[0] eq "autoconf"))) { + if ($argument eq "overview") { + $mode = "o"; + } elsif($argument eq "transfer") { + $mode = "t"; + } + $telnet->cmd("serverlist"); + my $line = $telnet->getline(Timeout=>1); + my @servers = split(/\|/, $line); + foreach (@servers) { + if ($_ =~ m/virtualserver_id=(\d+) virtual/) { + push(@num, $1); + } else { + die "ERROR: server string not recognized!\n"; + } + } + $telnet->waitfor("/error id=0 msg=ok/"); +} elsif ($argument =~ /^[+-]?\d+$/ ) { + $mode = "s"; + $server = $argument; +} else { + die "ERROR: unknown plugin mode: $argument\n"; +} + +#check for config mode +if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { + print "yes"; + exit 0; +} elsif (exists $ARGV[0] and $ARGV[0] eq "suggest") { + print "overview\n"; + print "transfer\n"; + foreach (@num) { + print $_."\n"; + } + exit 0; +} elsif (exists $ARGV[0] and $ARGV[0] eq "config") { + if ($mode eq "s") { + #single server mode + $telnet->cmd("use sid=".$server); + $telnet->waitfor("/error id=0 msg=ok/"); + $telnet->cmd("serverinfo"); + my $line = $telnet->getline(Timeout=>1); + + if ($line =~ m/virtualserver_id=(\d+) virtual/) { + my $id = $1; + } else { + die "ERROR: server string not recognized!\n"; + } + if ($line =~ m/virtualserver_name=(.*) virtualserver_welcomemessage/) { + $name = $1; + $name =~ s/\\s/ /g; + } else { + die "ERROR: server string not recognized!\n"; + } + $telnet->waitfor("/error id=0 msg=ok/"); + + print "graph_title Teamspeak Users ".$name."\n"; + print "graph_vlabel Connected Teamspeak Users\n"; + print "graph_category voip\n"; + print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; + print "users.label Users\n"; + print "users.info Connected users to ".$name."\n"; + print "users.type GAUGE\n"; + + exit 0; + } elsif ($mode eq "o") { + #overview mode + print "graph_title Teamspeak Users Overview\n"; + print "graph_vlabel Connected Teamspeak Users\n"; + print "graph_category voip\n"; + print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; + + foreach (@num) { + $telnet->cmd("use sid=".$_); + $telnet->waitfor("/error id=0 msg=ok/"); + $telnet->cmd("serverinfo"); + my $line = $telnet->getline(Timeout=>1); + if ($line =~ m/virtualserver_name=(.*) virtualserver_welcomemessage/) { + $name = $1; + $name =~ s/\\s/ /g; + } else { + die "ERROR: server string not recognized!\n"; + } + $telnet->waitfor("/error id=0 msg=ok/"); + print $_.".label ".$name."\n"; + print $_.".info Users connected on ".$name."\n"; + print $_.".type GAUGE\n"; + } + exit 0; + } elsif ($mode eq "t") { + #transfer mode + print "graph_title Teamspeak Transfer Overview\n"; + print "graph_vlabel Teamspeak Transfer\n"; + print "graph_category voip\n"; + print "graph_info This graph shows the Teamspeak3 Transfer Overview\n"; + print "transfer.label ~ Transfer per second\n"; + print "transfer.info Transfer per second over 5 min\n"; + print "transfer.type DERIVE\n"; + exit 0; + } +} else { + #go go magic, go! + if ($mode eq "s") { + #single mode + $telnet->cmd("use sid=".$server); + $telnet->waitfor("/error id=0 msg=ok/"); + $telnet->cmd("serverinfo"); + + my $line = $telnet->getline(Timeout=>1); + if ($line =~ m/virtualserver_clientsonline=(\d+) /) { + print "users.value ".($1-1)."\n"; + } else { + print "users.value 0\n"; + } + $telnet->waitfor("/error id=0 msg=ok/"); + } elsif ($mode eq "o") { + #overview mode + for (@num) { + $telnet->waitfor("/Welcome/"); + $telnet->cmd("use sid=".$_); + $telnet->waitfor("/error id=0 msg=ok/"); + $telnet->cmd("serverinfo"); + + my $line = $telnet->getline(Timeout=>1); + if ($line =~ m/virtualserver_clientsonline=(\d+) /) { + print $_.".value ".($1-1)."\n"; + } else { + print $_.".value 0\n"; + } + $telnet->waitfor("/error id=0 msg=ok/"); + } + } elsif ($mode eq "t") { + $telnet->cmd("hostinfo"); + my @tsave; + my $line = $telnet->getline(Timeout=>1); + if ($line =~ m/connection_bytes_received_total=(\d+) connection_bandwidth_sent_last_second_total/) { + $transfer = $1; + print "transfer.value ".(($transfer-($transfer%300))/300)."\n"; + } else { + die "ERROR: server string not recognized!\n"; + } + $telnet->waitfor("/error id=0 msg=ok/"); + + } +} + +#close telnet connection +$telnet->close; +exit; From 4d8456af03eb0ce08af1793e13ed9bc2a2fa260f Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 03:48:22 +0100 Subject: [PATCH 583/695] Introduce spelling tests via codespell --- .codespell.exclude | 45 +++++++++++++++++++++++++++++++++++++++++ .codespell.ignore-words | 5 +++++ .travis.yml | 2 ++ Makefile | 7 +++++++ 4 files changed, 59 insertions(+) create mode 100644 .codespell.exclude create mode 100644 .codespell.ignore-words create mode 100644 Makefile diff --git a/.codespell.exclude b/.codespell.exclude new file mode 100644 index 00000000..a27d8a60 --- /dev/null +++ b/.codespell.exclude @@ -0,0 +1,45 @@ + echo "succes.label Login success" + echo "succes.draw AREA" +echo "succes.value $success" +Tim Small + accesss => 'NFSPROC3_ACCESS (Check Access Permission)', +# Beginn des modifizierten Skriptes - Beginning of the modified script # + [ /(\d+) packets with ECN ECT\(0\) bit set$/, [ [ :packets, 'ECT(0) bit' ] ] ], + [ /(\d+) packets with ECN ECT\(1\) bit set$/, [ [ :packets, 'ECT(1) bit' ] ] ], + [ /(\d+) ARP entrys? timed out$/, [ [ :entries, 'timed out' ] ] ], +echo -en "recieved.value " + print "require dont.graph.anything [0-9]+\n" +# Informations générales : +# Marge de bruit 5.10 dB 5.60 dB +# Adresse MAC Freebox XX:XX:XX:XX:XX:XX +# Adresse IP 203.0.113.60 +# Adresse IP privée 192.0.2.1 +# Adresse IP DMZ 192.0.2.2 +# Adresse IP Freeplayer 192.0.2.0 +# Adresse MAC Adresse IP +# Linjen som grep'es ut kan se ut som dette: +# Linjen som grep'es ut kan se ut som dette: +VISITORS="$(echo 'munin' | curl --silent -X POST -H 'Content-type: text/xml' -d @- http://api.trafic.ro/rest/0.01/sumar-site/$RID | xmlstarlet sel -t -m "/sumar-site/vizitatori_ultimele_24_ore" -v ".")" +# - Serien Timer werden nun separat gezaehlt (anzahl pro Woche) +# - Timer werden nur ignoriert wenn sie 0(inaktiv) als Status haben + primary muss ein iterable oder StringType sein + secondary muss iterable oder StringType sein + print ("d.label Design voltage\nd.type GAUGE\nd.draw AREA\n"); + if ($str =~ /^(no?|0|f(alse)?)$/i) { +# (Based off the perl munin plugin by Joan Carles Soler) + + +Christian Loos +# - kein div 0 Fehler mehr wenn der Host nicht zu erreichen ist +# - im Namen kann Munin-konform der Hostname mit angegeben werden: vdr_localhost vdr_192.168.0.2, ... (localhost ist default) + print "platte.info Angabe, wieviel der von VDR beschreibbaren Festplatten belegt ist.\n"; + echo 'graph_title Samba Locked Files' + echo 'graph_vlabel number of locked files' + echo 'samba_locked.label Locked Files' + echo 'lock.label Locked files' + echo 'graph_info This graph shows the Memory used by process' + echo 'shares.label shared files' +# Based on Tim Wulkau's script. Thank you! + echo 'graph_title Memory usage by process' + echo 'VmSize.info The size of the virtual memory allocated to the process' +grep -v 'Preparing to download files' | \ diff --git a/.codespell.ignore-words b/.codespell.ignore-words new file mode 100644 index 00000000..67699dd7 --- /dev/null +++ b/.codespell.ignore-words @@ -0,0 +1,5 @@ +cas +conexant +ende +referer +wan diff --git a/.travis.yml b/.travis.yml index 10cd4ddb..eafd3de9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ dist: bionic addons: apt: packages: + - codespell - devscripts - python - python-flake8 @@ -84,4 +85,5 @@ before_install: # - MythTV script: + - make lint - prove diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ea8a844f --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: lint +lint: + # TODO: add "--ignore-words .codespell.ignore-words" as soon as travis supports a newer + # testing environment (containing codespell 0.11 or later). + find plugins/ -type f -not -name "*.png" -not -name "*.conf" -not -name "*.jar" -print0 \ + | xargs -0 codespell \ + --exclude-file .codespell.exclude From 1e81c032d5fa48d9435915db0182c3a56d24bfbf Mon Sep 17 00:00:00 2001 From: pboesch Date: Mon, 3 Feb 2020 15:04:06 +0100 Subject: [PATCH 584/695] twemproxy: add python3 support --- plugins/twemproxy/nutcracker_requests_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/twemproxy/nutcracker_requests_ b/plugins/twemproxy/nutcracker_requests_ index bc06d654..655ed61c 100755 --- a/plugins/twemproxy/nutcracker_requests_ +++ b/plugins/twemproxy/nutcracker_requests_ @@ -41,7 +41,7 @@ def process_data(): if(type(value) == dict): total = 0 # get server requests - for pool_key, pool_value in value.iteritems(): + for pool_key, pool_value in value.items(): if(type(pool_value) == dict): total += pool_value["requests"] print "requests_"+key+".value"+" "+str(total) @@ -52,7 +52,7 @@ def process_config(): print "graph_vlabel requests/s" data = get_stats(); - for key, value in data.iteritems(): + for key, value in data.items(): if(type(value) == dict): print "requests_"+key+".label "+key print "requests_"+key+".type COUNTER" From 8a52ef23f57dc79eb374598d18d32b69d6aba119 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Fri, 22 Nov 2019 19:58:59 +0100 Subject: [PATCH 585/695] Monitor mikrotik RAM usage --- plugins/router/snmp__mikrotik_ram | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 plugins/router/snmp__mikrotik_ram diff --git a/plugins/router/snmp__mikrotik_ram b/plugins/router/snmp__mikrotik_ram new file mode 100755 index 00000000..5c6c3d54 --- /dev/null +++ b/plugins/router/snmp__mikrotik_ram @@ -0,0 +1,106 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2019 Alejandro Suarez (teconecta.es) +# +# Munin plugin to monitor Mikrotik routers RAM utilization. +# Based on snmp__if_ plugin. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#%# family=snmpauto +#%# capabilities=snmpconf + +use strict; +use Net::SNMP; + +my $DEBUG = 0; + +my $host = $ENV{host} || undef; +my $port = $ENV{port} || 161; +my $community = $ENV{community} || "public"; + +my $response; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") +{ + print "require 1.3.6.1.2.1.25.2.3.1.5.65536 \n"; + exit 0; +} + +if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_mikrotik_ram$/) +{ + $host = $1; + if ($host =~ /^([^:]+):(\d+)$/) + { + $host = $1; + $port = $2; + } +} +elsif (!defined($host)) +{ + print "# Debug: $0 -- $1\n" if $DEBUG; + die "# Error: couldn't understand what I'm supposed to monitor."; +} + +my $sysRAMUsage = "1.3.6.1.2.1.25.2.3.1.6.65536"; +my $sysRAMTotal = "1.3.6.1.2.1.25.2.3.1.5.65536"; + +my ($session, $error) = Net::SNMP->session( + -hostname => $host, + -community => $community, + -port => $port + ); + + +if (!defined ($session)) +{ + die "Croaking: $error"; +} + + +if ($ARGV[0] and $ARGV[0] eq "config") +{ + print "host_name $host\n"; + if (!defined ($response = $session->get_request($sysRAMTotal))) + { + die "Croaking: " . $session->error(); + } +# print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotal} * 1024) . "\n"; + print "graph_title RAM usage\n"; + print "graph_category system\n"; + print "graph_info This graph shows the router's memory usage.\n"; + print "graph_order Total Used\n"; + print "graph_vlabel bytes\n"; + print "sysRAMTotal.label Total Memory\n"; + print "sysRAMTotal.draw AREA\n"; + print "sysRAMUsage.label Used Memory\n"; + print "sysRAMUsage.draw AREA\n"; + exit 0; +} + + +if (defined ($response = $session->get_request(-varbindlist => [$sysRAMUsage, $sysRAMTotal]))) +{ + print "sysRAMUsage.value ", $response->{$sysRAMUsage}*1024, "\n"; + print "sysRAMTotal.value ", $response->{$sysRAMTotal}*1024, "\n"; +} +else +{ + print "sysRAMUsage.value U\n"; +} + +# vim:syntax=perl From c04775f88b9b831e4f7d7c0ccd9cba53103833c0 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Fri, 22 Nov 2019 20:13:32 +0100 Subject: [PATCH 586/695] Monitor mikrotik Flash disk usage --- plugins/router/snmp__mikrotik_flash | 106 ++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 plugins/router/snmp__mikrotik_flash diff --git a/plugins/router/snmp__mikrotik_flash b/plugins/router/snmp__mikrotik_flash new file mode 100755 index 00000000..cd5e629e --- /dev/null +++ b/plugins/router/snmp__mikrotik_flash @@ -0,0 +1,106 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2019 Alejandro Suarez (teconecta.es) +# +# Munin plugin to monitor Mikrotik routers RAM utilization. +# Based on snmp__if_ plugin. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#%# family=snmpauto +#%# capabilities=snmpconf + +use strict; +use Net::SNMP; + +my $DEBUG = 0; + +my $host = $ENV{host} || undef; +my $port = $ENV{port} || 161; +my $community = $ENV{community} || "public"; + +my $response; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") +{ + print "require 1.3.6.1.2.1.25.2.3.1.5.65536 \n"; + exit 0; +} + +if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_mikrotik_flash$/) +{ + $host = $1; + if ($host =~ /^([^:]+):(\d+)$/) + { + $host = $1; + $port = $2; + } +} +elsif (!defined($host)) +{ + print "# Debug: $0 -- $1\n" if $DEBUG; + die "# Error: couldn't understand what I'm supposed to monitor."; +} + +my $sysRAMUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; +my $sysRAMTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; + +my ($session, $error) = Net::SNMP->session( + -hostname => $host, + -community => $community, + -port => $port + ); + + +if (!defined ($session)) +{ + die "Croaking: $error"; +} + + +if ($ARGV[0] and $ARGV[0] eq "config") +{ + print "host_name $host\n"; + if (!defined ($response = $session->get_request($sysRAMTotal))) + { + die "Croaking: " . $session->error(); + } +# print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotal} * 1024) . "\n"; + print "graph_title Flash disk usage\n"; + print "graph_category system\n"; + print "graph_info This graph shows the router's flash disk usage.\n"; + print "graph_order Total Used\n"; + print "graph_vlabel bytes\n"; + print "sysRAMTotal.label Total Memory\n"; + print "sysRAMTotal.draw AREA\n"; + print "sysRAMUsage.label Used Memory\n"; + print "sysRAMUsage.draw AREA\n"; + exit 0; +} + + +if (defined ($response = $session->get_request(-varbindlist => [$sysRAMUsage, $sysRAMTotal]))) +{ + print "sysRAMUsage.value ", $response->{$sysRAMUsage}*1024, "\n"; + print "sysRAMTotal.value ", $response->{$sysRAMTotal}*1024, "\n"; +} +else +{ + print "sysRAMUsage.value U\n"; +} + +# vim:syntax=perl From 1acb0665e8b90c485f4781382aa5371d9fdef480 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Fri, 29 Nov 2019 12:59:05 +0100 Subject: [PATCH 587/695] Added suggestion made by @sumpfralle --- plugins/router/snmp__mikrotik_flash | 28 ++++++++++++++-------------- plugins/router/snmp__mikrotik_ram | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/router/snmp__mikrotik_flash b/plugins/router/snmp__mikrotik_flash index cd5e629e..23051aee 100755 --- a/plugins/router/snmp__mikrotik_flash +++ b/plugins/router/snmp__mikrotik_flash @@ -2,7 +2,7 @@ # # Copyright (C) 2019 Alejandro Suarez (teconecta.es) # -# Munin plugin to monitor Mikrotik routers RAM utilization. +# Munin plugin to monitor Mikrotik routers Falsh disk utilization. # Based on snmp__if_ plugin. # # This program is free software; you can redistribute it and/or @@ -55,8 +55,8 @@ elsif (!defined($host)) die "# Error: couldn't understand what I'm supposed to monitor."; } -my $sysRAMUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; -my $sysRAMTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; +my $sysFalshUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; +my $sysFalshTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; my ($session, $error) = Net::SNMP->session( -hostname => $host, @@ -74,33 +74,33 @@ if (!defined ($session)) if ($ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; - if (!defined ($response = $session->get_request($sysRAMTotal))) + if (!defined ($response = $session->get_request($sysFalshTotal))) { die "Croaking: " . $session->error(); } -# print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; - print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotal} * 1024) . "\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysFalshTotal} * 1024) . "\n"; print "graph_title Flash disk usage\n"; print "graph_category system\n"; print "graph_info This graph shows the router's flash disk usage.\n"; print "graph_order Total Used\n"; print "graph_vlabel bytes\n"; - print "sysRAMTotal.label Total Memory\n"; - print "sysRAMTotal.draw AREA\n"; - print "sysRAMUsage.label Used Memory\n"; - print "sysRAMUsage.draw AREA\n"; + print "sysFalshTotal.label Total Memory\n"; + print "sysFalshTotal.draw AREA\n"; + print "sysFalshUsage.label Used Memory\n"; + print "sysFalshUsage.draw AREA\n"; exit 0; } -if (defined ($response = $session->get_request(-varbindlist => [$sysRAMUsage, $sysRAMTotal]))) +if (defined ($response = $session->get_request(-varbindlist => [$sysFalshUsage, $sysFalshTotal]))) { - print "sysRAMUsage.value ", $response->{$sysRAMUsage}*1024, "\n"; - print "sysRAMTotal.value ", $response->{$sysRAMTotal}*1024, "\n"; + print "sysFalshUsage.value ", $response->{$sysFalshUsage}*1024, "\n"; + print "sysFalshTotal.value ", $response->{$sysFalshTotal}*1024, "\n"; } else { - print "sysRAMUsage.value U\n"; + print "sysFalshUsage.value U\n"; + print "sysFalshTotal.value U\n"; } # vim:syntax=perl diff --git a/plugins/router/snmp__mikrotik_ram b/plugins/router/snmp__mikrotik_ram index 5c6c3d54..72433f85 100755 --- a/plugins/router/snmp__mikrotik_ram +++ b/plugins/router/snmp__mikrotik_ram @@ -78,7 +78,6 @@ if ($ARGV[0] and $ARGV[0] eq "config") { die "Croaking: " . $session->error(); } -# print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotal} * 1024) . "\n"; print "graph_title RAM usage\n"; print "graph_category system\n"; @@ -101,6 +100,7 @@ if (defined ($response = $session->get_request(-varbindlist => [$sysRAMUsage, $s else { print "sysRAMUsage.value U\n"; + print "sysRAMTotal.value U\n"; } # vim:syntax=perl From e1bc7ad6c74306f97d22adfcef166bd8b9dffcc9 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Wed, 25 Mar 2020 20:07:40 +0100 Subject: [PATCH 588/695] Fixed typo in variables --- plugins/router/snmp__mikrotik_flash | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/router/snmp__mikrotik_flash b/plugins/router/snmp__mikrotik_flash index 23051aee..ee6d2bfd 100755 --- a/plugins/router/snmp__mikrotik_flash +++ b/plugins/router/snmp__mikrotik_flash @@ -55,8 +55,8 @@ elsif (!defined($host)) die "# Error: couldn't understand what I'm supposed to monitor."; } -my $sysFalshUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; -my $sysFalshTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; +my $sysFlashUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; +my $sysFlashTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; my ($session, $error) = Net::SNMP->session( -hostname => $host, @@ -74,33 +74,34 @@ if (!defined ($session)) if ($ARGV[0] and $ARGV[0] eq "config") { print "host_name $host\n"; - if (!defined ($response = $session->get_request($sysFalshTotal))) + if (!defined ($response = $session->get_request($sysFlashTotal))) { die "Croaking: " . $session->error(); } - print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysFalshTotal} * 1024) . "\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysFlashTotal} * 1024) . "\n"; print "graph_title Flash disk usage\n"; print "graph_category system\n"; print "graph_info This graph shows the router's flash disk usage.\n"; print "graph_order Total Used\n"; print "graph_vlabel bytes\n"; - print "sysFalshTotal.label Total Memory\n"; - print "sysFalshTotal.draw AREA\n"; - print "sysFalshUsage.label Used Memory\n"; - print "sysFalshUsage.draw AREA\n"; + print "sysFlashTotal.label Total Memory\n"; + print "sysFlashTotal.draw AREA\n"; + print "sysFlashUsage.label Used Memory\n"; + print "sysFlashUsage.draw AREA\n"; exit 0; } -if (defined ($response = $session->get_request(-varbindlist => [$sysFalshUsage, $sysFalshTotal]))) +if (defined ($response = $session->get_request(-varbindlist => [$sysFlashUsage, $sysFlashTotal]))) { - print "sysFalshUsage.value ", $response->{$sysFalshUsage}*1024, "\n"; - print "sysFalshTotal.value ", $response->{$sysFalshTotal}*1024, "\n"; + print "sysFlashUsage.value ", $response->{$sysFlashUsage}*1024, "\n"; + print "sysFlashTotal.value ", $response->{$sysFlashTotal}*1024, "\n"; } else { - print "sysFalshUsage.value U\n"; - print "sysFalshTotal.value U\n"; + print "sysFlashUsage.value U\n"; + print "sysFlashTotal.value U\n"; } # vim:syntax=perl + From e9d20f6330224f502bd2ff71724d903e72abbfe6 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Wed, 25 Mar 2020 20:19:29 +0100 Subject: [PATCH 589/695] Added PerlDoc header --- plugins/router/snmp__mikrotik_flash | 51 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/plugins/router/snmp__mikrotik_flash b/plugins/router/snmp__mikrotik_flash index ee6d2bfd..c9d5f2f2 100755 --- a/plugins/router/snmp__mikrotik_flash +++ b/plugins/router/snmp__mikrotik_flash @@ -1,28 +1,41 @@ #!/usr/bin/perl -w -# -# Copyright (C) 2019 Alejandro Suarez (teconecta.es) -# -# Munin plugin to monitor Mikrotik routers Falsh disk utilization. -# Based on snmp__if_ plugin. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +=head1 NAME + +snmp__mikrotik_flash - Munin plugin to monitor Mikrotik routers Falsh disk utilization. + +=head1 APPLICABLE SYSTEMS + +Mikrotik Routers + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + [snmp_*] + env.version 2 + env.community public + +=head1 MAGIC MARKERS #%# family=snmpauto #%# capabilities=snmpconf +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2020 Alejandro Suarez (teconecta.es) +Based on snmp__if_ plugin. + +=head1 LICENSE + +GPLv2. + +=cut + use strict; use Net::SNMP; From 389ac9563864b5920948c95cb8735089aa00b205 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Wed, 25 Mar 2020 20:21:39 +0100 Subject: [PATCH 590/695] Added PerlDoc header --- plugins/router/snmp__mikrotik_ram | 51 +++++++++++++++++++------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/plugins/router/snmp__mikrotik_ram b/plugins/router/snmp__mikrotik_ram index 72433f85..ce4949bf 100755 --- a/plugins/router/snmp__mikrotik_ram +++ b/plugins/router/snmp__mikrotik_ram @@ -1,28 +1,41 @@ #!/usr/bin/perl -w -# -# Copyright (C) 2019 Alejandro Suarez (teconecta.es) -# -# Munin plugin to monitor Mikrotik routers RAM utilization. -# Based on snmp__if_ plugin. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +=head1 NAME + +snmp__mikrotik_flash - Munin plugin to monitor Mikrotik routers RAM utilization. + +=head1 APPLICABLE SYSTEMS + +Mikrotik Routers + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + [snmp_*] + env.version 2 + env.community public + +=head1 MAGIC MARKERS #%# family=snmpauto #%# capabilities=snmpconf +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2020 Alejandro Suarez (teconecta.es) +Based on snmp__if_ plugin. + +=head1 LICENSE + +GPLv2. + +=cut + use strict; use Net::SNMP; From 10534d0dd00113ddd4f031e460730eaddca2a6b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 26 Mar 2020 01:17:17 +0100 Subject: [PATCH 591/695] Merge SNMP plugins for Mikrotik routers into a single multigraph plugin --- plugins/router/snmp__mikrotik | 139 ++++++++++++++++++++++++++++ plugins/router/snmp__mikrotik_flash | 120 ------------------------ plugins/router/snmp__mikrotik_ram | 119 ------------------------ 3 files changed, 139 insertions(+), 239 deletions(-) create mode 100755 plugins/router/snmp__mikrotik delete mode 100755 plugins/router/snmp__mikrotik_flash delete mode 100755 plugins/router/snmp__mikrotik_ram diff --git a/plugins/router/snmp__mikrotik b/plugins/router/snmp__mikrotik new file mode 100755 index 00000000..5f6e3340 --- /dev/null +++ b/plugins/router/snmp__mikrotik @@ -0,0 +1,139 @@ +#!/usr/bin/perl -w + +=head1 NAME + +snmp__mikrotik - monitor Mikrotik routers via SNMP + +=head1 APPLICABLE SYSTEMS + +Mikrotik Routers + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + [snmp_*] + env.version 2 + env.community public + +=head1 MAGIC MARKERS + +#%# family=snmpauto +#%# capabilities=snmpconf + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2020 Alejandro Suarez (teconecta.es) +Based on snmp__if_ plugin. + +=head1 LICENSE + +GPLv2. + +=cut + +use strict; +use Net::SNMP; + +my $DEBUG = 0; + +my $host = $ENV{host} || undef; +my $port = $ENV{port} || 161; +my $community = $ENV{community} || "public"; + +my $sysFlashUsageOID = "1.3.6.1.2.1.25.2.3.1.6.131072"; +my $sysFlashTotalOID = "1.3.6.1.2.1.25.2.3.1.5.131072"; +my $sysRAMUsageOID = "1.3.6.1.2.1.25.2.3.1.6.65536"; +my $sysRAMTotalOID = "1.3.6.1.2.1.25.2.3.1.5.65536"; + +my $response; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "require $sysFlashUsageOID\n"; + print "require $sysFlashTotalOID\n"; + print "require $sysRAMUsageOID\n"; + print "require $sysRAMTotalOID\n"; + exit 0; +} + +if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_mikrotik$/) { + $host = $1; + if ($host =~ /^([^:]+):(\d+)$/) { + $host = $1; + $port = $2; + } +} elsif (!defined($host)) { + print "# Debug: $0 -- $1\n" if $DEBUG; + die "# Error: couldn't understand what I'm supposed to monitor."; +} + +my ($session, $error) = Net::SNMP->session( + -hostname => $host, + -community => $community, + -port => $port + ); + + +die "Croaking: $error" unless (defined ($session)); + + +if ($ARGV[0] and $ARGV[0] eq "config") { + print "host_name $host\n"; + $response = $session->get_request($sysFlashTotalOID); + if (defined $response) { + print "multigraph flash\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysFlashTotalOID} * 1024) . "\n"; + print "graph_title Flash disk usage\n"; + print "graph_category system\n"; + print "graph_info This graph shows the router's flash disk usage.\n"; + print "graph_order Total Used\n"; + print "graph_vlabel bytes\n"; + print "sysFlashTotal.label Total Memory\n"; + print "sysFlashTotal.draw AREA\n"; + print "sysFlashUsage.label Used Memory\n"; + print "sysFlashUsage.draw AREA\n"; + } + + $response = $session->get_request($sysRAMTotalOID); + if (defined $response) { + print "multigraph ram\n"; + print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotalOID} * 1024) . "\n"; + print "graph_title RAM usage\n"; + print "graph_category system\n"; + print "graph_info This graph shows the router's memory usage.\n"; + print "graph_order Total Used\n"; + print "graph_vlabel bytes\n"; + print "sysRAMTotal.label Total Memory\n"; + print "sysRAMTotal.draw AREA\n"; + print "sysRAMUsage.label Used Memory\n"; + print "sysRAMUsage.draw AREA\n"; + } + exit 0 unless (($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1); +} + + +print "multigraph flash\n"; +$response = $session->get_request(-varbindlist => [$sysFlashUsageOID, $sysFlashTotalOID]); +if (defined $response) { + print "sysFlashUsage.value ", $response->{$sysFlashUsageOID}*1024, "\n"; + print "sysFlashTotal.value ", $response->{$sysFlashTotalOID}*1024, "\n"; +} else { + print "sysFlashUsage.value U\n"; + print "sysFlashTotal.value U\n"; +} + +print "multigraph ram\n"; +$response = $session->get_request(-varbindlist => [$sysRAMUsageOID, $sysRAMTotalOID]); +if (defined $response) { + print "sysRAMUsage.value ", $response->{$sysRAMUsageOID}*1024, "\n"; + print "sysRAMTotal.value ", $response->{$sysRAMTotalOID}*1024, "\n"; +} else { + print "sysRAMUsage.value U\n"; + print "sysRAMTotal.value U\n"; +} + +# vim:syntax=perl diff --git a/plugins/router/snmp__mikrotik_flash b/plugins/router/snmp__mikrotik_flash deleted file mode 100755 index c9d5f2f2..00000000 --- a/plugins/router/snmp__mikrotik_flash +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/perl -w - -=head1 NAME - -snmp__mikrotik_flash - Munin plugin to monitor Mikrotik routers Falsh disk utilization. - -=head1 APPLICABLE SYSTEMS - -Mikrotik Routers - -=head1 CONFIGURATION - -As a rule SNMP plugins need site specific configuration. The default -configuration (shown here) will only work on insecure sites/devices. - [snmp_*] - env.version 2 - env.community public - -=head1 MAGIC MARKERS - -#%# family=snmpauto -#%# capabilities=snmpconf - -=head1 BUGS - -None known. - -=head1 AUTHOR - -Copyright (C) 2020 Alejandro Suarez (teconecta.es) -Based on snmp__if_ plugin. - -=head1 LICENSE - -GPLv2. - -=cut - -use strict; -use Net::SNMP; - -my $DEBUG = 0; - -my $host = $ENV{host} || undef; -my $port = $ENV{port} || 161; -my $community = $ENV{community} || "public"; - -my $response; - -if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") -{ - print "require 1.3.6.1.2.1.25.2.3.1.5.65536 \n"; - exit 0; -} - -if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_mikrotik_flash$/) -{ - $host = $1; - if ($host =~ /^([^:]+):(\d+)$/) - { - $host = $1; - $port = $2; - } -} -elsif (!defined($host)) -{ - print "# Debug: $0 -- $1\n" if $DEBUG; - die "# Error: couldn't understand what I'm supposed to monitor."; -} - -my $sysFlashUsage = "1.3.6.1.2.1.25.2.3.1.6.131072"; -my $sysFlashTotal = "1.3.6.1.2.1.25.2.3.1.5.131072"; - -my ($session, $error) = Net::SNMP->session( - -hostname => $host, - -community => $community, - -port => $port - ); - - -if (!defined ($session)) -{ - die "Croaking: $error"; -} - - -if ($ARGV[0] and $ARGV[0] eq "config") -{ - print "host_name $host\n"; - if (!defined ($response = $session->get_request($sysFlashTotal))) - { - die "Croaking: " . $session->error(); - } - print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysFlashTotal} * 1024) . "\n"; - print "graph_title Flash disk usage\n"; - print "graph_category system\n"; - print "graph_info This graph shows the router's flash disk usage.\n"; - print "graph_order Total Used\n"; - print "graph_vlabel bytes\n"; - print "sysFlashTotal.label Total Memory\n"; - print "sysFlashTotal.draw AREA\n"; - print "sysFlashUsage.label Used Memory\n"; - print "sysFlashUsage.draw AREA\n"; - exit 0; -} - - -if (defined ($response = $session->get_request(-varbindlist => [$sysFlashUsage, $sysFlashTotal]))) -{ - print "sysFlashUsage.value ", $response->{$sysFlashUsage}*1024, "\n"; - print "sysFlashTotal.value ", $response->{$sysFlashTotal}*1024, "\n"; -} -else -{ - print "sysFlashUsage.value U\n"; - print "sysFlashTotal.value U\n"; -} - -# vim:syntax=perl - diff --git a/plugins/router/snmp__mikrotik_ram b/plugins/router/snmp__mikrotik_ram deleted file mode 100755 index ce4949bf..00000000 --- a/plugins/router/snmp__mikrotik_ram +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/perl -w - -=head1 NAME - -snmp__mikrotik_flash - Munin plugin to monitor Mikrotik routers RAM utilization. - -=head1 APPLICABLE SYSTEMS - -Mikrotik Routers - -=head1 CONFIGURATION - -As a rule SNMP plugins need site specific configuration. The default -configuration (shown here) will only work on insecure sites/devices. - [snmp_*] - env.version 2 - env.community public - -=head1 MAGIC MARKERS - -#%# family=snmpauto -#%# capabilities=snmpconf - -=head1 BUGS - -None known. - -=head1 AUTHOR - -Copyright (C) 2020 Alejandro Suarez (teconecta.es) -Based on snmp__if_ plugin. - -=head1 LICENSE - -GPLv2. - -=cut - -use strict; -use Net::SNMP; - -my $DEBUG = 0; - -my $host = $ENV{host} || undef; -my $port = $ENV{port} || 161; -my $community = $ENV{community} || "public"; - -my $response; - -if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") -{ - print "require 1.3.6.1.2.1.25.2.3.1.5.65536 \n"; - exit 0; -} - -if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_mikrotik_ram$/) -{ - $host = $1; - if ($host =~ /^([^:]+):(\d+)$/) - { - $host = $1; - $port = $2; - } -} -elsif (!defined($host)) -{ - print "# Debug: $0 -- $1\n" if $DEBUG; - die "# Error: couldn't understand what I'm supposed to monitor."; -} - -my $sysRAMUsage = "1.3.6.1.2.1.25.2.3.1.6.65536"; -my $sysRAMTotal = "1.3.6.1.2.1.25.2.3.1.5.65536"; - -my ($session, $error) = Net::SNMP->session( - -hostname => $host, - -community => $community, - -port => $port - ); - - -if (!defined ($session)) -{ - die "Croaking: $error"; -} - - -if ($ARGV[0] and $ARGV[0] eq "config") -{ - print "host_name $host\n"; - if (!defined ($response = $session->get_request($sysRAMTotal))) - { - die "Croaking: " . $session->error(); - } - print "graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit " . ($response->{$sysRAMTotal} * 1024) . "\n"; - print "graph_title RAM usage\n"; - print "graph_category system\n"; - print "graph_info This graph shows the router's memory usage.\n"; - print "graph_order Total Used\n"; - print "graph_vlabel bytes\n"; - print "sysRAMTotal.label Total Memory\n"; - print "sysRAMTotal.draw AREA\n"; - print "sysRAMUsage.label Used Memory\n"; - print "sysRAMUsage.draw AREA\n"; - exit 0; -} - - -if (defined ($response = $session->get_request(-varbindlist => [$sysRAMUsage, $sysRAMTotal]))) -{ - print "sysRAMUsage.value ", $response->{$sysRAMUsage}*1024, "\n"; - print "sysRAMTotal.value ", $response->{$sysRAMTotal}*1024, "\n"; -} -else -{ - print "sysRAMUsage.value U\n"; - print "sysRAMTotal.value U\n"; -} - -# vim:syntax=perl From 0fbb80119c25a111d2677ddca958d86a04c9eb74 Mon Sep 17 00:00:00 2001 From: Sebastian Liechti Date: Mon, 30 Mar 2020 22:58:28 +0200 Subject: [PATCH 592/695] Simple plugin to monitor coturn turn/stun server ... via telnet --- plugins/coturn/coturn_sessions | 109 +++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 plugins/coturn/coturn_sessions diff --git a/plugins/coturn/coturn_sessions b/plugins/coturn/coturn_sessions new file mode 100755 index 00000000..de7ac894 --- /dev/null +++ b/plugins/coturn/coturn_sessions @@ -0,0 +1,109 @@ +#!/bin/sh + +set -e + +: << =cut + +=head1 NAME + +coturn - Simple plugin to monitor sessions on coturn server + +=head1 CONFIGURATION + +This plugin requires a password set in /etc/turnserver.conf for telnet +cli access. A telnet client has to be installed. Optionally you can +specify the port of the cli interface. + +[coturn_sessions] +env.clipassword VERYSECUREPASSWORD +env.port PORT + +=head1 AUTHOR + +Copyright (C) 2020 Sebastian L. + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities=autoconf + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +cliport=${port:-5766} +clipassword=${port:-} + +get_coturn_stats() { + echo "open 127.0.0.1 $cliport" + sleep 1 + echo "$clipassword" + sleep 1 + for i in udp tcp dtls tls + do + echo "pu $i" + sleep 1 + done +} + +pick_line() { + local result + result=$(echo "$1" | sed -n "$2"p ) + echo "${result:-"U"}" +} + + +case $1 in + autoconf) + if [ -x /usr/bin/turnserver ]; then + if [ -z "$(command -v telnet)" ]; then + echo "no (telnet not found)" + exit 0 + else + echo "yes" + exit 0 + fi + else + echo "no (/usr/bin/turnserver not found)" + exit 0 + fi + ;; + config) + + echo "graph_title Sessions on coturn server" + echo "graph_vlabel turn sessions by protocols" + echo "graph_info This graph shows active sessions sorted by different protocols on the coturn server." + echo "graph_args --base 1000 --lower-limit 0" + echo "udp_sessions.label udp sessions" + echo "udp_sessions.draw AREASTACK" + echo "udp_sessions.info Current number of udp sessions" + echo "udp_sessions.min 0" + echo "tcp_sessions.label tcp sessions" + echo "tcp_sessions.draw AREASTACK" + echo "tcp_sessions.info Current number of tcp sessions" + echo "tcp_sessions.min 0" + echo "dtls_sessions.label dtls sessions" + echo "dtls_sessions.draw AREASTACK" + echo "dtls_sessions.info Current number of dtls sessions" + echo "dtls_sessions.min 0" + echo "tls_sessions.label tls sessions" + echo "tls_sessions.draw AREASTACK" + echo "tls_sessions.info Current number of tls sessions" + echo "tls_sessions.min 0" + + exit 0 + ;; + +esac + +SESSIONS=$(get_coturn_stats | telnet 2> /dev/null | grep "Total sessions: " | sed 's/ Total sessions: //') + +echo "udp_sessions.value $(pick_line "$SESSIONS" 1)" +echo "tcp_sessions.value $(pick_line "$SESSIONS" 2)" +echo "dtls_sessions.value $(pick_line "$SESSIONS" 3)" +echo "tls_sessions.value $(pick_line "$SESSIONS" 4)" + From e22acbdda53ac3da9c3aefbfda4f65e2486c80ea Mon Sep 17 00:00:00 2001 From: Sebastian L Date: Fri, 3 Apr 2020 16:22:02 +0200 Subject: [PATCH 593/695] Small fix (intialize variables) --- plugins/coturn/coturn_sessions | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/coturn/coturn_sessions b/plugins/coturn/coturn_sessions index de7ac894..b34e8312 100755 --- a/plugins/coturn/coturn_sessions +++ b/plugins/coturn/coturn_sessions @@ -6,7 +6,7 @@ set -e =head1 NAME -coturn - Simple plugin to monitor sessions on coturn server +coturn_sessions - Simple plugin to monitor sessions on coturn server =head1 CONFIGURATION @@ -15,12 +15,12 @@ cli access. A telnet client has to be installed. Optionally you can specify the port of the cli interface. [coturn_sessions] -env.clipassword VERYSECUREPASSWORD +env.password VERYSECUREPASSWORD env.port PORT =head1 AUTHOR -Copyright (C) 2020 Sebastian L. +Copyright (C) 2020 Sebastian L. (https://momou.ch) =head1 LICENSE @@ -36,7 +36,7 @@ GPLv2 . "$MUNIN_LIBDIR/plugins/plugin.sh" cliport=${port:-5766} -clipassword=${port:-} +clipassword=${password:-} get_coturn_stats() { echo "open 127.0.0.1 $cliport" From 70b2815de9868acb031bad2029d50ae448b39cf3 Mon Sep 17 00:00:00 2001 From: Sebastian Liechti Date: Fri, 3 Apr 2020 21:13:21 +0200 Subject: [PATCH 594/695] Add jitsi-videobridge plugin Simple multigraph plugin with one graph for current session infos and one graph for total conferences created. --- plugins/jitsi_videobridge/jitsi_videobridge | 135 ++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 plugins/jitsi_videobridge/jitsi_videobridge diff --git a/plugins/jitsi_videobridge/jitsi_videobridge b/plugins/jitsi_videobridge/jitsi_videobridge new file mode 100755 index 00000000..68ac9f88 --- /dev/null +++ b/plugins/jitsi_videobridge/jitsi_videobridge @@ -0,0 +1,135 @@ +#!/bin/sh + +set -e + +: << =cut + +=head1 NAME + +jitsi_videobridge - Monitor sessions and conferences +on a jitsi-videobridge + +=head1 APPLICABLE SYSTEMS + +Jitsi-Videobridge instances + +=head1 CONFIGURATION + +Requires enabled colibri statistics on jitsi-videobridge and an +installed jq, a command-line json processor. + +You may specify the URL where to get the statistics + +[jitsi_videobridge] +env.url http://127.0.0.1:8080/colibri/stats + +... and you may disable the audiochannel when you don't use an +audio gateway. + +=head1 AUTHOR + +Copyright (C) 2020 Sebastian L. (https://momou.ch) + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +COLIBRI_URL=${url:-"http://127.0.0.1:8080/colibri/stats"} + +get_json_data() { + echo $(curl -s -f -m 2 "$COLIBRI_URL") +} + +case $1 in + + autoconf) + if [ -x /usr/bin/curl ]; then + if [ -x /usr/bin/jq ]; then + get_json_data || echo "no (no valid env.url)" && exit 0 + echo "yes" + exit 0 + else + echo "no (jq not found)" + exit 0 + fi + else + echo "no (/usr/bin/curl not found)" + exit 0 + fi + ;; + + config) + echo "multigraph jitsi_videobridge_sessions" + echo "graph_title Current jitsi-videobridge sessions" + echo "graph_info Current jitsi-videobridge sessions" + echo "graph_vlabel current sesssions" + echo "jitsi_videostreams.label videostreams" + echo "jitsi_videostreams.info current number of videostreams" + echo "jitsi_videostreams.min 0" + echo "jitsi_videostreams.draw AREA" + echo "graph_args --base 1000 -l 0" + echo "jitsi_videochannels.label videochannels" + echo "jitsi_videochannels.info current number of videochannels" + echo "jitsi_videochannels.min 0" + echo "jitsi_videochannels.draw AREASTACK" + echo "jitsi_audiochannels.label audiochannels" + echo "jitsi_audiochannels.info current number of audiochannels" + echo "jitsi_audiochannels.min 0" + echo "jitsi_audiochannels.draw AREASTACK" + echo "jitsi_conferences.label conferences" + echo "jitsi_conferences.info current number of conferences" + echo "jitsi_conferences.min 0" + echo "jitsi_conferences.draw LINE2" + echo "jitsi_participants.label participants" + echo "jitsi_participants.info current number of participants" + echo "jitsi_participants.min 0" + echo "jitsi_participants.draw LINE2" + echo "multigraph jitsi_videobridge_conferences" + echo "graph_title Total of jitsi-videobridge conferences" + echo "graph_info Total of jitsi-videobridge conferences" + echo "graph_vlabel conferences" + echo "graph_args --base 1000 -l 0" + echo "jitsi_total_conferences_created.label created conferences" + echo "jitsi_total_conferences_created.info total of created conferences" + echo "jitsi_total_conferences_created.min 0" + echo "jitsi_total_conferences_created.draw AREA" + echo "jitsi_total_conferences_completed.label completed conferences" + echo "jitsi_total_conferences_completed.info total of completed conferences" + echo "jitsi_total_conferences_completed.min 0" + echo "jitsi_total_conferences_completed.draw AREASTACK" + echo "jitsi_total_partially_failed_conferences.label partially failed conferences" + echo "jitsi_total_partially_failed_conferences.info total of partially failed conferences" + echo "jitsi_total_partially_failed_conferences.min 0" + echo "jitsi_total_partially_failed_conferences.draw AREASTACK" + echo "jitsi_total_failed_conferences.label failed conferences" + echo "jitsi_total_failed_conferences.info total of failed conferences" + echo "jitsi_total_failed_conferences.min 0" + echo "jitsi_total_failed_conferences.draw AREASTACK" + + exit 0 + ;; + +esac + +JSONSTATS=$(get_json_data) + +echo "multigraph jitsi_videobridge_sessions" +for KEY in videochannels audiochannels videostreams conferences participants; do + VALUE=$(echo "$JSONSTATS" | jq -r ".$KEY // \"U\"") + echo "jitsi_${KEY}.value $VALUE" +done + +echo "multigraph jitsi_videobridge_conferences" +for KEY in total_conferences_created total_failed_conferences total_partially_failed_conferences total_conferences_completed; do + VALUE=$(echo "$JSONSTATS" | jq -r ".$KEY // \"U\"") + echo "jitsi_${KEY}.value $VALUE" +done From e54c4474fa8099b8c35039019c94ea5559e15ccb Mon Sep 17 00:00:00 2001 From: Sebastian Liechti Date: Fri, 3 Apr 2020 23:17:59 +0200 Subject: [PATCH 595/695] Fix checking curl --- plugins/jitsi_videobridge/jitsi_videobridge | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/jitsi_videobridge/jitsi_videobridge b/plugins/jitsi_videobridge/jitsi_videobridge index 68ac9f88..78d4761d 100755 --- a/plugins/jitsi_videobridge/jitsi_videobridge +++ b/plugins/jitsi_videobridge/jitsi_videobridge @@ -45,25 +45,17 @@ GPLv2 COLIBRI_URL=${url:-"http://127.0.0.1:8080/colibri/stats"} -get_json_data() { - echo $(curl -s -f -m 2 "$COLIBRI_URL") -} - case $1 in autoconf) if [ -x /usr/bin/curl ]; then if [ -x /usr/bin/jq ]; then - get_json_data || echo "no (no valid env.url)" && exit 0 - echo "yes" - exit 0 + curl -s -f -m 2 -I "$COLIBRI_URL" > /dev/null && echo "yes" && exit 0 || echo "no (no valid env.url)" && exit 0 else - echo "no (jq not found)" - exit 0 + echo "no (jq not found)" && exit 0 fi else - echo "no (/usr/bin/curl not found)" - exit 0 + echo "no (/usr/bin/curl not found)" && exit 0 fi ;; @@ -120,7 +112,7 @@ case $1 in esac -JSONSTATS=$(get_json_data) +JSONSTATS=$(curl -s -f -m 2 "$COLIBRI_URL") echo "multigraph jitsi_videobridge_sessions" for KEY in videochannels audiochannels videostreams conferences participants; do From 0af7f4386f0bb361f5ff62b6496020f05fe976c6 Mon Sep 17 00:00:00 2001 From: Sebastian L Date: Sat, 4 Apr 2020 15:54:35 +0200 Subject: [PATCH 596/695] Rename plugin folder, check for json --- plugins/{jitsi_videobridge => jitsi}/jitsi_videobridge | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename plugins/{jitsi_videobridge => jitsi}/jitsi_videobridge (95%) diff --git a/plugins/jitsi_videobridge/jitsi_videobridge b/plugins/jitsi/jitsi_videobridge similarity index 95% rename from plugins/jitsi_videobridge/jitsi_videobridge rename to plugins/jitsi/jitsi_videobridge index 78d4761d..9fadac33 100755 --- a/plugins/jitsi_videobridge/jitsi_videobridge +++ b/plugins/jitsi/jitsi_videobridge @@ -50,7 +50,7 @@ case $1 in autoconf) if [ -x /usr/bin/curl ]; then if [ -x /usr/bin/jq ]; then - curl -s -f -m 2 -I "$COLIBRI_URL" > /dev/null && echo "yes" && exit 0 || echo "no (no valid env.url)" && exit 0 + curl -s -f -m 2 -I "$COLIBRI_URL" | grep -iq "Content-Type: application/json" && echo "yes" && exit 0 || echo "no (invalid or empty response from colibri API ($COLIBRI_URL))" && exit 0 else echo "no (jq not found)" && exit 0 fi From 3cb7002287ad783e1ac15027d58e79e1da99b5b0 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 29 Mar 2020 12:20:42 +0200 Subject: [PATCH 597/695] Aligning rtorrent rtrom_ modules same way as rtom_allsessions_ --- plugins/rtorrent/rtom_mem | 9 ++++++++- plugins/rtorrent/rtom_peers | 9 ++++++++- plugins/rtorrent/rtom_spdd | 10 +++++++++- plugins/rtorrent/rtom_vol | 8 +++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_mem b/plugins/rtorrent/rtom_mem index f691aab1..13ad209c 100755 --- a/plugins/rtorrent/rtom_mem +++ b/plugins/rtorrent/rtom_mem @@ -27,6 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -63,10 +64,16 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; +my $api = $ENV{"api"} || "current"; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $line = ""; +if ($api =~ /pre09/) { + $line = "get_memory_usage"; +} else { + $line = "pieces.memory.current"; +} -my $line = "get_memory_usage"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_peers b/plugins/rtorrent/rtom_peers index ae414542..b5adab66 100755 --- a/plugins/rtorrent/rtom_peers +++ b/plugins/rtorrent/rtom_peers @@ -27,6 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -74,11 +75,17 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; +my $api = $ENV{"api"} || "current"; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; -my $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +my $line = ""; +if ($api =~ /pre09/) { + $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +} else { + $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; +} my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_spdd b/plugins/rtorrent/rtom_spdd index e2b08835..8a6f24e7 100755 --- a/plugins/rtorrent/rtom_spdd +++ b/plugins/rtorrent/rtom_spdd @@ -39,6 +39,7 @@ # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # # Configuration example @@ -102,10 +103,17 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; +my $api = $ENV{"api"} || "current"; my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; +my $line = ""; +if ($api =~ /pre09/) { + $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; +} else { + $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; +} + my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index ea88fe13..4d366751 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -27,6 +27,7 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category +# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -78,6 +79,7 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; +my $api = $ENV{"api"} || "current"; my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; @@ -96,7 +98,11 @@ foreach ( @views ) { connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); } - $line = "d.multicall${_}d.get_hash="; + if ($api =~ /pre09/) { + $line = "d.multicall${_}d.get_hash="; + } else { + $line = "d.multicall2${_}d.hash="; + } $llen = length $line; $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; $hlen = length $header; From 54ad1ad4d560f802e7c3cbec1eac74358f0c126a Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Tue, 31 Mar 2020 21:24:50 +0200 Subject: [PATCH 598/695] discovering the version of rtorrent, and depending on that, using the correct functions in the xmlrpc call --- plugins/rtorrent/rtom_allsessions_mem | 41 ++++++++++++++++----- plugins/rtorrent/rtom_allsessions_peers | 43 +++++++++++++++++----- plugins/rtorrent/rtom_allsessions_spdd | 43 +++++++++++++++++----- plugins/rtorrent/rtom_allsessions_vol | 47 +++++++++++++++++++------ plugins/rtorrent/rtom_mem | 39 +++++++++++++++----- plugins/rtorrent/rtom_peers | 41 ++++++++++++++++----- plugins/rtorrent/rtom_spdd | 42 +++++++++++++++++----- plugins/rtorrent/rtom_vol | 44 +++++++++++++++++++---- 8 files changed, 272 insertions(+), 68 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 82c6dcd4..0e63c4db 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -27,7 +27,6 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -36,7 +35,6 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category -# env.api current # # [rtom_allsessions_*] # user username @@ -64,16 +62,41 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "current"; + +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} my $mem = 0; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "get_memory_usage"; -} else { - $line = "pieces.memory.current"; -} +my $function = rtorrent_version_lower_than('0.9.0') ? 'get_memory_usage' : 'pieces.memory.current'; +my $line = "$function"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 93a06a38..115b0e93 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -25,7 +25,6 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -34,7 +33,6 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category -# env.api current # # [rtom_allsessions_*] # user username @@ -74,17 +72,44 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "current"; + +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} + my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; -} else { - $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; -} +my $function_multicall = rtorrent_version_lower_than('0.9.0') ? 'd.multicall' : 'd.multicall2'; +my $function_hash = rtorrent_version_lower_than('0.9.0') ? 'd.get_hash=' : 'd.hash='; +my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index cf893cb5..70bc1f96 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -36,7 +36,6 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # # Configuration example @@ -46,7 +45,6 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category -# env.api pre09 # # [rtom_allsessions_*] # user username @@ -88,15 +86,42 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "current"; -my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; -} else { - $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); } +my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $function_totalup = rtorrent_version_lower_than('0.9.0') ? 'get_up_total' : 'throttle.global_up.total'; +my $function_totaldown = rtorrent_version_lower_than('0.9.0') ? 'get_down_total' : 'throttle.global_down.total'; +my $function_rateup = rtorrent_version_lower_than('0.9.0') ? 'get_upload_rate' : 'throttle.global_up.max_rate'; +my $function_ratedown = rtorrent_version_lower_than('0.9.0') ? 'get_download_rate' : 'throttle.global_down.max_rate'; +my $line = "system.multicallmethodName$function_totalupparamsmethodName$function_totaldownparamsmethodName$function_rateupparamsmethodName$function_ratedownparams"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index ed483e66..d067fde7 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -25,7 +25,6 @@ # src "socket" when using scgi_socket, or anything else when using scgi_port # socket rTorrent's rpc socket (scgi_local) - using scgi_local - needed, when "src" is set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -34,13 +33,11 @@ # env.src socket # env.socket /home/user/torrent/.socket/rpc.socket,/home/user/torrent/.socket/rpc.socket # env.category Category -# env.api current # # [rtom_allsessions_*] # user username # env.port 5000,5001,5002,5003 # env.category Category -# env.api pre09 # #%# family=auto @@ -79,18 +76,48 @@ my $src = $ENV{"src"} || ""; my @sockets = split /,/, $ENV{"socket"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my @ports = split /,/, $ENV{"port"} || ""; -my $api = $ENV{"api"} || "current"; + +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} + my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; +my $function_multicall; +my $function_hash; + foreach ( @views ) { my $num = 0; - my $line = ""; - if ($api =~ /pre09/) { - $line = "d.multicall${_}d.get_hash="; - } else { - $line = "d.multicall2${_}d.hash="; - } + $function_multicall = rtorrent_version_lower_than('0.9.0')? 'd.multicall' : 'd.multicall2'; + $function_hash = rtorrent_version_lower_than('0.9.0')? 'd.get_hash=' : 'd.hash='; + $line = "$function_multicall${_}$function_hash"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_mem b/plugins/rtorrent/rtom_mem index 13ad209c..63428052 100755 --- a/plugins/rtorrent/rtom_mem +++ b/plugins/rtorrent/rtom_mem @@ -27,7 +27,6 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -64,16 +63,40 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; -my $api = $ENV{"api"} || "current"; -my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "get_memory_usage"; -} else { - $line = "pieces.memory.current"; +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); } +my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +my $function = rtorrent_version_lower_than('0.9.0') ? 'get_memory_usage' : 'pieces.memory.current'; +my $line = "$function"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_peers b/plugins/rtorrent/rtom_peers index b5adab66..0fbfae9e 100755 --- a/plugins/rtorrent/rtom_peers +++ b/plugins/rtorrent/rtom_peers @@ -27,7 +27,6 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -75,17 +74,43 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; -my $api = $ENV{"api"} || "current"; + +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "d.multicallmaind.get_hash=p.multicall=,p.is_encrypted=,p.is_incoming="; -} else { - $line = "d.multicall2maind.hash=p.multicall=,p.is_encrypted=,p.is_incoming="; -} +my $function_multicall = rtorrent_version_lower_than('0.9.0') ? 'd.multicall' : 'd.multicall2'; +my $function_hash = rtorrent_version_lower_than('0.9.0') ? 'd.get_hash=' : 'd.hash='; +my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_spdd b/plugins/rtorrent/rtom_spdd index 8a6f24e7..b4476e35 100755 --- a/plugins/rtorrent/rtom_spdd +++ b/plugins/rtorrent/rtom_spdd @@ -39,7 +39,6 @@ # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category # diff "yes" for using bps for upload and Bps for download, or anything else for using Bps for both -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # # Configuration example @@ -103,16 +102,43 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; -my $api = $ENV{"api"} || "current"; +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $line = ""; -if ($api =~ /pre09/) { - $line = "system.multicallmethodNameget_up_totalparamsmethodNameget_down_totalparamsmethodNameget_upload_rateparamsmethodNameget_download_rateparams"; -} else { - $line = "system.multicallmethodNamethrottle.global_up.totalparamsmethodNamethrottle.global_down.totalparamsmethodNamethrottle.global_up.max_rateparamsmethodNamethrottle.global_down.max_rateparams"; -} +my $function_totalup = rtorrent_version_lower_than('0.9.0') ? 'get_up_total' : 'throttle.global_up.total'; +my $function_totaldown = rtorrent_version_lower_than('0.9.0') ? 'get_down_total' : 'throttle.global_down.total'; +my $function_rateup = rtorrent_version_lower_than('0.9.0') ? 'get_upload_rate' : 'throttle.global_up.max_rate'; +my $function_ratedown = rtorrent_version_lower_than('0.9.0') ? 'get_download_rate' : 'throttle.global_down.max_rate'; +my $line = "system.multicallmethodName$function_totalupparamsmethodName$function_totaldownparamsmethodName$function_rateupparamsmethodName$function_ratedownparams"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index 4d366751..e121aa39 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -27,7 +27,6 @@ # ip rTorrent's ip address - using scgi_port - needed, when "src" is NOT set to "socket" # port rTorrent's scgi port (scgi_port) - using scgi_port - needed, when "src" is NOT set to "socket" # category Change graph category -# api use "pre09" (pre 0.9.0) or "current" (0.9.0+, the default) API calls # # Configuration example # @@ -79,7 +78,36 @@ my $src = $ENV{"src"} || ""; my $ip = $ENV{"ip"} || "127.0.0.1"; my $port = $ENV{"port"} || "5000"; my $socket = $ENV{"socket"} || ""; -my $api = $ENV{"api"} || "current"; + +# detect rtorrent version +use version; +my $rtorrent_version; +sub rtorrent_version_lower_than { + if (not length $rtorrent_version){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + } else { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + } + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $rtorrent_version = $1; + } + } + close (SOCK); + } + return version->parse($rtorrent_version) < version->parse($_[0]); +} my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; @@ -88,6 +116,9 @@ my $llenmy; my $header; my $hlen; +my $function_multicall; +my $function_hash; + my $num; foreach ( @views ) { if ( ( defined $src ) && ( $src eq "socket" ) ) { @@ -98,11 +129,10 @@ foreach ( @views ) { connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); } - if ($api =~ /pre09/) { - $line = "d.multicall${_}d.get_hash="; - } else { - $line = "d.multicall2${_}d.hash="; - } + $function_multicall = rtorrent_version_lower_than('0.9.0')? 'd.multicall' : 'd.multicall2'; + $function_hash = rtorrent_version_lower_than('0.9.0')? 'd.get_hash=' : 'd.hash='; + $line = "$function_multicall${_}$function_hash"; + $llen = length $line; $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; $hlen = length $header; From 9534979768b91acafb1e04f1802d3de2345534aa Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 12:48:28 +0200 Subject: [PATCH 599/695] get the version from rtorrent out of the loop and cache it already to avoid SOCK to be overwritten --- plugins/rtorrent/rtom_allsessions_vol | 7 +++++-- plugins/rtorrent/rtom_vol | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index d067fde7..37b8a10e 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -104,9 +104,12 @@ sub rtorrent_version_lower_than { } close (SOCK); } - return version->parse($rtorrent_version) < version->parse($_[0]); + if(defined $_[0]){ + return version->parse($rtorrent_version) < version->parse($_[0]); + } } - +# init rtorrent_version +rtorrent_version_lower_than(); my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index e121aa39..1ea5678f 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -106,8 +106,12 @@ sub rtorrent_version_lower_than { } close (SOCK); } - return version->parse($rtorrent_version) < version->parse($_[0]); + if(defined $_[0]){ + return version->parse($rtorrent_version) < version->parse($_[0]); + } } +# init rtorrent_version +rtorrent_version_lower_than(); my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; From 3886860089d9d746627f10c94c2157295877d550 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 13:45:54 +0200 Subject: [PATCH 600/695] loop over every socket/port to get the version and use it afterwards as cache --- plugins/rtorrent/rtom_allsessions_mem | 88 ++++++++++++++--------- plugins/rtorrent/rtom_allsessions_peers | 91 +++++++++++++---------- plugins/rtorrent/rtom_allsessions_spdd | 95 +++++++++++++++---------- plugins/rtorrent/rtom_allsessions_vol | 90 +++++++++++++---------- 4 files changed, 218 insertions(+), 146 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 0e63c4db..a38e93e8 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -65,49 +65,70 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; -my $rtorrent_version; -sub rtorrent_version_lower_than { - if (not length $rtorrent_version){ - if ( ( defined $src ) && ( $src eq "socket" ) ) { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - } else { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); +my %rtorrent_version; +sub get_socket_version { + my $version; + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $version = $1; } - my $line_version= "system.client_version"; - my $llen = length $line_version; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; - $line_version= "${hlen}:${header},${line_version}"; - print SOCK $line_version; - flush SOCK; - my $pattern = qr/([0-9.]+)<\/string><\/value>/; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $rtorrent_version = $1; - } - } - close (SOCK); } - return version->parse($rtorrent_version) < version->parse($_[0]); + close (SOCK); + $rtorrent_version{$_[0]} = $version; } +sub rtorrent_version_lower_than { + if (keys %rtorrent_version == 0 && not defined $_[0]){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + get_socket_version $port; + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + get_socket_version $port; + close (SOCK); + } + } + } + if(defined $_[1]){ + return version->parse($rtorrent_version{$_[0]}) < version->parse($_[1]); + } +} +# init rtorrent_version +rtorrent_version_lower_than(); -my $mem = 0; my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $function = rtorrent_version_lower_than('0.9.0') ? 'get_memory_usage' : 'pieces.memory.current'; -my $line = "$function"; -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; +my $mem = 0; +sub construct_line { + my $function = rtorrent_version_lower_than($_[0], '0.9.0') ? 'get_memory_usage' : 'pieces.memory.current'; + my $line = "$function"; + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line = "${hlen}:${header},${line}"; + return $line; +} if ( ( defined $src ) && ( $src eq "socket" ) ) { for $socket (@sockets) { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($socket); flush SOCK; while ( $line = ) { if ( $line =~ /$pattern/ ) { @@ -121,8 +142,7 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($port); flush SOCK; while ( $line = ) { if ( $line =~ /$pattern/ ) { diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 115b0e93..c975030f 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -75,44 +75,64 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; -my $rtorrent_version; -sub rtorrent_version_lower_than { - if (not length $rtorrent_version){ - if ( ( defined $src ) && ( $src eq "socket" ) ) { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - } else { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); +my %rtorrent_version; +sub get_socket_version { + my $version; + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $version = $1; } - my $line_version= "system.client_version"; - my $llen = length $line_version; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; - $line_version= "${hlen}:${header},${line_version}"; - print SOCK $line_version; - flush SOCK; - my $pattern = qr/([0-9.]+)<\/string><\/value>/; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $rtorrent_version = $1; - } - } - close (SOCK); } - return version->parse($rtorrent_version) < version->parse($_[0]); + close (SOCK); + $rtorrent_version{$_[0]} = $version; } - +sub rtorrent_version_lower_than { + if (keys %rtorrent_version == 0 && not defined $_[0]){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + get_socket_version $port; + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + get_socket_version $port; + close (SOCK); + } + } + } + if(defined $_[1]){ + return version->parse($rtorrent_version{$_[0]}) < version->parse($_[1]); + } +} +# init rtorrent_version +rtorrent_version_lower_than(); my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; my $tpattern = qr/[0-9A-F]{20}/; - -my $function_multicall = rtorrent_version_lower_than('0.9.0') ? 'd.multicall' : 'd.multicall2'; -my $function_hash = rtorrent_version_lower_than('0.9.0') ? 'd.get_hash=' : 'd.hash='; -my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; +sub construct_line { + my $function_multicall = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.multicall' : 'd.multicall2'; + my $function_hash = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.get_hash=' : 'd.hash='; + my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line = "${hlen}:${header},${line}"; + return $line; +} my $tor = 0; my $tot = 0; @@ -123,14 +143,12 @@ my $ppline = ""; my $out = 0; my $pla = 0; - if ( ( defined $src ) && ( $src eq "socket" ) ) { for $socket (@sockets) { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($socket); flush SOCK; while ( $line = ) { if ( $line =~ /$tpattern/ ) { @@ -154,8 +172,7 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($port); flush SOCK; while ( $line = ) { if ( $line =~ /$tpattern/ ) { diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index 70bc1f96..76f69d16 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -89,43 +89,66 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; -my $rtorrent_version; -sub rtorrent_version_lower_than { - if (not length $rtorrent_version){ - if ( ( defined $src ) && ( $src eq "socket" ) ) { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - } else { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); +my %rtorrent_version; +sub get_socket_version { + my $version; + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $version = $1; } - my $line_version= "system.client_version"; - my $llen = length $line_version; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; - $line_version= "${hlen}:${header},${line_version}"; - print SOCK $line_version; - flush SOCK; - my $pattern = qr/([0-9.]+)<\/string><\/value>/; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $rtorrent_version = $1; - } - } - close (SOCK); } - return version->parse($rtorrent_version) < version->parse($_[0]); + close (SOCK); + $rtorrent_version{$_[0]} = $version; } -my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; -my $function_totalup = rtorrent_version_lower_than('0.9.0') ? 'get_up_total' : 'throttle.global_up.total'; -my $function_totaldown = rtorrent_version_lower_than('0.9.0') ? 'get_down_total' : 'throttle.global_down.total'; -my $function_rateup = rtorrent_version_lower_than('0.9.0') ? 'get_upload_rate' : 'throttle.global_up.max_rate'; -my $function_ratedown = rtorrent_version_lower_than('0.9.0') ? 'get_download_rate' : 'throttle.global_down.max_rate'; -my $line = "system.multicallmethodName$function_totalupparamsmethodName$function_totaldownparamsmethodName$function_rateupparamsmethodName$function_ratedownparams"; +sub rtorrent_version_lower_than { + if (keys %rtorrent_version == 0 && not defined $_[0]){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + get_socket_version $port; + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + get_socket_version $port; + close (SOCK); + } + } + } + if(defined $_[1]){ + return version->parse($rtorrent_version{$_[0]}) < version->parse($_[1]); + } +} +# init rtorrent_version +rtorrent_version_lower_than(); -my $llen = length $line; -my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; -my $hlen = length $header; +my $pattern = qr/<(int|i4|i8|ex\.i8)>([-]{0,1}\d+)<\/(int|i4|i8|ex\.i8)><\/value>/; +sub construct_line { + my $function_totalup = rtorrent_version_lower_than($_[0], '0.9.0') ? 'get_up_total' : 'throttle.global_up.total'; + my $function_totaldown = rtorrent_version_lower_than($_[0], '0.9.0') ? 'get_down_total' : 'throttle.global_down.total'; + my $function_rateup = rtorrent_version_lower_than($_[0], '0.9.0') ? 'get_upload_rate' : 'throttle.global_up.max_rate'; + my $function_ratedown = rtorrent_version_lower_than($_[0], '0.9.0') ? 'get_download_rate' : 'throttle.global_down.max_rate'; + my $line = "system.multicallmethodName$function_totalupparamsmethodName$function_totaldownparamsmethodName$function_rateupparamsmethodName$function_ratedownparams"; + + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line = "${hlen}:${header},${line}"; + return $line; +} my $up = -1; my $down = -1; @@ -135,8 +158,7 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($socket); flush SOCK; my $up_tmp = -1; my $down_tmp = -1; @@ -159,8 +181,7 @@ if ( ( defined $src ) && ( $src eq "socket" ) ) { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($port); flush SOCK; my $up_tmp = -1; my $down_tmp = -1; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 37b8a10e..91d92970 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -79,59 +79,74 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; -my $rtorrent_version; -sub rtorrent_version_lower_than { - if (not length $rtorrent_version){ - if ( ( defined $src ) && ( $src eq "socket" ) ) { - socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; - connect( SOCK, sockaddr_un( $socket ) ) or die $!; - } else { - socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); - connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); +my %rtorrent_version; +sub get_socket_version { + my $version; + my $line_version= "system.client_version"; + my $llen = length $line_version; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line_version= "${hlen}:${header},${line_version}"; + print SOCK $line_version; + flush SOCK; + my $pattern = qr/([0-9.]+)<\/string><\/value>/; + while ( $line = ) { + if ( $line =~ /$pattern/ ) { + $version = $1; } - my $line_version= "system.client_version"; - my $llen = length $line_version; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; - $line_version= "${hlen}:${header},${line_version}"; - print SOCK $line_version; - flush SOCK; - my $pattern = qr/([0-9.]+)<\/string><\/value>/; - while ( $line = ) { - if ( $line =~ /$pattern/ ) { - $rtorrent_version = $1; - } - } - close (SOCK); } - if(defined $_[0]){ - return version->parse($rtorrent_version) < version->parse($_[0]); - } + close (SOCK); + $rtorrent_version{$_[0]} = $version; +} +sub rtorrent_version_lower_than { + if (keys %rtorrent_version == 0 && not defined $_[0]){ + if ( ( defined $src ) && ( $src eq "socket" ) ) { + for $socket (@sockets) + { + socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; + connect( SOCK, sockaddr_un( $socket ) ) or die $!; + get_socket_version $port; + close (SOCK); + } + } else { + for $port (@ports) + { + socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); + connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); + get_socket_version $port; + close (SOCK); + } + } + } + if(defined $_[1]){ + return version->parse($rtorrent_version{$_[0]}) < version->parse($_[1]); + } } # init rtorrent_version rtorrent_version_lower_than(); my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; -my $function_multicall; -my $function_hash; +sub construct_line { + my $function_multicall = rtorrent_version_lower_than($_[0], '0.9.0')? 'd.multicall' : 'd.multicall2'; + my $function_hash = rtorrent_version_lower_than($_[0], '0.9.0')? 'd.get_hash=' : 'd.hash='; + my $line = "$function_multicall${_}$function_hash"; + my $llen = length $line; + my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; + my $hlen = length $header; + $line = "${hlen}:${header},${line}"; + return $line; +} foreach ( @views ) { my $num = 0; - $function_multicall = rtorrent_version_lower_than('0.9.0')? 'd.multicall' : 'd.multicall2'; - $function_hash = rtorrent_version_lower_than('0.9.0')? 'd.get_hash=' : 'd.hash='; - $line = "$function_multicall${_}$function_hash"; - my $llen = length $line; - my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; - my $hlen = length $header; if ( ( defined $src ) && ( $src eq "socket" ) ) { for $socket (@sockets) { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($socket); flush SOCK; while ( $line = ) { if ( $line =~ /$pattern/ ) { @@ -145,8 +160,7 @@ foreach ( @views ) { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - my $line = "${hlen}:${header},${line}"; - print SOCK $line; + print SOCK construct_line($port); flush SOCK; while ( $line = ) { if ( $line =~ /$pattern/ ) { From bf2eff5c6c57fab4ab7361bfbd43af3c6bdf5991 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 13:50:44 +0200 Subject: [PATCH 601/695] fix function name --- plugins/rtorrent/rtom_allsessions_mem | 6 +++--- plugins/rtorrent/rtom_allsessions_peers | 6 +++--- plugins/rtorrent/rtom_allsessions_spdd | 6 +++--- plugins/rtorrent/rtom_allsessions_vol | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index a38e93e8..6758bf60 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -66,7 +66,7 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; my %rtorrent_version; -sub get_socket_version { +sub get_rtorrent_version { my $version; my $line_version= "system.client_version"; my $llen = length $line_version; @@ -91,7 +91,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } else { @@ -99,7 +99,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index c975030f..4fe21d93 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -76,7 +76,7 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; my %rtorrent_version; -sub get_socket_version { +sub get_rtorrent_version { my $version; my $line_version= "system.client_version"; my $llen = length $line_version; @@ -101,7 +101,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } else { @@ -109,7 +109,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index 76f69d16..adff140e 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -90,7 +90,7 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; my %rtorrent_version; -sub get_socket_version { +sub get_rtorrent_version { my $version; my $line_version= "system.client_version"; my $llen = length $line_version; @@ -115,7 +115,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } else { @@ -123,7 +123,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 91d92970..342c9f52 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -80,7 +80,7 @@ my @ports = split /,/, $ENV{"port"} || ""; # detect rtorrent version use version; my %rtorrent_version; -sub get_socket_version { +sub get_rtorrent_version { my $version; my $line_version= "system.client_version"; my $llen = length $line_version; @@ -105,7 +105,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } else { @@ -113,7 +113,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_INET, SOCK_STREAM, getprotobyname( "tcp" ) ); connect( SOCK, sockaddr_in( $port, inet_aton( $ip ) ) ); - get_socket_version $port; + get_rtorrent_version $port; close (SOCK); } } From 439c0a20b45675c2bbbc1853e1080d290ab80418 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 13:52:55 +0200 Subject: [PATCH 602/695] fix argument for socket version get --- plugins/rtorrent/rtom_allsessions_mem | 2 +- plugins/rtorrent/rtom_allsessions_peers | 2 +- plugins/rtorrent/rtom_allsessions_spdd | 2 +- plugins/rtorrent/rtom_allsessions_vol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 6758bf60..fc203d5c 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -91,7 +91,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_rtorrent_version $port; + get_rtorrent_version $socket; close (SOCK); } } else { diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index 4fe21d93..bf26167a 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -101,7 +101,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_rtorrent_version $port; + get_rtorrent_version $socket; close (SOCK); } } else { diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index adff140e..aada54f5 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -115,7 +115,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_rtorrent_version $port; + get_rtorrent_version $socket; close (SOCK); } } else { diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 342c9f52..15956314 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -105,7 +105,7 @@ sub rtorrent_version_lower_than { { socket( SOCK, PF_UNIX, SOCK_STREAM, 0 ) or die; connect( SOCK, sockaddr_un( $socket ) ) or die $!; - get_rtorrent_version $port; + get_rtorrent_version $socket; close (SOCK); } } else { From 34c2bc57bc247bbd6f58a16bfd9139592b2e1764 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 14:01:03 +0200 Subject: [PATCH 603/695] extra arg for multicall2 was missing in _vol --- plugins/rtorrent/rtom_allsessions_vol | 7 ++++--- plugins/rtorrent/rtom_vol | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 15956314..5fce5eac 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -128,9 +128,10 @@ rtorrent_version_lower_than(); my $pattern = qr/([A-Z0-9]+)<\/string><\/value>/; sub construct_line { - my $function_multicall = rtorrent_version_lower_than($_[0], '0.9.0')? 'd.multicall' : 'd.multicall2'; - my $function_hash = rtorrent_version_lower_than($_[0], '0.9.0')? 'd.get_hash=' : 'd.hash='; - my $line = "$function_multicall${_}$function_hash"; + my $function_multicall = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.multicall' : 'd.multicall2'; + my $function_multicall_arg = rtorrent_version_lower_than($_[0], '0.9.0') ? '' : ''; + my $function_hash = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.get_hash=' : 'd.hash='; + my $line = "$function_multicall$function_multicall_arg${_}$function_hash"; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index 1ea5678f..fe842e09 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -121,6 +121,7 @@ my $header; my $hlen; my $function_multicall; +my $function_multicall_arg; my $function_hash; my $num; @@ -134,8 +135,9 @@ foreach ( @views ) { } $function_multicall = rtorrent_version_lower_than('0.9.0')? 'd.multicall' : 'd.multicall2'; + $function_multicall_arg = rtorrent_version_lower_than($_[0], '0.9.0') ? '' : ''; $function_hash = rtorrent_version_lower_than('0.9.0')? 'd.get_hash=' : 'd.hash='; - $line = "$function_multicall${_}$function_hash"; + $line = "$function_multicall$function_multicall_arg${_}$function_hash"; $llen = length $line; $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; From 3693adb343ef70a3fc6f4f817989c6c113b0c4f4 Mon Sep 17 00:00:00 2001 From: Younes Ichiche Date: Sun, 5 Apr 2020 14:04:41 +0200 Subject: [PATCH 604/695] fix multicall arg --- plugins/rtorrent/rtom_allsessions_peers | 3 ++- plugins/rtorrent/rtom_peers | 3 ++- plugins/rtorrent/rtom_vol | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index bf26167a..5526668c 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -125,8 +125,9 @@ my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value my $tpattern = qr/[0-9A-F]{20}/; sub construct_line { my $function_multicall = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.multicall' : 'd.multicall2'; + my $function_multicall_arg = rtorrent_version_lower_than($_[0], '0.9.0') ? '' : ''; my $function_hash = rtorrent_version_lower_than($_[0], '0.9.0') ? 'd.get_hash=' : 'd.hash='; - my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; + my $line = "$function_multicall$function_multicall_argmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_peers b/plugins/rtorrent/rtom_peers index 0fbfae9e..29b55934 100755 --- a/plugins/rtorrent/rtom_peers +++ b/plugins/rtorrent/rtom_peers @@ -109,8 +109,9 @@ my $pattern = qr/<(int|i4|i8|ex\.i8)>(\d+)<\/(int|i4|i8|ex\.i8)><\/value> my $tpattern = qr/[0-9A-F]{20}/; my $function_multicall = rtorrent_version_lower_than('0.9.0') ? 'd.multicall' : 'd.multicall2'; +my $function_multicall_arg = rtorrent_version_lower_than('0.9.0') ? '' : ''; my $function_hash = rtorrent_version_lower_than('0.9.0') ? 'd.get_hash=' : 'd.hash='; -my $line = "$function_multicallmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; +my $line = "$function_multicall$function_multicall_argmain$function_hashp.multicall=,p.is_encrypted=,p.is_incoming="; my $llen = length $line; my $header = "CONTENT_LENGTH\000${llen}\000SCGI\001\000"; my $hlen = length $header; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index fe842e09..59f15136 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -135,7 +135,7 @@ foreach ( @views ) { } $function_multicall = rtorrent_version_lower_than('0.9.0')? 'd.multicall' : 'd.multicall2'; - $function_multicall_arg = rtorrent_version_lower_than($_[0], '0.9.0') ? '' : ''; + $function_multicall_arg = rtorrent_version_lower_than('0.9.0') ? '' : ''; $function_hash = rtorrent_version_lower_than('0.9.0')? 'd.get_hash=' : 'd.hash='; $line = "$function_multicall$function_multicall_arg${_}$function_hash"; From 3ee9de82dc9806e4a2762e383de9b033880e3c90 Mon Sep 17 00:00:00 2001 From: Sebastian L Date: Thu, 9 Apr 2020 11:34:51 +0200 Subject: [PATCH 605/695] Split videostreams in separate graph for better readability --- plugins/jitsi/jitsi_videobridge | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/jitsi/jitsi_videobridge b/plugins/jitsi/jitsi_videobridge index 9fadac33..4c0d9056 100755 --- a/plugins/jitsi/jitsi_videobridge +++ b/plugins/jitsi/jitsi_videobridge @@ -64,10 +64,6 @@ case $1 in echo "graph_title Current jitsi-videobridge sessions" echo "graph_info Current jitsi-videobridge sessions" echo "graph_vlabel current sesssions" - echo "jitsi_videostreams.label videostreams" - echo "jitsi_videostreams.info current number of videostreams" - echo "jitsi_videostreams.min 0" - echo "jitsi_videostreams.draw AREA" echo "graph_args --base 1000 -l 0" echo "jitsi_videochannels.label videochannels" echo "jitsi_videochannels.info current number of videochannels" @@ -85,6 +81,14 @@ case $1 in echo "jitsi_participants.info current number of participants" echo "jitsi_participants.min 0" echo "jitsi_participants.draw LINE2" + echo "multigraph jitsi_videobridge_streams" + echo "graph_title Current jitsi-videobridge videostreams" + echo "graph_info Current jitsi-videobridge videostreams" + echo "graph_vlabel current videostreams" + echo "graph_args --base 1000 -l 0" + echo "jitsi_videostreams.label videostreams" + echo "jitsi_videostreams.info current number of videostreams" + echo "jitsi_videostreams.min 0" echo "multigraph jitsi_videobridge_conferences" echo "graph_title Total of jitsi-videobridge conferences" echo "graph_info Total of jitsi-videobridge conferences" @@ -115,11 +119,15 @@ esac JSONSTATS=$(curl -s -f -m 2 "$COLIBRI_URL") echo "multigraph jitsi_videobridge_sessions" -for KEY in videochannels audiochannels videostreams conferences participants; do +for KEY in videochannels audiochannels conferences participants; do VALUE=$(echo "$JSONSTATS" | jq -r ".$KEY // \"U\"") echo "jitsi_${KEY}.value $VALUE" done +echo "multigraph jitsi_videobridge_streams" +VALUE=$(echo "$JSONSTATS" | jq -r ".videostreams // \"U\"") +echo "jitsi_videostreams.value $VALUE" + echo "multigraph jitsi_videobridge_conferences" for KEY in total_conferences_created total_failed_conferences total_partially_failed_conferences total_conferences_completed; do VALUE=$(echo "$JSONSTATS" | jq -r ".$KEY // \"U\"") From 33af56af2a47214e1c58c93b9ade7a8aecb83b16 Mon Sep 17 00:00:00 2001 From: Sebastian L Date: Mon, 6 Apr 2020 00:26:28 +0200 Subject: [PATCH 606/695] Simple multigraph plugin for nextcloud --- plugins/nextcloud/nextcloud_ | 235 +++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100755 plugins/nextcloud/nextcloud_ diff --git a/plugins/nextcloud/nextcloud_ b/plugins/nextcloud/nextcloud_ new file mode 100755 index 00000000..9463c8d2 --- /dev/null +++ b/plugins/nextcloud/nextcloud_ @@ -0,0 +1,235 @@ +#!/bin/sh + +set -e + +: << =cut + +=head1 NAME + +nextcloud_ - Monitor usage of nextcloud instances + +=head1 APPLICABLE SYSTEMS + +Nexcloud instances + +=head1 CONFIGURATION + +Requires installed curl and jq, a command-line json processor. + +This is a wildcard plugin. To monitor a nextcloud instance, link +nextcloud_ to this file. You can even append a port +(:8443) to the file if needed. For example, + + ln -s /usr/share/munin/plugins/nextcloud_ \ + /etc/munin/plugins/nextcloud_cloud.domain.tld + +Set username and password in your munin-node configuration + +[nextcloud_cloud.domain.tld] +env.username +env.password + +It's advised to set an app password (for this plugin) in your nextcloud +instance and not to use the "real" password of your nextcloud user. + +=head1 AUTHOR + +Copyright (C) 2020 Sebastian L. (https://momou.ch) + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities=autoconf + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +API_PATH="/ocs/v2.php/apps/serverinfo/api/v1/info?format=json" +DOMAIN="${0##*nextcloud_}" +CLEANDOMAIN="$(clean_fieldname "$DOMAIN")" +USERNAME="${username:-}" +PASSWORD="${password:-}" + +print_json_data() { + local FIRST="$1" + [ -z "$FIRST" ] && exit 0 + shift 1 + for KEY in "$@"; do + VALUE=$(echo "$FIRST" | jq -cr ".$KEY") + echo "$KEY.value $VALUE" + done +} + +test_https() { + [ -z "$DOMAIN" ] && exit 0 + curl -s -f -m 2 -I "https://$DOMAIN" > /dev/null && echo true && exit 0 +} + +case $1 in + + autoconf) + if [ -x /usr/bin/curl ]; then + if [ -x /usr/bin/jq ]; then + [ "$(test_https)" ] && DOMAIN="https://$DOMAIN" || DOMAIN="http://$DOMAIN" + curl -s -f -m 2 -u "$USERNAME:$PASSWORD" -I "$DOMAIN$API_PATH" | grep -iq "Content-Type: application/json" && echo "yes" && exit 0 || echo "no (invalid or empty response from nextlcoud serverinfo api)" && exit 0 + else + echo "no (jq not found)" && exit 0 + fi + else + echo "no (/usr/bin/curl not found)" && exit 0 + fi + ;; + + config) + +cat << EOM +multigraph nextcloud_users_$CLEANDOMAIN +graph_title Nextcloud users on $DOMAIN +graph_args --base 1000 -l 0 +graph_printf %.0lf +graph_vlabel connected users +graph_info number of connected user +graph_category cloud +last5minutes.label last 5 minutes +last5minutes.info users connected in the last 5 minutes +last5minutes.min 0 +last1hour.label last hour +last1hour.info users connected in the last hour +last1hour.min 0 +last24hours.label last 24 hours +last24hours.info users connected in the last 24 hours +last24hours.min 0 +num_users.label number of users +num_users.info total number of users +num_users.min 0 +multigraph nextcloud_files_$CLEANDOMAIN +graph_title Nextcloud files on $DOMAIN +graph_args --base 1000 -l 0 +graph_printf %.0lf +graph_vlabel number of files +graph_info number of files +graph_category cloud +num_files.label number of files +num_files.info current number of files +num_files.min 0 +multigraph nextcloud_shares_$CLEANDOMAIN +graph_title Nextcloud shares on $DOMAIN +graph_args --base 1000 -l 0 +graph_printf %.0lf +graph_vlabel number of shares +graph_info number of shares +graph_category cloud +num_shares.label total number of shares +num_shares.info current over all total of shares +num_shares.min 0 +num_shares_user.label user shares +num_shares_user.info current total of user shares +num_shares_user.min 0 +num_shares_groups.label group shares +num_shares_groups.info current total of group shares +num_shares_groups.min 0 +num_shares_link.label link shares +num_shares_link.info current total of link shares +num_shares_link.min 0 +num_shares_mail.label mail shares +num_shares_mail.info current total of mail shares +num_shares_mail.min 0 +num_shares_room.label room shares +num_shares_room.info current total of room shares +num_shares_room.min 0 +num_shares_link_no_password.label link shares without password protection +num_shares_link_no_password.info current total of link shares without password protection +num_shares_link_no_password.min 0 +num_fed_shares_sent.label federated shares sent +num_fed_shares_sent.info current total of federated shares sent +num_fed_shares_sent.min 0 +num_fed_shares_received.label federated shares received +num_fed_shares_received.info current total of federated shares received +num_fed_shares_received.min 0 +multigraph nextcloud_dbsize_$CLEANDOMAIN +graph_title Nextcloud database size on $DOMAIN +graph_args --base 1024 -l 0 +graph_vlabel size in bytes +graph_info database database size in bytes +graph_category cloud +db_size.label database size in bytes +db_size.info database size in bytes +db_size.draw AREA +db_size.min 0 +multigraph nextcloud_storages_$CLEANDOMAIN +graph_title Nextcloud storages on $DOMAIN +graph_args --base 1000 -l 0 +graph_printf %.0lf +graph_vlabel number +graph_info number of storages +graph_category cloud +num_storages.label total number of storages +num_storages.info current total of storages +num_storages.min 0 +num_storages_local.label number of local storages +num_storages_local.info current number of local storages +num_storages_local.min 0 +num_storages_home.label number of home storages +num_storages_home.info current number of home storages +num_storages_home.min 0 +num_storages_other.label number of other storages +num_storages_other.info current number of other storages +num_storages_other.min 0 +multigraph nextcloud_app_$CLEANDOMAIN +graph_title Nextcloud apps on $DOMAIN +graph_args --base 1000 -l 0 +graph_printf %.0lf +graph_vlabel apps +graph_info number of installed and updatable apps +graph_category cloud +num_updates_available.label available app updates +num_updates_available.info number of available app updates +num_updates_available.min 0 +num_updates_available.warning 1 +num_installed.label installed apps +num_installed.info number of installed apps +num_installed.min 0 +EOM + exit 0 + ;; + +esac + +# Get JSON data +[ "$(test_https)" ] && DOMAIN="https://$DOMAIN" || DOMAIN="http://$DOMAIN" +JSONSTATS=$(curl -s -f -m 2 -u "$USERNAME:$PASSWORD" "$DOMAIN$API_PATH" | sed 's/\\/\\\\/g' | jq -cr ".ocs.data") +USERS=$(echo "$JSONSTATS" | jq -cr ".activeUsers") +STORAGE=$(echo "$JSONSTATS" | jq -cr ".nextcloud.storage") +SHARES=$(echo "$JSONSTATS" | jq -cr ".nextcloud.shares") +DBSIZE=$(echo "$JSONSTATS" | jq -cr ".server.database.size") +APPS=$(echo "$JSONSTATS" | jq -cr ".nextcloud.system.apps") + +# users +echo "multigraph nextcloud_users_$CLEANDOMAIN" +print_json_data "$USERS" last5minutes last1hour last24hours +print_json_data "$STORAGE" num_users + +# files +echo "multigraph nextcloud_files_$CLEANDOMAIN" +print_json_data "$STORAGE" num_files + +# storages +echo "multigraph nextcloud_storages_$CLEANDOMAIN" +print_json_data "$STORAGE" num_storages num_storages_local num_storages_home num_storages_other + +# shares +echo "multigraph nextcloud_shares_$CLEANDOMAIN" +print_json_data "$SHARES" num_shares num_shares_user num_shares_groups num_shares_link num_shares_mail num_shares_room num_shares_link_no_password num_fed_shares_sent num_fed_shares_received + +# dbsize +echo "multigraph nextcloud_dbsize_$CLEANDOMAIN" +echo "db_size.value $DBSIZE" + +# apps +echo "multigraph nextcloud_app_updates_$CLEANDOMAIN" +print_json_data "$APPS" num_installed num_updates_available From 667ee02b676e163eac0267c90b239e88f71aaefb Mon Sep 17 00:00:00 2001 From: Sebastian L Date: Tue, 7 Apr 2020 13:47:56 +0200 Subject: [PATCH 607/695] Small fix for nextcloud_apps_multigraph --- plugins/nextcloud/nextcloud_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/nextcloud/nextcloud_ b/plugins/nextcloud/nextcloud_ index 9463c8d2..e7e57fcd 100755 --- a/plugins/nextcloud/nextcloud_ +++ b/plugins/nextcloud/nextcloud_ @@ -180,7 +180,7 @@ num_storages_home.min 0 num_storages_other.label number of other storages num_storages_other.info current number of other storages num_storages_other.min 0 -multigraph nextcloud_app_$CLEANDOMAIN +multigraph nextcloud_apps_$CLEANDOMAIN graph_title Nextcloud apps on $DOMAIN graph_args --base 1000 -l 0 graph_printf %.0lf @@ -231,5 +231,5 @@ echo "multigraph nextcloud_dbsize_$CLEANDOMAIN" echo "db_size.value $DBSIZE" # apps -echo "multigraph nextcloud_app_updates_$CLEANDOMAIN" +echo "multigraph nextcloud_apps_$CLEANDOMAIN" print_json_data "$APPS" num_installed num_updates_available From 90ccaf4b068b6f6d71c7cad0cb398bfb30e341ff Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 16 Apr 2020 21:53:56 +1000 Subject: [PATCH 608/695] [router/freebox] fix vlabel Signed-off-by: Olivier Mehani --- plugins/router/freebox | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/router/freebox b/plugins/router/freebox index edfe6848..9cb6c16b 100755 --- a/plugins/router/freebox +++ b/plugins/router/freebox @@ -254,7 +254,7 @@ graph_config() { .adsl) echo "graph_title ADSL characteristics" echo 'graph_category network' - echo 'graph_vlabel dB in (+) / out (-)' + echo 'graph_vlabel dB in (-) / out (+)' echo 'graph_order noise_down noise attenuation_down attenuation' echo 'noise_down.label Noise down' @@ -270,7 +270,7 @@ graph_config() { .adsl_errors) echo 'graph_title ADSL error correction' echo 'graph_category network' - echo 'graph_vlabel errors in (+) / out (-)' + echo 'graph_vlabel errors in (-) / out (+)' echo 'graph_order fec_down fec crc_down crc hec_down hec' echo 'fec_down.label FEC down' @@ -303,7 +303,7 @@ graph_config() { .traffic) echo 'graph_title Traffic' echo 'graph_category network' - echo 'graph_vlabel bits per second in (+) / out (-)' + echo 'graph_vlabel bits per second in (-) / out (+)' echo 'graph_order atm_down atm wan_down wan eth_down eth usb_down usb switch_down switch' echo 'atm_down.label ATM down' @@ -366,7 +366,7 @@ graph_config() { *) echo 'graph_title Uplink traffic' echo 'graph_category network' - echo 'graph_vlabel bits per second in (+) / out (-)' + echo 'graph_vlabel bits per second in (-) / out (+)' echo 'graph_order main_atm_down main_atm main_wan_down main_wan' # XXX: summary data similar to (more detailed) traffic From 5392e84b8a2fc87717bec4af6cfb84c4624ccec3 Mon Sep 17 00:00:00 2001 From: Rowan Wookey Date: Fri, 17 Apr 2020 22:34:37 +0100 Subject: [PATCH 609/695] Fixed warnings generated by total_by_process_ Fixed the following: * Argument "config" isn't numeric in numeric eq (==) at line 89. * Use of uninitialized value $ARGV[1] in string eq at line 35 --- plugins/system/total_by_process_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/system/total_by_process_ b/plugins/system/total_by_process_ index d374f463..d4a9b825 100755 --- a/plugins/system/total_by_process_ +++ b/plugins/system/total_by_process_ @@ -32,7 +32,7 @@ $scriptname =~ s|.*/||; my $fieldname = ($scriptname =~ /^total_by_process_(.*)_(.*)/) ? $1 : undef; my $fieldtype = ($scriptname =~ /^total_by_process_(.*)_(.*)/) ? $2 : undef; -if (@ARGV and $ARGV[1] eq "suggest") +if (defined($ARGV[0]) and $ARGV[0] eq "suggest") { system("ps L | cut -d' ' -f1"); exit(0); @@ -86,7 +86,7 @@ foreach my $process (keys %total_by_process) close(PS); -if (@ARGV and $ARGV[1] == "config") +if (defined($ARGV[0]) and $ARGV[0] eq "config") { print < Date: Fri, 17 Apr 2020 21:18:37 +0100 Subject: [PATCH 610/695] Improved docker_ performance when getting CPU/Memory stats Added parallel processing using python's muliprocessing module to fork a new process each for each docker container when gathering stats. This vastly improves performance since using a for loop the script blocks until the stats are returned. --- plugins/docker/docker_ | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/plugins/docker/docker_ b/plugins/docker/docker_ index 61551901..cc51ffe5 100755 --- a/plugins/docker/docker_ +++ b/plugins/docker/docker_ @@ -50,6 +50,7 @@ https://docs.docker.com/compose/reference/envvars/): import os import sys import docker +from multiprocessing import Process, Queue def print_containers_status(client): @@ -84,9 +85,26 @@ def print_containers_status(client): print('dead.value', dead) -def print_containers_cpu(client): +def get_container_stats(container, q): + q.put(container.stats(stream=False)) + + +def parallel_container_stats(client): + proc_list = [] + stats = {} for container in client.containers.list(): - stats = container.stats(stream=False) + q = Queue() + p = Process(target=get_container_stats, args=(container, q)) + proc_list.append({'proc': p, 'queue': q, 'container': container}) + p.start() + for proc in proc_list: + proc['proc'].join() + stats[proc['container']] = proc['queue'].get() + return stats.items() + + +def print_containers_cpu(client): + for container, stats in parallel_container_stats(client): cpu_count = len(stats["cpu_stats"]["cpu_usage"]["percpu_usage"]) cpu_percent = 0.0 cpu_delta = float(stats["cpu_stats"]["cpu_usage"]["total_usage"]) \ @@ -99,9 +117,8 @@ def print_containers_cpu(client): def print_containers_memory(client): - for container in client.containers.list(): - stats = container.stats(stream=False)['memory_stats'] - print(container.name + '.value', stats['stats']['total_rss']) + for container, stats in parallel_container_stats(client): + print(container.name + '.value', stats['memory_stats']['stats']['total_rss']) def main(): From 642b48a4f70d154b793a8cd124d4f6377feccf22 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 11:01:44 +0200 Subject: [PATCH 611/695] Plugin postgres_queries3: fix documentation Thanks, Romain Maziere Closes: #1067 --- plugins/postgresql/postgres_queries3_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postgresql/postgres_queries3_ b/plugins/postgresql/postgres_queries3_ index c3b18169..0946a337 100755 --- a/plugins/postgresql/postgres_queries3_ +++ b/plugins/postgresql/postgres_queries3_ @@ -12,7 +12,7 @@ # Usage: # Place in /etc/munin/plugins/ (or link it there using ln -s) # Place table names after '_' and delimit with '-' -# EX: postgres_queries3_table1-table2-table3 +# EX: postgres_queries3_db1-db2-db3 # # Parameters: # config (required) From 15f1055e2c5bbff29214592a745d8bc133e6bca9 Mon Sep 17 00:00:00 2001 From: Vincas Dargis Date: Sun, 12 Apr 2020 18:08:08 +0300 Subject: [PATCH 612/695] bitcoind_: update for Bitcoin Core v0.19.0 release * Use new RPC calls to get relevant statistics, as some calls are either deprecated or completely removed. * Fix response printing when MUNIN_DEBUG is set. * Remove "fees" graph as there is no sane way to implement that. * Add missing success return. --- plugins/currency/bitcoin/bitcoind_ | 35 +++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 6a7bccb8..12fe03bd 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -69,7 +69,6 @@ def main(): command = sys.argv[1] if len(sys.argv) > 1 else None request_labels = {'balance': ('Wallet Balance', 'BTC'), 'connections': ('Peer Connections', 'Connections'), - 'fees': ("Tip Offered", "BTC"), 'transactions': ("Transactions", "Transactions", ('confirmed', 'waiting')), 'block_age': ("Last Block Age", "Seconds"), @@ -120,8 +119,9 @@ def main(): bitcoin_options.rpcport), username=bitcoin_options.rpcuser, password=bitcoin_options.rpcpassword) - (info, connect_error) = bitcoin.getinfo() - error = "Could not connect to Bitcoin server: {}".format(connect_error) + (info, connect_error) = bitcoin.getnetworkinfo() + if connect_error: + error = "Could not connect to Bitcoin server: {}".format(connect_error) if command == 'autoconf': if error: @@ -134,20 +134,35 @@ def main(): print(error, file=sys.stderr) return False + if request_var == 'balance': + # we use getbalance*s* (plural) as old getbalance is being deprecated, + # and we have to calculate total balance (owned and watch-only) manually now. + (info, error) = bitcoin.getbalances() + + total = sum(info[wallet_mode]['trusted'] + for wallet_mode in ('mine', 'watchonly') + if wallet_mode in info) + + info['balance'] = total + if request_var in ('transactions', 'block_age'): - (info, error) = bitcoin.getblockhash(info['blocks']) - (info, error) = bitcoin.getblock(info) + (info, error) = bitcoin.getblockchaininfo() + (info, error) = bitcoin.getblock(info['bestblockhash']) info['block_age'] = int(time.time()) - info['time'] info['confirmed'] = len(info['tx']) - if request_var in ('fees', 'transactions'): - (memory_pool, error) = bitcoin.getrawmempool() - if memory_pool: - info['waiting'] = len(memory_pool) + if request_var == 'difficulty': + (info, error) = bitcoin.getblockchaininfo() + + if request_var == 'transactions': + (memory_pool, error) = bitcoin.getmempoolinfo() + info['waiting'] = memory_pool['size'] for label in line_labels: print("%s.value %s" % (label, info[label])) + return True + def parse_conf(filename): """ Bitcoin config file parser. """ @@ -249,7 +264,7 @@ class Proxy: return (None, e) if DEBUG: - print('RPC Response (%s): %s' % (self.method, json.dumps(body, indent=4))) + print('RPC Response (%s): %s' % (self.method, json.dumps(json.loads(body), indent=4))) try: data = json.loads(body) From 9a2a81381cd0bbe9936f132b734e61d854937e5c Mon Sep 17 00:00:00 2001 From: Vincas Dargis Date: Thu, 16 Apr 2020 21:54:38 +0300 Subject: [PATCH 613/695] bitcoind_: return support for older Bitcoin Core versions Refactor to take Bitcoin Core minor version into consideration, and use that to implement legacy version support. --- plugins/currency/bitcoin/bitcoind_ | 51 ++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/plugins/currency/bitcoin/bitcoind_ b/plugins/currency/bitcoin/bitcoind_ index 12fe03bd..4bdf75e0 100755 --- a/plugins/currency/bitcoin/bitcoind_ +++ b/plugins/currency/bitcoin/bitcoind_ @@ -63,6 +63,45 @@ import urllib.request DEBUG = os.getenv('MUNIN_DEBUG') == '1' +def _get_version(info): + # v0.15.2 version is represented as 150200 + return info['version'] // 10000 + + +def _rpc_get_initial_info(connection): + (info, connect_error) = connection.getnetworkinfo() + if connect_error: + if isinstance(connect_error, urllib.error.HTTPError) and connect_error.code == 404: + # getinfo RPC exists in version <= 0.15 + (info, connect_error) = connection.getinfo() + if connect_error: + return (None, None, connect_error) + else: + return (None, None, connect_error) # pass all other not-404 errors + + return (info, _get_version(info), None) + + +def _rpc_get_balance(info, minor_version, connection): + # see https://github.com/bitcoin/bitcoin/blob/239d199667888e5d60309f15a38eed4d3afe56c4/ + # doc/release-notes/release-notes-0.19.0.1.md#new-rpcs + if minor_version >= 19: + # we use getbalance*s* (plural) as old getbalance is being deprecated, + # and we have to calculate total balance (owned and watch-only) manually now. + (result, error) = connection.getbalances() + + total = sum(result[wallet_mode]['trusted'] + for wallet_mode in ('mine', 'watchonly') + if wallet_mode in result) + + info['balance'] = total + return info + else: + (result, error) = connection.getbalance() + info['balance'] = result + return info + + def main(): # getinfo variable is read from command name - probably the sym-link name. request_var = sys.argv[0].split('_', 1)[1] or 'balance' @@ -119,7 +158,7 @@ def main(): bitcoin_options.rpcport), username=bitcoin_options.rpcuser, password=bitcoin_options.rpcpassword) - (info, connect_error) = bitcoin.getnetworkinfo() + (info, minor_version, connect_error) = _rpc_get_initial_info(bitcoin) if connect_error: error = "Could not connect to Bitcoin server: {}".format(connect_error) @@ -135,15 +174,7 @@ def main(): return False if request_var == 'balance': - # we use getbalance*s* (plural) as old getbalance is being deprecated, - # and we have to calculate total balance (owned and watch-only) manually now. - (info, error) = bitcoin.getbalances() - - total = sum(info[wallet_mode]['trusted'] - for wallet_mode in ('mine', 'watchonly') - if wallet_mode in info) - - info['balance'] = total + info = _rpc_get_balance(info, minor_version, bitcoin) if request_var in ('transactions', 'block_age'): (info, error) = bitcoin.getblockchaininfo() From 168f6f924fc8efc2719af852977e8c2da5ffb0e0 Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 18 Apr 2020 12:34:04 +0200 Subject: [PATCH 614/695] display units for tor_traffic --- plugins/tor/tor_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 921cd9ea..066ca897 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -458,7 +458,7 @@ class TorTraffic(TorPlugin): def conf(self): graph = {'title': 'Tor traffic', 'args': '-l 0 --base 1024', - 'vlabel': 'data', + 'vlabel': 'bytes/s', 'category': 'network', 'info': 'bytes read/written'} labels = {'read': {'label': 'read', 'min': 0, 'type': 'DERIVE'}, From 71f9280576321488f87c286b61a40d6b6ab7fcea Mon Sep 17 00:00:00 2001 From: Pierre-Alain TORET Date: Sat, 18 Apr 2020 12:35:32 +0200 Subject: [PATCH 615/695] change Python iteritems by items --- plugins/tor/tor_ | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ index 066ca897..7029cb9c 100755 --- a/plugins/tor/tor_ +++ b/plugins/tor/tor_ @@ -134,11 +134,11 @@ class TorPlugin(object): @staticmethod def conf_from_dict(graph, labels): # header - for key, val in graph.iteritems(): + for key, val in graph.items(): print('graph_{} {}'.format(key, val)) # values - for label, attributes in labels.iteritems(): - for key, val in attributes.iteritems(): + for label, attributes in labels.items(): + for key, val in attributes.items(): print('{}.{} {}'.format(label, key, val)) @staticmethod @@ -253,7 +253,7 @@ class TorConnections(TorPlugin): states = dict((state, 0) for state in stem.ORStatus) for connection in connections: states[connection.rsplit(None, 1)[-1]] += 1 - for state, count in states.iteritems(): + for state, count in states.items(): print('{}.value {}'.format(state.lower(), count)) except stem.connection.AuthenticationFailure as e: print('Authentication failed ({})'.format(e)) From edb4e1e81b3a4888925df8b510f116a7720f375a Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:38:52 +0200 Subject: [PATCH 616/695] Plugin apache_memory: specify common default values Previously the plugin did not work without configuration. Now it should work on many systems out of the box. --- plugins/apache/apache_memmory | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 4e829191..f754e00f 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -10,8 +10,8 @@ apache_memmory -Indicate the medium size of all the apache child process =head1 CONFIGURATION [apache_*] -env.apuser user_running_apache -env.binname apache_binary_name +env.apuser user_running_apache (default: "www-data") +env.binname apache_binary_name (default: "apache2") =head1 AUTHOR @@ -31,8 +31,8 @@ GPLv2 . $MUNIN_LIBDIR/plugins/plugin.sh -USR=$apuser -PROCS=$binname +USR=${apuser:-www-data} +PROCS=${binname:-apache2} if [ "$1" = "autoconf" ]; then From b087127c04dc996527a1989c83966d7d5731814b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:40:54 +0200 Subject: [PATCH 617/695] Plugin apache_memory: unify whitespace --- plugins/apache/apache_memmory | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index f754e00f..24583bb0 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -36,26 +36,24 @@ PROCS=${binname:-apache2} if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + echo yes + exit 0 fi if [ "$1" = "config" ]; then - echo 'graph_title Medium size of apache child process.' - echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel Kb' - echo 'graph_scale no' - echo 'graph_category webserver' - echo 'graph_info Indicate the memdium size of all the apache child process.' + echo 'graph_title Medium size of apache child process.' + echo 'graph_args --base 1000 -l 0 ' + echo 'graph_vlabel Kb' + echo 'graph_scale no' + echo 'graph_category webserver' + echo 'graph_info Indicate the memdium size of all the apache child process.' + echo "servers.label servers" + echo "servers.type GAUGE" + echo "servers.min 0" - - echo "servers.label servers" - echo "servers.type GAUGE" - echo "servers.min 0" - - exit 0 + exit 0 fi VAL1=`ps auxf | grep ${PROCS} | grep ^${USR} | grep -v grep | wc -l` From dd41152cdd6e5a12dd43dc4df03f4e5e46924b65 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:41:42 +0200 Subject: [PATCH 618/695] Plugin apache_memory: properly quote patterns and simplify calculation Previously the calculation had some issues: * patterns with whitespace caused errors * the number of processes could change between the steps for counting and averaging * handle "no match" properly (returning "U" - unknown - instead of an error) * avoid calculation based on unsafe "eval" --- plugins/apache/apache_memmory | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 24583bb0..87a93a72 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -56,12 +56,11 @@ if [ "$1" = "config" ]; then exit 0 fi -VAL1=`ps auxf | grep ${PROCS} | grep ^${USR} | grep -v grep | wc -l` - -VAL2=`ps auxf | grep ${PROCS} | grep ^${USR} | grep -v grep | awk '{s+=$6} END {print s}'` - -VAL3=`expr $VAL2 / $VAL1` - -echo "servers.value $VAL3" - +matched_processes=$(ps auxf | grep -- "$PROCS" | grep "^$USR" | grep -v grep) +if [ -n "$matched_processes" ]; then + average_memory=$(printf '%s' "$matched_processes" | awk '{count+=1; sum+=$6} END {print sum/count}') +else + average_memory="U" +fi +echo "servers.value $average_memory" From 1ac5fbdfe0dc941664711ee52414b2966957cdc9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:45:47 +0200 Subject: [PATCH 619/695] Plugin apache_memory: fix quoting --- plugins/apache/apache_memmory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 87a93a72..e8dd47e6 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -29,7 +29,7 @@ GPLv2 =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" USR=${apuser:-www-data} PROCS=${binname:-apache2} From d8fd604e8fd9718c495f1ecbb75d6405c3e55432 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:46:23 +0200 Subject: [PATCH 620/695] Plugin apache_memory: improve title --- plugins/apache/apache_memmory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index e8dd47e6..1390d4dc 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -42,7 +42,7 @@ fi if [ "$1" = "config" ]; then - echo 'graph_title Medium size of apache child process.' + echo 'graph_title Average size of apache child processes' echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Kb' echo 'graph_scale no' From e7fbfe0b8b12cde37600298ee4878a993ea798e2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:51:02 +0200 Subject: [PATCH 621/695] Plugin apache_memory: use base SI units for result Munin will take care for proper units dynamically. This breaks previously existing graphs. --- plugins/apache/apache_memmory | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 1390d4dc..2a391ee5 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -43,8 +43,8 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Average size of apache child processes' - echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel Kb' + echo 'graph_args --base 1024 -l 0 ' + echo 'graph_vlabel Bytes' echo 'graph_scale no' echo 'graph_category webserver' echo 'graph_info Indicate the memdium size of all the apache child process.' @@ -58,7 +58,7 @@ fi matched_processes=$(ps auxf | grep -- "$PROCS" | grep "^$USR" | grep -v grep) if [ -n "$matched_processes" ]; then - average_memory=$(printf '%s' "$matched_processes" | awk '{count+=1; sum+=$6} END {print sum/count}') + average_memory=$(printf '%s' "$matched_processes" | awk '{count+=1; sum+=$6} END {print sum / count * 1024}') else average_memory="U" fi From 62ff5981d2660c95907c531a214690d6bdf167dc Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 19:51:52 +0200 Subject: [PATCH 622/695] Rename plugin "apache_memmory" to "apache_memory" The original name was misspelled. --- plugins/apache/{apache_memmory => apache_memory} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/apache/{apache_memmory => apache_memory} (100%) diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memory similarity index 100% rename from plugins/apache/apache_memmory rename to plugins/apache/apache_memory From 16ab1d10e1eab0da40a9bcb410918ca4e0cb7303 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 20:02:04 +0200 Subject: [PATCH 623/695] Plugin apache_memory: fix spelling and mark as clean --- plugins/apache/apache_memory | 2 +- t/test-exception-wrapper.expected-failures | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/apache/apache_memory b/plugins/apache/apache_memory index 2a391ee5..0b9a62cf 100755 --- a/plugins/apache/apache_memory +++ b/plugins/apache/apache_memory @@ -5,7 +5,7 @@ =head1 NAME -apache_memmory -Indicate the medium size of all the apache child process +apache_memory -Indicate the medium size of all the apache child process =head1 CONFIGURATION diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 9f1e7b68..3b51a9d9 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -6,7 +6,6 @@ plugins/amule/amule_shares plugins/amule/amule_transfers plugins/amule/amule_uptime plugins/apache/apache_cache_disk_count -plugins/apache/apache_memmory plugins/apache/apache_servers plugins/apache/apache_threads plugins/apache/apache_tmemmory From dbaeb0253c54487808e0a191db1484c444bdc798 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 20:11:27 +0200 Subject: [PATCH 624/695] Plugins apache_tmemory and apache_servers: multiple changes * use base SI units for memory consumption (breaks graph values compared to older version) * fix quoting for regular expressions * use common defaults for configuration settings --- plugins/apache/apache_servers | 43 +++++++++------------ plugins/apache/apache_tmemmory | 45 ++++++++++------------ t/test-exception-wrapper.expected-failures | 2 - 3 files changed, 39 insertions(+), 51 deletions(-) diff --git a/plugins/apache/apache_servers b/plugins/apache/apache_servers index 9d20febe..2ec1cdb1 100755 --- a/plugins/apache/apache_servers +++ b/plugins/apache/apache_servers @@ -5,13 +5,13 @@ =head1 NAME -apache_servers -Indicate the number of apache servers running (child process) +apache_servers - Indicate the number of apache servers running (child process) =head1 CONFIGURATION [apache_*] -env.apuser user_runnin_apache -env.binname apache_binary_name +env.apuser user_running_apache (default: "www-data") +env.binname apache_binary_name (default: "apache2") =head1 AUTHOR @@ -28,10 +28,10 @@ GPLv2 =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" -USR=$apuser -PROCS=$binname +USR=${apuser:-www-data} +PROCS=${binname:-apache2} if [ "$1" = "autoconf" ]; then echo yes @@ -40,25 +40,20 @@ fi if [ "$1" = "config" ]; then - echo 'graph_title Number of apache servers running.' - echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel servers' - echo 'graph_scale no' - echo 'graph_category webserver' - echo 'graph_info Indicate the number of apache servers running (child process).' + echo 'graph_title Number of apache servers running' + echo 'graph_args --base 1000 -l 0 ' + echo 'graph_vlabel servers' + echo 'graph_scale no' + echo 'graph_category webserver' + echo 'graph_info Indicate the number of apache servers running (child process).' + echo "servers.label servers" + echo "servers.type GAUGE" + echo "servers.min 0" + echo "servers.info Number of apache processes" - - echo "servers.label servers" - echo "servers.type GAUGE" - echo "servers.min 0" - echo "servers.info I/O on nfs" - - exit 0 + exit 0 fi -VAL1=`ps auxf | grep ${PROCS} | grep ^${USR} | grep -v grep | wc -l` - -echo "servers.value $VAL1" - - +process_count=$(ps auxf | grep -- "$PROCS" | grep "^$USR" | grep -v grep | wc -l) +echo "servers.value $process_count" diff --git a/plugins/apache/apache_tmemmory b/plugins/apache/apache_tmemmory index 9a0c5d55..fcb26d4f 100755 --- a/plugins/apache/apache_tmemmory +++ b/plugins/apache/apache_tmemmory @@ -5,12 +5,13 @@ =head1 NAME -apache_tmemmory -Indicate the total memory used by apache +apache_tmemmory - Indicate the total memory used by apache =head1 CONFIGURATION [apache_*] -env.binname apache_binary_name +env.apuser user_running_apache (default: "www-data") +env.binname apache_binary_name (default: "apache2") =head1 AUTHOR @@ -27,38 +28,32 @@ GPLv2 =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" - -USR=$apuser -PROCS=$binname +USR=${apuser:-www-data} +PROCS=${binname:-apache2} if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + echo yes + exit 0 fi if [ "$1" = "config" ]; then - echo 'graph_title Total memory used by apache' - echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel Mb' - echo 'graph_scale no' - echo 'graph_category webserver' - echo 'graph_info Indicate the total memory used by apache.' + echo 'graph_title Total memory used by apache' + echo 'graph_args --base 1024 -l 0 ' + echo 'graph_vlabel bytes' + echo 'graph_scale no' + echo 'graph_category webserver' + echo 'graph_info Indicate the total memory used by apache.' - echo "servers.label servers" - echo "servers.type GAUGE" - echo "servers.min 0" + echo "servers.label servers" + echo "servers.type GAUGE" + echo "servers.min 0" - exit 0 + exit 0 fi -VAL1=`ps auxf | grep ${PROCS} | grep -v grep | awk '{s+=$6} END {print s}'` - -VAL2=`expr $VAL1 / 1024` - -echo "servers.value $VAL2" - - +total_memory=$(ps auxf | grep -- "$PROCS" | grep "^$USR" | grep -v grep | awk '{s+=$6} END {print s * 1024}') +echo "servers.value $total_memory" diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 3b51a9d9..5f298af6 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -6,9 +6,7 @@ plugins/amule/amule_shares plugins/amule/amule_transfers plugins/amule/amule_uptime plugins/apache/apache_cache_disk_count -plugins/apache/apache_servers plugins/apache/apache_threads -plugins/apache/apache_tmemmory plugins/apache/apache_users plugins/apache/page_load plugins/apache/qpid_bytedepth From f6738f415e10d57f330f71c9a61944b4a5055f96 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 21 Apr 2020 20:14:04 +0200 Subject: [PATCH 625/695] Plugin apache_memory: fix title description formatting The plugin gallery and "munin-run" relies on a space after the hyphen. --- plugins/apache/apache_memory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/apache/apache_memory b/plugins/apache/apache_memory index 0b9a62cf..4e5f84bb 100755 --- a/plugins/apache/apache_memory +++ b/plugins/apache/apache_memory @@ -5,7 +5,7 @@ =head1 NAME -apache_memory -Indicate the medium size of all the apache child process +apache_memory - Indicate the medium size of all the apache child process =head1 CONFIGURATION From 5a37346602f5890349e59564d281b1c819282929 Mon Sep 17 00:00:00 2001 From: Romain MAZIERE Date: Wed, 22 Apr 2020 18:13:37 +0200 Subject: [PATCH 626/695] Fix geowebcache-cache-hits-ratio --- plugins/geowebcache/geowebcache-cache-hits-ratio | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/geowebcache/geowebcache-cache-hits-ratio b/plugins/geowebcache/geowebcache-cache-hits-ratio index a14f4d5c..941b62e3 100755 --- a/plugins/geowebcache/geowebcache-cache-hits-ratio +++ b/plugins/geowebcache/geowebcache-cache-hits-ratio @@ -1,10 +1,10 @@ #!/usr/bin/perl # -*- perl -*- -# Author Rodolphe Quideville +# Author Rodolphe Quiédeville # Licence : GPLv2 # Code based on tomcat_volume plugin by -# Rune Nordbe Skillingstad +# Rune Nordbøe Skillingstad =head1 NAME @@ -41,7 +41,7 @@ HTTP port numbers =head1 AUTHOR -Rodolphe Quideville +Rodolphe Quiédeville =head1 USAGE @@ -111,8 +111,8 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { my $ua = LWP::UserAgent->new(timeout => $TIMEOUT); my $response = $ua->request(HTTP::Request->new('GET',$url)); -if ($response->content =~ ' Cache hit ratio:(\d\d?\.?\d+)% of requests' ) { - print "ratio.value " . $1; +if ($response->content =~ 'Cache hit ratio:(\d+\.\d+)% of requests' ) { + print "ratio.value " . $1 . "\n"; } else { print "ratio.value U\n"; } From 46f383f92cdf048936fe6523b9a663a25dcb75e2 Mon Sep 17 00:00:00 2001 From: Romain MAZIERE Date: Wed, 22 Apr 2020 18:10:45 +0200 Subject: [PATCH 627/695] Fix geowebcache-blankitems --- plugins/geowebcache/geowebcache-blankitems | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/geowebcache/geowebcache-blankitems b/plugins/geowebcache/geowebcache-blankitems index 68d00da6..be05230b 100755 --- a/plugins/geowebcache/geowebcache-blankitems +++ b/plugins/geowebcache/geowebcache-blankitems @@ -1,10 +1,10 @@ #!/usr/bin/perl # -*- perl -*- -# Author Rodolphe Quideville +# Author Rodolphe Quiédeville # Licence : GPLv2 # Code based on tomcat_volume plugin by -# Rune Nordbe Skillingstad +# Rune Nordbøe Skillingstad =head1 NAME @@ -41,7 +41,7 @@ HTTP port numbers =head1 AUTHOR -Rodolphe Quideville +Rodolphe Quiédeville =head1 USAGE @@ -112,11 +112,11 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { my $ua = LWP::UserAgent->new(timeout => $TIMEOUT); my $response = $ua->request(HTTP::Request->new('GET',$url)); -if ($response->content =~ 'Blank/KML/HTML:(\d+\.?\d+)% of requests') { +if ($response->content =~ 'Blank/KML/HTML:(\d+\.?\d+)% of requests') { my $value = $1; $value = $value * 1024 if ($2 eq 'k'); $value = $value * 1024 * 1024 if ($2 eq 'm'); - print "blank.value " . $value; + print "blank.value " . $value . "\n"; } else { print "blank.value U\n"; } From 0cfcf2ccbf2c54362cca60e1076de4e2371becb3 Mon Sep 17 00:00:00 2001 From: Romain MAZIERE Date: Wed, 22 Apr 2020 18:09:17 +0200 Subject: [PATCH 628/695] Fix geowebcache-bandwidth --- plugins/geowebcache/geowebcache-bandwidth | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/geowebcache/geowebcache-bandwidth b/plugins/geowebcache/geowebcache-bandwidth index 3863510b..f5c90cf1 100755 --- a/plugins/geowebcache/geowebcache-bandwidth +++ b/plugins/geowebcache/geowebcache-bandwidth @@ -1,10 +1,10 @@ #!/usr/bin/perl # -*- perl -*- -# Author Rodolphe Quideville +# Author Rodolphe Quiédeville # Licence : GPLv2 # Code based on tomcat_volume plugin by -# Rune Nordbe Skillingstad +# Rune Nordbøe Skillingstad =head1 NAME @@ -41,7 +41,7 @@ HTTP port numbers =head1 AUTHOR -Rodolphe Quideville +Rodolphe Quiédeville =head1 USAGE @@ -111,11 +111,11 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { my $ua = LWP::UserAgent->new(timeout => $TIMEOUT); my $response = $ua->request(HTTP::Request->new('GET',$url)); -if ($response->content =~ '60 seconds\d+\d+.\d+ /s\d+(\d+\.\d+) ([km]?)bps') { +if ($response->content =~ '60 seconds\d+\d+.\d+ /s\d+(\d+\.\d+) ([km]?)bps') { my $value = $1; $value = $value * 1024 if ($2 eq 'k'); $value = $value * 1024 * 1024 if ($2 eq 'm'); - print "bandw.value " . $value; + print "bandw.value " . $value . "\n"; } else { print "bandw.value U\n"; } From 3032db4890bc6b7ae7b2fcf0b0b81011aee56a40 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Fri, 27 Mar 2020 13:53:47 +0100 Subject: [PATCH 629/695] Added monitoring of router temperature --- plugins/router/snmp__mikrotik | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plugins/router/snmp__mikrotik b/plugins/router/snmp__mikrotik index 5f6e3340..bcbde9b9 100755 --- a/plugins/router/snmp__mikrotik +++ b/plugins/router/snmp__mikrotik @@ -49,6 +49,7 @@ my $sysFlashUsageOID = "1.3.6.1.2.1.25.2.3.1.6.131072"; my $sysFlashTotalOID = "1.3.6.1.2.1.25.2.3.1.5.131072"; my $sysRAMUsageOID = "1.3.6.1.2.1.25.2.3.1.6.65536"; my $sysRAMTotalOID = "1.3.6.1.2.1.25.2.3.1.5.65536"; +my $sysTempOID = "1.3.6.1.4.1.14988.1.1.3.10.0"; my $response; @@ -57,6 +58,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { print "require $sysFlashTotalOID\n"; print "require $sysRAMUsageOID\n"; print "require $sysRAMTotalOID\n"; + print "require $sysTempOID\n"; exit 0; } @@ -112,6 +114,17 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "sysRAMUsage.label Used Memory\n"; print "sysRAMUsage.draw AREA\n"; } + + $response = $session->get_request($sysTempOID); + if (defined $response) { + print "multigraph temp\n"; + print "graph_vlabel degC \n"; + print "graph_title Temperature\n"; + print "graph_category sensors\n"; + print "graph_info This graph shows the router's temperature.\n"; + print "sysTemp.label Temperature\n"; + print "sysTemp.type GAUGE\n"; + } exit 0 unless (($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1); } @@ -136,4 +149,12 @@ if (defined $response) { print "sysRAMTotal.value U\n"; } +print "multigraph temp\n"; +$response = $session->get_request(-varbindlist => [$sysTempOID]); +if (defined $response) { + print "sysTemp.value ", $response->{$sysTempOID}/10, "\n"; +} else { + print "sysTemp.value U\n"; +} + # vim:syntax=perl From 475fe56df24a2ea4f17452891ed89a8075431214 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Thu, 23 Apr 2020 00:50:07 +0200 Subject: [PATCH 630/695] Removed requirement Some routers doesn't have temperature sensors --- plugins/router/snmp__mikrotik | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/router/snmp__mikrotik b/plugins/router/snmp__mikrotik index bcbde9b9..6aaa0115 100755 --- a/plugins/router/snmp__mikrotik +++ b/plugins/router/snmp__mikrotik @@ -58,7 +58,6 @@ if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { print "require $sysFlashTotalOID\n"; print "require $sysRAMUsageOID\n"; print "require $sysRAMTotalOID\n"; - print "require $sysTempOID\n"; exit 0; } From 699158fd17d658027ee040296f9ee7f4be084446 Mon Sep 17 00:00:00 2001 From: Alejandro Suarez Date: Thu, 23 Apr 2020 00:54:29 +0200 Subject: [PATCH 631/695] Requirements workaround due to #1073 --- plugins/router/snmp__mikrotik | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/router/snmp__mikrotik b/plugins/router/snmp__mikrotik index 6aaa0115..2cf1880b 100755 --- a/plugins/router/snmp__mikrotik +++ b/plugins/router/snmp__mikrotik @@ -54,10 +54,10 @@ my $sysTempOID = "1.3.6.1.4.1.14988.1.1.3.10.0"; my $response; if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { - print "require $sysFlashUsageOID\n"; - print "require $sysFlashTotalOID\n"; - print "require $sysRAMUsageOID\n"; - print "require $sysRAMTotalOID\n"; + print "require $sysFlashUsageOID ^\\d\n"; + print "require $sysFlashTotalOID ^\\d\n"; + print "require $sysRAMUsageOID ^\\d\n"; + print "require $sysRAMTotalOID ^\\d\n"; exit 0; } From 8939111823f350d962b065ae7008c8c7df17bb90 Mon Sep 17 00:00:00 2001 From: Rowan Wookey Date: Sun, 26 Apr 2020 21:58:37 +0100 Subject: [PATCH 632/695] Fixed autoconf on http_request_time Previously it would error since the $url variable is the array key isn't the url to check it's a santized version --- plugins/http/http_request_time | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time index 8c796809..8c666f12 100755 --- a/plugins/http/http_request_time +++ b/plugins/http/http_request_time @@ -90,7 +90,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) my $ua = LWP::UserAgent->new(timeout => $timeout); foreach my $url (keys %URLS) { - my $response = $ua->request(HTTP::Request->new('GET',$url)); + my $response = $ua->request(HTTP::Request->new('GET',$URLS{$url}{'url'})); if ($response->is_success) { next; } From 24f488377753531ecf2d11e98027f6cbdf713f23 Mon Sep 17 00:00:00 2001 From: Doctor Date: Wed, 29 Apr 2020 14:41:00 +0200 Subject: [PATCH 633/695] Change regex to match "prepend" pattern --- plugins/postfix/policyd-spf-python | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postfix/policyd-spf-python b/plugins/postfix/policyd-spf-python index 3c169b40..233a3c04 100755 --- a/plugins/postfix/policyd-spf-python +++ b/plugins/postfix/policyd-spf-python @@ -69,7 +69,7 @@ fi # function get_log_count() { - egrep "policyd-spf\[[0-9]+\]: $1;" "$LOGFILE" | grep "$(date '+%b %e')" | wc -l + egrep "policyd-spf\[[0-9]+\]:(.*) $1" "$LOGFILE" | grep "$(date '+%b %e')" | wc -l } PASS=$(get_log_count "Pass") From 31f64337b6a03ba88d32c029827668ba081b2f70 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 16 May 2020 17:05:00 +0200 Subject: [PATCH 634/695] Plugin sabnzbd_*: fix compatbility with SABnzbd 3.x The change is probably compatible with SABnzbd 2.x, as well. Thanks, waywit for the change proposal. Closes: #1082 --- plugins/sabnzbd/sabnzbd_dataleft | 2 +- plugins/sabnzbd/sabnzbd_speed | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/sabnzbd/sabnzbd_dataleft b/plugins/sabnzbd/sabnzbd_dataleft index 3eed92c8..106e1739 100755 --- a/plugins/sabnzbd/sabnzbd_dataleft +++ b/plugins/sabnzbd/sabnzbd_dataleft @@ -35,7 +35,7 @@ use LWP::UserAgent; #defines my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8080/"; my $API = exists $ENV{'api'} ? $ENV{'api'} : ""; -my $URL = $HOST."/sabnzbd/api?mode=qstatus&output=xml&apikey=".$API; +my $URL = $HOST."/sabnzbd/api?mode=queue&output=xml&apikey=".$API; my $sURL = sprintf $URL; #config output diff --git a/plugins/sabnzbd/sabnzbd_speed b/plugins/sabnzbd/sabnzbd_speed index af48066d..bbd5b3f9 100755 --- a/plugins/sabnzbd/sabnzbd_speed +++ b/plugins/sabnzbd/sabnzbd_speed @@ -35,7 +35,7 @@ use LWP::UserAgent; #defines my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8080/"; my $API = exists $ENV{'api'} ? $ENV{'api'} : ""; -my $URL = $HOST."/sabnzbd/api?mode=qstatus&output=xml&apikey=".$API; +my $URL = $HOST."/sabnzbd/api?mode=queue&output=xml&apikey=".$API; my $sURL = sprintf $URL; #config output From e254623ba1f1e9ad05c06ce1fd1e1c692b079107 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 27 May 2020 17:55:50 +1000 Subject: [PATCH 635/695] [plugins/transmission] Fixups for consistency Use standard colours for the transmission_activity plot (but keep a darker one for paused torrents). Also, remove the ${host} from the graph_title, and add it in the graph_info, for better groupability. Signed-off-by: Olivier Mehani --- plugins/network/transmission | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/network/transmission b/plugins/network/transmission index f82092b5..e87613ef 100755 --- a/plugins/network/transmission +++ b/plugins/network/transmission @@ -88,11 +88,11 @@ passwd = os.getenv('pass') def config(): conf = Template("""multigraph ${plugin_name}_throughput -graph_title Transmission throughput for ${host} +graph_title Transmission throughput graph_vlabel bytes/${graph_period} in (-) / out (+) graph_args --base 1000 graph_category network -graph_info This graph shows the throughput for Transmission torrents +graph_info This graph shows the throughput for Transmission torrents on ${host} down.label throughput down.type COUNTER down.draw AREA @@ -105,19 +105,19 @@ up.draw AREA up.min 0 multigraph ${plugin_name}_activity -graph_title Transmission activity for ${host} +graph_title Transmission activity graph_vlabel torrents graph_args --base 1000 graph_category network -graph_info This graph shows the number of Transmission torrents +graph_info This graph shows the number of Transmission torrents on ${host} active.label active active.draw AREA active.min 0 -active.colour 77FF6F +active.colour COLOUR0 paused.label paused paused.draw STACK paused.min 0 -paused.colour 8F8F8F +paused.colour COLOUR8 """) print conf.safe_substitute(plugin_name=plugin_name, host=host) sys.exit(0) From 54c9563274aac0dfbf38d5bf8347ee56563004f0 Mon Sep 17 00:00:00 2001 From: Rowan Wookey Date: Fri, 3 Jul 2020 18:00:59 +0100 Subject: [PATCH 636/695] Modified http_request_time to allow specifying a user agent Similar to other settings for this plugin, there is a new env.urlN_agent setting which allows specifying a user agent --- plugins/http/http_request_time | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time index 8c666f12..cb8bebfc 100755 --- a/plugins/http/http_request_time +++ b/plugins/http/http_request_time @@ -16,6 +16,7 @@ env.url3_name some_munin_internal_name env.url3_label Some random page on our website env.url3_proxy http://firewall:3128 + env.url3_agent Mozilla/5.0 env.timeout 3 Timeout is the timeout of any HTTP request. Tune to avoid a complete @@ -70,11 +71,13 @@ for (my $i = 1; $ENV{"url$i"}; $i++) my $proxy = $ENV{"url${i}_proxy"}; my $name = $ENV{"url${i}_name"} || clean($url); my $label = $ENV{"url${i}_label"} || $url; + my $agent = $ENV{"url${i}_agent"}; $URLS{$name}={ url=>$url, proxy=>$proxy, label=>$label, + agent=>$agent, time=>'U' }; } @@ -141,9 +144,15 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) } my $ua = LWP::UserAgent->new(timeout => $timeout); +my $defaultAgent = $ua->agent; foreach my $name (keys %URLS) { my $url = $URLS{$name}; + if ($url->{agent}) { + $ua->agent($url->{agent}); + } else { + $ua->agent($defaultAgent); + } if ($url->{proxy}) { $ua->proxy(['http', 'ftp'], $url->{proxy}); } From 7dc76c873faa1f66672b8c7bf0c252978e7e49f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnsteinn=20=C3=9E=C3=B3risson?= Date: Mon, 6 Jul 2020 22:34:00 +0000 Subject: [PATCH 637/695] Replace split with explode `split` was deprecated in PHP 5.3.0 and removed in PHP 7.0.0, `explode` is a drop-in replacement. --- plugins/voip/murmur_users | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/voip/murmur_users b/plugins/voip/murmur_users index b5a37b5f..b3a64a6f 100755 --- a/plugins/voip/murmur_users +++ b/plugins/voip/murmur_users @@ -95,7 +95,7 @@ if(isset($argv[1]) && $argv[1] == "config") // do the magic if(!$limit || ($limit >=5000 ) || $limit <= 0) $limit = 5000; $out = shell_exec("tail -n ".$limit." \"".$logfile."\""); -$fp = split("\n",$out); +$fp = explode("\n",$out); if(!count(@$fp)) { fwrite(STDOUT, "0\n"); return 1; From a731424cf0aaf91792485a2ee8d659ccf1aed171 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 15 Jun 2020 16:23:05 +1000 Subject: [PATCH 638/695] [plugins/weather/wunderground] Add plugin to query station data from Weather Underground It's a bit backwards, but works a treat when you don't own the station but want the data in a better format. Includes warnings on UV and heat indices, and visible lines for wind direction and indices. Signed-off-by: Olivier Mehani --- .../wunderground_STATION-week.png | Bin 0 -> 44221 bytes plugins/weather/wunderground_ | 361 ++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 plugins/weather/example_graphs/wunderground_STATION-week.png create mode 100755 plugins/weather/wunderground_ diff --git a/plugins/weather/example_graphs/wunderground_STATION-week.png b/plugins/weather/example_graphs/wunderground_STATION-week.png new file mode 100644 index 0000000000000000000000000000000000000000..e978abb2510f0184e84dd026ac4f22469b7d3eeb GIT binary patch literal 44221 zcmZttb8w|!(>)HK*qnG`PbRi)+qP}nwryu(PHfwriEaPReD3F4_5Sg8)u~f;<*I#p z?_Rxnb=OXWysQ`kEDkIH06>rs7ghuSKyH8^A!tzGlM=;zWdMLO*Hc;DS<%3a(B9F` z)WX_?(AmS@gwVv@!V~~-U$4owNW^7J4EZ`ha{}*eAb_e`+tiQc+YuuFp{QupF%_Du zK2$vkO?_$S;69u4^>TIg8KuWF{8zEI=3bHex1!t@U)j^qo6Epc=k?ne z?a)>0@XF}Q^NRppUY)_U=jQ{wuY`oQIE3m-sH*ycMBW z-SWH{wvyJ@Gx?(@!Lzqvw&bg?lcRcGy?eK(4Y(AQ^^ET#jo*as-z{#q0-s;}uKo7p zq9m>kT6M=ReJXaM8VG;OT#cw*`RU~EV7!dDUCvbTjnA;W+T7T9@1T4&rS3d&^}K8l z1izQ99L&6J)Un0#i5dU?BK%NC`&tp{aclLOF>@tBS8ehpIi+!ZcEH|dcd2-4=)W5O z%D6gv+j`l0zmB5W@qHP)LieP1{nHWOk4Um;a%6PUbA6nBq;rg zSG%-&5F7J@nu}Icwx0g>)9-c9?{;IWvq@MWiep^s z=deknRcJxZLS9!C0?bpV-3!fi+|JI$({FH$f^o#Zt&=e@hc{9XnvTkG~IrfnKi)^hbl$Yo~#(fZ@Z|jG}XMQ zsdeGfw5m8!)3mO6X5)N@0zcV(zpi!V1Md{{9if)R{cAVHf@zxhj=FK0^$j=bTEpUx zimHyy153+ATR?=4>z2(EkE}gZG5g7(k}S`~Jriw%>wyg0j^mk4>&}hSN>Ar&+Rj&B zZOB#a+QyIWy3b7(U6TsvKTuD1JG?P%M6E%&T_H9N^_J$*Ge#n!x?^?Xd#+T3j35@0A}Sjza2 zzS;h8k{;o4Sy}glDS-^!Irt)K8H53fJJWT0zpX}KT1471H)R%<$dTTYwG{Zi(HJNTESN%J$F?&N~ zSV^NxDQ{2pk};7kqgqD6;~{(XGHr2n3TUr zu$H}nz-?!==;FN=7+3D?n3GH)*`kl&CAbgIyR}DZ*b~zJVUt#yn;D7fX&4KvL(L#n z5);BRck?zs^Oj0FpOoL~iR9?_v*B964Po1^vfuW@@*S@a^D}?>4Qu~d)3fen{IVB` zzb7hP>N?$z(Xs(Dr29r6yCWGJJ3TvxD}+yzYQ!# z6pl)nZ`1Hk^{Z=B8fz)rurO=8u8hGAfc_Y9V;rg9KeK$8`}*Er)tlGI#{DC5L~Yz^ zF58%e%&H-2A2hcCX@QB(#yVrgV8xSg=AgJnb?kq^!Nz<*B;lc?rxyaklAOu1SMQ-i zksTm~o4e$$(0%3t0y)H^v*+TA= z>8fpJUj8uoT@euJ$X05TG=c<+e?I+#e(p|=GBtRQx5_wbf;K80j~Rm8h^I|gqUISL zoqUfPqNaTUVTFrF$N6!7i3>rb3MyU>7?Ui!7XV#$pQ;7eF(4QaUm14>7}=0pK!1|! zu-ljsF}RnyaDj61>j(8Q=@x=3=#1L>?J{8GWOhJg%%^9RC|8GLeLZq#@< z)jLd%n26^QQs64Oc|tg+AG8(=$Lm7R-y@8*vx?<0h>R>Vl2;^qz|L5N1v3A;i-n{q zfGMyiCYZEoI#U`kO8Af?-U0#ZSapwy-i@=Vn|2YM2ut)AMg5rf8s5)vz0Da-l*=_% znq{8RHJ{C{EK)rOGhIV8V`P5}JG2fVGyZM-bx21JR2eZCeAG42{9k$RD9P8ce?ln0 zJiKBEuBI+~G3if^LsWi8Ba#z^(@9Hnr@1A8AM-Vh#<#961uPJ{%0wN1UhVyr*gGTnU8iu@Zea+PJ}otSW@rh5$KK zELQ7_sIqz;hmFmZa^#h%p_ghV5tjixbzb@3j=sX82Q!BvQ{fxF40E0e`|lAsvbgHg z2)=+>XMmHX`fBK=Lx*IqMM(q~wJ?t-fJ8pwWD}*^u7jq7S?(>%{@lh-Z^b>}BoopXb^*AKW-&CTurv44iFji%ED` zP^IDxh*OZG5s!N`#csID+;OtQ;(XUG@FmnrP)dl1t?2{gjHKV2h&SC_ zgd>m-t_BS!(@aJvf=pRkO9XoA*};9Zp%ZtHb=ib#&{g2tQayTzB13iNbyU?N^k!IF zbQ&i1pp1-dOv#){2O@7mx>x)v@>Q%>1d$rgQG zjxJ=tjIg3wkDifhfJU|(=}~~&!?pHFer1{hx;%vRyDYAe$cFutdnS!e2``rWsM4g6 z3mAfG0Gr6w68DE8CZ-*_E8U4iF|0ngl`A~z1UM63I#{+_seQw!h}M79W1jYBxo@ufurAH_`yjB+GKN& z)Dv2I6F?lkB}JRyVyDavNNBL}{*vt)|S!w-Rp$A^CM(71zCtc{x4 zAVH1yw`+jM+Eb#UNlEzIw-p3e+j|H8tlkIzxJR@6z&*iW3aj6#S%;P@7@)k=Dx$#! z(cv@eNU`#uJ@4UO5~vI69v$#@?*4VgsyzBJ%aVl$p9@uv{7*Q=OEk^ ziy=z2c{Diq-EU7&4jbI7(PX6i+PqP{lfG)j0C7X#P1tO|>!0pJ4D4746)C0mjHiSP3_vB%wK-eIO zzR{+yU7~QC8#mpv!_><`t;B4Pu3J-nbJ!wM{fwk-|9+Lm*p)%y?2OT+kh_~K0=@`t zji=M!8?UY(lZt;q`a%|M>?dCc0b>NOPy!YDci6Zl07qFia0m$!*=LrAT?zUIU4(m9 z0IVcykdhrty2v*?aG`XzX0T_A+-k_myuHvt3bQQIqi*Jbu0(>mp7p>kDPM`Dd{aC1 z-5f%L_S97coJ#{PJFu42*w%WQH`Q4~HSGL05#TS5sH;*ZP3$ewto86v7*2=~Su4G{3({}Lb-JNS&IEuD52w_b&iAXZ{& zR_VK(I%?UC;D&c%U`KvS%KZ@E5em2AlY`Xz(|qRJ5z#dQC7F+l!mR;gnl}gGN2Q0b zNPNkr|5RC!f*Cx7N@zg0KQcTV80ud`Hs{H!FP!*3Gkcg}yD#RT>*NUuT7|bHCLcD- zi7igQCujG`fH#j|Ilc$@h6!0-cgmv4j)-nWAJhkqr-uI1juu4YbS1_MC3@q1GRj5q zgnfDllJ~8ZVW10h;Qoij=&93J^aK2=3xiwZbEQfbM7->CJEy1tj)!Qp^bk`KHwLC8 z#3}!rj!F|UcBtF&5&yL@!ZHV@HMHYy5%ivy{&(!+S7M9&Q%43tLiaOpeWtca8Sj-G z)`k_yYWk-sCt5GsD4Tmq*B1Ov#Aj-49DCf@avNKZzpWrwPzuDDDglL-A zk_e{jL_{Y|$zmFV0>YhsEqe!Q6qF9tAba2i&q^2Qqf5kjzfU(HJJ*g!h?iTNObUqD z6|bGw{63ZNO3*Sv%>R0jN_JY%)wF89t<}*{Itl2#Dd#0lr9>poc2U;aBJ-~uHKg6B zl5BgXPf@FqtdYiwlJG4^EYw05HY=O$439HARfHfpqrXp=OJss}ri2UqJC8ZbH4Anl z@;sp*L;}BEQRwi^4%}=-sJZ)A`llfNH%H|?to@J*&Xb|P*>6hz5J&imICX=%fc9&rYD zvB;kOjfg`^Z9I@}(ojMVLN;4b=j$AmCF1#@Kkb)6!SH5{kml29NPa?zNm7IOxSVqm z(UszV?Hs=w6(o`9U{7v;(mQyRq(3=xxn?@Wn*8UC^BdyUzdphe%-}aab=wVm_wx>Twu0>Gxj2 zLxgQuqZ!b$lae7X4Yhe_F)ZN|tK=N=<=#F+gm@8#w-6D2jj3E~Sr4jYN-u&EG@@q! z2FhB*_xO=nCIOYUPm32a8cvGSccVWcoMuYt{AcfEAPL8$^DX*p;yGZsal1#zA8g@H zo{p)`LN2am#dNlzASOsk!-9g)qRK*~0=+H`zf?da^tZMjcFA9q(XSW=pFkm}&9?MJ z@*c<3c2O=N!ZJW^-b5Unxq1;4iB>zue8u}jiKFC-5+|qfBNbpAqhZ%#*7&80$iM^) z*(1cJp;REjmO__B^8%DkoK(|5PnCr^)HX7sta$xMUR3kgQcE+U-LP&iVB{RuL zz06^B1o$MONVh=q#B2}Me1k+8<-Z^nTa`SAcKhz&RF;5_czCb~zGLdPP3h4pfax9&?=p1in$URXMr732`qPnIImTd8o__XM zBr^knOd?C$I?N+soxRYg(P396!Zp@89-#@zIfad~kcHiOk~BI>ps88?i+-nFkQ3d zLT*6x{E~%6*`;$+STQ5JN_kaC!McNG2VbeGW|+o3DP+ZKfb78LpU61OJ`7wW{*5|^ zWDQsWSpxS(Z7GLZjcFG?v5vQmpHRoaEvA|=mxIt;H3xS^YxPJ-c#qgjbTO7Y50B`3 zYn>utAZ8^1H6&FKeGo-s|8)(iWbzi9h`DgClL!L&hjW)J60i?)_u3w(OuIXCua&r- zJ(~r4SjmbpQOQ+kw4Wmu3rQiyFPc;L3>$CcD^P0M?81?Df?PmVdn3!tFuJ_4=&{*L zQEntfh8eEcBm61;x$gYVyAp7`l60Xy&XisIox9I}-Jd7G1|(jWCm0FTur4Be_66iX zpQMH?pN$YT4jzR|p6KMPnq>ZQq3MrIAJUTR(qKa_h_djRcs8yb>c_4-$$nGrUPz1~ zn@m|j#8*qKhk}83(QmM9&Me5gL<>12O6avh*)g|t(7_vM@h@2?XWZDUk;C0MY;s3% zHhA6d)znvbJ}VY?y^S-sg*8>Yp!pK^u~hn(daiCzRzy7hH{SiYSPJ7oy~Wf5NF+u# z6saF*<|gpeMGfQ4Jtt6`r#+OI6q+F-@e!p$PTRfFiz>qY{(5W_{h*@N5@llut@U;G za?N1U@vt%ql}jo)-x9^2<7l6~OeEwN4sR9u&bd$r28=1_5~2J6tEff%23HDX*@ZaS zBuId@YQ-50w!*BgHYTDUgto^#d(_^mbts77GSvwDTzT$`!cv0itYYd1CCFG^wMs;R z6&4&SyhDj2;qe;sH_Ld6P_x%j^sjdEF2HRYRq2A^u7B z8vXonFK9vebMfe6NBAlcc~lOg;e+^E7(|YJlyvAWxkaG7Rz&_Dc4_oI zpE25PxNROPxpQ*&z;r<;dI~b_R`4KMQxOLlDCfuJEriXV5S=CB-351>sG4`{BKJ_A zjX#G;Gc}?LGV(~BZHcAOtv#K!=BPr3*1)@hV6JNrH7UZTPKIfKSE0YDKT@ep!UdUt z4=p<8FH|MJp#sAP4JbvVM#e;n8ik2n1YuGC^C?Nb#xJpC>} z`KKRQ58Rl}Bh0*ZD<@K{+=ZyCp0|xBv?@?*#UZ#7_w+0Zog{;_@GB2Q~WWgW_)raBh$@N_nH_s*5!zQHq? z<8XOICw>P*9TNLC<;9@Q)NwU@wF+8V?LpNa_M6N3x~bVNPC`TA$=~>+!ibR*7W_+y zz(-F%HC1;ZQia4yNX$Xl6gtTq)jPn+D&)@-Igv-TNudHEr)V7_Nm1JgLHk!jE#*VV zNSD%Ji~A@4IBSry#%Sdzlx9KJRPj00fIJqLRGRVbMZhR|T+`3;2a?3mLy!^mXsVRa z-#Fcd=qVf8uU>P)3f9v$A#ot-8*E8pK3QLv*h9P{LxQ9WpwZ==9%x3%9x4){phTcw z&II^(WLYB*0Whpz6inWGIz%vPTv!I_bttLb7xe>$_qNlQ1fw$9n>!#a(QiRxHm+Yt z#ySFIr3FCIyzxh+yYhcgOC>In#63%*xGUA8`qS0RrR;KMA82 zGed_VzxwnDaS@jr=BqftUWkguvnoe4$TU8#^FJa`$j6_v%)fbGM}2^&&xVzMQa8iY zPDWR8HRxyUS}q22YOFFkbLtb>X361P6I?PiY-wrIRiH}epNP&D!w2kXg~to%4>pYX z;-MH{7ZUPGkP5Ryx-d!oq8V?M*f|2R*04=S>e831Qv-E=d9>?oSSh$-AOeY_S26XV z`o0xD54M-0+AhMMhAeh}c0VaKybY<#KT)?@vR!e$ScWW`082wv6cdhmRtFvzdMY-R zo3JY$T<6IHxu;#-@?~6XwxS_tj^7E`r6F+KV(v+aNa0MDg zHS$ZBK!(-*@;)OQ&DZ^Cv!XdBKUf<_+cth^*1=h6x(YO!O7l=?K4K!sqbq?n9id*$ z3LTn5g@t*K$T%wB-@wHk0-S`hP*!{tQmZnJInGd#4KlumN_m?B^*lGo{h7NLe!F12 zjrY4?v7i?+<3Uo1s7PWcd_@Dm{Jfb=5~Ug{lh_C)HFhZx#yHK3cEGqgQF`mXE7pX! z?rb_1EnOQ*H0{P!y896$n(>}~@VHkLZk}q8RaS+pYo)&zakM(NToS)(j;*mO+}PvA z&AnO@de=<&ZLCjYn9vagBh!A~2tV2@b0vV5ajSMR`6XmDK!4XuF$P^Ir~J*4Dprm^ zi!-q-NpF_n=LnDT7({jn(Kdrha+)MNb`Hn$v%=d{8K}4MF$y}v;^rY%oiLerzS9pv zcu8;#m)RhGoJdAh%QysFJG_Sg`_*Sk6HjFMx)Vkt%U+dBACoGFKd61dLS8cVrA34u ztj+mCOfd4x94rgfSI|-)L^^icTy2|1WKamQ8Z|@yRdX6_)0ox#4Q@HK6uR$WNW3Xm23rp}i1yWPMUN%e}wZyM{GkWNBDmt|_( zE`w0^`N|(toRhT{1zGLkTB0lsM}L-P-3nr2*J-J|2#2L)QQjuNPv#(RUDDLTYVMMi zvj#{(hsklJJ?+MT8UwHBRM@z|P!J&>5lQqQI73{&H~!ixp>Uu31i`tpMrz z5e@}6c*rnrX~oE60)j9mHNaf{9c|;Mf_E3gd%bgX0DHn2rEb}YdS-E>pMfrj`f749 zIpV`3xCMOn56=S5fFLSvX{=MV}&h!O)z3-+vvB23#D)n zlp91aWov1eMR}Kq_v_ozL5^wRqv2CcQ1S1Q3tr6|8S5PUe4Dj1r()-ouPVkN*L)D| zh8Wn;pUDV)an*8aim-H$#KXba*Bp3oO<*3L(Z@z$bh~IMP=zW?N5|g?s?LG=I zX@6?}w8KVZ2vFP8CE}KW=)YKQkI7UG;D=6g;x*J3KR^@>dBr!L!kfwM+-Ztz#d0nf z&?|H(i*1Iz8Hf-y6nYlo;hizrk{+ze#Goocy~6cP{y|^cMZ`_&2lzNBwlqDa*{N|x zLo2}Q7F70XSz}}b6UEr?@L*%9T!EpM9kUzaavzF*lSWk;A;99EIX4l|YZlePl5e5f zq%lo_xlHoHREWMwR!r!%KAmlJE3R8+b@7;l4&U?8O665Fqn@Z1MERLd@qHDFoJF2O zXyo#jnL>#z*}Vb^Ub8pt3%m-l$a#^QCst5~SBk!K{y5s7->Z?Jh``L^3C(AG+C0@g zLjPfd7ye6dn3uNspkpxnQebBrdg(|aaG${Q)GFI%kcDKPg5ku{%OupB-z1vP$Az!Q z0G%Jqc#s#lD2>o`5=hhe$KbLtg;YMFEBPQKRg7>kB@K$1+k2m@v?|-m+bW&fOE6$3 zYPpNEeawmu2>1Dwdk_ooU}mf_*VX%ZE0JIVmULKN@m)lfSVVs{Ecp-~Y5?1oSx7jG zj{-E%zBzJhb6!fYPdv+o zzTSX6rJ2-sP0PF}ACm4% zvw``kqy7SITxVD|U!}dc$@KWc(0^;>d>NJLmFiU9T3j_BSeqoH1G71-BVp~WG-V&hyPnvcv5{^?5D}rASdGlI zr6m~0tn}d8qJi8xcwAfQkcF{cbgr)A(NG-celkA?bnk2Y{$OKv&Ok049#O;lQpp`! zPf@b-@mRwKEw#ok@cVfO8JCxU_aRj{!o{xe9-$Hg#|}e|zfD`98Z#dB%EcQ!ea zW6A+GM*M8b(`zm!H^{A@s9nah1VL)MeUY9UoHO0_WR?3Do0D$sp@m?t$V|=UjhvB} zk*~TtknZH{dV4;FL4l{26MT)5?)~=?HU_yL4!4cRs4g*vubKEP&>+qmAaURHz3RWy zP-~|pfKof!7%E6t;o=&UJEflz%GZH47kOd^BUU6p@eUwR(_wEQ%=|JX+nvhtl={dN z6qnaF?GdNkx%B2O0bR5_{wWTpOh_&oQ&OnFqZR_*A7U%if!<3CpyIr`;}n=(Xj{d# zpAflK4jZ~XOFTEuqLMy^ZPQv=IG7gaS9EKNDUL|wp8xGh2?nFPOCGqEz`|*$WLm(! zcv5EDOqSP|JZ)|K3k!Nxl}BR%=&!1D0IP<7yNWivYg!ZygjE7tyf z5VA6r%@9{b`c}*5Swbwz6BeU4@f~LV^J6V}6275CVaNsp&8iKN%1{QpY@yLxchFbj zZ-LpsVk>KjQ_#BEy+0TiW@2tC{m{ISv8%2_X_zkx=6kOS&x(nDN+zt!a*9e+|A|4^ z+8Mc0N_XzIR9#PHW^EH>Ypo8#3XDP6o_+N5Dd|~GL?y{=;=1;2lJoaYt}@SLF^?Kt zExJSI$)3t$7aJ(pw8VU(I`;9#RXfxnWFh|b1)B)-pBkBRlzIF?s)TpzX@skze2dv3LYH7j?DrdaNy2^A{)6pLfK45F?+ zn$Hwo1hur78jRC-QSmtmc|cWl5;0iO7^$B5daWF%x|B=^@9vFf&hpTt!;{3JOF%*& z)a-O>Z*v8bbUSHKYc?F@FP!+3u~t(OrJX`T^0+=$>I^1)Xe4S=kXHE9!JYdg@txP- zJe@*DR{&<~N8O-iL`R!YAWNd_KI(G)M0^L*b8c`vf^4z>YZr7_@1-70(Kiq|_{ezv zEneN;nn{Qi=R<;2>&|X+LVD}7*-Zo%KRKTCuxNPFfR4{bJ#(h>JuVZ`P&}8TJ8YOD zV%#O7$6*_T;S1MnLmGN>~ zM<>XsROp2Ti|0kYJ*7zvYF5J3+B}*CV3%-)?�=;_D{!4OJ$Y)CR*WCQr0sJDMiH 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 literal 0 HcmV?d00001 diff --git a/plugins/weather/wunderground_ b/plugins/weather/wunderground_ new file mode 100755 index 00000000..c81f0d87 --- /dev/null +++ b/plugins/weather/wunderground_ @@ -0,0 +1,361 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +wunderground - Plugin to monitor weather stations through Weather Underground + +The precipitation rate is recalculated from the daily cumulative sum +precipTotal, as a DERIVE value, rather than using the immediate precipRate as a +GAUGE. This allows to have more correct aggregates for the +weekly/monstly/yearly graphs, particularly in case of missing values. + +However, to work around the limitation that DERIVE only supports integers, the +decimal point is also shifted two places right (*100) from the reported value, +so even small values can be represented. The value is then rescaled to cancel +this shift (/100), and expressed per hour rather than second (*3600), as it is +the usual unit for precipitation. + +=head1 CONFIGURATION + + [wunderground] + env.api_key 6532d6454b8aa370768e63d6ba5a832e # this is the default; it seems to be what the website uses + env.station_id KCASANFR1708 + env.units metric # optional, this is the default + env.base_url # optional, default to https://api.weather.com/v2/pws/observations/current + env.connect_timeout 1 # optional, amount to wait for requests, in seconds + +Alternatively, the station_id can be encoded in the name of the symlink as +wunderground_STATIONID (e.g., wundergound_KCASANFR1708). This allows to monitor +multiple stations at once. The configuration can then omit the station_id (it +will be ignored if present), and only one section can be used for all instances +of the plugin. + + [wunderground_*] + env.api_key 6532d6454b8aa370768e63d6ba5a832e # this is the default; it seems to be what the website uses + env.units metric # optional, this is the default + env.base_url # optional, default to https://api.weather.com/v2/pws/observations/current + env.connect_timeout 1 # optional, amount to wait for requests, in seconds + +=head1 AUTHOR + +Olivier Mehani + +Copyright (C) 2020 Olivier Mehani + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + +# Example output +# +# curl 'https://api.weather.com/v2/pws/observations/current?apiKey=6532d6454b8aa370768e63d6ba5a832e&stationId=KCASANFR1708&numericPrecision=decimal&format=json&units=m' +#{"observations":[{"stationID":"KCASANFR1708","obsTimeUtc":"2020-06-15T06:30:54Z","obsTimeLocal":"2020-06-14 23:30:54","neighborhood":"Van Ness - Civic Center","softwareType":"Weather logger V3.0.8","country":"US","solarRadiation":null,"lon":-122.423,"realtimeFrequency":null,"epoch":1592202654,"lat":37.788,"uv":null,"winddir":null,"humidity":90.0,"qcStatus":1,"imperial":{"temp":58.6,"heatIndex":58.6,"dewpt":55.8,"windChill":null,"windSpeed":null,"windGust":null,"pressure":29.85,"precipRate":null,"precipTotal":null,"elev":187.0}}]} + +set -eu + +# shellcheck disable=SC1090 +. "${MUNIN_LIBDIR}/plugins/plugin.sh" + +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +PLUGIN_NAME="$(basename "${0}")" +STATION_ID="$(echo "${PLUGIN_NAME}" | sed 's/.*_//')" +# Use the station ID from the config only if the plugin doesn't specify one +STATION_ID=${STATION_ID:-${station_id:-KCASANFR1708}} + +API_KEY=${api_key:-6532d6454b8aa370768e63d6ba5a832e} +UNITS=${units:-metric} +BASE_URL=${base_url:-https://api.weather.com/v2/pws/observations/current} +CONNECT_TIMEOUT=${connect_timeout:-1} + +UNITS_ARG='&units=m' +DISTANCE_UNIT='m' +PRESSURE_UNIT='hPa' +PRECIPITATION_UNIT='mm' +SPEED_UNIT='km/h' +TEMP_UNIT='°C' +# https://en.wikipedia.org/wiki/Wind_chill#/media/File:Windchill_effect_en.svg +WIND_CHILL_CAUTION=-35: +WIND_CHILL_DANGER=-60: +# https://en.wikipedia.org/wiki/Heat_index#Table_of_values +HEAT_INDEX_CAUTION=27 +HEAT_INDEX_EXTREME_CAUTION=33 +HEAT_INDEX_DANGER=41 +HEAT_INDEX_EXTREME_DANGER=54 +if [ "${UNITS}" = "imperial" ]; then + UNITS_ARG='&units=e' + DISTANCE_UNIT='ft' + PRESSURE_UNIT='in' + PRECIPITATION_UNIT='in' + SPEED_UNIT='mph' + TEMP_UNIT='°F' + WIND_CHILL_CAUTION=-31: + WIND_CHILL_DANGER=-76:w + HEAT_INDEX_CAUTION=80 + HEAT_INDEX_EXTREME_CAUTION=91 + HEAT_INDEX_DANGER=105 + HEAT_INDEX_EXTREME_DANGER=130 +fi +API_URL="${BASE_URL}?apiKey=${API_KEY}&stationId=${STATION_ID}&numericPrecision=decimal&format=json${UNITS_ARG}" + +check_deps() { + for CMD in curl jq; do + if ! command -v "${CMD}" >/dev/null; then + echo "no (${CMD} not found)" + fi + done +} + +CURL_ARGS="-s --connect-timeout ${CONNECT_TIMEOUT}" +fetch() { + # shellcheck disable=SC2086 + curl -f ${CURL_ARGS} "$@" \ + || { echo "error fetching ${*}" >&2; false; } +} + +config() { + local STATION_INFO="in \(.neighborhood), \(.country) reported by station \(.stationID) (\(.lon), \(.lat), \(.${UNITS}.elev) m) at \(.obsTimeLocal) (\(.obsTimeUtc))" + fetch "${API_URL}" | jq -r ".observations[0] + | @text \" +multigraph wunderground_${STATION_ID} +graph_title Weather in \(.neighborhood) +graph_info Weather ${STATION_INFO} +graph_category weather +graph_vlabel Temperature / UV Index / Precipitation +temp.label Temperature [${TEMP_UNIT}] +windChill.label Wind chill [${TEMP_UNIT}] +heatIndex.label Heat index [$TEMP_UNIT] +uv.label UV index +precipRate.draw AREA +precipRate.label Precipitation rate [${PRECIPITATION_UNIT} per hour] + +multigraph wunderground_${STATION_ID}.air_humidity +graph_title Humidity in \(.neighborhood) +graph_info Humidity ${STATION_INFO} +graph_category weather +graph_args -l 0 --upper-limit 100 +graph_vlabel Humidity [%] +humidity.label Humidity +humidity.min 0 +humidity.max 100 + +multigraph wunderground_${STATION_ID}.location +graph_title Location of \(.stationID) +graph_info Track geographic coordinates of station \(.stationID); last: \(.lon), \(.lat), \(.${UNITS}.elev) ${DISTANCE_UNIT} at \(.obsTimeLocal) (\(.obsTimeUtc)) +graph_category weather +graph_scale no +graph_vlabel lon/lat [°] / elevation [${DISTANCE_UNIT}] +lon.label Longitude [°] +lat.label Latitude [°] +elev.label Elevation [${DISTANCE_UNIT}] + +multigraph wunderground_${STATION_ID}.precipitation +graph_title Precipitation in \(.neighborhood) +graph_info Precipitation ${STATION_INFO} +graph_category weather +graph_args -l 0 --base 1000 +graph_vlabel Precipitation [${PRECIPITATION_UNIT} per hour] +precipRate.label Precipitation rate +avgRate.label Average precipitation rate +avgRate.type DERIVE +avgRate.draw AREA +avgRate.min 0 +avgRate.cdef avgRate,36,* + +multigraph wunderground_${STATION_ID}.air_pressure +graph_title Pressure in \(.neighborhood) +graph_info Pressure ${STATION_INFO} +graph_category weather +graph_scale no +graph_vlabel Pressure [${PRESSURE_UNIT}] +pressure.label Pressure + +multigraph wunderground_${STATION_ID}.solar_radiation +graph_title Solar radiation in \(.neighborhood) +graph_info Solar radiation ${STATION_INFO} +graph_category weather +graph_args -l 0 --base 1000 +graph_vlabel Solar radiation [W/m^2] +solarRadiation.label Solar radiation + +multigraph wunderground_${STATION_ID}.temperature +temp.label Temperature +graph_title Temperature in \(.neighborhood) +graph_info Temperature ${STATION_INFO} +graph_category weather +graph_vlabel Temperature [${TEMP_UNIT}] +temp.label Temperature + +dewpt.label Dew point +dewpt.info Temperature to which air must be cooled to become saturated with water vapor. When cooled further, the airborne water vapor will condense to form liquid water (dew). When air cools to its dew point through contact with a surface that is colder than the air, water will condense on the surface. When the temperature is below the freezing point of water, the dew point is called the frost point, as frost is formed rather than dew. + +windChill.label Wind chill +windChill.info Represent the lowering of body temperature due to the passing-flow of lower-temperature air. Wind chill numbers are always lower than the air temperature for values where the formula is valid. When the apparent temperature is higher than the air temperature, the heat index is used instead. +windChill.warning ${WIND_CHILL_CAUTION} +windChill.critical ${WIND_CHILL_DANGER} +windChillCaution.label Wind chill Caution +windChillCaution.info Danger of frostbite +windChillCaution.colour 5358f6 +windChillCaution.line ${WIND_CHILL_CAUTION} +windChillDanger.label Wind chill Danger +windChillDanger.info Great danger of frostbite +windChillDanger.colour 5c1ff5 +windChillDanger.line ${WIND_CHILL_DANGER} + +heatIndex.label Heat index +heatIndex.info Index that combines air temperature and relative humidity, in shaded areas, to posit a human-perceived equivalent temperature, as how hot it would feel if the humidity were some other value in the shade. +heatIndex.warning ${HEAT_INDEX_EXTREME_CAUTION} +heatIndex.critical ${HEAT_INDEX_DANGER} +heatIndexCaution.label Heat index Caution +heatIndexCaution.info Fatigue is possible with prolonged exposure and activity. Continuing activity could result in heat cramps. +heatIndexCaution.colour ffff66 +heatIndexCaution.line ${HEAT_INDEX_CAUTION} +heatIndexECaution.label Heat index Extreme Caution +heatIndexECaution.info Heat cramps and heat exhaustion are possible. Continuing activity could result in heat stroke. +heatIndexECaution.colour ffd700 +heatIndexECaution.line ${HEAT_INDEX_EXTREME_CAUTION} +heatIndexDanger.label Heat index Danger +heatIndexDanger.info Heat cramps and heat exhaustion are likely; heat stroke is probable with continued activity. +heatIndexDanger.colour ff8c00 +heatIndexDanger.line ${HEAT_INDEX_DANGER} +heatIndexEDanger.label Heat index Extreme Danger +heatIndexEDanger.info Heat stroke is imminent. +heatIndexEDanger.colour ff0000 +heatIndexEDanger.line ${HEAT_INDEX_EXTREME_DANGER} + +multigraph wunderground_${STATION_ID}.uv_index +graph_title UV Index in \(.neighborhood) +graph_info UV index ${STATION_INFO} +graph_category weather +graph_args -l 0 +graph_vlabel UV index +uv.label UV index +uv.min 0 +uv.warning 5 +uv.critical 7 +moderate.label Moderate +moderate.info Stay in shade near midday when the Sun is strongest. If outdoors, wear Sun protective clothing, a wide-brimmed hat, and UV-blocking sunglasses. Generously apply broad spectrum SPF 30+ sunscreen every 1.5 hours, even on cloudy days, and after swimming or sweating. Bright surfaces, such as sand, water, and snow, will increase UV exposure. +moderate.colour fff300 +moderate.line 3 +high.label High +high.info Reduce time in the Sun between 10 a.m. and 4 p.m. If outdoors, seek shade and wear Sun protective clothing, a wide-brimmed hat, and UV-blocking sunglasses. Generously apply broad spectrum SPF 30+ sunscreen every 1.5 hours, even on cloudy days, and after swimming or sweating. Bright surfaces, such as sand, water, and snow, will increase UV exposure. +high.colour f18b00 +high.line 6 +veryhigh.label Very high +veryhigh.info Minimize Sun exposure between 10 a.m. and 4 p.m. If outdoors, seek shade and wear Sun protective clothing, a wide-brimmed hat, and UV-blocking sunglasses. Generously apply broad spectrum SPF 30+ sunscreen every 1.5 hours, even on cloudy days, and after swimming or sweating. Bright surfaces, such as sand, water, and snow, will increase UV exposure. +veryhigh.colour e53210 +veryhigh.line 8 +extreme.label Extreme +extreme.info Try to avoid Sun exposure between 10 a.m. and 4 p.m. If outdoors, seek shade and wear Sun protective clothing, a wide-brimmed hat, and UV-blocking sunglasses. Generously apply broad spectrum SPF 30+ sunscreen every 1.5 hours, even on cloudy days, and after swimming or sweating. Bright surfaces, such as sand, water, and snow, will increase UV exposure. +extreme.colour b567a4 +extreme.line 11 + +multigraph wunderground_${STATION_ID}.wind +graph_title Wind Speed in \(.neighborhood) +graph_info Wind speed and gusts ${STATION_INFO} +graph_category weather +graph_args -l 0 --base 1000 +graph_vlabel Wind speed [${SPEED_UNIT}] +windSpeed.label Wind speed +windGust.label Wind gusts + +multigraph wunderground_${STATION_ID}.wind_direction +graph_title Wind Direction in \(.neighborhood) +graph_info Wind direction ${STATION_INFO} +graph_category weather +graph_args --base 1000 -l 0 --upper-limit 360 +graph_vlabel Wind [°] +winddir.label Wind origin +winddir.min 0 +winddir.max 360 +winddir.line 0 +north.label North +north.colour COLOUR0 +north.line 360 +east.label East +east.colour COLOUR1 +east.line 90 +south.label South +south.colour COLOUR2 +south.line 180 +west.label West +west.colour COLOUR9 +west.line 270 +\"" +} + +get_data() { + fetch "${API_URL}" | jq -r ".observations[0] + | @text \" +multigraph wunderground_${STATION_ID} +temp.value \(.${UNITS}.temp) +windChill.value \(.${UNITS}.windChill) +heatIndex.value \(.${UNITS}.heatIndex) +uv.value \(.uv) +precipRate.value \(.${UNITS}.precipRate) + +multigraph wunderground_${STATION_ID}.air_humidity +humidity.value \(.humidity) + +multigraph wunderground_${STATION_ID}.location +lon.value \(.lon) +lat.value \(.lat) +elev.value \(.${UNITS}.elev) + +multigraph wunderground_${STATION_ID}.precipitation +precipRate.value \(.${UNITS}.precipRate) +avgRate.value \(.${UNITS}.precipTotal*100 | round) +avgRate.extinfo Immediate precipitation: \(.${UNITS}.precipRate) ${PRECIPITATION_UNIT}/h; Daily total: \(.${UNITS}.precipTotal) ${PRECIPITATION_UNIT} + +multigraph wunderground_${STATION_ID}.air_pressure +pressure.value \(.${UNITS}.pressure) + +multigraph wunderground_${STATION_ID}.solar_radiation +solarRadiation.value \(.solarRadiation) + +multigraph wunderground_${STATION_ID}.temperature +temp.value \(.${UNITS}.temp) +dewpt.value \(.${UNITS}.dewpt) +windChill.value \(.${UNITS}.windChill) +heatIndex.value \(.${UNITS}.heatIndex) + +multigraph wunderground_${STATION_ID}.uv_index +uv.value \(.uv) + +multigraph wunderground_${STATION_ID}.wind +windSpeed.value \(.${UNITS}.windSpeed) +windGust.value \(.${UNITS}.windGust) + +multigraph wunderground_${STATION_ID}.wind_direction +winddir.value \(.winddir) +\"" | sed 's/ null$/U/' +} + +main () { + check_deps + + case ${1:-} in + config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then + get_data + fi + ;; + *) + get_data + ;; + esac +} + +main "${1:-}" From 83737c4fe062b9bd1eeb56d61740357dd0c0c914 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 14 Jun 2020 22:58:37 +1000 Subject: [PATCH 639/695] [plugins/solar/fronius] Monitor Solar Inverters using the Fronius Solar API This includes caching of the inverter info with a 12h TTL, so the graph still renders when the inverter has gone to sleep at night. Signed-off-by: Olivier Mehani squash! [plugins/solar/fronius] Monitor Solar Inverters using the Fronius Solar API Use generic caching function to also cache real-time data. This allows to avoid N/As overnight, which don't aggregate nicely on the yearly graph. --- plugins/isp/internode_usage | 2 +- plugins/solar/example_graphs/fronius-week.png | Bin 0 -> 31267 bytes plugins/solar/fronius | 288 ++++++++++++++++++ 3 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 plugins/solar/example_graphs/fronius-week.png create mode 100755 plugins/solar/fronius diff --git a/plugins/isp/internode_usage b/plugins/isp/internode_usage index 93f51054..1d9cf035 100755 --- a/plugins/isp/internode_usage +++ b/plugins/isp/internode_usage @@ -38,7 +38,7 @@ multiple times, by symlinking it as 'internode_usage_SERVICEID'. =head1 CACHING As the API is sometimes flakey, the initial service information is cached -locally, with a one-hour lifetime, before hitting the base API again. However, +locally, with a day's lifetime, before hitting the base API again. However, if hitting the API to refresh the cache fails, the stale cache is used anyway, to have a better chance of getting the data out nonetheless. diff --git a/plugins/solar/example_graphs/fronius-week.png b/plugins/solar/example_graphs/fronius-week.png new file mode 100644 index 0000000000000000000000000000000000000000..1c56e0790ad513add2834f09bf91bcae0d519465 GIT binary patch literal 31267 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 literal 0 HcmV?d00001 diff --git a/plugins/solar/fronius b/plugins/solar/fronius new file mode 100755 index 00000000..c31cacce --- /dev/null +++ b/plugins/solar/fronius @@ -0,0 +1,288 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +fronius - Plugin to monitor Fronius Solar inverter using the JSON Solar API. + +The Solar API reports both an immediate power output reading at +time-of-request, and an incremental sum of daily and yearly produced energy. +This plugin uses the yearly energy sum as a DERIVE value, and calculates the +average power output during the last measurement interval. This will likely be +lower than the immediate reading, but the aggregation in weekly/monthly/yearly +graphs will be more correct. The immediate power output is output as extra +information. + +=head1 CONFIGURATION + + [fronius] + env.inverter_base_url http://fronius # this is the default + env.host_name solar_inverter # optional, host name to report data as in munin + env.connect_timeout 1 # optional, amount to wait for requests, in seconds + +=head1 CACHING + +As the inverter may go to sleep at night, the initial service information is cached +locally, with a twelve-hour lifetime, before hitting the Solar API again. However, +if hitting the API to refresh the cache fails, the stale cache is used anyway, +to have a better chance of getting the config data out nonetheless. + +=head1 CAVEAT + +Only tested on a Fronius Primo. + +=head1 AUTHOR + +Olivier Mehani + +Copyright (C) 2020 Olivier Mehani + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + +# Example outputs +# +## http://fronius/solar_api/v1/GetInverterInfo.cgi +#GetInverterInfo=' +#{ +# "Body" : { +# "Data" : { +# "1" : { +# "CustomName" : "Primo 5.0-1 (1)", +# "DT" : 76, +# "ErrorCode" : 0, +# "PVPower" : 5200, +# "Show" : 1, +# "StatusCode" : 7, +# "UniqueID" : "1098861" +# } +# } +# }, +# "Head" : { +# "RequestArguments" : {}, +# "Status" : { +# "Code" : 0, +# "Reason" : "", +# "UserMessage" : "" +# }, +# "Timestamp" : "2020-06-11T10:55:23+10:00" +# } +#} +#' +# +## http://fronius/solar_api/v1/GetPowerFlowRealtimeData.fcgi +#GetPowerFlowRealtimeData=' +#{ +# "Body" : { +# "Data" : { +# "Inverters" : { +# "1" : { +# "DT" : 76, +# "E_Day" : 1201, +# "E_Total" : 1201, +# "E_Year" : 1201.4000244140625, +# "P" : 2521 +# } +# }, +# "Site" : { +# "E_Day" : 1201, +# "E_Total" : 1201, +# "E_Year" : 1201.4000244140625, +# "Meter_Location" : "unknown", +# "Mode" : "produce-only", +# "P_Akku" : null, +# "P_Grid" : null, +# "P_Load" : null, +# "P_PV" : 2521, +# "rel_Autonomy" : null, +# "rel_SelfConsumption" : null +# }, +# "Version" : "12" +# } +# }, +# "Head" : { +# "RequestArguments" : {}, +# "Status" : { +# "Code" : 0, +# "Reason" : "", +# "UserMessage" : "" +# }, +# "Timestamp" : "2020-06-11T10:55:21+10:00" +# } +#} +#' +# +## http://fronius/solar_api/v1/GetActiveDeviceInfo.cgi?DeviceClass=SensorCard +#GetActiveDeviceInfo=' +#{ +# "Body" : { +# "Data" : {} +# }, +# "Head" : { +# "RequestArguments" : { +# "DeviceClass" : "SensorCard" +# }, +# "Status" : { +# "Code" : 0, +# "Reason" : "", +# "UserMessage" : "" +# }, +# "Timestamp" : "2020-06-11T10:55:24+10:00" +# } +#} +#' +# +## http://fronius/solar_api/v1/GetLoggerConnectionInfo.cgi +#GetLoggerConnectionInfo=' +#{ +# "Body" : { +# "Data" : { +# "SolarNetConnectionState" : 2, +# "SolarWebConnectionState" : 2, +# "WLANConnectionState" : 2 +# } +# }, +# "Head" : { +# "RequestArguments" : {}, +# "Status" : { +# "Code" : 0, +# "Reason" : "", +# "UserMessage" : "" +# }, +# "Timestamp" : "2020-06-11T10:55:25+10:00" +# } +#} +#' + +set -eu + +# shellcheck disable=SC1090 +. "${MUNIN_LIBDIR}/plugins/plugin.sh" + +if [ "${MUNIN_DEBUG:-0}" = 1 ]; then + set -x +fi + +INVERTER_BASE_URL=${inverter_base_url:-http://fronius} +HOST_NAME=${host_name:-} +CONNECT_TIMEOUT=${connect_timeout:-1} + +check_deps() { + for CMD in curl jq recode; do + if ! command -v "${CMD}" >/dev/null; then + echo "no (${CMD} not found)" + fi + done +} + +CURL_ARGS="-s --connect-timeout ${CONNECT_TIMEOUT}" +fetch() { + # shellcheck disable=SC2086 + curl -f ${CURL_ARGS} "$@" \ + || { echo "error fetching ${*}" >&2; false; } +} + +get_inverter_info() { + fetch "${INVERTER_BASE_URL}/solar_api/v1/GetInverterInfo.cgi" \ + | recode html..ascii +} + +get_power_flow_realtime_data() { + fetch "${INVERTER_BASE_URL}/solar_api/v1/GetPowerFlowRealtimeData.fcgi" + #echo "${GetPowerFlowRealtimeData} +} + +# Run the command and arguments passed as arguments to this method, and cache +# the response. The first argument is a timeout (in minutes) after which the +# cache is ignored and a new request is attempted. If the request fails, the +# cache is used. If the timeout is 0, the request is always attempted, using +# the cache as a backup on failure. +cached() { + timeout="${1}" + shift + fn="${1}" + shift + # shellcheck disable=SC2124 + args="${@}" + + # shellcheck disable=SC2039 + api_data='' + # shellcheck disable=SC2039 + cachefile="${MUNIN_PLUGSTATE}/$(basename "${0}").${fn}.cache.json" + if [ -n "$(find "${cachefile}" -mmin "-${timeout}" 2>/dev/null)" ]; then + api_data=$(cat "${cachefile}") + else + # shellcheck disable=SC2086 + api_data="$("${fn}" ${args} \ + || true)" + + if [ -n "${api_data}" ]; then + echo "${api_data}" > "${cachefile}" + else + api_data=$(cat "${cachefile}") + fi + fi + echo "${api_data}" +} + +config() { + if test -n "${HOST_NAME}"; then + echo "host_name ${HOST_NAME}" + fi + # graph_period is not a shell variable + cat <<'EOF' +graph_title Solar Inverter Output +graph_info Power generated from solar inverters +graph_total Total output +graph_category sensors +graph_vlabel Average output [W] +graph_args -l 0 --base 1000 +EOF +cached 720 get_inverter_info | jq -r '.Body.Data + | to_entries[] + | @text " +inverter\(.key).label \(.value.CustomName) +inverter\(.key).info Power generated by the solar array (total size \(.value.PVPower / 1000) kW) connected to inverter \(.value.CustomName) (ID: \(.value.UniqueID)) +inverter\(.key).cdef inverter\(.key),3600,* +inverter\(.key).type DERIVE +inverter\(.key).min 0 +inverter\(.key).max \(.value.PVPower) +inverter\(.key).draw AREASTACK +"' +} + +get_data() { +cached 0 get_power_flow_realtime_data | jq -r '.Body.Data.Inverters + | to_entries[] + | @text " +inverter\(.key).value \(.value.E_Year | round) +inverter\(.key).extinfo Immediate output: \(.value.P) W; Daily total: \(.value.E_Day | round) Wh; Yearly total: \(.value.E_Year / 1000 | round) kWh +"' +} + +main () { + check_deps + + case ${1:-} in + config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then + get_data + fi + ;; + *) + get_data + ;; + esac +} + +main "${1:-}" From bd1302c3f2529be040af76547b241aa2bf96faf6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 12 Jul 2020 19:14:07 +0200 Subject: [PATCH 640/695] Plugin unifi_api: handle missing error fields in response It seems to be possible, that the response lacks the values for `rx_errors` and `tx_errors`. This should not result in warnings due to undefined values. Thanks, kboenke! Closes: #1095 --- plugins/ubiquiti/unifi_api | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ubiquiti/unifi_api b/plugins/ubiquiti/unifi_api index d689a75d..003a8fc4 100755 --- a/plugins/ubiquiti/unifi_api +++ b/plugins/ubiquiti/unifi_api @@ -1033,7 +1033,7 @@ sub make_data { 'label' => $label . '-' . $thisDevice->{'name'}, 'pckt' => $_->{$name . '-rx_packets'} + $_->{$name . '-tx_packets'}, 'dret' => $_->{$name . '-rx_dropped'} + $_->{$name . '-tx_retries'} + $_->{$name . '-tx_dropped'}, - 'err' => $_->{$name . '-rx_errors'} + $_->{$name . '-tx_errors'}, + 'err' => ($_->{$name . '-rx_errors'} || 0) + ($_->{$name . '-tx_errors'} || 0), 'type' => $label }; } @@ -1209,4 +1209,4 @@ sub env_default_bool_true { } # Quick 2 digit zero pad -sub zPad { return sprintf("%02d", $_[0]); } \ No newline at end of file +sub zPad { return sprintf("%02d", $_[0]); } From ce29a0f7ae2c59161f3c3ea6e3e9a62bdb1df1a0 Mon Sep 17 00:00:00 2001 From: Anton Shestakov Date: Fri, 17 Jul 2020 01:36:10 +0800 Subject: [PATCH 641/695] hwmon: recognize frequencies Discovered thanks to an RX 570, which can show sclk and mclk via /sys/class/hwmon. --- plugins/sensors/hwmon | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/sensors/hwmon b/plugins/sensors/hwmon index 66e44801..1a890c87 100755 --- a/plugins/sensors/hwmon +++ b/plugins/sensors/hwmon @@ -94,6 +94,13 @@ my %sensors = ( graph_args => "--base 1000 -l 0", denominator => 1000000 # microWatts -> Watts }, + freq => { + inputs => [], + title => "Frequencies", + vlabel => "Hz", + graph_args => "--base 1000 -l 0", + denominator => 1 + }, humidity => { inputs => [], title => "Humidity", From 21379f58415f169b83872b825479a4c71dc9ff8b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 7 Aug 2020 02:47:10 +0200 Subject: [PATCH 642/695] Add plugin "rsnapshot_duration" The plugin monitors the duration of rsnapshot backups for each single backup source. The duration is parsed from rsnapshot's log file. The rsnapshot configuration setting "loglevel 3" is required. --- plugins/rsnapshot/rsnapshot_duration | 193 +++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100755 plugins/rsnapshot/rsnapshot_duration diff --git a/plugins/rsnapshot/rsnapshot_duration b/plugins/rsnapshot/rsnapshot_duration new file mode 100755 index 00000000..7abc6944 --- /dev/null +++ b/plugins/rsnapshot/rsnapshot_duration @@ -0,0 +1,193 @@ +#!/bin/sh + +: <<=cut + +=head1 NAME + +rsnapshot_duration - monitor duration of rsnapshot backups in detail + + +=head1 APPLICABLE SYSTEMS + +Any system with local access to an rsnapshot log file. + + +=head1 CONFIGURATION + +rsnapshot's configuration file (e.g. /etc/rsnapshot.conf) needs to +contain the directive "loglevel 3" in order to write the required +log messages to the log file. + + +The following configuration represents the plugin's defaults: + + [rsnapshot_duration] + env.rsnapshot_log_file /var/log/rsnapshot + env.rsnapshot_operation_name beta + env.rsnapshot_description Backup Duration + +"rsnapshot_description" is used for the title of the graph. + +"rsnapshot_operation_name" refers to the interval/retain/level of the +rsnapshot operation to be monitored. This should be the lowest +executed backup interval (e.g. "daily" or "beta"). In case of the +rsnapshot setting "sync_first 1", it should be "sync". + +Multiple rsnapshot log files (with different sets of backup sources) +can be monitored. Simply create one plugin symlink for each log file +and specify suitable plugin configurations for each symlink name. + + +=head1 INTERPRETATION + +The backup duration for each single "backup" task is calculated based +on the difference of the timestamps in the log file. + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +SPDX-License-Identifier: GPL-3.0-or-later + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + + +=cut + + +set -eu + + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + + +LOG_FILE=${rsnapshot_log_file:-/var/log/rsnapshot.log} +RSNAPSHOT_OPERATION_NAME=${rsnapshot_operation_name:-beta} +RSNAPSHOT_DESCRIPTION=${rsnapshot_description:-Backup Duration} + + +# retrieve the latest set of log files for a complete backup operation +get_latest_process_log() { + tac "$LOG_FILE" \ + | awk ' + BEGIN { in_process = 0; } + / '"$RSNAPSHOT_OPERATION_NAME"': completed[, ]/ { in_process = 1; } + / '"$RSNAPSHOT_OPERATION_NAME"': started$/ { if (in_process == 1) exit; } + { if (in_process == 1) print($0); }' \ + | tac +} + + +# parse rsnapshot log lines +# output format: +# BACKUP_NAME DURATION_SECONDS [ERROR_MESSAGES] +get_backups_with_duration() { + get_latest_process_log | while read -r timestamp command details; do + parsed_timestamp=$(date +%s --date "$(printf '%s' "$timestamp" | tr -d '[]')") + if [ -n "${backup_name:-}" ] && printf '%s' "$command" | grep -q "/rsnapshot$" && printf '%s' "$details" | grep -q "ERROR:"; then + backup_errors=$(printf '%s' "$details" | sed 's/^.*ERROR: //') + elif printf '%s' "$command" | grep -q "/rsync$"; then + if [ -n "${backup_name:-}" ]; then + printf '%s\t%d\t%s\n' "$backup_name" "$((parsed_timestamp - backup_start))" "${backup_errors:-}" + fi + backup_name=$(printf '%s' "$details" | sed 's#/$##' | sed 's#^.*/##') + backup_start=$parsed_timestamp + backup_errors= + elif printf '%s' "$command" | grep -q "/rm$"; then + # the backup is finished + if [ -n "${backup_name:-}" ]; then + printf '%s\t%d\t%s\n' "$backup_name" "$((parsed_timestamp - backup_start))" "${backup_errors:-}" + fi + break + fi + done | sort +} + + +do_autoconf() { + if [ -e "$LOG_FILE" ]; then + if [ -r "$LOG_FILE" ]; then + if command -v "tac" >/dev/null; then + echo "yes" + else + echo "no (executable 'tac' (coreutils) is missing)" + fi + else + echo "no (rsnapshot log file is not readable: $LOG_FILE)" + fi + else + echo "no (rsnapshot log file missing: $LOG_FILE)" + fi + exit 0 +} + + +get_backup_fieldname() { + local backup_name="$1" + clean_fieldname "backup_${backup_name}" +} + + +print_backup_details() { + local backup_name="$1" + local backup_duration="$2" + local backup_messages="$3" + local fieldname + fieldname=$(get_backup_fieldname "$backup_name") + printf '%s.value %s\n' "$fieldname" "$backup_duration" + if [ -n "$backup_messages" ]; then + printf '%s.extinfo %s\n' "$fieldname" "$backup_messages" + fi +} + + +do_config() { + local do_emit_values="${1:-0}" + echo "graph_title rsnapshot - $RSNAPSHOT_DESCRIPTION" + echo 'graph_vlabel Duration of backup in minutes' + echo 'graph_category backup' + echo 'graph_scale no' + get_backups_with_duration | while read -r name duration messages; do + fieldname=$(clean_fieldname "backup_$name") + printf '%s.label %s\n' "$fieldname" "$name" + printf '%s.draw %s\n' "$fieldname" "AREASTACK" + # The duration is stored as an SI unit (seconds). + # The visualization as the number of minutes should be suitable for most backups. + printf '%s.cdef %s,60,/\n' "$fieldname" "$fieldname" + if [ "$do_emit_values" = "1" ]; then + print_backup_details "$name" "$duration" "$messages" + fi + done +} + + +do_fetch() { + get_backups_with_duration | while read -r name duration messages; do + print_backup_details "$name" "$duration" "$messages" + done +} + + +case ${1:-} in + autoconf) + do_autoconf + ;; + config) + do_config "${MUNIN_CAP_DIRTYCONFIG:-0}" + ;; + "") + do_fetch + ;; + *) + echo >&2 "Unknown command: $1" + exit 1 + ;; +esac From 58e371308a8b5805a6f4a81f49bd32127dfc8dd7 Mon Sep 17 00:00:00 2001 From: zivillian Date: Sat, 8 Aug 2020 16:26:15 +0200 Subject: [PATCH 643/695] extend workaround for multiple dashes --- plugins/disk/dm_cache_occupancy_ | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/disk/dm_cache_occupancy_ b/plugins/disk/dm_cache_occupancy_ index cddee218..3d45b5fd 100755 --- a/plugins/disk/dm_cache_occupancy_ +++ b/plugins/disk/dm_cache_occupancy_ @@ -50,7 +50,10 @@ GPLv2 CVOL=${0##*dm_cache_occupancy_} #workaround for http://munin-monitoring.org/ticket/1236 -CVOL=${CVOL/____/-} +while [[ $CVOL == *"____"* ]] +do + CVOL=${CVOL/____/-} +done case $1 in autoconf) From ca2484b357142deabc3ffb1122e821ff54058511 Mon Sep 17 00:00:00 2001 From: zivillian Date: Sat, 8 Aug 2020 16:27:58 +0200 Subject: [PATCH 644/695] extend workaround for multiple dashes --- plugins/disk/dm_cache_statistics_ | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/disk/dm_cache_statistics_ b/plugins/disk/dm_cache_statistics_ index 94a959e2..97257e1c 100755 --- a/plugins/disk/dm_cache_statistics_ +++ b/plugins/disk/dm_cache_statistics_ @@ -50,7 +50,10 @@ GPLv2 CVOL=${0##*dm_cache_statistics_} #workaround for http://munin-monitoring.org/ticket/1236 -CVOL=${CVOL/____/-} +while [[ $CVOL == *"____"* ]] +do + CVOL=${CVOL/____/-} +done case $1 in autoconf) From 9e2b918229bee9dab97c99dbfa2af536734439c2 Mon Sep 17 00:00:00 2001 From: Rowan Wookey Date: Tue, 4 Aug 2020 19:58:59 +0100 Subject: [PATCH 645/695] Added env to exclude containers from cpu/memory docker graphs It maybe desirable to not generate stats for ephemeral containers. This commit adds an env variable EXCLUDE_CONTAINER_NAME which is a regex, if it matches then the container is excluded from the cpu and memory graphs. --- plugins/docker/docker_ | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/docker/docker_ b/plugins/docker/docker_ index cc51ffe5..c337da79 100755 --- a/plugins/docker/docker_ +++ b/plugins/docker/docker_ @@ -38,11 +38,22 @@ directory /etc/munin/plugin-conf.d/ with the following config (you can also use Docker environment variables here as described in https://docs.docker.com/compose/reference/envvars/): +You can use the EXCLUDE_CONTAINER_NAME environment variable to specify a regular expression +which if matched will exclude the matching containers from the memory and cpu graphs. + +For example + +env.EXCLUDE_CONTAINER_NAME runner + +Would exclude all containers with the word "runner" in the name. + + =over 2 [docker_*] user root env.DOCKER_HOST unix://var/run/docker.sock + env.EXCLUDE_CONTAINER_NAME regexp =back """ @@ -50,6 +61,7 @@ https://docs.docker.com/compose/reference/envvars/): import os import sys import docker +import re from multiprocessing import Process, Queue @@ -92,7 +104,10 @@ def get_container_stats(container, q): def parallel_container_stats(client): proc_list = [] stats = {} + exclude = os.getenv('EXCLUDE_CONTAINER_NAME') for container in client.containers.list(): + if exclude and re.search(exclude, container.name): + break q = Queue() p = Process(target=get_container_stats, args=(container, q)) proc_list.append({'proc': p, 'queue': q, 'container': container}) From 8cff506f07e375b1828f91b2c4e34191e70216c6 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 14 Aug 2020 22:27:12 +0200 Subject: [PATCH 646/695] Plugin multi_tcp_ping: gracefully handle DNS resolution problems Previously the plugin exited with an error, if the target name could not be resolved. Now this problem is handled just like an "unreachable" situation. Thanks, nitram2342! Closes: #1091 --- plugins/ping/multi_tcp_ping | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/ping/multi_tcp_ping b/plugins/ping/multi_tcp_ping index ee88033d..a1b1dbeb 100755 --- a/plugins/ping/multi_tcp_ping +++ b/plugins/ping/multi_tcp_ping @@ -108,7 +108,9 @@ sub ping_host { $p->service_check(1); $p->{port_num} = $host->[1] || $defaults{port}; - ($ret, $time, $ip) = $p->ping($host->[0]); + eval { + ($ret, $time, $ip) = $p->ping($host->[0]); + }; $time = $defaults{unreachable} if !$ret; print "${addr}.value $time\n"; From 113008b06daf56d2a1e89c518fde5207d4d4102a Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Mon, 20 Jul 2020 19:28:22 +0200 Subject: [PATCH 647/695] use name from commandline parameter for kvm --- plugins/libvirt/kvm_cpu | 7 +++++++ plugins/libvirt/kvm_io | 7 +++++++ plugins/libvirt/kvm_mem | 7 +++++++ plugins/libvirt/kvm_net | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index f43f2902..b8aeecf8 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -52,6 +52,13 @@ def clean_vm_name(vm_name): if suffix: vm_name = re.sub(suffix,'',vm_name) + parts = vm_name.split('\x00') + if (parts[0].endswith('kvm')): + try: + return parts[parts.index('-name')+1] + except ValueError: + pass + return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) def detect_kvm(): diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index d1aff23d..329db9ad 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -52,6 +52,13 @@ def clean_vm_name(vm_name): if suffix: vm_name = re.sub(suffix,'',vm_name) + parts = vm_name.split('\x00') + if (parts[0].endswith('kvm')): + try: + return parts[parts.index('-name')+1] + except ValueError: + pass + return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) def fetch(vms): diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index d316bae3..65e6599f 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -49,6 +49,13 @@ def clean_vm_name(vm_name): if suffix: vm_name = re.sub(suffix,'',vm_name) + parts = vm_name.split('\x00') + if (parts[0].endswith('kvm')): + try: + return parts[parts.index('-name')+1] + except ValueError: + pass + return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) def fetch(vms): diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index e9b6a711..e7b1b8db 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -100,6 +100,14 @@ def clean_vm_name(vm_name): suffix = os.getenv("vmsuffix") if suffix: vm_name = re.sub(suffix, "", vm_name) + + parts = vm_name.split('\x00') + if (parts[0].endswith('kvm')): + try: + return parts[parts.index('-name')+1] + except ValueError: + pass + return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) From 00f8fce4f8c9478d0ed7517992287a48fcfff593 Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Mon, 20 Jul 2020 19:29:33 +0200 Subject: [PATCH 648/695] prevent duplicate graphs and unify line style --- plugins/libvirt/kvm_io | 5 +---- plugins/libvirt/kvm_net | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 329db9ad..72288694 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -33,12 +33,11 @@ graph_args --base 1024 print "%s_read.label %s" % (vm, vm) print "%s_read.type COUNTER" % vm print "%s_read.min 0" % vm - print "%s_read.draw LINE1" % vm print "%s_read.info I/O used by virtual machine %s" % (vm, vm) + print "%s_read.graph no" % vm print "%s_write.label %s" % (vm, vm) print "%s_write.type COUNTER" % vm print "%s_write.min 0" % vm - print "%s_write.draw LINE1" % vm print "%s_write.negative %s_read" % (vm, vm) print "%s_write.info I/O used by virtual machine %s" % (vm, vm) @@ -51,14 +50,12 @@ def clean_vm_name(vm_name): suffix = os.getenv('vmsuffix') if suffix: vm_name = re.sub(suffix,'',vm_name) - parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: return parts[parts.index('-name')+1] except ValueError: pass - return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) def fetch(vms): diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index e7b1b8db..71e6cd59 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -82,12 +82,11 @@ def config(vm_names): print("%s_in.label %s" % (vm, vm)) print("%s_in.type COUNTER" % vm) print("%s_in.min 0" % vm) - print("%s_in.draw LINE2" % vm) + print("%s_in.graph no" % vm) print("%s_out.negative %s_in" % (vm, vm)) print("%s_out.label %s" % (vm, vm)) print("%s_out.type COUNTER" % vm) print("%s_out.min 0" % vm) - print("%s_out.draw LINE2" % vm) def clean_vm_name(vm_name): @@ -100,14 +99,12 @@ def clean_vm_name(vm_name): suffix = os.getenv("vmsuffix") if suffix: vm_name = re.sub(suffix, "", vm_name) - parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: return parts[parts.index('-name')+1] except ValueError: pass - return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name) From 225a9156dfc697b15cd06b6d0fd8365a84d2441a Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Sat, 15 Aug 2020 10:06:27 +0200 Subject: [PATCH 649/695] add proxmox comment --- plugins/libvirt/kvm_cpu | 1 + plugins/libvirt/kvm_io | 1 + plugins/libvirt/kvm_mem | 1 + plugins/libvirt/kvm_net | 1 + 4 files changed, 4 insertions(+) diff --git a/plugins/libvirt/kvm_cpu b/plugins/libvirt/kvm_cpu index b8aeecf8..4eebbd81 100755 --- a/plugins/libvirt/kvm_cpu +++ b/plugins/libvirt/kvm_cpu @@ -52,6 +52,7 @@ def clean_vm_name(vm_name): if suffix: vm_name = re.sub(suffix,'',vm_name) + # proxmox uses kvm with -name parameter parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: diff --git a/plugins/libvirt/kvm_io b/plugins/libvirt/kvm_io index 72288694..1e39126c 100755 --- a/plugins/libvirt/kvm_io +++ b/plugins/libvirt/kvm_io @@ -50,6 +50,7 @@ def clean_vm_name(vm_name): suffix = os.getenv('vmsuffix') if suffix: vm_name = re.sub(suffix,'',vm_name) + # proxmox uses kvm with -name parameter parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: diff --git a/plugins/libvirt/kvm_mem b/plugins/libvirt/kvm_mem index 65e6599f..eaa86fda 100755 --- a/plugins/libvirt/kvm_mem +++ b/plugins/libvirt/kvm_mem @@ -49,6 +49,7 @@ def clean_vm_name(vm_name): if suffix: vm_name = re.sub(suffix,'',vm_name) + # proxmox uses kvm with -name parameter parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: diff --git a/plugins/libvirt/kvm_net b/plugins/libvirt/kvm_net index 71e6cd59..381bd511 100755 --- a/plugins/libvirt/kvm_net +++ b/plugins/libvirt/kvm_net @@ -99,6 +99,7 @@ def clean_vm_name(vm_name): suffix = os.getenv("vmsuffix") if suffix: vm_name = re.sub(suffix, "", vm_name) + # proxmox uses kvm with -name parameter parts = vm_name.split('\x00') if (parts[0].endswith('kvm')): try: From aee6a152407de87595cc55d6a678a7a1da79508a Mon Sep 17 00:00:00 2001 From: Michael Fritscher Date: Sun, 16 Aug 2020 11:59:37 +0200 Subject: [PATCH 650/695] =?UTF-8?q?Fixes=20quota=20f=C3=BCr=20zvols.=20Wit?= =?UTF-8?q?hout=20this,=20the=20log=20gets=20spammed=20with?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > 2020/08/16 04:45:24 [ERROR] In RRD: Error updating /var/lib/munin/localdomain/localhost.localdomain-zfs_list_rpool__name-quota-g.rrd: /var/lib/munin/localdomain/localhost.localdomain-zfs_list_rpool__name-quota-g.rrd: Function update_pdp_prep, case DST_GAUGE - Cannot convert '-' to float --- plugins/zfs/zfs_list | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index c7abf7da..7307f9d5 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -79,6 +79,12 @@ for i in `zfs list -Hp | awk '{print $1}'`; do values=( $(zfs get -p usedbydataset,usedbychildren,usedbysnapshots,usedbyrefreservation,available,quota $i | awk 'BEGIN {total=0;} { if( NR==1 ) next; } !/quota/ {total=total+$3;} {print $3} END{print total;}') ) fsname=$(clean_fieldname $(echo "$i" | sed 's/\//__/g')) + if [ ${values[5]} = "-" ]; then + quota=0 + else + quota=${values[5]} + fi + echo < Date: Wed, 15 Jul 2020 02:34:17 +0200 Subject: [PATCH 651/695] [plugins/systemd/systemd_units] Optionally hide extinfo of active services These services are running fine, so the extinfo would mostly just clutter the webui while making the extinfo of relevant (i.e. down) services harder to find. --- plugins/systemd/systemd_units | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/plugins/systemd/systemd_units b/plugins/systemd/systemd_units index 47f43541..e65bf9e0 100755 --- a/plugins/systemd/systemd_units +++ b/plugins/systemd/systemd_units @@ -31,6 +31,8 @@ C): An example configuration might be something like this: +=over 2 + [systemd_units] env.failed_warning 0 env.failed_critical 5 @@ -38,10 +40,15 @@ An example configuration might be something like this: env.inactive_critical 20 env.exclude boring env.inactive_exclude sleepy + env.silence_active_extinfo 1 + +=back In the example above, we've overridden the default warning and critical levels for failed units, added warning and critical levels for inactive units, then filtered out boring units from all results and filtered out sleepy -units from results for the inactive state. +units from results for the inactive state. In addition to that, only more extensive info of non-active units +are displayed in order to quickly see which units are failing and why in the webui. (By default, all extra info +about all units is displayed.) =head1 AUTHOR @@ -63,6 +70,7 @@ GPLv2 failed_warning="${failed_warning:-0}" failed_critical="${failed_critical:-10}" +silence_active_extinfo="${silence_active_extinfo:-0}" states="active \ reloading \ @@ -123,8 +131,14 @@ fetch () { count=$(echo "$state_unit_list" | grep -c "$state$") echo "$state.value $count" extinfo=$(echo "$state_unit_list" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') - if [ -n "$extinfo" ]; then - echo "$state.extinfo" "$extinfo" + if [ "$silence_active_extinfo" = "0" ]; then + if [ -n "$extinfo" ]; then + echo "$state.extinfo" "$extinfo" + fi + else + if [ -n "$extinfo" ] && [ "$state" != "active" ]; then + echo "$state.extinfo" "$extinfo" + fi fi done } From a1c4bcb25a952de86c277dbc537eeea57ae45311 Mon Sep 17 00:00:00 2001 From: pcy Date: Wed, 15 Jul 2020 02:38:10 +0200 Subject: [PATCH 652/695] [plugins/user/membyuser] Minor enhancements Don't use a fixed scale Use resident set size as measure for a process' memory usage instead of vsz More robust username enumeration --- plugins/user/membyuser | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/plugins/user/membyuser b/plugins/user/membyuser index bb85e406..ec050ac0 100755 --- a/plugins/user/membyuser +++ b/plugins/user/membyuser @@ -2,10 +2,18 @@ # # Plugin to monitor Memory usage inspired by cpubyuser # -# 2012-05-23 Sebastien Campion +# Usage: Place in /etc/munin/plugins/ (or link it there using ln -s) +# Add this to your /etc/munin/plugin-conf.d/munin-node: +# [membyuser] +# user root # required if /proc can't be read from by any user! -LU=`ps auh | cut -d' ' -f 1 | sort -u` -USERS=`echo $LU` +## 2012-05-23 Sebastien Campion +# changed on 2019-08-30 by pcy : +# - change category from 'memory' to 'system' (so it appears next to cpubyuser) +# - use rss instead of vsz +# - more robust username enumeration + +USERS="$(ps ax --format uname | tail +2 | sort -u | grep -v -e '^root$')" if [ "$1" = "autoconf" ]; then if [ -n "$USERS" ]; then @@ -17,12 +25,11 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - echo "graph_args --base 1000 -r --lower-limit 0" + echo "graph_args --base 1024" echo "graph_title Memory usage, by user" echo "graph_category memory" echo "graph_info This graph shows memory usage, for monitored users." - echo "graph_vlabel KB" - echo "graph_scale no" + echo "graph_vlabel Bytes" echo "graph_period second" _USERS=${USERS//[-.]/_} echo "graph_order $_USERS others" @@ -32,17 +39,12 @@ if [ "$1" = "config" ]; then echo "${_USER}.label $USER" echo "${_USER}.info Memory 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 -ps -e -o "%z%U" | \ +ps -e -o rss,user | \ awk -v USERS="$USERS" ' { if ($2 != "USER") MEM_USER[$2]+=$1 } END { @@ -52,9 +54,9 @@ ps -e -o "%z%U" | \ if (m != 0) { _user=user gsub(/[-.]/,"_", _user); - print _user".value", MEM_USER[user] + print _user".value", (MEM_USER[user] * 1024) } else - others_sum += MEM_USER[user] + others_sum += (MEM_USER[user] * 1024) } print "others.value", others_sum; }' From 0031acd6375385a20d50bb154d9432cd0786d7d3 Mon Sep 17 00:00:00 2001 From: pcy Date: Wed, 15 Jul 2020 02:36:07 +0200 Subject: [PATCH 653/695] [plugins/user/cpubyuser] Bugfixes and enhancements - Fix USERS=ALL only enumerating processes with a tty or pty attached (and thus not eg. user-owned FastCGI processes, owned by eg. Apache) - ps(1) is used to make process list parsing easier and more robust - OTHERS_FIELD now defaults to disabled --- plugins/user/cpubyuser | 78 ++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/plugins/user/cpubyuser b/plugins/user/cpubyuser index eb3edd70..d961023d 100755 --- a/plugins/user/cpubyuser +++ b/plugins/user/cpubyuser @@ -2,15 +2,26 @@ # # 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) +# Usage: Place in /etc/munin/plugins/ (or link it there using ln -s) # Add this to your /etc/munin/plugin-conf.d/munin-node: # [cpubyuser] +# user root # required if /proc can't be read from by any user! # env.USERS root yann +# env.OTHER_FIELD others # enable 'others'-list, set the label/field name # -# If env.USERS is set to ALL, count all logged in users. +# root and yann being a list of the users to monitor. # -# root and yann being a list of the users to monitor. -# You need to also make sure that awk is installed +# If env.USERS is set to ALL, count all logged in users, and if set to +# ALLPROC, *all* users with a running process will be listed, except for root. +# +# You need to also make sure that awk is installed +# +# 2019-08-30 v 1.4 pcy : +# - add USERS=ALLPROC, not relying on a tty or pty being present for user detection +# - OTHERS_FIELD now defaults to disabled, explicitely give it a +# value to reenable it (eg. 'others') +# - use ps(1) instead of top(1) for easier and more robust +# parsing/summary calculation # # 2008-12-08 v 1.3.1 Hanisch Elián: # - support for dots in user names. @@ -39,9 +50,12 @@ . "$MUNIN_LIBDIR/plugins/plugin.sh" -OTHER_FIELD="others" -[ "$USERS" = "ALL" ] && USERS=$(w --no-header | awk '{ print $1 }' | sort | uniq) - +[ -z "$USERS" ] && USERS=ALL +if [ "$USERS" = "ALLPROC" ]; then + USERS="$(ps ax --format uname | tail +2 | sort -u | grep -v -e '^root$')" +elif [ "$USERS" = "ALL" ]; then + USERS="$(w --no-header | cut -d' ' -f 1 | sort -u)" +fi if [ "$1" = "autoconf" ]; then if [ -n "$USERS" ]; then @@ -61,44 +75,42 @@ 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 $OTHER_FIELD" + echo "graph_order $user_fields $(clean_fieldname "$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" - echo "${user_field}.type GAUGE" - echo "${user_field}.draw AREASTACK" + if [ -n "$user" ]; then + 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" + fi done exit fi -top -b -n 1 | sed '1,/^ *PID /d' | \ +OTHER_PRINT="" +[ -z "$OTHER_FIELD" ] || OTHER_PRINT="print \"$(clean_fieldname "$OTHER_FIELD")\", others_sum;" + +ps ax --format "%cpu user" | tail +2 | \ awk -v USERS="$USERS" ' # 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 } + { CPU_USER[$2]=$1 } END { - 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 - } - # 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; + split(USERS, user_array) + for (user in CPU_USER) { + m = match(USERS,user) + if (m != 0) { + _user=user + gsub(/[-.]/,"_",_user); + print _user, (CPU_USER[user]) + } else + others_sum += CPU_USER[user] + } + '"$OTHER_PRINT"' }' | while read -r user count; do # apply fieldname cleanup echo "$(clean_fieldname "$user").value $count" From 6fdf699575030421818ce65fb4d1c416acffa61f Mon Sep 17 00:00:00 2001 From: pcy Date: Thu, 16 Jul 2020 00:23:26 +0200 Subject: [PATCH 654/695] [plugins/user/procbyuser] added plugin to monitor processes of a user Graphs the number of processes owned by a user over time --- plugins/user/procbyuser | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100755 plugins/user/procbyuser diff --git a/plugins/user/procbyuser b/plugins/user/procbyuser new file mode 100755 index 00000000..41f87be6 --- /dev/null +++ b/plugins/user/procbyuser @@ -0,0 +1,100 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 INTRODUCTION + +Plugin to monitor the amount of processes owned by a user + +=head1 INSTALLATION + +Usage: Place in /etc/munin/plugins/ (or link it there using ln -s) + +=head1 CONFIGURATION + +Add this to your /etc/munin/plugin-conf.d/munin-node: + +=over 2 + + [procbyuser] + user root # required if /proc can't be read from by any user! + env.USERS root yann # defaults to ALL, i.e. display all users. 'root' is counted as one of the 'others' + env.OTHER_FIELD others # enable 'others'-list, set the label/field name + +=back + +=head1 HISTORY + +2019-09-06 v 1.0 pcy : created (based on cpubyuser) + +=head1 USAGE + +Parameters understood: + + config (required) + autoconf (optional - used by munin-config) + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf + +=cut + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +[ -z "$USERS" ] && USERS=ALL +if [ "$USERS" = "ALL" ]; then + USERS="$(ps ax --format uname | tail +2 | sort -u | grep -v -e '^root$')" +fi + +if [ "$1" = "autoconf" ]; then + echo "yes" +fi + +if [ "$1" = "config" ]; then + echo "graph_args -r --lower-limit 0" + echo "graph_title Processes per user" + echo "graph_category processes" + echo "graph_info This graph shows the amount of processes owned by a user." + echo "graph_vlabel processes" + 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 $(clean_fieldname "$OTHER_FIELD")" + for user in $USERS "$OTHER_FIELD"; do + if [ -n "$user" ]; then + user_field="$(clean_fieldname "$user")" + echo "${user_field}.label $user" + echo "${user_field}.info processes of user $user" + echo "${user_field}.type GAUGE" + echo "${user_field}.draw AREASTACK" + fi + done + exit +fi + +OTHER_PRINT="" +[ -z "$OTHER_FIELD" ] || OTHER_PRINT="print \"$(clean_fieldname "$OTHER_FIELD")\", others_sum;" + +ps ax -o user:42= -o cmd= | \ + awk -v USERS="$USERS" ' + !($2 ~ /^\[/) { NUSER[$1]=NUSER[$1]+1 } # filter away kernel threads + END { + others_sum = 0 + split(USERS, user_array) + for (user in NUSER) { + m = match(USERS,user) + if (m != 0) { + _user=user + gsub(/[-.]/,"_",_user); + print _user, (NUSER[user]) + } else + others_sum += NUSER[user] + } + '"$OTHER_PRINT"' + }' | while read -r user count; do + # apply fieldname cleanup + echo "$(clean_fieldname "$user").value $count" + done From ee226a609e4b705ff87bb96e9d2bb76bbbd004fa Mon Sep 17 00:00:00 2001 From: pcy Date: Mon, 20 Jul 2020 02:19:21 +0200 Subject: [PATCH 655/695] [plugins/mysql/mysql_disk_by_prefix] Add plugin for DB size by user --- plugins/mysql/mysql_disk_by_prefix | 151 +++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 plugins/mysql/mysql_disk_by_prefix diff --git a/plugins/mysql/mysql_disk_by_prefix b/plugins/mysql/mysql_disk_by_prefix new file mode 100755 index 00000000..4e27ad98 --- /dev/null +++ b/plugins/mysql/mysql_disk_by_prefix @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# -*- python -*- + +""" +=head1 INTRODUCTION + +Plugin to monitor MySQL database disk usage per prefix. A prefix can be eg. +'user' for the databases 'user_testdb' and 'user_db2', as can be seen in +certain shared hosting setups. + +=head1 APPLICABLE SYSTEMS + +Local access to the database files is required (which are stored in +/var/lib/mysql by default). + +=head1 INSTALLATION + +Place in /etc/munin/plugins/ (or link it there using ln -s) + +=head1 CONFIGURATION + +Add this to your /etc/munin/plugin-conf.d/munin-node: + +=over 2 + + [mysql_disk_by_prefix] + user mysql + env.prefixes_with_underscores foo_bar d_ritchie # prefixes that include an underscore + env.db_minsize 1024000 # minimum db size in order to report a specific prefix, + # in bytes (defaults to 50M). "0" for none. + env.mysql_db_dir /var/lib/mysql # default value + +=back + +=head1 HISTORY + +2019-07-25: v 1.0 pcy : created + +=head1 USAGE + +Parameters understood: + + config (required) + autoconf (optional - used by munin-config) + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf +""" + + +import os +import re +import subprocess as sp +import sys + + +def weakbool(x): + return x.lower().strip() in {'true', 'yes', 'y', 1} + + +LIMIT = os.getenv('db_minsize', str(50000 * 1024)) +try: + LIMIT = int(LIMIT) +except ValueError: + LIMIT = 0 + +exceptions = os.getenv('prefix_with_underscores', '').split(' ') +if exceptions == ['']: + exceptions = [] + +mysqldir = os.getenv('mysql_db_dir', '/var/lib/mysql') + + +def name_from_path(path): + filename = os.path.basename(path) + for name in exceptions: + if filename.startswith(name): + return name + name = filename.split('_')[0] + + def decode_byte(m): + return bytes.fromhex(m.group(1)).decode('utf-8') + + # Decode MySQL's encoding of non-ascii characters in the table names + return re.sub('@00([0-9a-z]{2})', decode_byte, name) + + +def calc_dir_size(directory): + total = 0 + + for filename in os.listdir(directory): + filedir = os.path.join(directory, filename) + + if os.path.islink(filedir): + continue + if os.path.isfile(filedir): + total += os.path.getsize(filedir) + + return total + + +def size_per_subdir(parentdir): + for subdir in os.listdir(parentdir): + dirpath = os.path.join(parentdir, subdir) + + if os.path.islink(dirpath): + continue + if os.path.isdir(dirpath): + yield calc_dir_size(dirpath), name_from_path(os.path.split(dirpath)[1]) + + +def sizes_by_name(limit=None): + sizes = {} + for size, name in size_per_subdir(mysqldir): + sizes[name] = sizes.get(name, 0) + size + for name, total_size in sizes.items(): + if limit <= 0 or limit <= total_size: + yield name, total_size + + +def fetch(): + for name, total_size in sorted(sizes_by_name(limit=LIMIT), key=lambda x: x[0]): + print('{}.value {}'.format(name, total_size)) + + +def main(): + if len(sys.argv) == 1: + fetch() + elif sys.argv[1] == 'config': + print('graph_title MySQL disk usage by prefix') + print('graph_vlabel bytes') + print('graph_category db') + + names = sorted(name for name, _ in sizes_by_name(limit=LIMIT)) + for name in names: + print('{0}.label {0}'.format(name)) + print('{}.type GAUGE'.format(name)) + print('{}.draw AREASTACK'.format(name)) + elif sys.argv[1] == 'autoconf': + print('yes' if os.path.isdir(mysqldir) + else "no (can't find MySQL's data directory)") + else: + fetch() + + +if __name__ == "__main__": + main() + +# flake8: noqa: E265 From 06919f18016c421eb1a280d8592f9181c39a92fa Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Aug 2020 16:20:47 +0200 Subject: [PATCH 656/695] Plugin mysql_disk_by_prefix: remove flake8 override and unused import The flake8 line was interpreted as "ignore this file". --- plugins/mysql/mysql_disk_by_prefix | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/mysql/mysql_disk_by_prefix b/plugins/mysql/mysql_disk_by_prefix index 4e27ad98..517164d5 100755 --- a/plugins/mysql/mysql_disk_by_prefix +++ b/plugins/mysql/mysql_disk_by_prefix @@ -52,7 +52,6 @@ Parameters understood: import os import re -import subprocess as sp import sys @@ -147,5 +146,3 @@ def main(): if __name__ == "__main__": main() - -# flake8: noqa: E265 From a1cff256cdc4790ce6b1ef2e420ef55390b1a14d Mon Sep 17 00:00:00 2001 From: pcy Date: Sat, 18 Jul 2020 02:42:27 +0200 Subject: [PATCH 657/695] [plugins/dovecot/dovecot] make 'connected users' graph DERIVEable --- plugins/dovecot/dovecot | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot index a9e3de10..363a1cf1 100755 --- a/plugins/dovecot/dovecot +++ b/plugins/dovecot/dovecot @@ -8,6 +8,7 @@ # Contributions by: # - Stephane Enten # - Steve Schnepp +# - pcy (make 'Connected Users' DERIVE, check existence of logfile in autoconf) # # Parameters understood: # @@ -16,7 +17,7 @@ # # Config variables: # -# logfile - Where to find the syslog file +# logfile - Where to find the syslog file # # Add the following line to a file in /etc/munin/plugin-conf.d: # env.logfile /var/log/your/logfile.log @@ -34,7 +35,7 @@ LOGFILE=${logfile:-/var/log/mail.log} ###################### if [ "$1" = "autoconf" ]; then - echo yes + [ -f "$LOGFILE" ] && echo yes || echo "no (logfile $LOGFILE not found)" exit 0 fi @@ -53,6 +54,7 @@ if [ "$1" = "config" ]; then done echo 'connected.label Connected Users' + echo "connected.type DERIVE" exit 0 fi @@ -86,7 +88,7 @@ echo -n echo -en "login_tls.value " VALUE=$(egrep -c '[dovecot]?.*Login.*TLS' $LOGFILE) if [ ! -z "$VALUE" ]; then - echo "$VALUE" + echo "$VALUE" else echo "0" fi @@ -97,7 +99,7 @@ echo -n echo -en "login_ssl.value " VALUE=$(egrep -c '[dovecot]?.*Login.*SSL' $LOGFILE) if [ ! -z "$VALUE" ]; then - echo "$VALUE" + echo "$VALUE" else echo "0" fi @@ -108,7 +110,7 @@ echo -n echo -en "login_imap.value " VALUE=$(egrep -c '[dovecot]?.*imap.*Login' $LOGFILE) if [ ! -z "$VALUE" ]; then - echo "$VALUE" + echo "$VALUE" else echo "0" fi @@ -119,8 +121,9 @@ echo -n echo -en "login_pop3.value " VALUE=$(egrep -c '[dovecot]?.*pop3.*Login' $LOGFILE) if [ ! -z "$VALUE" ]; then - echo "$VALUE" + echo "$VALUE" else echo "0" fi echo -n + From 0853d409f35456add85447a0c7260f9cf1d4ad98 Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Thu, 23 Jul 2020 17:08:26 +0200 Subject: [PATCH 658/695] docker_cpu: fix data for container names starting with numbers --- plugins/docker/docker_cpu | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/docker/docker_cpu b/plugins/docker/docker_cpu index cb56fa90..9194edc6 100755 --- a/plugins/docker/docker_cpu +++ b/plugins/docker/docker_cpu @@ -62,10 +62,13 @@ for my $i (1 .. $#containers) my @fields = split / +/, $containers[$i]; my $id = $fields[0]; my $name = $fields[$#fields]; + my $label = $name; # manage container name containing arithmetic operators and dots. E.g, my-container. $name =~ s/[-\+*\/\.]/_/g; # truncate container name with "," character. $name =~ s/,.*//g; + # prefix if container starts with 0-9 + $name =~ s/^([0-9])/c$1/; if (open(my $file, '<', "/sys/fs/cgroup/cpuacct/docker/$id/cpuacct.usage")) { my $total_cpu_ns = <$file>; @@ -75,7 +78,7 @@ for my $i (1 .. $#containers) { my @ncpu = split / /, <$file>; close $file; - push @result, {'name'=>$name, 'total_cpu_ns'=>$total_cpu_ns, 'ncpu'=>$#ncpu}; + push @result, {'name'=>$name, 'label'=>$label, 'total_cpu_ns'=>$total_cpu_ns, 'ncpu'=>$#ncpu}; } } } @@ -100,7 +103,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") foreach(@result) { - print "$$_{'name'}.label $$_{'name'}\n"; + print "$$_{'name'}.label $$_{'label'}\n"; print "$$_{'name'}.draw LINE2\n"; print "$$_{'name'}.min 0\n"; print "$$_{'name'}.type DERIVE\n"; From 260c8c8ff4ed04b8d33b3079725d7d4cb3560968 Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Fri, 24 Jul 2020 14:02:30 +0200 Subject: [PATCH 659/695] docker_cpu: fix warnings when docker ps has empty list --- plugins/docker/docker_cpu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/docker/docker_cpu b/plugins/docker/docker_cpu index 9194edc6..62c5d080 100755 --- a/plugins/docker/docker_cpu +++ b/plugins/docker/docker_cpu @@ -86,9 +86,9 @@ for my $i (1 .. $#containers) if (defined $ARGV[0] and $ARGV[0] eq "config") { my $nanoSecondsInSecond=1000000000; - my $graphlimit = $result[0]{'ncpu'}; + my $graphlimit = 1; foreach(@result){ - if ($$_{'ncpu'} > $graphlimit){ + if ($$_{'ncpu'} || 1 > $graphlimit){ $graphlimit = $$_{'ncpu'}; } } From 7761514c09bfdd8aaa070907b7511ceb3a62cf13 Mon Sep 17 00:00:00 2001 From: pcy Date: Thu, 16 Jul 2020 00:24:49 +0200 Subject: [PATCH 660/695] [plugins/git/gitlab_statistics] added plugin to display GitLab stats Graphs common GitLab statistics (user, repo, fork, ... count) using its HTTP API statistics endpoint (requires a token to be set and passed to the plugin) --- plugins/git/gitlab_statistics | 109 ++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 plugins/git/gitlab_statistics diff --git a/plugins/git/gitlab_statistics b/plugins/git/gitlab_statistics new file mode 100755 index 00000000..7ed144d2 --- /dev/null +++ b/plugins/git/gitlab_statistics @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# -*- python -*- + +""" +=head1 INTRODUCTION + +Plugin to monitor Gitlab status + +=head1 INSTALLATION + +Usage: Place in /etc/munin/plugins/ (or link it there using ln -s) + +=head1 CONFIGURATION + +Add this to your /etc/munin/plugin-conf.d/munin-node: + +=over 2 + + [gitlab_statistics] + env.logarithmic 1 + env.hostname gitlab.example.com # required + env.token YourPrivateTokenHere # required + +=back + +=head1 HISTORY + +2019-10-02: v 1.0 pcy : created + +=head1 USAGE + +Parameters understood: + + config (required) + autoconf (optional - used by munin-config) + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf +""" + + +import os +import json +import urllib +import sys + + +def weakbool(x): + return x.lower().strip() in {"true", "yes", "y", "1"} + + +url = None +if 'hostname' in os.environ and 'token' in os.environ: + url = "https://" + os.getenv('hostname') \ + + "/api/v4/application/statistics?private_token=" \ + + os.getenv('token') + +logarithmic = weakbool(os.getenv('logarithmic', 'N')) + + +def reqjson(): + try: + raw_data = urllib.request.urlopen(url) + return json.loads(raw_data) + except IOError as e: + print("Cannot reach the GitLab API endpoint.", file=sys.stderr) + exit(1) + + +def autoconf(): + if 'hostname' not in os.environ: + print("no ('hostname' envvar not set)") + elif 'token' not in os.environ: + print("no ('token' envvar not set)") + else: + print("yes") + + +def config(): + print("""\ +graph_title GitLab statistics +graph_vlabel amount +graph_category devel""") + if logarithmic: + print("graph_args --logarithmic") + + for x in reqjson().keys(): + print(x + ".label " + x) + + +def fetch(): + rj = reqjson() + for (x, y) in rj.items(): + print("%s.value %d" % (x, int(y.replace(',', '')))) + + +if len(sys.argv) >= 2: + if sys.argv[1] == 'autoconf': + autoconf() + elif sys.argv[1] == 'config': + config() + else: + fetch() +else: + fetch() + +# flake8: noqa: E265 From 271e21d34e3ae1f5ad4c9d58558ba939b6377d24 Mon Sep 17 00:00:00 2001 From: pcy Date: Thu, 16 Jul 2020 00:19:52 +0200 Subject: [PATCH 661/695] [plugin/user/cronjobs] added plugin to monitor cronjobs Graphs the number of cronjobs active at a certain moment, per user --- plugins/user/cronjobs | 165 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100755 plugins/user/cronjobs diff --git a/plugins/user/cronjobs b/plugins/user/cronjobs new file mode 100755 index 00000000..12859d8f --- /dev/null +++ b/plugins/user/cronjobs @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# -*- python -*- +""" +=head1 NAME + +Plugin to monitor the number of cronjobs running per user, gathering data from +syslog. + +=head1 INSTALLATION + +Usage: Place in /etc/munin/plugins/ (or link it there using ln -s) + +=head1 CONFIGURATION + +Add this to your /etc/munin/plugin-conf.d/munin-node: + +=over 2 + + [cronjobs] + user root + env.syslog_path /var/log/syslog # default value + env.cron_ident_regex crond? # for finding cron entries in the syslog, + case-insensitive (default value) + +=back + +The plugin needs to run as root in order to read from syslog. + +=head1 HISTORY + +2019-09-09: v 1.0 pcy : created + +=head1 USAGE + +Parameters understood: + + config (required) + autoconf (optional - used by munin-config) + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf +""" + + +from datetime import datetime, timedelta, timezone +import os +import re +import shutil +import socket +import sys +import struct +import time + + +syslog_path = os.getenv('syslog_path', '/var/log/syslog') +cron_ident_regex = os.getenv('cron_ident_regex', 'crond?') + +# expected format: +# CRON[]: () CMD ... +# example: +# Aug 16 22:00:01 zap CRON[23060]: (root) CMD (/usr/local/bin/dnsconfig -s) +cron_syslog_regex = re.compile('^.* %s %s\[[0-9]*\]: \((.*?)\)' # noqa: W605 + % (socket.gethostname(), cron_ident_regex), + re.I) + +STATEFILE = os.getenv('MUNIN_STATEFILE') + + +def loadstate(): + if not os.path.isfile(STATEFILE): + return None + + with open(STATEFILE, 'rb') as f: + tstamp = struct.unpack('d', f.read())[0] + return datetime.fromtimestamp(tstamp, tz=timezone.utc) + + +def savestate(state): + with open(STATEFILE, 'wb') as f: + f.write(struct.pack('d', state.timestamp())) + + +def extrcronuser(line: str): + t = cron_syslog_regex.search(line) + if t is None: + return None + gr = t.groups() + return gr[0] if len(gr) == 1 else None + + +def getcronlines(): + with open(syslog_path, 'r') as f: + for x in f.readlines(): + user = extrcronuser(x) + if user is not None: + yield (x, user) + + +def getcronusers(lines): + return set(x[1] for x in lines) + + +def autoconf(): + if shutil.which('crontab') is None: + print("no (need cron installed)") + elif not os.access(syslog_path, os.R_OK): + print("no (cannot access syslog file '%s')" % syslog_path) + else: + print("yes") + + +def config(): + usernames = getcronusers(getcronlines()) + print("""\ +graph_title Cron jobs per user +graph_vlabel jobs +graph_category processes""") + for n in usernames: + print(n + ".label " + n) + print(n + ".info jobs of user " + n) + + +def fetch(): + STATE = loadstate() + # why is there no stdlib function for this?! + localtz = timezone(timedelta(seconds=time.localtime().tm_gmtoff)) + now = datetime.now() + now_withtz = datetime.now(tz=localtz) + yearsfx = ' ' + str(now.year) + pyearsfx = ' ' + str(now.year - 1) + cronlines = list(getcronlines()) + allnames = getcronusers(cronlines) + counts = {} + + hostname = socket.gethostname() + for ln, name in cronlines: + datestr = ln[:ln.index(hostname)].strip() + logdate = datetime.strptime(datestr + yearsfx, "%b %d %H:%M:%S %Y") + if logdate > now: + logdate = datetime.strptime(datestr + pyearsfx, "%b %d %H:%M:%S %Y") + # add timezone info (ugly hack), as strptime doesn't want to do this + logdate = now_withtz + (logdate - now) + if STATE is None or logdate > STATE: + counts[name] = (counts[name] + 1) if name in counts else 1 + + for n in allnames: + if n in counts: + print("%s.value %d" % (n, counts[n])) + else: + print("%s.value 0" % n) + + savestate(now_withtz) + + +if len(sys.argv) >= 2: + if sys.argv[1] == 'autoconf': + autoconf() + elif sys.argv[1] == 'config': + config() + else: + fetch() +else: + fetch() From 10ca12569acda3ccc36467a5b9943569cd3d8401 Mon Sep 17 00:00:00 2001 From: pcy Date: Mon, 20 Jul 2020 02:18:20 +0200 Subject: [PATCH 662/695] [plugins/mysql/mysql_audit] Added plugin for monitoring the audit log Shows the number of active over time --- plugins/mysql/mysql_audit | 189 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100755 plugins/mysql/mysql_audit diff --git a/plugins/mysql/mysql_audit b/plugins/mysql/mysql_audit new file mode 100755 index 00000000..1d5343a6 --- /dev/null +++ b/plugins/mysql/mysql_audit @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# -*- python -*- + +""" +=head1 INTRODUCTION + +Plugin to monitor the MySQL audit log connection count + +=head1 APPLICABLE SYSTEMS + +MySQL needs to be configured manually in order to enable the audit log. This +can be done as follows: + +=over 2 + + plugin-load = server_audit=server_audit.so + server_audit_events = connect + server_audit_output_type=file + server_audit_file_path = /var/log/mysql/audit.log + server_audit_file_rotate_size = 512M + server_audit_logging = ON + server_audit_file_rotations = 5 + +=back + +=head1 INSTALLATION + +Place in /etc/munin/plugins/ (or link it there using ln -s) + +=head1 CONFIGURATION + +Add this to your /etc/munin/plugin-conf.d/munin-node: + +=over 2 + + [mysql_audit] + user mysql + env.logfile /var/log/mysql/audit.log + env.sorted 1 # sort output (if not: unsorted) + env.toplist 10 # only the top 10 (if unset: all of them). implies sorted + +=back + +=head1 HISTORY + +2017-11-03: v 1.0 Bert Van de Poel : created +2020-07-19: v 1.1 pcy : added config options + +=head1 USAGE + +Parameters understood: + + config (required) + autoconf (optional - used by munin-config) + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf +""" + + +from datetime import datetime, timedelta, timezone +import operator +import os +import struct +import sys + + +def weakbool(x): + return x.lower().strip() in {'true', 'yes', 'y', '1'} + + +logfile = os.getenv('logfile', '/var/log/mysql/audit.log') +toplist = int(os.getenv('toplist', '0')) +sortlist = weakbool(os.getenv('sorted', 'N')) or toplist > 0 + +STATEFILE = os.getenv('MUNIN_STATEFILE') + + +def loadstate(): + if not os.path.isfile(STATEFILE): + return None + + with open(STATEFILE, 'rb') as f: + tstamp = struct.unpack('d', f.read())[0] + return datetime.fromtimestamp(tstamp, tz=timezone.utc) + + +def savestate(state): + with open(STATEFILE, 'wb') as f: + f.write(struct.pack('d', state.timestamp())) + + +def reverse_lines(filename, BUFSIZE=4096): + with open(filename, "r") as f: + f.seek(0, 2) + p = f.tell() + remainder = "" + while True: + sz = min(BUFSIZE, p) + p -= sz + f.seek(p) + buf = f.read(sz) + remainder + if '\n' not in buf: + remainder = buf + else: + i = buf.index('\n') + for L in buf[i + 1:].split("\n")[::-1]: + yield L + remainder = buf[:i] + if p == 0: + break + yield remainder + + +def get_data(do_save=True): + occurrences = {} + begin = datetime.now(timezone.utc) + begin_local = datetime.now() + + state = loadstate() + if state is None: + # need to do something here to prevent reading indefinitely + state = begin - timedelta(minutes=5) + + for line in reverse_lines(logfile): + if ',CONNECT,' not in line: + continue + + splitted = line.split(',') + key = splitted[2] + date = datetime.strptime(splitted[0], '%Y%m%d %H:%M:%S') + # hack to add timezone data to the datetime + date = begin + (date - begin_local) + + if date < state: + break + + occurrences[key] = occurrences.get(key, 0) + 1 + + if do_save: + savestate(begin) + + return occurrences + + +def autoconf(): + print("no (logfile not found)" if os.path.isfile(logfile) else "yes") + + +def configure(): + print('graph_title MySQL Audit connect count') + print('graph_vlabel Connections') + print('graph_category mysql') + + occurrences = get_data(False) + occitems = occurrences.items() + occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0), + reverse=sortlist) + if toplist > 0: + occitems = occitems[:toplist] + + for key, value in occitems: + print('{}.label {}'.format(key.lower(), key)) + print('{}.type GAUGE'.format(key.lower())) + print('{}.draw AREASTACK'.format(key.lower())) + + +def fetch(): + occurrences = get_data() + occitems = occurrences.items() + occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0), + reverse=sortlist) + if toplist > 0: + occitems = occitems[:toplist] + + for key, value in occitems: + print('{}.value {}'.format(key, value)) + + +if len(sys.argv) == 2 and sys.argv[1] == "autoconf": + autoconf() +elif len(sys.argv) == 2 and sys.argv[1] == "config": + configure() +else: + fetch() + +# flake8: noqa: E265 From 852aa41ab774a18973eaccedc242ec8a46fadd77 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 19 Aug 2020 22:47:03 +0200 Subject: [PATCH 663/695] Remove global flake8 override --- plugins/git/gitlab_statistics | 4 +--- plugins/mysql/mysql_audit | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/git/gitlab_statistics b/plugins/git/gitlab_statistics index 7ed144d2..7a76bf97 100755 --- a/plugins/git/gitlab_statistics +++ b/plugins/git/gitlab_statistics @@ -64,7 +64,7 @@ def reqjson(): try: raw_data = urllib.request.urlopen(url) return json.loads(raw_data) - except IOError as e: + except IOError: print("Cannot reach the GitLab API endpoint.", file=sys.stderr) exit(1) @@ -105,5 +105,3 @@ if len(sys.argv) >= 2: fetch() else: fetch() - -# flake8: noqa: E265 diff --git a/plugins/mysql/mysql_audit b/plugins/mysql/mysql_audit index 1d5343a6..14a3878a 100755 --- a/plugins/mysql/mysql_audit +++ b/plugins/mysql/mysql_audit @@ -185,5 +185,3 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": configure() else: fetch() - -# flake8: noqa: E265 From 6052c56d945c87c017d59061f9cb18ca97d51ad6 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sat, 22 Aug 2020 15:45:51 -0700 Subject: [PATCH 664/695] plugins/puppet/puppet_runtime: fix formatting Used rufo https://github.com/ruby-formatter/rufo --- plugins/puppet/puppet_runtime | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/plugins/puppet/puppet_runtime b/plugins/puppet/puppet_runtime index 11598776..620ac06e 100755 --- a/plugins/puppet/puppet_runtime +++ b/plugins/puppet/puppet_runtime @@ -15,9 +15,9 @@ # reports how long the puppet agent took to apply the catalog def get_runtime - logfile = ENV['puppet_logfile'] || '/var/log/messages' + logfile = ENV["puppet_logfile"] || "/var/log/messages" t = Time.now - dateformat = ENV['puppet_logformat'] || "^%b %d" + dateformat = ENV["puppet_logformat"] || "^%b %d" today = t.strftime(dateformat) File.open(logfile).grep(/#{today}/).grep(/Finished catalog run in/).reverse_each do |line| if line =~ /in (.*) seconds/ @@ -28,18 +28,17 @@ def get_runtime end case ARGV[0] - when 'config' - puts "graph_category other" - puts "graph_args --base 1000 -l 0" - puts "graph_scale no" - puts "graph_title puppet catalog run time" - puts "graph_vlabel Seconds" - puts "runtime.label Catalog application time" - exit 0 - when 'autoconf' - puts "yes" - exit 0 - else - get_runtime +when "config" + puts "graph_category other" + puts "graph_args --base 1000 -l 0" + puts "graph_scale no" + puts "graph_title puppet catalog run time" + puts "graph_vlabel Seconds" + puts "runtime.label Catalog application time" + exit 0 +when "autoconf" + puts "yes" + exit 0 +else + get_runtime end - From 10f2d97ffebd576492f96356c6bb7e35918df441 Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 16:38:38 +0200 Subject: [PATCH 665/695] [plugins/disk/btrfs_device_usage] Add plugin to monitor btrfs usage on a device level. Shows the used size of each device in a btrfs pool in bytes and percent. --- plugins/disk/btrfs_device_usage | 96 +++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100755 plugins/disk/btrfs_device_usage diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage new file mode 100755 index 00000000..4aca7d16 --- /dev/null +++ b/plugins/disk/btrfs_device_usage @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +# +# This file contains a munin-plugin to gather btrfs statistics per device. +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this plugin. If not, see . + + +import btrfs +import sys + +def munin_config(fs): + fsid = str(fs.fsid).replace('-', '_') + + print("multigraph btrfs_device_usage_byte_" + fsid) + print("graph_title btrfs usage by device in bytes on " + fs.path) + print("graph_args --base 1024 -l 0") + print("graph_scale yes") + print("graph_vlabel bytes") + print("graph_category disk") + print("graph_info This graph shows bytes used by btrfs on every device") + + devices = fs.devices() + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + this_dev_name = this_dev_info.path.replace('/dev/', '') + print("btrfs_bytes_"+ fsid + "_" + str(this_device.devid) + ".label " + this_dev_name) + + print("") + + print("multigraph btrfs_device_usage_percent_" + fsid) + print("graph_title btrfs usage by device in percent on " + fs.path) + print("graph_args --base 1000 -l 0") + print("graph_scale no") + print("graph_vlabel %") + print("graph_category disk") + print("graph_info This graph shows percentage used by btrfs on every device. Maesured in percentage of device capacity.") + + devices = fs.devices() + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + this_dev_name = this_dev_info.path.replace('/dev/', '') + print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".label " + this_dev_name) + print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".warning 95") + print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".critical 98") + + print("") + +def munin_values(fs): + fsid = str(fs.fsid).replace('-', '_') + devices = fs.devices() + + print("multigraph btrfs_device_usage_byte_" + fsid) + + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + print("btrfs_bytes_"+ fsid + "_" + str(this_device.devid) + ".value " + str(this_dev_info.bytes_used)) + + print("") + + # Reset device iterator + devices = fs.devices() + + print("multigraph btrfs_device_usage_percent_" + fsid) + + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + usage = 100.0 * this_dev_info.bytes_used / this_dev_info.total_bytes + print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".value " + str(round(usage, 2))) + + print("") + + +def main(): + for path in btrfs.utils.mounted_filesystem_paths(): + with btrfs.FileSystem(path) as fs: + if len(sys.argv) > 1 and sys.argv[1] == "config": + munin_config(fs) + else: + munin_values(fs) + + +if __name__ == "__main__": + main() + +exit(0) From 7bf77a008db2e3d682e1ee89bcb9689584795b29 Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 16:43:21 +0200 Subject: [PATCH 666/695] [plugins/disk/btrfs_device_stats] Add a plugin to monitor device stats from btrfs. Monitors the different device-counters (mostly for errors) of the disks in a btrfs pool. --- plugins/disk/btrfs_device_stats | 140 ++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100755 plugins/disk/btrfs_device_stats diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats new file mode 100755 index 00000000..3f9b23a9 --- /dev/null +++ b/plugins/disk/btrfs_device_stats @@ -0,0 +1,140 @@ +#!/usr/bin/python3 +# +# This file contains a munin-plugin to gather btrfs statistics per device. +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this plugin. If not, see . + + +import btrfs +import sys + + +def munin_config(fs): + fsid = str(fs.fsid).replace('-', '_') + print("multigraph btrfs_device_stats_" + fsid) + print("graph_args --base 1000 -l 0") + print("graph_vlabel total btrfs attribute value") + print("graph_title btrfs total device stats for " + fs.path) + print("graph_category disk") + print("graph_info This graph shows the total stats of devices used by btrfs") + + print("corruption_errs_total.label Corruption Errors") + print("flush_errs_total.label Flush Errors") + print("generation_errs_total.label Generation Errors") + print("read_errs_total.label Read Errors") + print("write_errs_total.label Write Errors") + print("nr_items_total.label Nr. of Items") + print("flags_total.label Nr. of Flags") + + print("") + + devices = fs.devices() + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + this_dev_name = this_dev_info.path.replace('/dev/', '') + print("multigraph btrfs_device_stats_" + fsid + "." + str(this_device.devid)) + print("graph_args --base 1000 -l 0") + print("graph_vlabel btrfs attribute value") + print("graph_title btrfs device stats for " + this_dev_name) + print("graph_category disk") + print("graph_info This graph shows stats of devices used by btrfs") + + print("corruption_errs.label Corruption Errors") + print("corruption_errs.warming 1") + print("flush_errs.label Flush Errors") + print("flush_errs.warming 1") + print("generation_errs.label Generation Errors") + print("generation_errs.warming 1") + print("read_errs.label Read Errors") + print("read_errs.warming 1") + print("write_errs.label Write Errors") + print("write_errs.warming 1") + print("nr_items.label Nr. of Items") + print("flags.label Nr. of Flags") + print("flags.warming 1") + + print("") + + +def munin_values(fs): + corruption_errs_total = 0 + flush_errs_total = 0 + generation_errs_total = 0 + read_errs_total = 0 + write_errs_total = 0 + nr_items_total = 0 + flags_total = 0 + + fsid = str(fs.fsid).replace('-', '_') + devices = fs.devices() + + for this_device in devices: + this_dev_info = fs.dev_info(this_device.devid) + this_dev_name = this_dev_info.path.replace('/dev/', '') + this_dev_stat = fs.dev_stats(this_device.devid, False) + + corruption_errs = this_dev_stat.corruption_errs + flush_errs = this_dev_stat.flush_errs + generation_errs = this_dev_stat.generation_errs + read_errs = this_dev_stat.read_errs + write_errs = this_dev_stat.write_errs + nr_items = this_dev_stat.nr_items + flags = this_dev_stat.flags + + corruption_errs_total = corruption_errs_total + corruption_errs + flush_errs_total = flush_errs_total + flush_errs + generation_errs_total = generation_errs_total + generation_errs + read_errs_total = read_errs_total + read_errs + write_errs_total = write_errs_total + write_errs + nr_items_total = nr_items_total + nr_items + flags_total = flags_total + flags + + print("multigraph btrfs_device_stats_" + fsid + "." + str(this_device.devid)) + + print("corruption_errs.value " + str(corruption_errs)) + print("flush_errs.value " + str(flush_errs)) + print("generation_errs.value " + str(generation_errs)) + print("read_errs.value " + str(read_errs)) + print("write_errs.value " + str(write_errs)) + print("nr_items.value " + str(nr_items)) + print("flags.value " + str(flags)) + + print("") + + print("multigraph btrfs_device_stats_" + fsid) + + print("corruption_errs_total.value " + str(corruption_errs_total)) + print("flush_errs_total.value " + str(flush_errs_total)) + print("generation_errs_total.value " + str(generation_errs_total)) + print("read_errs_total.value " + str(read_errs_total)) + print("write_errs_total.value " + str(write_errs_total)) + print("nr_items_total.value " + str(nr_items_total)) + print("flags_total.value " + str(flags_total)) + + print("") + + +def main(): + for path in btrfs.utils.mounted_filesystem_paths(): + with btrfs.FileSystem(path) as fs: + if len(sys.argv) > 1 and sys.argv[1] == "config": + munin_config(fs) + else: + munin_values(fs) + + +if __name__ == "__main__": + main() + +exit(0) From 5f88f9ad0695e42197c14494345316ccf24c21eb Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 17:34:21 +0200 Subject: [PATCH 667/695] Fix issues brought up by CI. - Remove trailing whitespace - Remove unused variable. --- plugins/disk/btrfs_device_stats | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats index 3f9b23a9..2cbf1640 100755 --- a/plugins/disk/btrfs_device_stats +++ b/plugins/disk/btrfs_device_stats @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python3 # # This file contains a munin-plugin to gather btrfs statistics per device. # @@ -81,7 +81,6 @@ def munin_values(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) - this_dev_name = this_dev_info.path.replace('/dev/', '') this_dev_stat = fs.dev_stats(this_device.devid, False) corruption_errs = this_dev_stat.corruption_errs From 7d220d0ae683a926d73aaeed252a10630ced206c Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 17:45:08 +0200 Subject: [PATCH 668/695] [plugins/disk/btrfs_device_usage] Fix issues brought up by CI. - Remove trailing whitespace. - Add whitespace around operators. - Add linebreaks to shorten the lines. --- plugins/disk/btrfs_device_usage | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage index 4aca7d16..04af4642 100755 --- a/plugins/disk/btrfs_device_usage +++ b/plugins/disk/btrfs_device_usage @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python3 # # This file contains a munin-plugin to gather btrfs statistics per device. # @@ -19,6 +19,7 @@ import btrfs import sys + def munin_config(fs): fsid = str(fs.fsid).replace('-', '_') @@ -34,7 +35,8 @@ def munin_config(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') - print("btrfs_bytes_"+ fsid + "_" + str(this_device.devid) + ".label " + this_dev_name) + print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + \ + ".label " + this_dev_name) print("") @@ -44,18 +46,21 @@ def munin_config(fs): print("graph_scale no") print("graph_vlabel %") print("graph_category disk") - print("graph_info This graph shows percentage used by btrfs on every device. Maesured in percentage of device capacity.") + print("graph_info This graph shows percentage used by btrfs on every \ + device. Maesured in percentage of device capacity.") devices = fs.devices() for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') - print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".label " + this_dev_name) - print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".warning 95") - print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".critical 98") + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + \ + ".label " + this_dev_name) + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".warning 95") + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".critical 98") print("") + def munin_values(fs): fsid = str(fs.fsid).replace('-', '_') devices = fs.devices() @@ -64,7 +69,8 @@ def munin_values(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) - print("btrfs_bytes_"+ fsid + "_" + str(this_device.devid) + ".value " + str(this_dev_info.bytes_used)) + print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + \ + ".value " + str(this_dev_info.bytes_used)) print("") @@ -76,7 +82,8 @@ def munin_values(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) usage = 100.0 * this_dev_info.bytes_used / this_dev_info.total_bytes - print("btrfs_percent_"+ fsid + "_" + str(this_device.devid) + ".value " + str(round(usage, 2))) + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + \ + ".value " + str(round(usage, 2))) print("") From f12bfed79562026775da361ba5d51cd53198b5c1 Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 18:15:40 +0200 Subject: [PATCH 669/695] [plugins/disk/btrfs_device_stats] Remove unused variable. --- plugins/disk/btrfs_device_stats | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats index 2cbf1640..639fff2f 100755 --- a/plugins/disk/btrfs_device_stats +++ b/plugins/disk/btrfs_device_stats @@ -80,7 +80,6 @@ def munin_values(fs): devices = fs.devices() for this_device in devices: - this_dev_info = fs.dev_info(this_device.devid) this_dev_stat = fs.dev_stats(this_device.devid, False) corruption_errs = this_dev_stat.corruption_errs From 675dedf1d4776699a7e5a5fba5e41c9335957a18 Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Sun, 23 Aug 2020 18:17:21 +0200 Subject: [PATCH 670/695] [plugins/disk/btrfs_device_usage] Reformat linebreaks. Following suggestions from linting. --- plugins/disk/btrfs_device_usage | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage index 04af4642..1876f235 100755 --- a/plugins/disk/btrfs_device_usage +++ b/plugins/disk/btrfs_device_usage @@ -35,8 +35,8 @@ def munin_config(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') - print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + \ - ".label " + this_dev_name) + print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + + ".label " + this_dev_name) print("") @@ -53,8 +53,8 @@ def munin_config(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') - print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + \ - ".label " + this_dev_name) + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + + ".label " + this_dev_name) print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".warning 95") print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".critical 98") @@ -69,8 +69,8 @@ def munin_values(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) - print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + \ - ".value " + str(this_dev_info.bytes_used)) + print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + + ".value " + str(this_dev_info.bytes_used)) print("") @@ -82,8 +82,8 @@ def munin_values(fs): for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) usage = 100.0 * this_dev_info.bytes_used / this_dev_info.total_bytes - print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + \ - ".value " + str(round(usage, 2))) + print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + + ".value " + str(round(usage, 2))) print("") From bffbc23a4e118c16b90273e331b1a5b812fe56da Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Mon, 24 Aug 2020 21:37:29 +0200 Subject: [PATCH 671/695] Fix linting issues. --- plugins/disk/btrfs_device_stats | 32 +++++++++++++------------- plugins/disk/btrfs_device_usage | 40 ++++++++++++++++----------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats index 639fff2f..8555c280 100755 --- a/plugins/disk/btrfs_device_stats +++ b/plugins/disk/btrfs_device_stats @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # # This file contains a munin-plugin to gather btrfs statistics per device. # @@ -28,7 +28,7 @@ def munin_config(fs): print("graph_title btrfs total device stats for " + fs.path) print("graph_category disk") print("graph_info This graph shows the total stats of devices used by btrfs") - + print("corruption_errs_total.label Corruption Errors") print("flush_errs_total.label Flush Errors") print("generation_errs_total.label Generation Errors") @@ -36,9 +36,9 @@ def munin_config(fs): print("write_errs_total.label Write Errors") print("nr_items_total.label Nr. of Items") print("flags_total.label Nr. of Flags") - + print("") - + devices = fs.devices() for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) @@ -49,7 +49,7 @@ def munin_config(fs): print("graph_title btrfs device stats for " + this_dev_name) print("graph_category disk") print("graph_info This graph shows stats of devices used by btrfs") - + print("corruption_errs.label Corruption Errors") print("corruption_errs.warming 1") print("flush_errs.label Flush Errors") @@ -63,7 +63,7 @@ def munin_config(fs): print("nr_items.label Nr. of Items") print("flags.label Nr. of Flags") print("flags.warming 1") - + print("") @@ -75,13 +75,13 @@ def munin_values(fs): write_errs_total = 0 nr_items_total = 0 flags_total = 0 - + fsid = str(fs.fsid).replace('-', '_') devices = fs.devices() - + for this_device in devices: this_dev_stat = fs.dev_stats(this_device.devid, False) - + corruption_errs = this_dev_stat.corruption_errs flush_errs = this_dev_stat.flush_errs generation_errs = this_dev_stat.generation_errs @@ -89,7 +89,7 @@ def munin_values(fs): write_errs = this_dev_stat.write_errs nr_items = this_dev_stat.nr_items flags = this_dev_stat.flags - + corruption_errs_total = corruption_errs_total + corruption_errs flush_errs_total = flush_errs_total + flush_errs generation_errs_total = generation_errs_total + generation_errs @@ -97,9 +97,9 @@ def munin_values(fs): write_errs_total = write_errs_total + write_errs nr_items_total = nr_items_total + nr_items flags_total = flags_total + flags - + print("multigraph btrfs_device_stats_" + fsid + "." + str(this_device.devid)) - + print("corruption_errs.value " + str(corruption_errs)) print("flush_errs.value " + str(flush_errs)) print("generation_errs.value " + str(generation_errs)) @@ -107,11 +107,11 @@ def munin_values(fs): print("write_errs.value " + str(write_errs)) print("nr_items.value " + str(nr_items)) print("flags.value " + str(flags)) - + print("") - + print("multigraph btrfs_device_stats_" + fsid) - + print("corruption_errs_total.value " + str(corruption_errs_total)) print("flush_errs_total.value " + str(flush_errs_total)) print("generation_errs_total.value " + str(generation_errs_total)) @@ -119,7 +119,7 @@ def munin_values(fs): print("write_errs_total.value " + str(write_errs_total)) print("nr_items_total.value " + str(nr_items_total)) print("flags_total.value " + str(flags_total)) - + print("") diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage index 1876f235..e7265de8 100755 --- a/plugins/disk/btrfs_device_usage +++ b/plugins/disk/btrfs_device_usage @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # # This file contains a munin-plugin to gather btrfs statistics per device. # @@ -22,7 +22,7 @@ import sys def munin_config(fs): fsid = str(fs.fsid).replace('-', '_') - + print("multigraph btrfs_device_usage_byte_" + fsid) print("graph_title btrfs usage by device in bytes on " + fs.path) print("graph_args --base 1024 -l 0") @@ -30,16 +30,16 @@ def munin_config(fs): print("graph_vlabel bytes") print("graph_category disk") print("graph_info This graph shows bytes used by btrfs on every device") - + devices = fs.devices() for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + - ".label " + this_dev_name) - + ".label " + this_dev_name) + print("") - + print("multigraph btrfs_device_usage_percent_" + fsid) print("graph_title btrfs usage by device in percent on " + fs.path) print("graph_args --base 1000 -l 0") @@ -47,46 +47,46 @@ def munin_config(fs): print("graph_vlabel %") print("graph_category disk") print("graph_info This graph shows percentage used by btrfs on every \ - device. Maesured in percentage of device capacity.") - + device. Maesured in percentage of device capacity.") + devices = fs.devices() for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) this_dev_name = this_dev_info.path.replace('/dev/', '') print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + - ".label " + this_dev_name) + ".label " + this_dev_name) print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".warning 95") print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + ".critical 98") - + print("") def munin_values(fs): fsid = str(fs.fsid).replace('-', '_') devices = fs.devices() - + print("multigraph btrfs_device_usage_byte_" + fsid) - + for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) print("btrfs_bytes_" + fsid + "_" + str(this_device.devid) + - ".value " + str(this_dev_info.bytes_used)) - + ".value " + str(this_dev_info.bytes_used)) + print("") - + # Reset device iterator devices = fs.devices() - + print("multigraph btrfs_device_usage_percent_" + fsid) - + for this_device in devices: this_dev_info = fs.dev_info(this_device.devid) usage = 100.0 * this_dev_info.bytes_used / this_dev_info.total_bytes print("btrfs_percent_" + fsid + "_" + str(this_device.devid) + - ".value " + str(round(usage, 2))) - + ".value " + str(round(usage, 2))) + print("") - + def main(): for path in btrfs.utils.mounted_filesystem_paths(): From 13d5b23442f00ba7f83d24c0737a945a9e4cd420 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 26 Aug 2020 00:17:18 +1000 Subject: [PATCH 672/695] [plugin/docker_] Improvements to docker_ plugin (#1094) * Add support for docker_network * Actually show non running containers counts * Show intermediate and dangling images counts * Add warnings on dangling images and dead containers * Fully support autoconf and suggest * Add graph_total to the status (thereby making the containers graph redundant), CPU and Memory graphs * Show most graphs as AREASTACK, this allows to get a clearer view of both individual and total use * Use LINESTACK1 to work around munin-contrib/munin#1343 * Include all containers in CPU/Memory/Network config, so data from non-running containers is still displayed * Sprinkle some info and extinfo * Add ClientWrapper around docker module, providing caching and sorting * Reverse-engineer author list from git log --- plugins/docker/docker_ | 323 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 284 insertions(+), 39 deletions(-) diff --git a/plugins/docker/docker_ b/plugins/docker/docker_ index c337da79..f2d951b5 100755 --- a/plugins/docker/docker_ +++ b/plugins/docker/docker_ @@ -5,7 +5,7 @@ docker_ - Docker wildcard-plugin to monitor a L host. This wildcard plugin provides at the moment only the suffixes C, C, C, -C, C and C. +C, C, C and C. =head1 INSTALLATION @@ -18,6 +18,7 @@ C, C and C. ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_cpu ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_images ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_memory + ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_network ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_status ln -s /usr/share/munin/plugins/docker_ /etc/munin/plugins/docker_volumes @@ -56,45 +57,181 @@ Would exclude all containers with the word "runner" in the name. env.EXCLUDE_CONTAINER_NAME regexp =back + +=head1 AUTHORS + +This section has been reverse-engineered from git logs + +* Codimp : original rewrite +* Rowan Wookey : performance improvement +* Olivier Mehani : Network support, ClientWrapper, gerenal + cleanup + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf suggest + """ import os import sys -import docker import re +from functools import cached_property from multiprocessing import Process, Queue +def sorted_by_creation_date(func): + def sorted_func(*args, **kwargs): + return sorted( + func(*args, **kwargs), + key=( + lambda x: x.attrs['CreatedAt'] + if 'CreatedAt' in x.attrs + else x.attrs['Created'] + ) + ) + return sorted_func + + +class ClientWrapper: + """ + A small wrapper for the docker client, to centralise some parsing logic, + and support caching. + + In addition, when the exclude_re parameter is not None, + any container which name is matched by the RE will not be excluded from reports. + """ + client = None + exclude = None + + def __init__(self, client, exclude_re=None): + self.client = client + if exclude_re: + self.exclude = re.compile(exclude_re) + + @cached_property + @sorted_by_creation_date + def containers(self): + return self.client.containers.list() + + @cached_property + @sorted_by_creation_date + def all_containers(self): + return [c for c in self.client.containers.list(all=True) + if not self.exclude + or not self.exclude.search(c.name)] + + @cached_property + @sorted_by_creation_date + def intermediate_images(self): + return list( + set(self.all_images) + .difference( + set(self.images) + .difference( + set(self.dangling_images) + ) + ) + ) + + @cached_property + @sorted_by_creation_date + def all_images(self): + return self.client.images.list(all=True) + + @cached_property + @sorted_by_creation_date + def images(self): + images = self.client.images.list() + return list( + set(images) + .difference( + set(self.dangling_images)) + ) + + @cached_property + @sorted_by_creation_date + def dangling_images(self): + return self.client.images.list(filters={'dangling': True}) + + @cached_property + @sorted_by_creation_date + def volumes(self): + return self.client.volumes.list() + + +def container_summary(container): + summary = container.name + attributes = container_attributes(container) + if attributes: + summary += f' ({attributes})' + return summary + + +def container_attributes(container): + attributes = container.image.tags + attributes.append(container.attrs['Created']) + return ', '.join(attributes) + + def print_containers_status(client): - running = 0 - paused = 0 - created = 0 - restarting = 0 - removing = 0 - exited = 0 - dead = 0 - for container in client.containers.list(): + running = [] + paused = [] + created = [] + restarting = [] + removing = [] + exited = [] + dead = [] + for container in client.all_containers: if container.status == 'running': - running += 1 + running.append(container) elif container.status == 'paused': - paused += 1 + paused.append(container) elif container.status == 'created': - created += 1 + created.append(container) elif container.status == 'restarting': - restarting += 1 + restarting.append(container) elif container.status == 'removing': - removing += 1 + removing.append(container) elif container.status == 'exited': - exited += 1 + exited.append(container) elif container.status == 'dead': - dead += 1 - print('running.value', running) - print('paused.value', paused) - print('created.value', created) - print('restarting.value', restarting) - print('removing.value', removing) - print('exited.value', exited) - print('dead.value', dead) + dead.append(container) + print('running.value', len(running)) + print('running.extinfo', ', '.join(container_summary(c) for c in running)) + print('paused.value', len(paused)) + print('paused.extinfo', ', '.join(container_summary(c) for c in paused)) + print('created.value', len(created)) + print('created.extinfo', ', '.join(container_summary(c) for c in created)) + print('restarting.value', len(restarting)) + print('restarting.extinfo', ', '.join(container_summary(c) for c in restarting)) + print('removing.value', len(removing)) + print('removing.extinfo', ', '.join(container_summary(c) for c in removing)) + print('exited.value', len(exited)) + print('exited.extinfo', ', '.join(container_summary(c) for c in exited)) + print('dead.value', len(dead)) + print('dead.extinfo', ', '.join(container_summary(c) for c in dead)) + + +def image_summary(image): + attributes = image.tags + attributes.append(image.attrs['Created']) + attributes.append(f"{round(image.attrs['Size']/1024**2, 2)} MiB") + return f"{image.short_id} ({', '.join(attributes)})" + + +def print_images_count(client): + images = client.images + intermediate = client.intermediate_images + dangling = client.dangling_images + + print('intermediate_quantity.value', len(intermediate)) + print('intermediate_quantity.extinfo', ', '.join(image_summary(i) for i in intermediate)) + print('images_quantity.value', len(images)) + print('images_quantity.extinfo', ', '.join(image_summary(i) for i in images)) + print('dangling_quantity.value', len(dangling)) + print('dangling_quantity.extinfo', ', '.join(image_summary(i) for i in dangling)) def get_container_stats(container, q): @@ -104,10 +241,7 @@ def get_container_stats(container, q): def parallel_container_stats(client): proc_list = [] stats = {} - exclude = os.getenv('EXCLUDE_CONTAINER_NAME') - for container in client.containers.list(): - if exclude and re.search(exclude, container.name): - break + for container in client.containers: q = Queue() p = Process(target=get_container_stats, args=(container, q)) proc_list.append({'proc': p, 'queue': q, 'container': container}) @@ -122,18 +256,39 @@ def print_containers_cpu(client): for container, stats in parallel_container_stats(client): cpu_count = len(stats["cpu_stats"]["cpu_usage"]["percpu_usage"]) cpu_percent = 0.0 - cpu_delta = float(stats["cpu_stats"]["cpu_usage"]["total_usage"]) \ - - float(stats["precpu_stats"]["cpu_usage"]["total_usage"]) - system_delta = float(stats["cpu_stats"]["system_cpu_usage"]) \ - - float(stats["precpu_stats"]["system_cpu_usage"]) + cpu_delta = (float(stats["cpu_stats"]["cpu_usage"]["total_usage"]) + - float(stats["precpu_stats"]["cpu_usage"]["total_usage"])) + system_delta = (float(stats["cpu_stats"]["system_cpu_usage"]) + - float(stats["precpu_stats"]["system_cpu_usage"])) if system_delta > 0.0: cpu_percent = cpu_delta / system_delta * 100.0 * cpu_count print(container.name + '.value', cpu_percent) + print(container.name + '.extinfo', container_attributes(container)) def print_containers_memory(client): for container, stats in parallel_container_stats(client): print(container.name + '.value', stats['memory_stats']['stats']['total_rss']) + print(container.name + '.extinfo', container_attributes(container)) + + +def print_containers_network(client): + for container, stats in parallel_container_stats(client): + tx_bytes = 0 + rx_bytes = 0 + for data in stats['networks'].values(): + tx_bytes += data['tx_bytes'] + rx_bytes += data['rx_bytes'] + print(container.name + '_up.value', tx_bytes) + print(container.name + '_down.value', rx_bytes) + print(container.name + '.extinfo', container_attributes(container)) + + +def volume_summary(volume): + summary = f"{volume.short_id}" + if volume.attrs['Labels']: + summary += " ({', '.join(volume.attrs['Labels'])})" + return summary def main(): @@ -143,28 +298,70 @@ def main(): mode = "" wildcard = sys.argv[0].split("docker_")[1].split("_")[0] + try: + import docker + client = docker.from_env() + if mode == "autoconf": + client.ping() + print('yes') + sys.exit(0) + except Exception as e: + print(f'no ({e})') + if mode == "autoconf": + sys.exit(0) + sys.exit(1) + if mode == "suggest": - print("containers") print("cpu") print("images") print("memory") + print("network") print("status") print("volumes") + sys.exit(0) - client = docker.from_env() + client = ClientWrapper(client, + exclude_re=os.getenv('EXCLUDE_CONTAINER_NAME')) if wildcard == "status": if mode == "config": print("graph_title Docker status") print("graph_vlabel containers") print("graph_category virtualization") + print("graph_total All containers") print("running.label RUNNING") + print("running.draw AREASTACK") + print("running.info Running containers can be manipulated with " + "`docker container [attach|kill|logs|pause|restart|stop] ` or " + "commands run in them with `docker container exec " + "[--detach|--interactive,--privileged,--tty] `" + ) print("paused.label PAUSED") + print("paused.draw AREASTACK") + print("paused.info Paused containers can be resumed with " + "`docker container unpause `") print("created.label CREATED") + print("created.draw AREASTACK") + print("created.info New containers can be created with " + "`docker container create --name ` or " + "`docker container run --name `") print("restarting.label RESTARTING") + print("restarting.draw AREASTACK") + print("restarting.info Containers can be restarted with " + "`docker container restart `") print("removing.label REMOVING") + print("removing.draw AREASTACK") + print("removing.info Containers can be removed with " + "`docker container rm `") print("exited.label EXITED") + print("exited.draw AREASTACK") + print("exited.info Exited containers can be started with " + "`docker container start [--attach] `") print("dead.label DEAD") + print("dead.draw AREASTACK") + print("dead.warning 1") + print("dead.info Dead containers can be started with " + "`docker container start `") else: print_containers_status(client) elif wildcard == "containers": @@ -174,23 +371,42 @@ def main(): print("graph_category virtualization") print("containers_quantity.label Containers") else: - print('containers_quantity.value', len(client.containers.list())) + print('containers_quantity.value', len(client.containers)) elif wildcard == "images": if mode == "config": print("graph_title Docker images") print("graph_vlabel images") print("graph_category virtualization") + print("graph_total All images") + print("intermediate_quantity.label Intermediate images") + print("intermediate_quantity.draw AREASTACK") + print("intermediate_quantity.info All unused images can be deleted with " + "`docker image prune --all`") print("images_quantity.label Images") + print("images_quantity.draw AREASTACK") + print("images_quantity.info Images can be used in containers with " + "`docker container create --name ` or " + "`docker container run --name `") + print("dangling_quantity.label Dangling images") + print("dangling_quantity.draw AREASTACK") + print("dangling_quantity.info Dangling images can be deleted with " + "`docker image prune`" + "or tagged with `docker image tag `") + print("dangling_quantity.warning 10") else: - print('images_quantity.value', len(client.images.list())) + print_images_count(client) elif wildcard == "volumes": if mode == "config": print("graph_title Docker volumes") print("graph_vlabel volumes") print("graph_category virtualization") print("volumes_quantity.label Volumes") + print("volumes_quantity.draw AREASTACK") + print("volumes_quantity.info Unused volumes can be deleted with " + "`docker volume prune`") else: - print('volumes_quantity.value', len(client.volumes.list())) + print('volumes_quantity.value', len(client.volumes)) + print('volumes_quantity.extinfo', ', '.join(volume_summary(v) for v in client.volumes)) elif wildcard == "cpu": if mode == "config": graphlimit = str(os.cpu_count() * 100) @@ -201,8 +417,11 @@ def main(): print("graph_vlabel CPU usage (%)") print("graph_category virtualization") print("graph_info This graph shows docker container CPU usage.") - for container in client.containers.list(): + print("graph_total Total CPU usage") + for container in client.all_containers: print("{}.label {}".format(container.name, container.name)) + print("{}.draw AREASTACK".format(container.name)) + print("{}.info {}".format(container.name, container_attributes(container))) else: print_containers_cpu(client) elif wildcard == "memory": @@ -212,10 +431,36 @@ def main(): print("graph_vlabel Bytes") print("graph_category virtualization") print("graph_info This graph shows docker container memory usage.") - for container in client.containers.list(): + print("graph_total Total memory usage") + for container in client.all_containers: print("{}.label {}".format(container.name, container.name)) + print("{}.draw AREASTACK".format(container.name)) + print("{}.info {}".format(container.name, container_attributes(container))) else: print_containers_memory(client) + elif wildcard == "network": + if mode == "config": + print("graph_title Docker containers network usage") + print("graph_args --base 1024 -l 0") + print("graph_vlabel bits in (-) / out (+) per ${graph_period}") + print("graph_category virtualization") + print("graph_info This graph shows docker container network usage.") + print("graph_total Total network usage") + for container in client.all_containers: + print("{}_down.label {}_received".format(container.name, container.name)) + print("{}_down.type DERIVE".format(container.name)) + print("{}_down.min 0".format(container.name)) + print("{}_down.graph no".format(container.name)) + print("{}_down.cdef {}_down,8,*".format(container.name, container.name)) + print("{}_up.label {}".format(container.name, container.name)) + print("{}_up.draw LINESTACK1".format(container.name)) + print("{}_up.type DERIVE".format(container.name)) + print("{}_up.min 0".format(container.name)) + print("{}_up.negative {}_down".format(container.name, container.name)) + print("{}_up.cdef {}_up,8,*".format(container.name, container.name)) + print("{}_up.info {}".format(container.name, container_attributes(container))) + else: + print_containers_network(client) if __name__ == '__main__': From 72eeb4155f9b9f3f28c6fed8c4477acff836c93e Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 15:22:14 +0200 Subject: [PATCH 673/695] spelling: change "licenced" to "licensed" Even though "licenced" is proper (British English) spelling, "spellcheck" complains about it (probably being focused on American English). The change should just clean up the output of the spelling linter and should not be interpreted as a language preference. Sorry, (British) folks! --- plugins/oracle/oracle__connections | 2 +- plugins/oracle/oracle__database_hitratio | 2 +- plugins/oracle/oracle__locks | 2 +- plugins/oracle/oracle__tablespace_usage | 2 +- plugins/oracle/oracle_connections | 2 +- plugins/postgresql/pg__connections | 2 +- plugins/postgresql/postgres_block_read_ | 2 +- plugins/postgresql/postgres_space_ | 2 +- plugins/power/apcupsd_ww | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/oracle/oracle__connections b/plugins/oracle/oracle__connections index 9c2d362c..1f7a2b21 100755 --- a/plugins/oracle/oracle__connections +++ b/plugins/oracle/oracle__connections @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Plugin for monitor oracle connections. # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/oracle/oracle__database_hitratio b/plugins/oracle/oracle__database_hitratio index 6e3c8003..75a363d6 100755 --- a/plugins/oracle/oracle__database_hitratio +++ b/plugins/oracle/oracle__database_hitratio @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Plugin for monitor oracle database reads. # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/oracle/oracle__locks b/plugins/oracle/oracle__locks index c515dbce..5e4a5969 100755 --- a/plugins/oracle/oracle__locks +++ b/plugins/oracle/oracle__locks @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Plugin for monitor oracle locks # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/oracle/oracle__tablespace_usage b/plugins/oracle/oracle__tablespace_usage index 637a77c9..4a66712c 100755 --- a/plugins/oracle/oracle__tablespace_usage +++ b/plugins/oracle/oracle__tablespace_usage @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Plugin for monitor oracle database reads. # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/oracle/oracle_connections b/plugins/oracle/oracle_connections index 587b34f8..65996ca1 100755 --- a/plugins/oracle/oracle_connections +++ b/plugins/oracle/oracle_connections @@ -6,7 +6,7 @@ # Author: Kevin Kunkel (kunkel.kevin@gmail.com) on December 11, 2007 # (Based off the perl munin plugin by Joan Carles Soler) # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/postgresql/pg__connections b/plugins/postgresql/pg__connections index 2c9e2824..e19222af 100755 --- a/plugins/postgresql/pg__connections +++ b/plugins/postgresql/pg__connections @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # Plugin for monitor postgres connections without DBI. # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/postgresql/postgres_block_read_ b/plugins/postgresql/postgres_block_read_ index f8afa224..4f1fc6e9 100755 --- a/plugins/postgresql/postgres_block_read_ +++ b/plugins/postgresql/postgres_block_read_ @@ -16,7 +16,7 @@ # # Copyright Bjrn Ruberg 2006 # -# Licenced under GPL v2. +# Licensed under GPL v2. # # Usage: # diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index 7085c14f..a8e9156e 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -3,7 +3,7 @@ # (Temporary) source: http://munin.projects.linpro.no/ticket/63 # Written by Bjrn Ruberg (bjorn@linpro.no) 2006 # Rewritten by Moses Moore 2006-04-08 moc.iazom@sesom -# Licenced under GPL +# Licensed under GPL # Magic markers #%# family=auto diff --git a/plugins/power/apcupsd_ww b/plugins/power/apcupsd_ww index 0b7c1a6c..f144537f 100755 --- a/plugins/power/apcupsd_ww +++ b/plugins/power/apcupsd_ww @@ -5,7 +5,7 @@ # Version 1.3 # # Copyright (C) 2005-2008 Behan Webster -# Licenced under GPL 2.0 +# Licensed under GPL 2.0 # # Written by: Behan Webster # German translation by: Bianco Veigel From d1a69fe25b04ef4f3a0bc157caf3d0827761feb1 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 15:41:17 +0200 Subject: [PATCH 674/695] lint: ignore python's compiled files for codespell --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ea8a844f..fbdfd286 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,6 @@ lint: # TODO: add "--ignore-words .codespell.ignore-words" as soon as travis supports a newer # testing environment (containing codespell 0.11 or later). - find plugins/ -type f -not -name "*.png" -not -name "*.conf" -not -name "*.jar" -print0 \ + find plugins/ -type f -not -name "*.png" -not -name "*.conf" -not -name "*.jar" -not -name "*.pyc" -print0 \ | xargs -0 codespell \ --exclude-file .codespell.exclude From 36748826629f63d430e6bebe0e4c600369c2061b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 15:45:57 +0200 Subject: [PATCH 675/695] Fix spelling mistakes --- plugins/cpu/cpuutil | 2 +- plugins/jitsi/jitsi_videobridge | 2 +- plugins/mysql/mysql_audit | 6 +++--- plugins/netapp/snmp__netapp_cifs | 2 +- plugins/netapp/snmp__netapp_cpu | 2 +- plugins/network/brc_rssi | 2 +- plugins/network/netatalk | 2 +- plugins/network/speedtest-download-bandwidth | 10 +++++----- plugins/nginx/nginx_upstream_multi_ | 2 +- plugins/php/eaccelerator | 2 +- plugins/php/php_apc_ | 2 +- plugins/php/php_opcache | 2 +- plugins/postfix/postfix-queue-size | 2 +- plugins/postfix/postfix_mailqueue_ | 2 +- plugins/user/cpubyuser | 4 ++-- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/plugins/cpu/cpuutil b/plugins/cpu/cpuutil index 57421392..aaf1dc01 100755 --- a/plugins/cpu/cpuutil +++ b/plugins/cpu/cpuutil @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo "sys.info CPU time spent in running the kernel code" echo_func user user STACK - echo 'user.info CPU time spent by tunning application code' + echo 'user.info CPU time spent by tuning application code' echo_func wio iowait STACK echo 'wio.info CPU idle time in waiting IO (disk)' diff --git a/plugins/jitsi/jitsi_videobridge b/plugins/jitsi/jitsi_videobridge index 4c0d9056..51e434d5 100755 --- a/plugins/jitsi/jitsi_videobridge +++ b/plugins/jitsi/jitsi_videobridge @@ -63,7 +63,7 @@ case $1 in echo "multigraph jitsi_videobridge_sessions" echo "graph_title Current jitsi-videobridge sessions" echo "graph_info Current jitsi-videobridge sessions" - echo "graph_vlabel current sesssions" + echo "graph_vlabel current sessions" echo "graph_args --base 1000 -l 0" echo "jitsi_videochannels.label videochannels" echo "jitsi_videochannels.info current number of videochannels" diff --git a/plugins/mysql/mysql_audit b/plugins/mysql/mysql_audit index 14a3878a..fb68af82 100755 --- a/plugins/mysql/mysql_audit +++ b/plugins/mysql/mysql_audit @@ -128,9 +128,9 @@ def get_data(do_save=True): if ',CONNECT,' not in line: continue - splitted = line.split(',') - key = splitted[2] - date = datetime.strptime(splitted[0], '%Y%m%d %H:%M:%S') + split = line.split(',') + key = split[2] + date = datetime.strptime(split[0], '%Y%m%d %H:%M:%S') # hack to add timezone data to the datetime date = begin + (date - begin_local) diff --git a/plugins/netapp/snmp__netapp_cifs b/plugins/netapp/snmp__netapp_cifs index 0e4b8f6c..23acc2c7 100755 --- a/plugins/netapp/snmp__netapp_cifs +++ b/plugins/netapp/snmp__netapp_cifs @@ -8,7 +8,7 @@ # NFS automounted mounted on server. # Example: the SNMP id for cpuBusyTimePerCent is # snmp.1.3.6.1.4.1.789.1.2.1.3.0 -# and retrival of this value is done by +# and retrieval of this value is done by # snmpget -v 1 -c public netappfiler 1.3.6.1.4.1.789.1.2.1.3.0 # # Requires snmpget and assumes public community. diff --git a/plugins/netapp/snmp__netapp_cpu b/plugins/netapp/snmp__netapp_cpu index 5aef035c..95350e55 100755 --- a/plugins/netapp/snmp__netapp_cpu +++ b/plugins/netapp/snmp__netapp_cpu @@ -8,7 +8,7 @@ # NFS automounted mounted on server. # Example: the SNMP id for cpuBusyTimePerCent is # snmp.1.3.6.1.4.1.789.1.2.1.3.0 -# and retrival of this value is done by +# and retrieval of this value is done by # snmpget -v 1 -c public netappfiler 1.3.6.1.4.1.789.1.2.1.3.0 # # Requires snmpget and assumes public community. diff --git a/plugins/network/brc_rssi b/plugins/network/brc_rssi index 146bdc87..ae5a886c 100755 --- a/plugins/network/brc_rssi +++ b/plugins/network/brc_rssi @@ -46,7 +46,7 @@ graph_vlabel dB(?) graph_category network graph_info This plugin shows the RSSI (Received Signal Strength Indication) as reported by the Access Point (AP) driver. The plugin is specific to broadcom wireless chipsets such as used on WRT hardware. We're not quite sure about the units the RSSI is measured in as this is not documented. Both dB and dBm are apparently candidates. Higher is better. EOF - # Atempt to find default. "Set default" and "assign default" syntax is + # Attempt to find default. "Set default" and "assign default" syntax is # not available in busybox (which is used in openwrt firmware) it seems. # So work around it with case. : ${WIFISIDE:=$(nvram get lan_ifname 2>/dev/null)} diff --git a/plugins/network/netatalk b/plugins/network/netatalk index 58c53c68..86b59da7 100755 --- a/plugins/network/netatalk +++ b/plugins/network/netatalk @@ -124,7 +124,7 @@ fi echo "proc.value" $(ps ax --no-headers -o command | grep "^$afpdPath" | wc -l) # Connected users (user): -# We will ignore root (having UID=0 it's line will be first) (assomption done: there will have only one line corresponding to root in `ps` output) +# We will ignore root (having UID=0 it's line will be first) (assumption done: there will have only one line corresponding to root in `ps` output) connectedUsers=$(ps anx --no-headers -o uid,command | sed 's/^ *//g' | grep "^[0-9]* $afpdPath" | sort -n | tail -n +2 | awk '{print $1}') echo "user.value" `echo $connectedUsers | wc -w` diff --git a/plugins/network/speedtest-download-bandwidth b/plugins/network/speedtest-download-bandwidth index 7f930b1c..545ece40 100755 --- a/plugins/network/speedtest-download-bandwidth +++ b/plugins/network/speedtest-download-bandwidth @@ -21,8 +21,8 @@ $labels = array( "free" => "test-debit.free.fr" ); -//Connexion Mbits (30/100) -$connexion = 35; +//Connection Mbits (30/100) +$connection = 35; // CONFIG ------------------------------------------------------------------ if ($argc > 1 && $argv[1]=='config'){ @@ -33,10 +33,10 @@ graph_args --base 1024 graph_vlabel DL (MB/s) grenouille.label Grenouille (NC) grenouille.type GAUGE -maximum.label Connexion (max) +maximum.label Connection (max) maximum.type GAUGE maximum.colour ff0000 -maximum.max ".$connexion."00000 +maximum.max ".$connection."00000 "; $order=""; @@ -76,7 +76,7 @@ foreach ($mire as $label => $url) { $output .= "$label.value ".round($cache[$label])."\n"; } -$output .= "maximum.value ".round($connexion * 1024 * 1024 / 10)."\n"; +$output .= "maximum.value ".round($connection * 1024 * 1024 / 10)."\n"; echo $output; // SPEED TEST -------------------------------------------------------------- diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index a1ee02f0..455f3594 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -75,7 +75,7 @@ httpStatusString = ( "400:Bad request;401:Unauthorized;402:Payment required;403:Forbidden;404:Not found;" "405:Method not allowed;406:Not acceptable;407:Proxy Authentication Required;" "408:Request timeout;409:Conflict;410:Gone;411:Length required;412:Precondition failed;" - "413:Request entity too large;414:Request URI too large;415:Usupported media type;" + "413:Request entity too large;414:Request URI too large;415:Unsupported media type;" "416:Request range not satisfiable;417:Expectation failed;422:Unprocessable entity;" "423:Locked;424:Failed dependency;425:Unordered collection;426:Upgrade required;" "449:Retry with;456:Unrecoverable error;500:Internal server error;501:Not implemented;" diff --git a/plugins/php/eaccelerator b/plugins/php/eaccelerator index 40526816..d7885118 100755 --- a/plugins/php/eaccelerator +++ b/plugins/php/eaccelerator @@ -62,7 +62,7 @@ WGET_FLAGS="-Yoff"; # refer to wget manual, you may set extra parameters like di ################################################################# ################################################################# -# Settigs required for autoconf +# Settings required for autoconf #%# family=manual #%# capabilities=autoconf diff --git a/plugins/php/php_apc_ b/plugins/php/php_apc_ index bbf152e3..d9be9ce8 100755 --- a/plugins/php/php_apc_ +++ b/plugins/php/php_apc_ @@ -26,7 +26,7 @@ # ################################################################# ################################################################# -# Settigs required for autoconf +# Settings required for autoconf #%# family=auto #%# capabilities=autoconf suggest diff --git a/plugins/php/php_opcache b/plugins/php/php_opcache index 89d18b4a..da27a69c 100755 --- a/plugins/php/php_opcache +++ b/plugins/php/php_opcache @@ -10,7 +10,7 @@ # [php_opcache] # env.URL http://example/php_opcache.php ############################################################################### -# Settigs required for autoconf +# Settings required for autoconf #%# family=auto #%# capabilities=autoconf suggest diff --git a/plugins/postfix/postfix-queue-size b/plugins/postfix/postfix-queue-size index 5f9db598..df5092fe 100755 --- a/plugins/postfix/postfix-queue-size +++ b/plugins/postfix/postfix-queue-size @@ -81,7 +81,7 @@ munin-node. =cut -# atempt to get spooldir via postconf, but environment overrides. +# attempt to get spooldir via postconf, but environment overrides. # Remember that postconf is not available unless postfix is. POSTCONFSPOOL="$(postconf -h queue_directory 2>/dev/null || echo /var/spool/postfix)" diff --git a/plugins/postfix/postfix_mailqueue_ b/plugins/postfix/postfix_mailqueue_ index c2d4cb7b..abd110f6 100755 --- a/plugins/postfix/postfix_mailqueue_ +++ b/plugins/postfix/postfix_mailqueue_ @@ -82,7 +82,7 @@ munin-node. =cut -# atempt to get spooldir via postconf, but environment overrides. +# attempt to get spooldir via postconf, but environment overrides. # Remember that postconf is not available unless postfix is. CONFIG=${0##*postfix_mailqueue_} diff --git a/plugins/user/cpubyuser b/plugins/user/cpubyuser index d961023d..2b383b82 100755 --- a/plugins/user/cpubyuser +++ b/plugins/user/cpubyuser @@ -18,8 +18,8 @@ # # 2019-08-30 v 1.4 pcy : # - add USERS=ALLPROC, not relying on a tty or pty being present for user detection -# - OTHERS_FIELD now defaults to disabled, explicitely give it a -# value to reenable it (eg. 'others') +# - OTHERS_FIELD now defaults to disabled, explicitly give it a +# value to re-enable it (eg. 'others') # - use ps(1) instead of top(1) for easier and more robust # parsing/summary calculation # From 3bc93969188636157097c28ec1d78a6bb4e8cb28 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 15:53:00 +0200 Subject: [PATCH 676/695] Unify file encodings to UTF8 --- plugins/apache/apache_watch_ | 2 +- plugins/cpu/cpuload_ | 2 +- plugins/nagios/nagiosstatus | 2 +- plugins/network/ldap_connections | 2 +- plugins/network/zenus_ | 4 ++-- plugins/nginx/nginx-combined | 6 +++--- plugins/ntp/ntpdate_ | 2 +- plugins/postgresql/postgres_block_read_ | 2 +- plugins/postgresql/postgres_space_ | 2 +- plugins/qmail/qmailsend | 2 +- plugins/qmail/qmailsend_plesk | 2 +- plugins/quake/qstatcod4and5_ | 2 +- plugins/security/forefront_ | 2 +- plugins/sensors/mbmon | 2 +- plugins/spotweb/spotweb_cat_average | 2 +- plugins/spotweb/spotweb_cat_total | 2 +- plugins/spotweb/spotweb_total | 2 +- plugins/teamspeak/teamspeak_user | 2 +- plugins/tomcat/tomcat_access | 2 +- plugins/varnish/varnish2_ | 2 +- plugins/vmware/esxi__sensors | 2 +- 21 files changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/apache/apache_watch_ b/plugins/apache/apache_watch_ index ae74ca55..f2a861c3 100755 --- a/plugins/apache/apache_watch_ +++ b/plugins/apache/apache_watch_ @@ -12,7 +12,7 @@ # Must be symlinked to what the graph should monitor. Run with --suggest # to see valid targets - or just run munin-node-configure --shell # -# Written by Bjrn Ruberg 2006-2007 +# Written by Bjørn Ruberg 2006-2007 # # Magic markers: #%# family=auto diff --git a/plugins/cpu/cpuload_ b/plugins/cpu/cpuload_ index 347a7a79..21af7e1e 100755 --- a/plugins/cpu/cpuload_ +++ b/plugins/cpu/cpuload_ @@ -8,7 +8,7 @@ # [cpuload_postfix] # env.process master,qmgmr,proxymap,pickup # -# GNU GPL, Bjrn Ruberg +# GNU GPL, Bjørn Ruberg # Find process names from env variables for the given plugin # If not, extract from filename. diff --git a/plugins/nagios/nagiosstatus b/plugins/nagios/nagiosstatus index aa5fc49e..8222e380 100755 --- a/plugins/nagios/nagiosstatus +++ b/plugins/nagios/nagiosstatus @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copyright (C) 2008 Rune Nordbe Skillingstad +# Copyright (C) 2008 Rune Nordbøe Skillingstad # # Plugin to monitor status in Nagios # diff --git a/plugins/network/ldap_connections b/plugins/network/ldap_connections index 8f80d437..68167959 100755 --- a/plugins/network/ldap_connections +++ b/plugins/network/ldap_connections @@ -13,7 +13,7 @@ # Modif pour OpenBSD # # Revision 1.6 2007/03/02 07:52:52 cvserver -# pas LISTEN pour les IPs utilises (en cas de *:389) +# pas LISTEN pour les IPs utilisées (en cas de *:389) # # Revision 1.5 2007/03/01 16:06:53 cvserver # corrections: diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 9d8cb76f..561a7e93 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -126,7 +126,7 @@ sub save_data { # Push the hash values on to the array foreach ( keys %$hashref ) { - push @save_vector, $_ . '' . $hashref->{$_}; + push @save_vector, $_ . '¬' . $hashref->{$_}; } $::logger->info( @@ -148,7 +148,7 @@ sub load_data { my $hashref; foreach (@save_vector) { - my ( $key, $value ) = split //; + my ( $key, $value ) = split /¬/; $hashref->{$key} = $value; } my $force_save = 0; diff --git a/plugins/nginx/nginx-combined b/plugins/nginx/nginx-combined index e5ba25b1..c7e52085 100755 --- a/plugins/nginx/nginx-combined +++ b/plugins/nginx/nginx-combined @@ -5,12 +5,12 @@ #%# capabilities=autoconf # nginx_combine_ --- Determine the current status of Nginx # using the http_stub_status module. -# extend of nginx_status_ plugin of Antnio P. P. Almeida +# extend of nginx_status_ plugin of António P. P. Almeida -# Copyright (C) 2010 Antnio P. P. Almeida +# Copyright (C) 2010 António P. P. Almeida # Copyright (C) 2010 Minato Miray -# Author: Antnio P. P. Almeida , +# Author: António P. P. Almeida , # Author: Minato Miray ####################################### diff --git a/plugins/ntp/ntpdate_ b/plugins/ntp/ntpdate_ index bb62d5bd..b5f4ae29 100755 --- a/plugins/ntp/ntpdate_ +++ b/plugins/ntp/ntpdate_ @@ -6,7 +6,7 @@ # # config (required) # -# Author: Rune Nordbe Skillingstad +# Author: Rune Nordbøe Skillingstad # # Magic markers - optional - used by installation scripts and # munin-node-configure: diff --git a/plugins/postgresql/postgres_block_read_ b/plugins/postgresql/postgres_block_read_ index 4f1fc6e9..4f601e05 100755 --- a/plugins/postgresql/postgres_block_read_ +++ b/plugins/postgresql/postgres_block_read_ @@ -14,7 +14,7 @@ # http://www.postgresql.org/docs/7.4/interactive/monitoring-stats.html # for a (short) description. # -# Copyright Bjrn Ruberg 2006 +# Copyright Bjørn Ruberg 2006 # # Licensed under GPL v2. # diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index a8e9156e..46cd2b5f 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -1,7 +1,7 @@ #!/usr/bin/perl # (Temporary) source: http://munin.projects.linpro.no/ticket/63 -# Written by Bjrn Ruberg (bjorn@linpro.no) 2006 +# Written by Bjørn Ruberg (bjorn@linpro.no) 2006 # Rewritten by Moses Moore 2006-04-08 moc.iazom@sesom # Licensed under GPL diff --git a/plugins/qmail/qmailsend b/plugins/qmail/qmailsend index 107f7f7f..01281639 100755 --- a/plugins/qmail/qmailsend +++ b/plugins/qmail/qmailsend @@ -2,7 +2,7 @@ # # Plugin to show amount of individual outgoing smtp-replies per hour # -# Contributed by Hkon Nessjen +# Contributed by Håkon Nessjøen # # Magic markers - optional - used by installation scripts and # munin-config: diff --git a/plugins/qmail/qmailsend_plesk b/plugins/qmail/qmailsend_plesk index 585c020b..34e94259 100755 --- a/plugins/qmail/qmailsend_plesk +++ b/plugins/qmail/qmailsend_plesk @@ -2,7 +2,7 @@ # # Plugin to show amount of individual outgoing smtp-replies per hour # -# Contributed by Hkon Nessjen +# Contributed by Håkon Nessjøen # # Magic markers - optional - used by installation scripts and # munin-config: diff --git a/plugins/quake/qstatcod4and5_ b/plugins/quake/qstatcod4and5_ index f1fd36d1..e02dbd55 100755 --- a/plugins/quake/qstatcod4and5_ +++ b/plugins/quake/qstatcod4and5_ @@ -5,7 +5,7 @@ # Email : poil@quake.fr # # First release : 18/10/2007 # #---------------------------------------------------------------# -# Edited: Rouven David Nal - peperoni # +# Edited: Rouven David Naßl - peperoni # # Edit : 09/01/2009 # # Plugin edited for COD4+COD5 # # Email: peperoni@sac-esports.de # diff --git a/plugins/security/forefront_ b/plugins/security/forefront_ index e1400eb5..47ce449d 100755 --- a/plugins/security/forefront_ +++ b/plugins/security/forefront_ @@ -2,7 +2,7 @@ # # Plugin to monitor Forefront Client Security status in MOM database # -# Copyright (c) 2008 Rune Nordbe Skillingstad - +# Copyright (c) 2008 Rune Nordbøe Skillingstad - # # 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 diff --git a/plugins/sensors/mbmon b/plugins/sensors/mbmon index 345dd98c..d19835d9 100755 --- a/plugins/sensors/mbmon +++ b/plugins/sensors/mbmon @@ -29,7 +29,7 @@ graph_title CPU temperature graph_order temp0 temp1 temp2 graph_args --base 1000 -l 0 graph_category sensors -graph_vlabel temp in C +graph_vlabel temp in °C temp0.label Temperature0 temp1.label Temperature1 temp2.label Temperature2 diff --git a/plugins/spotweb/spotweb_cat_average b/plugins/spotweb/spotweb_cat_average index 4349151e..1022d21a 100755 --- a/plugins/spotweb/spotweb_cat_average +++ b/plugins/spotweb/spotweb_cat_average @@ -2,7 +2,7 @@ # # Munin plugin for number of spots by categories in a MySQL database # -# Copyright (C) 2011 - Rowdy Schwachfer (http://rowdy.nl) +# Copyright (C) 2011 - Rowdy Schwachöfer (http://rowdy.nl) # # Spotweb : http://github.com/spotweb/spotweb # diff --git a/plugins/spotweb/spotweb_cat_total b/plugins/spotweb/spotweb_cat_total index ed09e79c..0900de81 100755 --- a/plugins/spotweb/spotweb_cat_total +++ b/plugins/spotweb/spotweb_cat_total @@ -2,7 +2,7 @@ # # Munin plugin for total spot by categories in a spotweb MySQL database # -# Copyright (C) 2011 - Rowdy Schwachfer (http://rowdy.nl) +# Copyright (C) 2011 - Rowdy Schwachöfer (http://rowdy.nl) # # Spotweb : http://github.com/spotweb/spotweb # diff --git a/plugins/spotweb/spotweb_total b/plugins/spotweb/spotweb_total index c61a7321..4ebdb6f9 100755 --- a/plugins/spotweb/spotweb_total +++ b/plugins/spotweb/spotweb_total @@ -2,7 +2,7 @@ # # Munin plugin for different stats over a spotweb MySQL database # -# Copyright (C) 2011 - Rowdy Schwachfer (http://rowdy.nl) +# Copyright (C) 2011 - Rowdy Schwachöfer (http://rowdy.nl) # # Spotweb : http://github.com/spotweb/spotweb # Original idea : smeerbartje (http://gathering.tweakers.net/forum/myreact/190949) diff --git a/plugins/teamspeak/teamspeak_user b/plugins/teamspeak/teamspeak_user index 14b1f5fd..beba06ea 100755 --- a/plugins/teamspeak/teamspeak_user +++ b/plugins/teamspeak/teamspeak_user @@ -28,7 +28,7 @@ my $hostname = "localhost"; # serveraddress my $port = 10011; # querryport (default: 10011) my @serverids = (1); # array of virtualserverids (1,2,3,4,...) -my $username = ""; # only set if the default queryuser hasnt enough rights (should work without this) +my $username = ""; # only set if the default queryuser hasn't enough rights (should work without this) my $password = ""; # SCRIPT START! diff --git a/plugins/tomcat/tomcat_access b/plugins/tomcat/tomcat_access index 0adb5331..cce015a4 100755 --- a/plugins/tomcat/tomcat_access +++ b/plugins/tomcat/tomcat_access @@ -40,7 +40,7 @@ tomcat-users.xml example: =head1 AUTHOR Ricardo Fraile -Rune Nordbe Skillingstad +Rune Nordbøe Skillingstad =head1 LICENSE diff --git a/plugins/varnish/varnish2_ b/plugins/varnish/varnish2_ index cb3a6b42..10d20a3c 100755 --- a/plugins/varnish/varnish2_ +++ b/plugins/varnish/varnish2_ @@ -1,6 +1,6 @@ #!/usr/bin/perl -# Original author: Bjrn Ruberg +# Original author: Bjørn Ruberg # Updated to Varnish 2.0/added instructions: Daniel Wirtz # # Installation (Debian): diff --git a/plugins/vmware/esxi__sensors b/plugins/vmware/esxi__sensors index 6cf1ce69..63563b1c 100755 --- a/plugins/vmware/esxi__sensors +++ b/plugins/vmware/esxi__sensors @@ -74,7 +74,7 @@ import pywbem NS = 'root/cimv2' sensor_data = { - 2 : {'prefix':'temp', 'title':'Temperatures', 'unit':'C'}, + 2 : {'prefix':'temp', 'title':'Temperatures', 'unit':'°C'}, 3 : {'prefix':'volt', 'title':'Voltages', 'unit':'Volts'}, 4 : {'prefix':'amp', 'title':'Current', 'unit':'Amps'}, 5 : {'prefix':'fan', 'title':'Fans', 'unit':'RPM'} From 40827a051a2af7c4fdc829c6ff76971b7c37af34 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 16:58:50 +0200 Subject: [PATCH 677/695] Python plugins: fix style issues --- plugins/prosody/prosody_ | 2 +- plugins/router/arris-tg3442 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 9f7c1821..4a3fab53 100755 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -105,7 +105,7 @@ def main(): sys.exit(0) else: - client_presence_re = re.compile("[\]] (.*?)\(\d+\)") + client_presence_re = re.compile(r"[\]] (.*?)\(\d+\)") telnet = telnetlib.Telnet(host, port) telnet.write(b"c2s:show()\n") telnet_response = telnet.read_until(b"clients", 5) diff --git a/plugins/router/arris-tg3442 b/plugins/router/arris-tg3442 index a232ef29..604807e1 100755 --- a/plugins/router/arris-tg3442 +++ b/plugins/router/arris-tg3442 @@ -243,10 +243,10 @@ if __name__ == "__main__": # graph config print(f"multigraph docsis_{g['name']}") print(f"graph_title {g['title']}") - print(f"graph_category network") + print("graph_category network") print(f"graph_vlabel {g['vlabel']}") print(f"graph_info {g['info']}") - print(f"graph_scale no") + print("graph_scale no") # channels for c in g['data']: From 26c29daa2bd060d925ed2dc1d2c4727e0b299030 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 17:38:56 +0200 Subject: [PATCH 678/695] t/test-exception-wrapper: fix documentation --- t/test-exception-wrapper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/test-exception-wrapper b/t/test-exception-wrapper index e667542e..522f1a89 100755 --- a/t/test-exception-wrapper +++ b/t/test-exception-wrapper @@ -3,7 +3,7 @@ # Run a check for a given file. A failure is tolerated (and expected), if the filename is listed # in a file containing expected failures. # -# Parameters: SHELL_DIALECT SCRIPT_FILENAME +# Parameters: SCRIPT_FILENAME TEST_COMMAND [TEST_COMMAND_ARGUMENTS] # # See EXPECTED_FAILURES_LIST_FILENAME for the filename pattern of a file, containing the relative # names of all scripts, that are expected to fail. This wrapper script will fail, if the exit From b0b39b018e200cd0b771675fd0a6489f6ee9f045 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 16:52:39 +0200 Subject: [PATCH 679/695] Ruby plugins: apply style changes as suggested by "rubocop --fix-layout" --- plugins/bsd/netstat_bsd_m_ | 145 ++-- plugins/ejabberd/ejabberd_scanlog | 211 +++--- plugins/http/mongrel_memory | 66 +- plugins/http/mongrel_process_memory | 57 +- plugins/icecast/icecast2_simple | 10 +- plugins/minecraft/minecraft-users | 12 +- plugins/moblock/moblock_connections | 59 +- plugins/mssql/microsoft-sql | 147 ++-- .../microsoft-sql-buffer-cache-hit-ratio | 129 ++-- plugins/mssql/microsoft-sql-data-file-sizes | 145 ++-- plugins/mssql/microsoft-sql-log-file-size | 145 ++-- plugins/network/http__tp_link | 2 +- plugins/network/netstat_s_ | 674 +++++++++--------- plugins/oracle/oracle-pga-monitor | 159 +++-- plugins/oracle/oracle-sga | 174 ++--- plugins/other/delayed_jobs_queue_size | 14 +- plugins/other/port_ | 76 +- plugins/passenger/passenger_memory | 2 +- plugins/passenger/passenger_processes | 1 - plugins/passenger/passenger_status | 42 +- plugins/php/php_eaccelerator | 52 +- plugins/router/ag241-adsl | 171 ++--- .../d-link-dir-655-router-statistics-plugin | 25 +- plugins/snmp/snmp_room_alert_ | 3 +- plugins/ssh/sshd_invalid_countries_ruby | 72 +- plugins/thin/thin_memory | 78 +- plugins/thin/thin_threads | 78 +- plugins/thin/thins_peak_memory | 74 +- plugins/unicorn/unicorn_memory_status | 5 +- plugins/unicorn/unicorn_status | 3 +- 30 files changed, 1447 insertions(+), 1384 deletions(-) diff --git a/plugins/bsd/netstat_bsd_m_ b/plugins/bsd/netstat_bsd_m_ index 082c4831..82b79fa1 100755 --- a/plugins/bsd/netstat_bsd_m_ +++ b/plugins/bsd/netstat_bsd_m_ @@ -1,48 +1,53 @@ #!/usr/bin/env ruby -# netstat_bsd_m revision 1 (Feb 2012) -# -# This plugin shows various statistics from 'netstat -m' -# -# Required privileges: none -# -# OS: -# Supposed: BSD -# Tested: FreeBSD 8.2 -# -# Author: Artem Sheremet -# +=begin + +netstat_bsd_m revision 1 (Feb 2012) + +This plugin shows various statistics from 'netstat -m' + +Required privileges: none + +OS: + Supposed: BSD + Tested: FreeBSD 8.2 + +Author: Artem Sheremet + #%# family=auto #%# capabilities=autoconf suggest +=end + + # original filename PLUGIN_NAME = 'netstat_bsd_m_' class String - def escape - self.gsub /[^\w]/, '_' - end + def escape + self.gsub /[^\w]/, '_' + end - unless method_defined? :start_with? - def start_with?(str) - self[0...str.size] == str - end - end + unless method_defined? :start_with? + def start_with?(str) + self[0...str.size] == str + end + end end def netstat_m(filter = nil) - Hash[`netstat -m`.split($/).map { |line| - if line =~ /^([\d\/K]+) (.*) \(([\w\/+]+)\)$/ - # 7891K/22385K/30276K bytes allocated to network (current/cache/total) - values, desc, names = $1, $2, $3 - [desc, names.split('/').zip(values.split '/')] if filter.nil? or desc.escape == filter - elsif line =~ /^(\d+) (.*)$/ - # 12327 requests for I/O initiated by sendfile - value, desc = $1, $2 - [desc, [[ :value, value ]]] if filter.nil? or desc.escape == filter - end - }.compact] + Hash[`netstat -m`.split($/).map { |line| + if line =~ /^([\d\/K]+) (.*) \(([\w\/+]+)\)$/ + # 7891K/22385K/30276K bytes allocated to network (current/cache/total) + values, desc, names = $1, $2, $3 + [desc, names.split('/').zip(values.split '/')] if filter.nil? or desc.escape == filter + elsif line =~ /^(\d+) (.*)$/ + # 12327 requests for I/O initiated by sendfile + value, desc = $1, $2 + [desc, [[:value, value]]] if filter.nil? or desc.escape == filter + end + }.compact] end stat_name = File.basename($0, '.*').escape @@ -50,47 +55,47 @@ stat_name.slice! 0, PLUGIN_NAME.size if stat_name.start_with? PLUGIN_NAME case ARGV.first when 'autoconf' - puts `uname -s`.include?('FreeBSD') ? 'yes' : 'no' + puts `uname -s`.include?('FreeBSD') ? 'yes' : 'no' when 'suggest' - puts netstat_m.keys.map(&:escape).join $/ + puts netstat_m.keys.map(&:escape).join $/ when 'config' - data = netstat_m(stat_name) - if data.empty? - warn "no data for <#{stat_name}>. Try running with 'suggest'" - else - desc, values = data.first - stack = values.size > 1 - first = true - puts <. Try running with 'suggest'" + else + desc, values = data.first + stack = values.size > 1 + first = true + puts <<~CONFIG + graph_title Netstat: #{desc} + graph_category network + graph_vlabel current + graph_order #{values.map { |name, _| name.to_s.escape }.join ' '} + CONFIG + puts values.map { |name, _| + esc_name = name.to_s.escape + "#{esc_name}.draw " + if %w(total max).include? name + 'LINE' + elsif stack + if first + first = false + 'AREA' + else + 'STACK' + end + else + 'LINE2' + end + "\n#{esc_name}.label #{name}" + }.join $/ + end when nil # fetch - data = netstat_m(stat_name) - unless data.empty? - puts data.first.last.map { |name, value| - value = value.to_i * 1024 if value.end_with? 'K' - "#{name.to_s.escape}.value #{value}" - }.join $/ - end + data = netstat_m(stat_name) + unless data.empty? + puts data.first.last.map { |name, value| + value = value.to_i * 1024 if value.end_with? 'K' + "#{name.to_s.escape}.value #{value}" + }.join $/ + end else - warn "unrecognized argument <#{ARGV.first}>" + warn "unrecognized argument <#{ARGV.first}>" end diff --git a/plugins/ejabberd/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog index 10dd1a5e..c46f6869 100755 --- a/plugins/ejabberd/ejabberd_scanlog +++ b/plugins/ejabberd/ejabberd_scanlog @@ -28,137 +28,138 @@ DEFAULT_CACHE = { :start => 0 } $debug_mode = ARGV.first == 'debug' if $debug_mode - log_info = DEFAULT_CACHE + log_info = DEFAULT_CACHE else - begin - log_info = YAML.load IO.read(CACHE_FILE) - rescue - log_info = DEFAULT_CACHE - end + begin + log_info = YAML.load IO.read(CACHE_FILE) + rescue + log_info = DEFAULT_CACHE + end - if File.size(LOG_FILE) < log_info[:start] - # logrotate? - log_info = DEFAULT_CACHE - end + if File.size(LOG_FILE) < log_info[:start] + # logrotate? + log_info = DEFAULT_CACHE + end end if ARGV.first == 'reset' - log_info = { :start => File.size(LOG_FILE)-1 } - puts 'Log reset' + log_info = { :start => File.size(LOG_FILE) - 1 } + puts 'Log reset' end new_data = '' File.open(LOG_FILE, 'rb') do |flog| - flog.seek(log_info[:start]) - new_data = flog.read + flog.seek(log_info[:start]) + new_data = flog.read end KNOWN_LOG_TYPES = [ - # each element is an instance of Array. 1st item: error description, others: text to search log for - ['EJAB-1482 Crash when waiting for item', - ['wait_for_']], - ['EJAB-1483 ODBC sup failure (wrong PID?)', - ['ejabberd_odbc_sup']], - ['EJAB-1483 ODBC sup wrong PID failure echo', - ["mod_pubsub_odbc,'-unsubscribe"]], - ['DNS failure', - ['You should check your DNS configuration']], - ['Database unavailable/too slow', - ['Database was not available or too slow']], - ['State machine terminated: timeout', - ['State machine', - 'terminating', - 'Reason for', - 'timeout']], - ['The auth module returned an error', - ['The authentication module', - 'returned an error']], - ['MySQL disconnected', - ['mysql', - 'Received unknown signal, exiting']], - ['Connecting to MySQL: failed', - ['mysql', - 'Failed connecting to']], - ['Timeout while running a hook', - ['ejabberd_hooks', - 'timeout']], - ['SQL transaction restarts exceeded', - ['SQL transaction restarts exceeded']], - ['Unexpected info', - ['nexpected info']], - ['Other sql_cmd timeout', - ['sql_cmd']], - ['System limit hit: ports', # check with length(erlang:ports())., set in ejabberdctl config file - ['system_limit', - 'open_port']], - ['Other system limit hit', # processes? check with erlang:system_info(process_count)., erlang:system_info(process_limit)., set in ejabberdctl cfg - ['system_limit']], - ['Generic server terminating', - ['Generic server', - 'terminating']], - ['Mnesia table shrinked', - ['shrinking table']], - ['Admin access failed', - ['Access of', - 'failed with error']], - ['MySQL sock timedout', - ['mysql_', - ': Socket', - 'timedout']], - ['Configuration error', - ['{badrecord,config}']], - ['Strange vCard error (vhost)', - ['error found when trying to get the vCard']], - ['Mnesia is overloaded', - ['Mnesia is overloaded']], - ['MySQL: init failed recv data', - ['mysql_conn: init failed receiving data']], - ['TCP Error', - ['Failed TCP']] + # each element is an instance of Array. 1st item: error description, others: text to search log for + ['EJAB-1482 Crash when waiting for item', + ['wait_for_']], + ['EJAB-1483 ODBC sup failure (wrong PID?)', + ['ejabberd_odbc_sup']], + ['EJAB-1483 ODBC sup wrong PID failure echo', + ["mod_pubsub_odbc,'-unsubscribe"]], + ['DNS failure', + ['You should check your DNS configuration']], + ['Database unavailable/too slow', + ['Database was not available or too slow']], + ['State machine terminated: timeout', + ['State machine', + 'terminating', + 'Reason for', + 'timeout']], + ['The auth module returned an error', + ['The authentication module', + 'returned an error']], + ['MySQL disconnected', + ['mysql', + 'Received unknown signal, exiting']], + ['Connecting to MySQL: failed', + ['mysql', + 'Failed connecting to']], + ['Timeout while running a hook', + ['ejabberd_hooks', + 'timeout']], + ['SQL transaction restarts exceeded', + ['SQL transaction restarts exceeded']], + ['Unexpected info', + ['nexpected info']], + ['Other sql_cmd timeout', + ['sql_cmd']], + ['System limit hit: ports', # check with length(erlang:ports())., set in ejabberdctl config file + ['system_limit', + 'open_port']], + ['Other system limit hit', # processes? check with erlang:system_info(process_count)., erlang:system_info(process_limit)., set in ejabberdctl cfg + ['system_limit']], + ['Generic server terminating', + ['Generic server', + 'terminating']], + ['Mnesia table shrinked', + ['shrinking table']], + ['Admin access failed', + ['Access of', + 'failed with error']], + ['MySQL sock timedout', + ['mysql_', + ': Socket', + 'timedout']], + ['Configuration error', + ['{badrecord,config}']], + ['Strange vCard error (vhost)', + ['error found when trying to get the vCard']], + ['Mnesia is overloaded', + ['Mnesia is overloaded']], + ['MySQL: init failed recv data', + ['mysql_conn: init failed receiving data']], + ['TCP Error', + ['Failed TCP']] ] def log_type(text) - KNOWN_LOG_TYPES.find_index { |entry| - entry[1].all? { |substr| text.include? substr } - } + KNOWN_LOG_TYPES.find_index { |entry| + entry[1].all? { |substr| text.include? substr } + } end new_data.split("\n=").each { |report| - next if report.empty? - report =~ /\A(\w+) REPORT==== (.*) ===\n(.*)\z/m - type, time, text = $1, $2, $3 - next unless type and time and text + next if report.empty? - log_info[type] = (log_info[type] || 0) + 1 - if sub_type = log_type(text) - log_info[sub_type] = (log_info[sub_type] || 0) + 1 - elsif $debug_mode - warn "Unparsed log entry #{type}: #{text} at #{time}" - end + report =~ /\A(\w+) REPORT==== (.*) ===\n(.*)\z/m + type, time, text = $1, $2, $3 + next unless type and time and text + + log_info[type] = (log_info[type] || 0) + 1 + if sub_type = log_type(text) + log_info[sub_type] = (log_info[sub_type] || 0) + 1 + elsif $debug_mode + warn "Unparsed log entry #{type}: #{text} at #{time}" + end } log_info[:start] += new_data.size File.open(CACHE_FILE, 'w') { |f| f.write log_info.to_yaml } unless $debug_mode if ARGV.first == 'config' - puts <) -# Ryan Woodrum -# Matthias Marschall (mm@agileweboperations.com) -# + +=begin + +mongrel_memory - A munin plugin for OpenSolaris to monitor memory size of + each individual mongrel process +Copyright (C) 2009 Matthias Marschall - mm@agileweboperations.com + +Based on: +mongrel_process_memory - A munin plugin to monitor memory size of + each individual mongrel process +Copyright (C) 2007 Ben VandenBos and Avvo, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Author: Ben VandenBos +Contributors: Adam Jacob () + Ryan Woodrum + Matthias Marschall (mm@agileweboperations.com) + #%# family=auto #%# capabilities=autoconf +=end + + module Munin class MongrelProcessMemory - def run pid_port_map = get_pids() port_list = Hash.new @@ -48,8 +53,8 @@ module Munin pids += `pgrep ruby`.split("\n") pids.each { |pid| l = `pargs -l #{pid}` - l =~ /-p (\d+)/ - h[pid] = $1 if $1 + l =~ /-p (\d+)/ + h[pid] = $1 if $1 } h end @@ -57,7 +62,6 @@ module Munin def autoconf get_pids().length > 0 end - end end diff --git a/plugins/http/mongrel_process_memory b/plugins/http/mongrel_process_memory index 958679ea..fae85415 100755 --- a/plugins/http/mongrel_process_memory +++ b/plugins/http/mongrel_process_memory @@ -1,36 +1,40 @@ #!/usr/bin/env ruby -# -# mongrel_process_memory - A munin plugin to monitor memory size of -# each individual mongrel process -# Copyright (C) 2007 Ben VandenBos and Avvo, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Author: Ben VandenBos -# Contributors: Adam Jacob () -# Ryan Woodrum -# + +=begin + +mongrel_process_memory - A munin plugin to monitor memory size of + each individual mongrel process +Copyright (C) 2007 Ben VandenBos and Avvo, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Author: Ben VandenBos +Contributors: Adam Jacob () + Ryan Woodrum + #%# family=auto #%# capabilities=autoconf +=end + + module Munin class MongrelProcessMemory - def run h = get_pids() ps_output = "" - #I have no doubt that this is a terrible way of doing this. + # I have no doubt that this is a terrible way of doing this. h.each do |k, v| ps_output = ps_output + `ps --no-heading l #{k}` end @@ -59,8 +63,8 @@ module Munin pids = `pgrep mongrel_rails` pids.each { |p| l = `ps #{p}` - l =~ /-p (\d+)/ - h[p] = $1 + l =~ /-p (\d+)/ + h[p] = $1 } h end @@ -68,7 +72,6 @@ module Munin def autoconf pids.length > 0 end - end end diff --git a/plugins/icecast/icecast2_simple b/plugins/icecast/icecast2_simple index dc6c2202..543078e5 100755 --- a/plugins/icecast/icecast2_simple +++ b/plugins/icecast/icecast2_simple @@ -18,8 +18,8 @@ require 'open-uri' def get_conf # Default values - conf = {:host => '127.0.0.1', :port => 8000, - :username => 'admin', :password => 'hackme' } + conf = { :host => '127.0.0.1', :port => 8000, + :username => 'admin', :password => 'hackme' } conf.keys.each do |key| env_key = sprintf('icecast_%s', key) conf[key] = ENV[env_key] if ENV.has_key?(env_key) @@ -31,8 +31,8 @@ def get_data(conf) begin data = Hpricot(open(sprintf('http://%s:%s/admin/stats', conf[:host], conf[:port]), - :http_basic_authentication=>[conf[:username], - conf[:password]])) + :http_basic_authentication => [conf[:username], + conf[:password]])) rescue OpenURI::HTTPError puts "Cannot connect: HTTP connection error" exit 1 @@ -43,7 +43,7 @@ end def get_values(data) vals = {} [:sources, :clients].each do |key| - elem = data/key + elem = data / key if elem.nil? vals[key] = 0 else diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index 8a4c12ee..045937d5 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -8,12 +8,12 @@ require 'socket' if ARGV[0] == 'config' - puts "graph_title Connected players" - puts "graph_vlabel players" - puts "players.label players" - puts "graph_info Number of players connected to Minecraft" - puts "graph_category games" - exit + puts "graph_title Connected players" + puts "graph_vlabel players" + puts "players.label players" + puts "graph_info Number of players connected to Minecraft" + puts "graph_category games" + exit end host = ENV['host'] diff --git a/plugins/moblock/moblock_connections b/plugins/moblock/moblock_connections index 3b34f14b..0c68f2ef 100755 --- a/plugins/moblock/moblock_connections +++ b/plugins/moblock/moblock_connections @@ -1,25 +1,30 @@ #!/usr/bin/env ruby -# -# Plugin to monitor the number of connections blocked by moblock. -# -# Requirements: -# -# Moblock up and running with generated log files going to /var/log/moblock -# -# Parameters supported: -# -# config -# autoconf -# -# Configurable variables -# -# logfile - Override default moblock logfile -# -# Magic markers -# + +=begin + +Plugin to monitor the number of connections blocked by moblock. + +Requirements: + + Moblock up and running with generated log files going to /var/log/moblock + +Parameters supported: + + config + autoconf + +Configurable variables + + logfile - Override default moblock logfile + +Magic markers + #%# family=auto #%# capabilities=autoconf +=end + + # # Initialize vars # @@ -54,7 +59,7 @@ end # # Grep moblock logs for stats # -def fetch(debug=false) +def fetch(debug = false) num_in = %x{cat #{$logfile} | grep --extended-regexp 'IN: ' | wc -l} num_out = %x{cat #{$logfile} | grep --extended-regexp 'OUT: ' | wc -l} num_total = num_in.to_i + num_out.to_i @@ -80,12 +85,12 @@ end # Handle command line args # case ARGV.first - when 'config' - config - when 'debug' - fetch true - when 'autoconf' - autoconf - else - fetch +when 'config' + config +when 'debug' + fetch true +when 'autoconf' + autoconf +else + fetch end diff --git a/plugins/mssql/microsoft-sql b/plugins/mssql/microsoft-sql index c56c703c..42e12d48 100755 --- a/plugins/mssql/microsoft-sql +++ b/plugins/mssql/microsoft-sql @@ -1,49 +1,54 @@ #!/usr/bin/env ruby -# -# Munin Plugin for MSSQL - transaction monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-18 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) /etc/odbc.ini and /etc/freetds.conf -# 2) rubygems -# 3) ruby-dbi -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/mssql_transaction.rb /etc/munin/plugins/mssql_transaction.rb -# -# Parameters: -# autoconf -# config (required) -# -# Config variables: -# sqluser : mssql user who has view server state privilege -# sqlpass : password for the mssql user -# dsn : datasource name as defined in /etc/odbc.ini -# instance: instance to monitor -# + +=begin + +Munin Plugin for MSSQL - transaction monitoring + +Author: Wilfred Chau +Date: 2011-05-18 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/mssql_transaction.rb /etc/munin/plugins/mssql_transaction.rb + +Parameters: + autoconf + config (required) + +Config variables: + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'dbi' @@ -56,7 +61,7 @@ instance = 'AdventureWorks' # Queries # # -dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) +dbh = DBI.connect("DBI:ODBC:#{dsn}", sqluser, sqlpass) instance_name_query = "SELECT distinct instance_name FROM sys.dm_os_performance_counters @@ -69,10 +74,10 @@ transaction_query = "select cntr_value from sys.dm_os_performance_counters and object_name = 'SQLServer:Databases' and instance_name = ?" -all_instance_names = Array.new +all_instance_names = Array.new sth = dbh.execute(instance_name_query) sth.fetch do |row| - all_instance_names.push(row[0].strip) + all_instance_names.push(row[0].strip) end sth.finish @@ -80,33 +85,33 @@ sth.finish # autoconf # if ARGV[0] == "autoconf" - if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" - puts "graph_title MSSQL Transactions/s" - puts "graph_category db" - puts "graph_info This graph shows transactions/s" - puts "graph_vlabel transactions/s" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1000 -r --lower-limit 0" + puts "graph_title MSSQL Transactions/s" + puts "graph_category db" + puts "graph_info This graph shows transactions/s" + puts "graph_vlabel transactions/s" + puts "graph_scale no" + puts "graph_period second" - all_instance_names.sort.each do |s| - puts "#{s}.label #{s}" - puts "#{s}.info INSTANCE: #{s}" - puts "#{s}.type DERIVE" - puts "#{s}.draw LINE1" - end + all_instance_names.sort.each do |s| + puts "#{s}.label #{s}" + puts "#{s}.info INSTANCE: #{s}" + puts "#{s}.type DERIVE" + puts "#{s}.draw LINE1" + end - exit 0 + exit 0 end # @@ -114,11 +119,11 @@ end # sth = dbh.prepare(transaction_query) all_instance_names.sort.each do |k| - sth.execute(k) - sth.fetch do |row| - # since type is DERIVE, need to convert value to integer then to string - puts "#{k.to_s}.value #{row[0].to_i.to_s}" - end + sth.execute(k) + sth.fetch do |row| + # since type is DERIVE, need to convert value to integer then to string + puts "#{k.to_s}.value #{row[0].to_i.to_s}" + end end sth.finish dbh.disconnect diff --git a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio index ca376f16..8e225617 100755 --- a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +++ b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio @@ -1,48 +1,53 @@ #!/usr/bin/env ruby -# -# Munin Plugin for MSSQL - Buffer cache hit ratio monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-19 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) /etc/odbc.ini and /etc/freetds.conf -# 2) rubygems -# 3) ruby-dbi -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/mssql_buffercachehitratio.rb /etc/munin/plugins/mssql_buffercachehitratio.rb -# -# Parameters: -# autoconf -# config (required) -# -# Config variables: -# sqluser : mssql user who has view server state privilege -# sqlpass : password for the mssql user -# dsn : datasource name as defined in /etc/odbc.ini -# + +=begin + +Munin Plugin for MSSQL - Buffer cache hit ratio monitoring + +Author: Wilfred Chau +Date: 2011-05-19 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/mssql_buffercachehitratio.rb /etc/munin/plugins/mssql_buffercachehitratio.rb + +Parameters: + autoconf + config (required) + +Config variables: + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'dbi' @@ -54,7 +59,7 @@ dsn = 'TESTSQL' # Queries # # -dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) +dbh = DBI.connect("DBI:ODBC:#{dsn}", sqluser, sqlpass) buffercachehitratio_query = "select (a.cntr_value * 1.0 / b.cntr_value) * 100.0 from sys.dm_os_performance_counters a @@ -70,36 +75,36 @@ buffercachehitratio_query = "select (a.cntr_value * 1.0 / b.cntr_value) * 100.0 # autoconf # if ARGV[0] == "autoconf" - if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" - puts "graph_title MSSQL Buffer Cache Hit Ratio " - puts "graph_category db" - puts "graph_info This graph shows Buffer Cache Hit Ratio" - puts "graph_vlabel %" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1000 -r --lower-limit 0" + puts "graph_title MSSQL Buffer Cache Hit Ratio " + puts "graph_category db" + puts "graph_info This graph shows Buffer Cache Hit Ratio" + puts "graph_vlabel %" + puts "graph_scale no" + puts "graph_period second" - puts "bc_hitratio.label BufferCacheHitRatio" - puts "bc_hitratio.info BufferCacheHitRatio" - puts "bc_hitratio.type GAUGE" - puts "bc_hitratio.draw LINE1" + puts "bc_hitratio.label BufferCacheHitRatio" + puts "bc_hitratio.info BufferCacheHitRatio" + puts "bc_hitratio.type GAUGE" + puts "bc_hitratio.draw LINE1" - exit 0 + exit 0 end sth = dbh.execute(buffercachehitratio_query) sth.fetch do |row| - puts "bc_hitratio.value #{row[0].strip.to_s}" + puts "bc_hitratio.value #{row[0].strip.to_s}" end sth.finish dbh.disconnect diff --git a/plugins/mssql/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes index 9e72eab4..4d4b78db 100755 --- a/plugins/mssql/microsoft-sql-data-file-sizes +++ b/plugins/mssql/microsoft-sql-data-file-sizes @@ -1,49 +1,54 @@ #!/usr/bin/env ruby -# -# Munin Plugin for MSSQL - Data file size monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-19 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) /etc/odbc.ini and /etc/freetds.conf -# 2) rubygems -# 3) ruby-dbi -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/mssql_datafilesizes.rb /etc/munin/plugins/mssql_datafilesizes.rb -# -# Parameters: -# autoconf -# config (required) -# -# Config variables: -# sqluser : mssql user who has view server state privilege -# sqlpass : password for the mssql user -# dsn : datasource name as defined in /etc/odbc.ini -# instance: instance to monitor -# + +=begin + +Munin Plugin for MSSQL - Data file size monitoring + +Author: Wilfred Chau +Date: 2011-05-19 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/mssql_datafilesizes.rb /etc/munin/plugins/mssql_datafilesizes.rb + +Parameters: + autoconf + config (required) + +Config variables: + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'dbi' @@ -56,7 +61,7 @@ instance = 'AdventureWorks' # Queries # # -dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) +dbh = DBI.connect("DBI:ODBC:#{dsn}", sqluser, sqlpass) instance_name_query = "SELECT distinct instance_name FROM sys.dm_os_performance_counters @@ -67,10 +72,10 @@ transaction_query = "select cntr_value/1024.0 from sys.dm_os_performance_counter and object_name = 'SQLServer:Databases' and instance_name = ?" -all_instance_names = Array.new +all_instance_names = Array.new sth = dbh.execute(instance_name_query) sth.fetch do |row| - all_instance_names.push(row[0].strip) + all_instance_names.push(row[0].strip) end sth.finish @@ -78,33 +83,33 @@ sth.finish # autoconf # if ARGV[0] == "autoconf" - if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title MSSQL DB File Sizes" - puts "graph_category db" - puts "graph_info This graph shows DB File Sizes (MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_title MSSQL DB File Sizes" + puts "graph_category db" + puts "graph_info This graph shows DB File Sizes (MB)" + puts "graph_vlabel MB" + puts "graph_scale no" + puts "graph_period second" - all_instance_names.sort.each do |s| - puts "#{s}.label #{s}" - puts "#{s}.info INSTANCE: #{s}" - puts "#{s}.type GAUGE" - puts "#{s}.draw LINE1" - end + all_instance_names.sort.each do |s| + puts "#{s}.label #{s}" + puts "#{s}.info INSTANCE: #{s}" + puts "#{s}.type GAUGE" + puts "#{s}.draw LINE1" + end - exit 0 + exit 0 end # @@ -112,10 +117,10 @@ end # sth = dbh.prepare(transaction_query) all_instance_names.sort.each do |k| - sth.execute(k) - sth.fetch do |row| - puts "#{k.to_s}.value #{row[0].to_s}" - end + sth.execute(k) + sth.fetch do |row| + puts "#{k.to_s}.value #{row[0].to_s}" + end end sth.finish dbh.disconnect diff --git a/plugins/mssql/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size index 14c7ffc2..46e777e1 100755 --- a/plugins/mssql/microsoft-sql-log-file-size +++ b/plugins/mssql/microsoft-sql-log-file-size @@ -1,49 +1,54 @@ #!/usr/bin/env ruby -# -# Munin Plugin for MSSQL - log files monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-19 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) /etc/odbc.ini and /etc/freetds.conf -# 2) rubygems -# 3) ruby-dbi -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/mssql_logfilesizes.rb /etc/munin/plugins/mssql_logfilesizes.rb -# -# Parameters: -# autoconf -# config (required) -# -# Config variables: -# sqluser : mssql user who has view server state privilege -# sqlpass : password for the mssql user -# dsn : datasource name as defined in /etc/odbc.ini -# instance: instance to monitor -# + +=begin + +Munin Plugin for MSSQL - log files monitoring + +Author: Wilfred Chau +Date: 2011-05-19 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/mssql_logfilesizes.rb /etc/munin/plugins/mssql_logfilesizes.rb + +Parameters: + autoconf + config (required) + +Config variables: + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'dbi' @@ -56,7 +61,7 @@ instance = 'AdventureWorks' # Queries # # -dbh = DBI.connect("DBI:ODBC:#{dsn}",sqluser,sqlpass) +dbh = DBI.connect("DBI:ODBC:#{dsn}", sqluser, sqlpass) instance_name_query = "SELECT distinct instance_name FROM sys.dm_os_performance_counters @@ -67,10 +72,10 @@ logfilesize_query = "SELECT cntr_value/1024.0 from sys.dm_os_performance_counter AND object_name = 'SQLServer:Databases' AND instance_name = ?" -all_instance_names = Array.new +all_instance_names = Array.new sth = dbh.execute(instance_name_query) sth.fetch do |row| - all_instance_names.push(row[0].strip) + all_instance_names.push(row[0].strip) end sth.finish @@ -78,41 +83,41 @@ sth.finish # autoconf # if ARGV[0] == "autoconf" - if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title MSSQL DB Log File Sizes" - puts "graph_category db" - puts "graph_info This graph shows DB Log File Sizes (MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_title MSSQL DB Log File Sizes" + puts "graph_category db" + puts "graph_info This graph shows DB Log File Sizes (MB)" + puts "graph_vlabel MB" + puts "graph_scale no" + puts "graph_period second" - all_instance_names.sort.each do |s| - puts "#{s}.label #{s}" - puts "#{s}.info INSTANCE: #{s}" - puts "#{s}.type GAUGE" - puts "#{s}.draw LINE1" - end + all_instance_names.sort.each do |s| + puts "#{s}.label #{s}" + puts "#{s}.info INSTANCE: #{s}" + puts "#{s}.type GAUGE" + puts "#{s}.draw LINE1" + end - exit 0 + exit 0 end sth = dbh.prepare(logfilesize_query) all_instance_names.sort.each do |k| - sth.execute(k) - sth.fetch do |row| - puts "#{k.to_s}.value #{row[0].to_s}" - end + sth.execute(k) + sth.fetch do |row| + puts "#{k.to_s}.value #{row[0].to_s}" + end end sth.finish dbh.disconnect diff --git a/plugins/network/http__tp_link b/plugins/network/http__tp_link index 54a7eb5f..2214b8e6 100755 --- a/plugins/network/http__tp_link +++ b/plugins/network/http__tp_link @@ -45,7 +45,7 @@ require 'net/http' class TPAdslStats def initialize(host, user, password) - Net::HTTP.start( host ) do |http| + Net::HTTP.start(host) do |http| req = Net::HTTP::Get.new('/statsadsl.html') req.basic_auth user, password response = http.request(req) diff --git a/plugins/network/netstat_s_ b/plugins/network/netstat_s_ index cd16e627..d5764ef7 100755 --- a/plugins/network/netstat_s_ +++ b/plugins/network/netstat_s_ @@ -1,22 +1,26 @@ #!/usr/bin/env ruby -# netstat_s revision 6 (Nov 2013) -# -# This plugin shows various statistics from 'netstat -s' -# -# Required privileges: none -# -# OS: -# Supposed: BSD, Linux (only a few items, see netstat_multi for more) -# Tested: FreeBSD: 8.2, 8.3, 9.1 -# Linux : Debian 6 (kernel 2.6.32), Arch (kernel 3.11.6), CentOS 6 -# -# Author: Artem Sheremet -# +=begin + +netstat_s revision 6 (Nov 2013) + +This plugin shows various statistics from 'netstat -s' + +Required privileges: none + +OS: + Supposed: BSD, Linux (only a few items, see netstat_multi for more) + Tested: FreeBSD: 8.2, 8.3, 9.1 + Linux : Debian 6 (kernel 2.6.32), Arch (kernel 3.11.6), CentOS 6 + +Author: Artem Sheremet + #%# family=auto #%# capabilities=autoconf suggest +=end + # original filename PLUGIN_NAME = 'netstat_s_' @@ -24,348 +28,348 @@ $os = `uname -s`.strip.downcase.to_sym $debug_mode = ARGV.first == 'debug' class String - def escape - self.gsub /[^\w]/, '_' - end + def escape + self.gsub /[^\w]/, '_' + end - unless method_defined? :start_with? - def start_with?(str) - self[0...str.size] == str - end - end + unless method_defined? :start_with? + def start_with?(str) + self[0...str.size] == str + end + end - unless method_defined? :lines - def lines - self.split($/).to_enum - end - end + unless method_defined? :lines + def lines + self.split($/).to_enum + end + end end class Graph - def initialize(name, protocol, parse_expr) - @name, @protocol, @parse_expr = name, protocol, parse_expr - end + def initialize(name, protocol, parse_expr) + @name, @protocol, @parse_expr = name, protocol, parse_expr + end - def config - config_options = [] + def config + config_options = [] - # first, build a list of multigraphs (one graph per unit) - # Hash key is unit, and the value is array of labels - multigraphs = {} - @parse_expr.each { |expr, descr| - next unless descr # no label - skip this entry - descr.each { |entry| - labels_array = (multigraphs[entry[0]] ||= []) - labels_array.push [entry[1], entry[2]] - } - } + # first, build a list of multigraphs (one graph per unit) + # Hash key is unit, and the value is array of labels + multigraphs = {} + @parse_expr.each { |expr, descr| + next unless descr # no label - skip this entry - multigraphs.each_pair { |unit, labels_and_negatives| - # now just add options to the config + descr.each { |entry| + labels_array = (multigraphs[entry[0]] ||= []) + labels_array.push [entry[1], entry[2]] + } + } - config_options.concat [ - "multigraph #{name(unit)}", - "graph_title Netstat: #{@protocol}: #{@name}#{" (#{unit})" if multigraphs.size > 1}", - "graph_category network", - "graph_order #{labels_and_negatives.map { |label, _negative| label.escape }.join(' ')}" - ] + multigraphs.each_pair { |unit, labels_and_negatives| + # now just add options to the config - config_options.push "graph_args --base 1024" if unit == :bytes - has_negatives = false + config_options.concat [ + "multigraph #{name(unit)}", + "graph_title Netstat: #{@protocol}: #{@name}#{" (#{unit})" if multigraphs.size > 1}", + "graph_category network", + "graph_order #{labels_and_negatives.map { |label, _negative| label.escape }.join(' ')}" + ] - labels_and_negatives.each { |label, negative| - label_esc = label.escape - has_negatives = true unless negative == nil + config_options.push "graph_args --base 1024" if unit == :bytes + has_negatives = false - if negative == true - # the value has no opposite and is negative - config_options.concat [ - "#{label_esc}.graph no", - "#{label_esc}_neg.type DERIVE", - "#{label_esc}_neg.min 0", - "#{label_esc}_neg.draw LINE", - "#{label_esc}_neg.label #{label}", - "#{label_esc}_neg.negative #{label_esc}" - ] - else - config_options.concat [ - "#{label_esc}.type DERIVE", - "#{label_esc}.min 0", - "#{label_esc}.draw LINE", - "#{label_esc}.label #{label}" - ] - end + labels_and_negatives.each { |label, negative| + label_esc = label.escape + has_negatives = true unless negative == nil - if negative == false - # the value has no opposite and is positive - config_options.concat [ - "#{label_esc}_neg.graph off", - "#{label_esc}.negative #{label_esc}_neg" - ] - elsif negative - negative_esc = negative.escape - config_options.concat [ - "#{label_esc}.negative #{negative_esc}", - "#{negative_esc}.graph no" - ] - end - } + if negative == true + # the value has no opposite and is negative + config_options.concat [ + "#{label_esc}.graph no", + "#{label_esc}_neg.type DERIVE", + "#{label_esc}_neg.min 0", + "#{label_esc}_neg.draw LINE", + "#{label_esc}_neg.label #{label}", + "#{label_esc}_neg.negative #{label_esc}" + ] + else + config_options.concat [ + "#{label_esc}.type DERIVE", + "#{label_esc}.min 0", + "#{label_esc}.draw LINE", + "#{label_esc}.label #{label}" + ] + end - config_options.push "graph_vlabel per second#{" in (-) / out (+)" if has_negatives}" - } + if negative == false + # the value has no opposite and is positive + config_options.concat [ + "#{label_esc}_neg.graph off", + "#{label_esc}.negative #{label_esc}_neg" + ] + elsif negative + negative_esc = negative.escape + config_options.concat [ + "#{label_esc}.negative #{negative_esc}", + "#{negative_esc}.graph no" + ] + end + } - config_options - end + config_options.push "graph_vlabel per second#{" in (-) / out (+)" if has_negatives}" + } - def fetch(data) - output_data = [] + config_options + end - # first build a set of multigraphs, one per unit. - # Hash key is unit, and the value is a hash of 'escaped label' => 'value' - multigraphs = {} - @parse_expr.each { |expr, descr| - next unless descr # no label - skip this entry - index = data.index { |line| line =~ expr } - if index - data.delete_at index - $~[1..-1].zip(descr).each { |value, info| - unit, label = info - (multigraphs[unit] ||= {})[label.escape] = value - } - else - warn "no line found for #{expr}, #{descr}" if $debug_mode - end - } + def fetch(data) + output_data = [] - multigraphs.each_pair { |unit, values| - output_data.push "multigraph #{name(unit)}" - output_data += values.map { |label, value| "#{label}.value #{value}" } - } + # first build a set of multigraphs, one per unit. + # Hash key is unit, and the value is a hash of 'escaped label' => 'value' + multigraphs = {} + @parse_expr.each { |expr, descr| + next unless descr # no label - skip this entry - output_data - end + index = data.index { |line| line =~ expr } + if index + data.delete_at index + $~[1..-1].zip(descr).each { |value, info| + unit, label = info + (multigraphs[unit] ||= {})[label.escape] = value + } + else + warn "no line found for #{expr}, #{descr}" if $debug_mode + end + } - def name(unit) - "#{PLUGIN_NAME}#{@protocol}_#{@name.escape}_#{unit}" - end + multigraphs.each_pair { |unit, values| + output_data.push "multigraph #{name(unit)}" + output_data += values.map { |label, value| "#{label}.value #{value}" } + } + + output_data + end + + def name(unit) + "#{PLUGIN_NAME}#{@protocol}_#{@name.escape}_#{unit}" + end end def graphs_for(protocol) - case protocol - # Order of the graps in each section is important for parsing. - # At the same time, it is not important for munin, so we are OK placing it in parsing order here. - when 'tcp' - $os == :linux ? [ - Graph.new('sent', protocol, [ - # Description of the elements of arrays below: - # 0: regexp to parse the line - # 1: Array for each matching group in the regular expression. - # 0: unit name - # 1: label - # 2 (optional): negative label - # It could be reasonable to add more elements as warning and critical values. + case protocol + # Order of the graps in each section is important for parsing. + # At the same time, it is not important for munin, so we are OK placing it in parsing order here. + when 'tcp' + $os == :linux ? [ + Graph.new('sent', protocol, [ + # Description of the elements of arrays below: + # 0: regexp to parse the line + # 1: Array for each matching group in the regular expression. + # 0: unit name + # 1: label + # 2 (optional): negative label + # It could be reasonable to add more elements as warning and critical values. - [ /(\d+) segments send out$/, [ [ :segments, 'total' ] ] ], - [ /(\d+) segments retransmitted$/, [ [ :segments, 'retransmitted' ] ] ] - ]), + [/(\d+) segments send out$/, [[:segments, 'total']]], + [/(\d+) segments retransmitted$/, [[:segments, 'retransmitted']]] + ]), - Graph.new('received', protocol, [ - [ /(\d+) segments received$/, [ [ :segments, 'total' ] ] ], - [ /(\d+) bad segments received.$/, [ [ :segments, 'bad' ] ] ] - ]), + Graph.new('received', protocol, [ + [/(\d+) segments received$/, [[:segments, 'total']]], + [/(\d+) bad segments received.$/, [[:segments, 'bad']]] + ]), - Graph.new('connections', protocol, [ - [ /(\d+) active connections openings$/, [ [ :connections, 'active openings' ] ] ], - [ /(\d+) passive connection openings$/, [ [ :connections, 'passive openings' ] ] ], - [ /(\d+) failed connection attempts$/, [ [ :connections, 'failed attempts' ] ] ], - [ /(\d+) connection resets received$/, [ [ :connections, 'RST received' ] ] ], - [ /(\d+) connections established$/, [ [ :connections, 'established' ] ] ], - [ /(\d+) resets sent$/, [ [ :connections, 'RST sent' ] ] ] - ]), + Graph.new('connections', protocol, [ + [/(\d+) active connections openings$/, [[:connections, 'active openings']]], + [/(\d+) passive connection openings$/, [[:connections, 'passive openings']]], + [/(\d+) failed connection attempts$/, [[:connections, 'failed attempts']]], + [/(\d+) connection resets received$/, [[:connections, 'RST received']]], + [/(\d+) connections established$/, [[:connections, 'established']]], + [/(\d+) resets sent$/, [[:connections, 'RST sent']]] + ]), - Graph.new('timeouts', protocol, [ - [ /(\d+) timeouts after SACK recovery$/, [ [ :segments, 'after SACK recovery' ] ] ], - [ /(\d+) other TCP timeouts$/, [ [ :segments, 'other TCP' ] ] ], - [ /(\d+) timeouts in loss state$/, [ [ :segments, 'in a loss state' ] ] ] - ]) - ] : [ - Graph.new('sent', protocol, [ - [ /(\d+) packets sent$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) data packets \((\d+) bytes\)$/, [ [ :packets, 'data' ], [ :bytes, 'data' ] ] ], - [ /(\d+) data packets \((\d+) bytes\) retransmitted$/, [ [ :packets, 'retransmitted' ], [ :bytes, 'retransmitted' ] ] ], - [ /(\d+) data packets unnecessarily retransmitted$/, [ [ :packets, 'unnecessarily retransmitted' ] ] ], - [ /(\d+) resends initiated by MTU discovery$/, [ [ :packets, 'resends initiated by MTU discovery' ] ] ], - [ /(\d+) ack-only packets \((\d+) delayed\)$/, [ [ :packets, 'ack-only' ], [ :packets, 'ack-only delayed' ] ] ], - [ /(\d+) URG only packets$/, [ [ :packets, 'URG only' ] ] ], - [ /(\d+) window probe packets$/, [ [ :packets, 'window probe' ] ] ], - [ /(\d+) window update packets$/, [ [ :packets, 'window update' ] ] ], - [ /(\d+) control packets$/, [ [ :packets, 'control' ] ] ] - ]), + Graph.new('timeouts', protocol, [ + [/(\d+) timeouts after SACK recovery$/, [[:segments, 'after SACK recovery']]], + [/(\d+) other TCP timeouts$/, [[:segments, 'other TCP']]], + [/(\d+) timeouts in loss state$/, [[:segments, 'in a loss state']]] + ]) + ] : [ + Graph.new('sent', protocol, [ + [/(\d+) packets sent$/, [[:packets, 'total']]], + [/(\d+) data packets \((\d+) bytes\)$/, [[:packets, 'data'], [:bytes, 'data']]], + [/(\d+) data packets \((\d+) bytes\) retransmitted$/, [[:packets, 'retransmitted'], [:bytes, 'retransmitted']]], + [/(\d+) data packets unnecessarily retransmitted$/, [[:packets, 'unnecessarily retransmitted']]], + [/(\d+) resends initiated by MTU discovery$/, [[:packets, 'resends initiated by MTU discovery']]], + [/(\d+) ack-only packets \((\d+) delayed\)$/, [[:packets, 'ack-only'], [:packets, 'ack-only delayed']]], + [/(\d+) URG only packets$/, [[:packets, 'URG only']]], + [/(\d+) window probe packets$/, [[:packets, 'window probe']]], + [/(\d+) window update packets$/, [[:packets, 'window update']]], + [/(\d+) control packets$/, [[:packets, 'control']]] + ]), - Graph.new('received', protocol, [ - [ /(\d+) packets received$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) acks \(for (\d+) bytes\)$/, [ [ :packets, 'acks' ], [ :bytes, 'acks' ] ] ], - [ /(\d+) duplicate acks$/, [ [ :packets, 'duplicate acks' ] ] ], - [ /(\d+) acks for unsent data$/, [ [ :packets, 'acks for unsent data' ] ] ], - [ /(\d+) packets \((\d+) bytes\) received in-sequence$/, [ [ :packets, 'in-sequence' ], [ :bytes, 'in-sequence' ] ] ], - [ /(\d+) completely duplicate packets \((\d+) bytes\)$/, [ [ :packets, 'completely duplicate' ], [ :bytes, 'completely duplicate' ] ] ], - [ /(\d+) old duplicate packets$/, [ [ :packets, 'old duplicate' ] ] ], - [ /(\d+) packets with some dup\. data \((\d+) bytes duped\)$/, [ [ :packets, 'some dup. data' ], [ :bytes, 'partial dups' ] ] ], - [ /(\d+) out-of-order packets \((\d+) bytes\)$/, [ [ :packets, 'out-of-order' ], [ :bytes, 'out-of-order' ] ] ], - [ /(\d+) packets \((\d+) bytes\) of data after window$/, [ [ :packets, 'data after window' ], [ :bytes, 'data after window' ] ] ], - [ /(\d+) window probes$/, [ [ :packets, 'window probes' ] ] ], - [ /(\d+) window update packets$/, [ [ :packets, 'window update' ] ] ], - [ /(\d+) packets received after close$/, [ [ :packets, 'after close' ] ] ], - [ /(\d+) discarded for bad checksums$/, [ [ :packets, 'bad checksums' ] ] ], - [ /(\d+) discarded for bad header offset fields?$/, [ [ :packets, 'bad header offset flds' ] ] ], - [ /(\d+) discarded because packet too short$/, [ [ :packets, 'too short' ] ] ], - [ /(\d+) discarded due to memory problems$/, [ [ :packets, 'discarded: memory problems' ] ] ], - [ /(\d+) ignored RSTs in the windows$/, [ [ :packets, 'ignored RSTs in windows' ] ] ], - [ /(\d+) segments updated rtt \(of (\d+) attempts\)$/, [ [ :packets, 'RTT: updated' ], [ :packets, 'RTT: attempts to update' ] ] ] - ]), + Graph.new('received', protocol, [ + [/(\d+) packets received$/, [[:packets, 'total']]], + [/(\d+) acks \(for (\d+) bytes\)$/, [[:packets, 'acks'], [:bytes, 'acks']]], + [/(\d+) duplicate acks$/, [[:packets, 'duplicate acks']]], + [/(\d+) acks for unsent data$/, [[:packets, 'acks for unsent data']]], + [/(\d+) packets \((\d+) bytes\) received in-sequence$/, [[:packets, 'in-sequence'], [:bytes, 'in-sequence']]], + [/(\d+) completely duplicate packets \((\d+) bytes\)$/, [[:packets, 'completely duplicate'], [:bytes, 'completely duplicate']]], + [/(\d+) old duplicate packets$/, [[:packets, 'old duplicate']]], + [/(\d+) packets with some dup\. data \((\d+) bytes duped\)$/, [[:packets, 'some dup. data'], [:bytes, 'partial dups']]], + [/(\d+) out-of-order packets \((\d+) bytes\)$/, [[:packets, 'out-of-order'], [:bytes, 'out-of-order']]], + [/(\d+) packets \((\d+) bytes\) of data after window$/, [[:packets, 'data after window'], [:bytes, 'data after window']]], + [/(\d+) window probes$/, [[:packets, 'window probes']]], + [/(\d+) window update packets$/, [[:packets, 'window update']]], + [/(\d+) packets received after close$/, [[:packets, 'after close']]], + [/(\d+) discarded for bad checksums$/, [[:packets, 'bad checksums']]], + [/(\d+) discarded for bad header offset fields?$/, [[:packets, 'bad header offset flds']]], + [/(\d+) discarded because packet too short$/, [[:packets, 'too short']]], + [/(\d+) discarded due to memory problems$/, [[:packets, 'discarded: memory problems']]], + [/(\d+) ignored RSTs in the windows$/, [[:packets, 'ignored RSTs in windows']]], + [/(\d+) segments updated rtt \(of (\d+) attempts\)$/, [[:packets, 'RTT: updated'], [:packets, 'RTT: attempts to update']]] + ]), - Graph.new('connections', protocol, [ - [ /(\d+) connection requests$/, [ [ :connections, 'requests' ] ] ], - [ /(\d+) connection accepts$/, [ [ :connections, 'accepts' ] ] ], - [ /(\d+) bad connection attempts$/, [ [ :connections, 'bad attempts' ] ] ], - [ /(\d+) listen queue overflows$/, [ [ :connections, 'listen queue overflows' ] ] ], - [ /(\d+) connections established \(including accepts\)$/, [ [ :connections, 'established' ] ] ], - [ /(\d+) connections closed \(including (\d+) drops\)$/, [ [ :connections, 'closed' ], [ :connections, 'dropped' ] ] ], - [ /(\d+) connections updated cached RTT on close$/, [ [ :connections, 'closed & upd cached RTT' ] ] ], - [ /(\d+) connections updated cached RTT variance on close$/, [ [ :connections, 'closed & upd cached RTT variance' ] ] ], - [ /(\d+) connections updated cached ssthresh on close$/, [ [ :connections, 'closed & upd cached ssthresh' ] ] ], - [ /(\d+) embryonic connections dropped$/, [ [ :connections, 'embryonic dropped' ] ] ] - ]), + Graph.new('connections', protocol, [ + [/(\d+) connection requests$/, [[:connections, 'requests']]], + [/(\d+) connection accepts$/, [[:connections, 'accepts']]], + [/(\d+) bad connection attempts$/, [[:connections, 'bad attempts']]], + [/(\d+) listen queue overflows$/, [[:connections, 'listen queue overflows']]], + [/(\d+) connections established \(including accepts\)$/, [[:connections, 'established']]], + [/(\d+) connections closed \(including (\d+) drops\)$/, [[:connections, 'closed'], [:connections, 'dropped']]], + [/(\d+) connections updated cached RTT on close$/, [[:connections, 'closed & upd cached RTT']]], + [/(\d+) connections updated cached RTT variance on close$/, [[:connections, 'closed & upd cached RTT variance']]], + [/(\d+) connections updated cached ssthresh on close$/, [[:connections, 'closed & upd cached ssthresh']]], + [/(\d+) embryonic connections dropped$/, [[:connections, 'embryonic dropped']]] + ]), - Graph.new('timeouts', protocol, [ - [ /(\d+) retransmit timeouts$/, [ [ :connections, 'retransmit' ] ] ], - [ /(\d+) connections dropped by rexmit timeout$/, [ [ :connections, 'retransmit: dropped' ] ] ], - [ /(\d+) persist timeouts$/, [ [ :connections, 'persist' ] ] ], - [ /(\d+) connections dropped by persist timeout$/, [ [ :connections, 'persist: dropped' ] ] ], - [ /(\d+) Connections \(fin_wait_2\) dropped because of timeout$/, [ [ :connections, 'fin_wait_2: dropped' ] ] ], - [ /(\d+) keepalive timeouts$/, [ [ :connections, 'keepalive' ] ] ], - [ /(\d+) keepalive probes sent$/, [ [ :connections, 'keepalive: probes sent' ] ] ], - [ /(\d+) connections dropped by keepalive$/, [ [ :connections, 'keepalive: dropped' ] ] ] - ]), + Graph.new('timeouts', protocol, [ + [/(\d+) retransmit timeouts$/, [[:connections, 'retransmit']]], + [/(\d+) connections dropped by rexmit timeout$/, [[:connections, 'retransmit: dropped']]], + [/(\d+) persist timeouts$/, [[:connections, 'persist']]], + [/(\d+) connections dropped by persist timeout$/, [[:connections, 'persist: dropped']]], + [/(\d+) Connections \(fin_wait_2\) dropped because of timeout$/, [[:connections, 'fin_wait_2: dropped']]], + [/(\d+) keepalive timeouts$/, [[:connections, 'keepalive']]], + [/(\d+) keepalive probes sent$/, [[:connections, 'keepalive: probes sent']]], + [/(\d+) connections dropped by keepalive$/, [[:connections, 'keepalive: dropped']]] + ]), - Graph.new('correct predictions', protocol, [ - [ /(\d+) correct ACK header predictions$/, [ [ :predictions, 'ACK header' ] ] ], - [ /(\d+) correct data packet header predictions$/, [ [ :predictions, 'data packet header' ] ] ] - ]), + Graph.new('correct predictions', protocol, [ + [/(\d+) correct ACK header predictions$/, [[:predictions, 'ACK header']]], + [/(\d+) correct data packet header predictions$/, [[:predictions, 'data packet header']]] + ]), - Graph.new('SYN', protocol, [ - [ /(\d+) syncache entries added$/, [ [ :entries, 'cache added' ] ] ], - [ /(\d+) cookies sent$/, [ [ :entries, 'cookies sent' ] ] ], - [ /(\d+) cookies received$/, [ [ :entries, 'cookies received' ] ] ], - [ /(\d+) retransmitted$/, [ [ :entries, 'retransmitted' ] ] ], - [ /(\d+) dupsyn$/, [ [ :entries, 'duplicates' ] ] ], - [ /(\d+) dropped$/, [ [ :entries, 'dropped' ] ] ], - [ /(\d+) completed$/, [ [ :entries, 'completed' ] ] ], - [ /(\d+) bucket overflow$/, [ [ :entries, 'bucket overflow' ] ] ], - [ /(\d+) cache overflow$/, [ [ :entries, 'cache overflow' ] ] ], - [ /(\d+) reset$/, [ [ :entries, 'reset' ] ] ], - [ /(\d+) stale$/, [ [ :entries, 'stale' ] ] ], - [ /(\d+) aborted$/, [ [ :entries, 'aborted' ] ] ], - [ /(\d+) badack$/, [ [ :entries, 'bad ACK' ] ] ], - [ /(\d+) unreach$/, [ [ :entries, 'unreachable' ] ] ], - [ /(\d+) zone failures$/, [ [ :entries, 'zone failures' ] ] ], - [ /(\d+) hostcache entries added$/, [ [ :entries, 'hostcache added' ] ] ], - [ /(\d+) bucket overflow$/, [ [ :entries, 'hostcache overflow' ] ] ] - ]), + Graph.new('SYN', protocol, [ + [/(\d+) syncache entries added$/, [[:entries, 'cache added']]], + [/(\d+) cookies sent$/, [[:entries, 'cookies sent']]], + [/(\d+) cookies received$/, [[:entries, 'cookies received']]], + [/(\d+) retransmitted$/, [[:entries, 'retransmitted']]], + [/(\d+) dupsyn$/, [[:entries, 'duplicates']]], + [/(\d+) dropped$/, [[:entries, 'dropped']]], + [/(\d+) completed$/, [[:entries, 'completed']]], + [/(\d+) bucket overflow$/, [[:entries, 'bucket overflow']]], + [/(\d+) cache overflow$/, [[:entries, 'cache overflow']]], + [/(\d+) reset$/, [[:entries, 'reset']]], + [/(\d+) stale$/, [[:entries, 'stale']]], + [/(\d+) aborted$/, [[:entries, 'aborted']]], + [/(\d+) badack$/, [[:entries, 'bad ACK']]], + [/(\d+) unreach$/, [[:entries, 'unreachable']]], + [/(\d+) zone failures$/, [[:entries, 'zone failures']]], + [/(\d+) hostcache entries added$/, [[:entries, 'hostcache added']]], + [/(\d+) bucket overflow$/, [[:entries, 'hostcache overflow']]] + ]), - Graph.new('SACK', protocol, [ - [ /(\d+) SACK recovery episodes$/, [ [ :packets, 'recovery episodes' ] ] ], - [ /(\d+) segment rexmits in SACK recovery episodes$/, [ [ :packets, 'segment rexmits' ] ] ], - [ /(\d+) byte rexmits in SACK recovery episodes$/, [ [ :bytes, 'bytes rexmitted' ] ] ], - [ /(\d+) SACK options \(SACK blocks\) received$/, [ [ :packets, 'options blocks rcvd' ] ] ], - [ /(\d+) SACK options \(SACK blocks\) sent$/, [ [ :packets, 'options blocks sent' ] ] ], - [ /(\d+) SACK scoreboard overflow$/, [ [ :packets, 'scoreboard overflow' ] ] ] - ]), + Graph.new('SACK', protocol, [ + [/(\d+) SACK recovery episodes$/, [[:packets, 'recovery episodes']]], + [/(\d+) segment rexmits in SACK recovery episodes$/, [[:packets, 'segment rexmits']]], + [/(\d+) byte rexmits in SACK recovery episodes$/, [[:bytes, 'bytes rexmitted']]], + [/(\d+) SACK options \(SACK blocks\) received$/, [[:packets, 'options blocks rcvd']]], + [/(\d+) SACK options \(SACK blocks\) sent$/, [[:packets, 'options blocks sent']]], + [/(\d+) SACK scoreboard overflow$/, [[:packets, 'scoreboard overflow']]] + ]), - Graph.new('ECN', protocol, [ - [ /(\d+) packets with ECN CE bit set$/, [ [ :packets, 'CE bit' ] ] ], - [ /(\d+) packets with ECN ECT\(0\) bit set$/, [ [ :packets, 'ECT(0) bit' ] ] ], - [ /(\d+) packets with ECN ECT\(1\) bit set$/, [ [ :packets, 'ECT(1) bit' ] ] ], - [ /(\d+) successful ECN handshakes$/, [ [ :packets, 'successful handshakes' ] ] ], - [ /(\d+) times ECN reduced the congestion window$/, [ [ :packets, 'congestion window reduced' ] ] ] - ]) - ] - when 'udp' - $os == :linux ? [ - ] : [ - Graph.new('received', protocol, [ - [ /(\d+) datagrams received$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) with incomplete header$/, [ [ :packets, 'incomplete header' ] ] ], - [ /(\d+) with bad data length field$/, [ [ :packets, 'bad data length field' ] ] ], - [ /(\d+) with bad checksum$/, [ [ :packets, 'bad checksum' ] ] ], - [ /(\d+) with no checksum$/, [ [ :packets, 'no checksum' ] ] ], - [ /(\d+) dropped due to no socket$/, [ [ :packets, 'dropped: no socket' ] ] ], - [ /(\d+) broadcast\/multicast datagrams undelivered$/, [ [ :packets, '*cast undelivered' ] ] ], - [ /(\d+) dropped due to full socket buffers$/, [ [ :packets, 'dropped: no buffers' ] ] ], - [ /(\d+) not for hashed pcb$/, [ [ :packets, 'not for hashed pcb' ] ] ], - [ /(\d+) delivered$/, [ [ :packets, 'delivered' ] ] ] - ]), + Graph.new('ECN', protocol, [ + [/(\d+) packets with ECN CE bit set$/, [[:packets, 'CE bit']]], + [/(\d+) packets with ECN ECT\(0\) bit set$/, [[:packets, 'ECT(0) bit']]], + [/(\d+) packets with ECN ECT\(1\) bit set$/, [[:packets, 'ECT(1) bit']]], + [/(\d+) successful ECN handshakes$/, [[:packets, 'successful handshakes']]], + [/(\d+) times ECN reduced the congestion window$/, [[:packets, 'congestion window reduced']]] + ]) + ] + when 'udp' + $os == :linux ? [] : [ + Graph.new('received', protocol, [ + [/(\d+) datagrams received$/, [[:packets, 'total']]], + [/(\d+) with incomplete header$/, [[:packets, 'incomplete header']]], + [/(\d+) with bad data length field$/, [[:packets, 'bad data length field']]], + [/(\d+) with bad checksum$/, [[:packets, 'bad checksum']]], + [/(\d+) with no checksum$/, [[:packets, 'no checksum']]], + [/(\d+) dropped due to no socket$/, [[:packets, 'dropped: no socket']]], + [/(\d+) broadcast\/multicast datagrams undelivered$/, [[:packets, '*cast undelivered']]], + [/(\d+) dropped due to full socket buffers$/, [[:packets, 'dropped: no buffers']]], + [/(\d+) not for hashed pcb$/, [[:packets, 'not for hashed pcb']]], + [/(\d+) delivered$/, [[:packets, 'delivered']]] + ]), - Graph.new('sent', protocol, [ - [ /(\d+) datagrams output$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) times multicast source filter matched$/, [ [ :packets, 'multicast src filter match' ] ] ] - ]) - ] - when 'ip' - $os == :linux ? [ - ] : [ - Graph.new('received', protocol, [ - [ /(\d+) total packets received$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) bad header checksums$/, [ [ :packets, 'bad header checksum' ] ] ], - [ /(\d+) with size smaller than minimum$/, [ [ :packets, 'size smaller than min' ] ] ], - [ /(\d+) with data size < data length$/, [ [ :packets, 'data size < data length' ] ] ], - [ /(\d+) with ip length > max ip packet size$/, [ [ :packets, 'ip length > max ip packet sz' ] ] ], - [ /(\d+) with header length < data size$/, [ [ :packets, 'header length < data size' ] ] ], - [ /(\d+) with data length < header length$/, [ [ :packets, 'data length < header length' ] ] ], - [ /(\d+) with bad options$/, [ [ :packets, 'bad options' ] ] ], - [ /(\d+) with incorrect version number$/, [ [ :packets, 'incorrect version' ] ] ], - [ /(\d+) fragments? received$/, [ [ :packets, 'fragments' ] ] ], - [ /(\d+) fragments? dropped \(dup or out of space\)$/, [ [ :packets, 'frags dropped: dup/out of spc' ] ] ], - [ /(\d+) fragments? dropped after timeout$/, [ [ :packets, 'frags dropped: timeout' ] ] ], - [ /(\d+) packets? reassembled ok$/, [ [ :packets, 'reassembled ok' ] ] ], - [ /(\d+) packets? for this host$/, [ [ :packets, 'for this host' ] ] ], - [ /(\d+) packets? for unknown\/unsupported protocol$/, [ [ :packets, 'for unknown/unsup protocol' ] ] ], - [ /(\d+) packets? forwarded \((\d+) packets fast forwarded\)$/, [ [ :packets, 'forwarded' ], [ :packets, 'fast forwarded' ] ] ], - [ /(\d+) packets? not forwardable$/, [ [ :packets, 'not forwardable' ] ] ], - [ /(\d+) packets? received for unknown multicast group$/, [ [ :packets, 'unknown multicast grp' ] ] ] - ]), + Graph.new('sent', protocol, [ + [/(\d+) datagrams output$/, [[:packets, 'total']]], + [/(\d+) times multicast source filter matched$/, [[:packets, 'multicast src filter match']]] + ]) + ] + when 'ip' + $os == :linux ? [] : [ + Graph.new('received', protocol, [ + [/(\d+) total packets received$/, [[:packets, 'total']]], + [/(\d+) bad header checksums$/, [[:packets, 'bad header checksum']]], + [/(\d+) with size smaller than minimum$/, [[:packets, 'size smaller than min']]], + [/(\d+) with data size < data length$/, [[:packets, 'data size < data length']]], + [/(\d+) with ip length > max ip packet size$/, [[:packets, 'ip length > max ip packet sz']]], + [/(\d+) with header length < data size$/, [[:packets, 'header length < data size']]], + [/(\d+) with data length < header length$/, [[:packets, 'data length < header length']]], + [/(\d+) with bad options$/, [[:packets, 'bad options']]], + [/(\d+) with incorrect version number$/, [[:packets, 'incorrect version']]], + [/(\d+) fragments? received$/, [[:packets, 'fragments']]], + [/(\d+) fragments? dropped \(dup or out of space\)$/, [[:packets, 'frags dropped: dup/out of spc']]], + [/(\d+) fragments? dropped after timeout$/, [[:packets, 'frags dropped: timeout']]], + [/(\d+) packets? reassembled ok$/, [[:packets, 'reassembled ok']]], + [/(\d+) packets? for this host$/, [[:packets, 'for this host']]], + [/(\d+) packets? for unknown\/unsupported protocol$/, [[:packets, 'for unknown/unsup protocol']]], + [/(\d+) packets? forwarded \((\d+) packets fast forwarded\)$/, [[:packets, 'forwarded'], [:packets, 'fast forwarded']]], + [/(\d+) packets? not forwardable$/, [[:packets, 'not forwardable']]], + [/(\d+) packets? received for unknown multicast group$/, [[:packets, 'unknown multicast grp']]] + ]), - Graph.new('sent', protocol, [ - [ /(\d+) packets? sent from this host$/, [ [ :packets, 'total' ] ] ], - [ /(\d+) redirects? sent$/, [ [ :packets, 'redirect' ] ] ], - [ /(\d+) packets? sent with fabricated ip header$/, [ [ :packets, 'fabricated IP head' ] ] ], - [ /(\d+) output packets? dropped due to no bufs, etc\.$/, [ [ :packets, 'dropped: no bufs, etc' ] ] ], - [ /(\d+) output packets? discarded due to no route$/, [ [ :packets, 'discarded: no route' ] ] ], - [ /(\d+) output datagrams? fragmented$/, [ [ :packets, 'fragmented' ] ] ], - [ /(\d+) fragments? created$/, [ [ :packets, 'fragments created' ] ] ], - [ /(\d+) datagrams? that can't be fragmented$/, [ [ :packets, "can't be fragmented" ] ] ], - [ /(\d+) tunneling packets? that can't find gif$/, [ [ :packets, 'tunneling, gif not found' ] ] ], - [ /(\d+) datagrams? with bad address in header$/, [ [ :packets, 'bad address in header' ] ] ] - ]) - ] - when 'arp' - $os == :linux ? [] : [ - Graph.new('packets', protocol, [ - # This is just a total, so ignore the value but keep regexp to avoid 'not parsed' warning. - [ /(\d+) ARP packets? received$/ ], - [ /(\d+) ARP requests? received$/, [ [ :packets, 'requests received' ] ] ], - [ /(\d+) ARP repl(?:y|ies) received$/, [ [ :packets, 'replies received' ] ] ], - [ /(\d+) ARP requests? sent$/, [ [ :packets, 'requests', 'requests received' ] ] ], - [ /(\d+) ARP repl(?:y|ies) sent$/, [ [ :packets, 'replies', 'replies received' ] ] ], - [ /(\d+) total packets? dropped due to no ARP entry$/, [ [ :packets, 'no entry' ] ] ] - ]), + Graph.new('sent', protocol, [ + [/(\d+) packets? sent from this host$/, [[:packets, 'total']]], + [/(\d+) redirects? sent$/, [[:packets, 'redirect']]], + [/(\d+) packets? sent with fabricated ip header$/, [[:packets, 'fabricated IP head']]], + [/(\d+) output packets? dropped due to no bufs, etc\.$/, [[:packets, 'dropped: no bufs, etc']]], + [/(\d+) output packets? discarded due to no route$/, [[:packets, 'discarded: no route']]], + [/(\d+) output datagrams? fragmented$/, [[:packets, 'fragmented']]], + [/(\d+) fragments? created$/, [[:packets, 'fragments created']]], + [/(\d+) datagrams? that can't be fragmented$/, [[:packets, "can't be fragmented"]]], + [/(\d+) tunneling packets? that can't find gif$/, [[:packets, 'tunneling, gif not found']]], + [/(\d+) datagrams? with bad address in header$/, [[:packets, 'bad address in header']]] + ]) + ] + when 'arp' + $os == :linux ? [] : [ + Graph.new('packets', protocol, [ + # This is just a total, so ignore the value but keep regexp to avoid 'not parsed' warning. + [/(\d+) ARP packets? received$/], + [/(\d+) ARP requests? received$/, [[:packets, 'requests received']]], + [/(\d+) ARP repl(?:y|ies) received$/, [[:packets, 'replies received']]], + [/(\d+) ARP requests? sent$/, [[:packets, 'requests', 'requests received']]], + [/(\d+) ARP repl(?:y|ies) sent$/, [[:packets, 'replies', 'replies received']]], + [/(\d+) total packets? dropped due to no ARP entry$/, [[:packets, 'no entry']]] + ]), - Graph.new('entries', protocol, [ - [ /(\d+) ARP entrys? timed out$/, [ [ :entries, 'timed out' ] ] ], - [ /(\d+) Duplicate IPs seen$/, [ [ :entries, 'duplicate IPs seen' ] ] ] - ]) - ] - end + Graph.new('entries', protocol, [ + [/(\d+) ARP entrys? timed out$/, [[:entries, 'timed out']]], + [/(\d+) Duplicate IPs seen$/, [[:entries, 'duplicate IPs seen']]] + ]) + ] + end end proto_name = File.basename($0, '.*').escape @@ -374,31 +378,31 @@ proto_name.slice! 0, PLUGIN_NAME.size if proto_name.start_with? PLUGIN_NAME proto_name = 'tcp' if proto_name.empty? def netstat_s(protocol) - if $os == :linux - %w(tcp udp).include?(protocol) ? - `netstat -s --#{protocol}` : - `netstat -s --raw` - else - `netstat -sp #{protocol}` - end.lines.reject { |line| line =~ /^\w+:/ } + if $os == :linux + %w(tcp udp).include?(protocol) ? + `netstat -s --#{protocol}` : + `netstat -s --raw` + else + `netstat -sp #{protocol}` + end.lines.reject { |line| line =~ /^\w+:/ } end case ARGV.first when 'autoconf' - puts [:linux, :freebsd].include?($os) ? 'yes' : 'no' + puts [:linux, :freebsd].include?($os) ? 'yes' : 'no' when 'suggest' - puts $os == :linux ? %w(tcp) : %w(tcp udp ip arp) + puts $os == :linux ? %w(tcp) : %w(tcp udp ip arp) when 'config' - graphs_for(proto_name).each { |graph| - puts graph.config.join $/ - } + graphs_for(proto_name).each { |graph| + puts graph.config.join $/ + } else - data = netstat_s(proto_name) - graphs_for(proto_name).each { |graph| - puts graph.fetch(data).join $/ - } + data = netstat_s(proto_name) + graphs_for(proto_name).each { |graph| + puts graph.fetch(data).join $/ + } - warn "not parsed:\n#{data.join}" unless data.empty? if $debug_mode + warn "not parsed:\n#{data.join}" unless data.empty? if $debug_mode end # awful performance when scrolling through those regexps above diff --git a/plugins/oracle/oracle-pga-monitor b/plugins/oracle/oracle-pga-monitor index b7fd0915..a6929ad8 100755 --- a/plugins/oracle/oracle-pga-monitor +++ b/plugins/oracle/oracle-pga-monitor @@ -1,54 +1,59 @@ #!/usr/bin/env ruby -# -# Munin Plugin for PGA memory components monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-13 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) env.ORACLE_HOME set in munin-node -# 2) rubygems -# 3) oci8 - DBI gem for connecting to Oracle -# * instruction of installing oci8 is available here: -# http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/oracle__pga.rb /etc/munin/plugins/oracle__pga.rb -# ** replace with your oralce sid -# -# Parameters: -# autoconf -# config (required) -# -# Configurable variables: -# orauser : oracle user who has select privilege to query v$pgastat view -# orapass : password for the oracle user -# dbport : port used by the monitored instance (notice: numeric value) -# dbname : database to be monitored -# dbhost : host or ip address of db instance -# -# + +=begin + +Munin Plugin for PGA memory components monitoring + +Author: Wilfred Chau +Date: 2011-05-13 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) env.ORACLE_HOME set in munin-node + 2) rubygems + 3) oci8 - DBI gem for connecting to Oracle + * instruction of installing oci8 is available here: + http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/oracle__pga.rb /etc/munin/plugins/oracle__pga.rb + ** replace with your oralce sid + +Parameters: + autoconf + config (required) + +Configurable variables: + orauser : oracle user who has select privilege to query v$pgastat view + orapass : password for the oracle user + dbport : port used by the monitored instance (notice: numeric value) + dbname : database to be monitored + dbhost : host or ip address of db instance + + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'oci8' @@ -58,17 +63,16 @@ dbport = 1522 dbname = 'orcl' dbhost = 'localhost' -tnsname = "(DESCRIPTION = +tnsname = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = #{dbhost})(PORT = #{dbport})) (CONNECT_DATA = (SID = #{dbname})))" -def runQuery (name,query) - rows = $conn.exec(query) - puts "#{name}.value #{rows.fetch().to_s}" - rows.close +def runQuery(name, query) + rows = $conn.exec(query) + puts "#{name}.value #{rows.fetch().to_s}" + rows.close end - # # Queries # @@ -78,45 +82,44 @@ pga_target_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024) pga_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024),value),2)) pga from V$PGASTAT where name = 'total PGA inuse'" -pga_components = { "pga_target" => pga_target_query, - "pga_in_use" => pga_query - } +pga_components = { "pga_target" => pga_target_query, + "pga_in_use" => pga_query } # # autoconf # if ARGV[0] == "autoconf" - if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title Oracle PGA from #{dbname}" - puts "graph_category db" - puts "graph_info This graph shows the PGA memory usage (in MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_title Oracle PGA from #{dbname}" + puts "graph_category db" + puts "graph_info This graph shows the PGA memory usage (in MB)" + puts "graph_vlabel MB" + puts "graph_scale no" + puts "graph_period second" - pga_components.keys.each do |p| - puts "#{p}.label #{p}" - puts "#{p}.info PGA: #{p}" - puts "#{p}.type GAUGE" - puts "#{p}.draw LINE1" - end + pga_components.keys.each do |p| + puts "#{p}.label #{p}" + puts "#{p}.info PGA: #{p}" + puts "#{p}.type GAUGE" + puts "#{p}.draw LINE1" + end - exit 0 + exit 0 end $conn = OCI8.new(orauser, orapass, tnsname) pga_components.each do |pc, query| - runQuery(pc, query) + runQuery(pc, query) end $conn.logoff diff --git a/plugins/oracle/oracle-sga b/plugins/oracle/oracle-sga index a00ceeb1..c1aa5d12 100755 --- a/plugins/oracle/oracle-sga +++ b/plugins/oracle/oracle-sga @@ -1,53 +1,58 @@ #!/usr/bin/env ruby -# -# Munin Plugin for SGA memory components monitoring -# -# Author: Wilfred Chau -# Date: 2011-05-12 -# Version: 1.0 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# -# Prerequistes: -# 1) env.ORACLE_HOME set in munin-node -# 2) rubygems -# 3) oci8 - DBI gem for connecting to Oracle -# * instruction of installing oci8 is available here: -# http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html -# -# Usage: -# 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) -# 2) chmod to allow executable to others -# 3) create symbolic link in /etc/munin/plugins -# ln -s /usr/share/munin/plugins/oracle_orcl_sga.rb /etc/munin/plugins/oracle_orcl_sga.rb -# -# Parameters: -# autoconf -# config (required) -# -# Configurable variables: -# orauser : oracle user who has select privilege to query v$sgastat view -# orapass : password for the oracle user -# dbport : port used by the monitored instance (notice: numeric value) -# dbname : database to be monitored -# dbhost : host or ip address of db instance -# -# + +=begin + +Munin Plugin for SGA memory components monitoring + +Author: Wilfred Chau +Date: 2011-05-12 +Version: 1.0 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +Prerequistes: + 1) env.ORACLE_HOME set in munin-node + 2) rubygems + 3) oci8 - DBI gem for connecting to Oracle + * instruction of installing oci8 is available here: + http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html + +Usage: + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins + ln -s /usr/share/munin/plugins/oracle_orcl_sga.rb /etc/munin/plugins/oracle_orcl_sga.rb + +Parameters: + autoconf + config (required) + +Configurable variables: + orauser : oracle user who has select privilege to query v$sgastat view + orapass : password for the oracle user + dbport : port used by the monitored instance (notice: numeric value) + dbname : database to be monitored + dbhost : host or ip address of db instance + + #%# family=auto #%# capabilities=autoconf +=end + + require 'rubygems' require 'oci8' @@ -57,17 +62,16 @@ dbport = 1522 dbname = 'orcl' dbhost = 'localhost' -tnsname = "(DESCRIPTION = +tnsname = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = #{dbhost})(PORT = #{dbport})) (CONNECT_DATA = (SID = #{dbname})))" -def runQuery (name,query) - rows = $conn.exec(query) - puts "#{name}.value #{rows.fetch().to_s}" - rows.close +def runQuery(name, query) + rows = $conn.exec(query) + puts "#{name}.value #{rows.fetch().to_s}" + rows.close end - # # Queries # @@ -98,56 +102,54 @@ log_buffer_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, NULL, decode(name, 'log_buffer', (bytes)/(1024*1024),0),0)),2)) sga_lbuffer from V$SGASTAT" - -memory_components = { "fixed_area" => fixed_area_query, +memory_components = { "fixed_area" => fixed_area_query, "buffer_cache" => buffer_cache_query, - "java_pool" => java_pool_query, - "large_pool" => large_pool_query, - "log_buffer" => log_buffer_query, - "shared_pool" => shared_pool_query - } + "java_pool" => java_pool_query, + "large_pool" => large_pool_query, + "log_buffer" => log_buffer_query, + "shared_pool" => shared_pool_query } # # autoconf # if ARGV[0] == "autoconf" - if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 - puts "yes" - else - puts "no" - puts "Usage: #{__FILE__} autoconf|conf" - end - exit 0 + if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 + puts "yes" + else + puts "no" + puts "Usage: #{__FILE__} autoconf|conf" + end + exit 0 # # config definition # elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title Oracle SGA from #{dbname}" - puts "graph_category db" - puts "graph_info This graph shows the SGA memory usage (in MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts "graph_args --base 1024k -r --lower-limit 0" + puts "graph_title Oracle SGA from #{dbname}" + puts "graph_category db" + puts "graph_info This graph shows the SGA memory usage (in MB)" + puts "graph_vlabel MB" + puts "graph_scale no" + puts "graph_period second" - memory_components.keys.each do |m| - puts "#{m}.label #{m}" - puts "#{m}.info SGA: #{m}" - puts "#{m}.type GAUGE" + memory_components.keys.each do |m| + puts "#{m}.label #{m}" + puts "#{m}.info SGA: #{m}" + puts "#{m}.type GAUGE" - # make sure fixed_area is at the bottom of the stack - if ( m == 'fixed_area' ) - puts "#{m}.draw AREA" - else - puts "#{m}.draw STACK" - end - end + # make sure fixed_area is at the bottom of the stack + if (m == 'fixed_area') + puts "#{m}.draw AREA" + else + puts "#{m}.draw STACK" + end + end - exit 0 + exit 0 end $conn = OCI8.new(orauser, orapass, tnsname) memory_components.each do |mc, query| - runQuery(mc, query) + runQuery(mc, query) end $conn.logoff diff --git a/plugins/other/delayed_jobs_queue_size b/plugins/other/delayed_jobs_queue_size index cabb9bbb..28fb638c 100755 --- a/plugins/other/delayed_jobs_queue_size +++ b/plugins/other/delayed_jobs_queue_size @@ -14,18 +14,17 @@ require 'mysql' require 'yaml' class Grapher - def initialize(db_conf) @db_conf = db_conf end def config - puts <<-END_CONFIG -graph_title Delayed_Jobs Queue Size -graph_args -l 0 -graph_vlabel jobs to be run -jobs.label jobs -jobs.type GAUGE + puts <<~END_CONFIG + graph_title Delayed_Jobs Queue Size + graph_args -l 0 + graph_vlabel jobs to be run + jobs.label jobs + jobs.type GAUGE END_CONFIG end @@ -39,7 +38,6 @@ jobs.type GAUGE value = result.fetch_hash.values.first puts "jobs.value #{value}" end - end if __FILE__ == $0 diff --git a/plugins/other/port_ b/plugins/other/port_ index 32f70ab2..585166f5 100755 --- a/plugins/other/port_ +++ b/plugins/other/port_ @@ -15,64 +15,62 @@ require 'rubygems' require 'munin' -SERVICE = $0.split( '_' ).last +SERVICE = $0.split('_').last SERVICE_F = '/etc/services' -PORT = /^[\d]+(\.[\d]+){0,1}$/ === SERVICE ? SERVICE : %x[grep #{SERVICE} #{SERVICE_F}].split( "\t\t" )[1].split( '/' )[0] +PORT = /^[\d]+(\.[\d]+){0,1}$/ === SERVICE ? SERVICE : %x[grep #{SERVICE} #{SERVICE_F}].split("\t\t")[1].split('/')[0] class PortMonit < Munin::Plugin - graph_attributes "#{SERVICE} port usage, known as #{PORT}", - :category => 'network', - :info => 'This graph shows connection split by the state of the socket.', - :vlabel => 'Current connections' + :category => 'network', + :info => 'This graph shows connection split by the state of the socket.', + :vlabel => 'Current connections' - declare_field :ESTABLISHED, - :label => 'Established', :draw => :AREA, - :type => :GAUGE, :min => 0 + declare_field :ESTABLISHED, + :label => 'Established', :draw => :AREA, + :type => :GAUGE, :min => 0 - declare_field :CLOSE_WAIT, - :label => 'Waiting close', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :CLOSE_WAIT, + :label => 'Waiting close', :draw => :STACK, + :type => :GAUGE, :min => 0 - declare_field :TIME_WAIT, - :label => 'Waiting after close', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :TIME_WAIT, + :label => 'Waiting after close', :draw => :STACK, + :type => :GAUGE, :min => 0 - declare_field :CLOSING, - :label => 'Closing', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :CLOSING, + :label => 'Closing', :draw => :STACK, + :type => :GAUGE, :min => 0 - declare_field :LAST_ACK, - :label => 'Waiting for acknowledgement', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :LAST_ACK, + :label => 'Waiting for acknowledgement', :draw => :STACK, + :type => :GAUGE, :min => 0 - declare_field :FIN_WAIT_1, - :label => 'Socket closed, connection shutting down', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :FIN_WAIT_1, + :label => 'Socket closed, connection shutting down', :draw => :STACK, + :type => :GAUGE, :min => 0 - declare_field :FIN_WAIT_2, - :label => 'Connection closed, Socket still waiting', :draw => :STACK, - :type => :GAUGE, :min => 0 + declare_field :FIN_WAIT_2, + :label => 'Connection closed, Socket still waiting', :draw => :STACK, + :type => :GAUGE, :min => 0 def retrieve_values + @_netstat = %x[netstat -n -P tcp | egrep "\.#{PORT} "].split("\n") - @_netstat = %x[netstat -n -P tcp | egrep "\.#{PORT} "].split( "\n" ) - - - { :ESTABLISHED => count( @_netstat, 'ESTABLISHED' ), - :CLOSE_WAIT => count( @_netstat, 'CLOSE_WAIT' ), - :CLOSING => count( @_netstat, 'CLOSING' ), - :LAST_ACK => count( @_netstat, 'LAST_ACK' ), - :FIN_WAIT_1 => count( @_netstat, 'FIN_WAIT_1' ), - :FIN_WAIT_2 => count( @_netstat, 'FIN_WAIT_2' ), - :TIME_WAIT => count( @_netstat, 'TIME_WAIT' ) } + { :ESTABLISHED => count(@_netstat, 'ESTABLISHED'), + :CLOSE_WAIT => count(@_netstat, 'CLOSE_WAIT'), + :CLOSING => count(@_netstat, 'CLOSING'), + :LAST_ACK => count(@_netstat, 'LAST_ACK'), + :FIN_WAIT_1 => count(@_netstat, 'FIN_WAIT_1'), + :FIN_WAIT_2 => count(@_netstat, 'FIN_WAIT_2'), + :TIME_WAIT => count(@_netstat, 'TIME_WAIT') } end private - def count( source, regex ) + + def count(source, regex) @_result = 0 - source.each { |obj| @_result += 1 if obj.match( regex ) } + source.each { |obj| @_result += 1 if obj.match(regex) } return @_result end diff --git a/plugins/passenger/passenger_memory b/plugins/passenger/passenger_memory index b63a361c..c1138457 100755 --- a/plugins/passenger/passenger_memory +++ b/plugins/passenger/passenger_memory @@ -24,10 +24,10 @@ passenger_rss = nil `#{memory_stats_command}`.each_line do |line| next unless /### Total private dirty RSS: (\d+\.\d+) MB/.match(line) + passenger_rss = $~[1] unless apache_rss.nil? apache_rss ||= $~[1] end puts "apache_rss.value #{apache_rss}" puts "passenger_rss.value #{passenger_rss}" - diff --git a/plugins/passenger/passenger_processes b/plugins/passenger/passenger_processes index d8097e96..1af4e27b 100755 --- a/plugins/passenger/passenger_processes +++ b/plugins/passenger/passenger_processes @@ -39,4 +39,3 @@ puts "max.value #{max}" puts "count.value #{count}" puts "active.value #{active}" puts "queued.value #{queued.to_i}" - diff --git a/plugins/passenger/passenger_status b/plugins/passenger/passenger_status index 2cb44099..8c992c54 100755 --- a/plugins/passenger/passenger_status +++ b/plugins/passenger/passenger_status @@ -1,27 +1,27 @@ #!/usr/bin/env ruby def output_config - puts <<-END -graph_category webserver -graph_title status -graph_vlabel count -graph_info This graph shows how much passenger process are working, available and how much queries are waiting. -max.label max processes -max.draw AREA -max.info Maximum processes allowed to run simultaneously. -sessions.label queued requests -sessions.draw LINE2 -sessions.info Requests queued, waiting to be processed. -running.label running processes -running.draw LINE1 -running.info The number of application instances that are currently alive. -active.label active processes -active.draw LINE1 -active.info The number of application instances that are currently processing requests. -waiting.label waiting requests -waiting.draw LINE2 -waiting.info Requests waiting to be queued. -END + puts <<~END + graph_category webserver + graph_title status + graph_vlabel count + graph_info This graph shows how much passenger process are working, available and how much queries are waiting. + max.label max processes + max.draw AREA + max.info Maximum processes allowed to run simultaneously. + sessions.label queued requests + sessions.draw LINE2 + sessions.info Requests queued, waiting to be processed. + running.label running processes + running.draw LINE1 + running.info The number of application instances that are currently alive. + active.label active processes + active.draw LINE1 + active.info The number of application instances that are currently processing requests. + waiting.label waiting requests + waiting.draw LINE2 + waiting.info Requests waiting to be queued. + END exit 0 end diff --git a/plugins/php/php_eaccelerator b/plugins/php/php_eaccelerator index 7c08adda..1d89df71 100755 --- a/plugins/php/php_eaccelerator +++ b/plugins/php/php_eaccelerator @@ -31,7 +31,7 @@ user = ENV['user'] || 'user' pwd = ENV['password'] || 'password' url = ENV['url'] || 'http://127.0.0.1/control.php' -if ARGV[0]=="config" +if ARGV[0] == "config" print "EAccelerator Monitoring\n" print "graph_title PHP Eaccelerator\n" print "graph_category webserver\n" @@ -47,58 +47,56 @@ if ARGV[0]=="config" exit end -one_liners=0 -three_liners=0 -key="" +one_liners = 0 +three_liners = 0 +key = "" -open(url, :http_basic_authentication=>[user, pwd]) do |f| +open(url, :http_basic_authentication => [user, pwd]) do |f| f.each do |line| - if three_liners>0 - three_liners=three_liners+1 + if three_liners > 0 + three_liners = three_liners + 1 - if three_liners==2 + if three_liners == 2 print "Memoryusagepercentage.value " end - if three_liners==3 + if three_liners == 3 print "Memoryusage.value " end - if three_liners==4 + if three_liners == 4 print "Memorymax.value " end - print line.gsub!(/[^0-9.]/s,"") + print line.gsub!(/[^0-9.]/s, "") print "\n" end - if one_liners>0 - one_liners=one_liners+1 + if one_liners > 0 + one_liners = one_liners + 1 print "#{key}.value " - print line.gsub!(/[^0-9.]/s,"") + print line.gsub!(/[^0-9.]/s, "") print "\n" end - if one_liners>1 - line="" - one_liners=0 + if one_liners > 1 + line = "" + one_liners = 0 end - if three_liners>3 - line="" - three_liners=0 + if three_liners > 3 + line = "" + three_liners = 0 end if line =~ /Memory usage/ - key=line.gsub!(/(<[^>]*>)|\n|\t| /s,"") - three_liners=three_liners+1 + key = line.gsub!(/(<[^>]*>)|\n|\t| /s, "") + three_liners = three_liners + 1 end - if line =~ /Free memory/ || line =~ /Cached scripts/ || line =~ /Removed scripts/ || line =~ /Cached keys/ - key=line.gsub!(/(<[^>]*>)|\n|\t| /s,"") - one_liners=one_liners+1 + if line =~ /Free memory/ || line =~ /Cached scripts/ || line =~ /Removed scripts/ || line =~ /Cached keys/ + key = line.gsub!(/(<[^>]*>)|\n|\t| /s, "") + one_liners = one_liners + 1 end - end end - diff --git a/plugins/router/ag241-adsl b/plugins/router/ag241-adsl index 23c6174c..1aba2300 100755 --- a/plugins/router/ag241-adsl +++ b/plugins/router/ag241-adsl @@ -1,51 +1,56 @@ #!/usr/bin/env ruby -# munin plugin to retrieve connection statistics from the web admin interface -# on a Linksys AG241v2 ADSL modem -# Makes use of the http://modemaddress/ADSLCStatus.htm page -#This plugin has only been tested on a Debian testing system +=begin -# This modem also has some basic SNMP support so you can configure it -# as per the instructions on the munin wiki -# http://munin.projects.linpro.no/wiki/Using_SNMP_plugins -# By default the SNMP server is disabled, you can enable it in the web admin -# You will need to set up the "virtual node" configuration as detailed -# for snmp plugins +munin plugin to retrieve connection statistics from the web admin interface +on a Linksys AG241v2 ADSL modem +Makes use of the http://modemaddress/ADSLCStatus.htm page -# Plugin will require some configuration in /etc/munin/plugin-conf.d/ag241_MODEMADDRESS -# e.g. -# [ag241_vocume.stargate_*] -# env.user admin -# env.pass password -# #env.port 80 +This plugin has only been tested on a Debian testing system -# Once you have the above config set you will need to symlink the plugin to -# /etc/munin/plugins/ag241_MODEMADDRESS_syncrate -# /etc/munin/plugins/ag241_MODEMADDRESS_attenutation -# /etc/munin/plugins/ag241_MODEMADDRESS_noise -# now restart munin-node. -# hopefully in 20-30mins you will have some nice graphs +This modem also has some basic SNMP support so you can configure it +as per the instructions on the munin wiki +http://munin.projects.linpro.no/wiki/Using_SNMP_plugins +By default the SNMP server is disabled, you can enable it in the web admin +You will need to set up the "virtual node" configuration as detailed +for snmp plugins +Plugin will require some configuration in /etc/munin/plugin-conf.d/ag241_MODEMADDRESS +e.g. +[ag241_vocume.stargate_*] +env.user admin +env.pass password +#env.port 80 -#Some magical munin foo... +Once you have the above config set you will need to symlink the plugin to +/etc/munin/plugins/ag241_MODEMADDRESS_syncrate +/etc/munin/plugins/ag241_MODEMADDRESS_attenutation +/etc/munin/plugins/ag241_MODEMADDRESS_noise +now restart munin-node. +hopefully in 20-30mins you will have some nice graphs + +Some magical munin foo... #%# family=manual #%# capabilities= +=end + + # Require this module, it is part of the standard ruby lib AFAIK require 'net/http' -#default parameters +# default parameters host = nil port = ENV['port'] || 80 user = ENV['user'] || 'admin' -pass = ENV['pass'] || 'forhax' #don't remember what the default admin password was +pass = ENV['pass'] || 'forhax' # don't remember what the default admin password was stat = nil # Check executable "name" for parameter count params = $0.split('_') if params.size != 3 - puts "Incorrect number of parameters" - exit 1 + puts "Incorrect number of parameters" + exit 1 end # first param after the plugin name is the host to query @@ -54,79 +59,79 @@ host = params[1] stat = params[2] unless ENV['debug'].nil? - puts "user = "+ user - puts "pass = "+ pass - puts "host = "+ host - puts "port = "+ port - puts "stat = "+ stat + puts "user = " + user + puts "pass = " + pass + puts "host = " + host + puts "port = " + port + puts "stat = " + stat end # Dump the graph configuration data if ARGV[0] == 'config' - puts 'host_name ' + host - puts 'graph_category network' + puts 'host_name ' + host + puts 'graph_category network' - case stat - when 'syncrate' - puts 'graph_info This graph shows the ADSL line sync rate.' - puts 'graph_title ADSL line sync rate' - puts 'graph_vlabel connection rate bits / second' - puts 'graph_args --base 1000 -l 0 ' - when 'attenuation' - puts 'graph_info This graph shows the ADSL line attenuation.' - puts 'graph_title ADSL line attenuation' - puts 'graph_vlabel attenuation dB' - when 'margin','noise' - puts 'graph_info This graph shows the ADSL SNR margin.' - puts 'graph_title ADSL line SNR margin' - puts 'graph_vlabel noise margin dB' - end - puts 'down.label downstream' - puts 'up.label upstream' - exit 0 + case stat + when 'syncrate' + puts 'graph_info This graph shows the ADSL line sync rate.' + puts 'graph_title ADSL line sync rate' + puts 'graph_vlabel connection rate bits / second' + puts 'graph_args --base 1000 -l 0 ' + when 'attenuation' + puts 'graph_info This graph shows the ADSL line attenuation.' + puts 'graph_title ADSL line attenuation' + puts 'graph_vlabel attenuation dB' + when 'margin', 'noise' + puts 'graph_info This graph shows the ADSL SNR margin.' + puts 'graph_title ADSL line SNR margin' + puts 'graph_vlabel noise margin dB' + end + puts 'down.label downstream' + puts 'up.label upstream' + exit 0 end # Connect to the webadmin -http = Net::HTTP.start(host,port) +http = Net::HTTP.start(host, port) req = Net::HTTP::Get.new('/ADSLCStatus.htm') # send the login info req.basic_auth user, pass response = http.request(req) s = response.body -#Make sure we got the page successfully +# Make sure we got the page successfully if response.code != '200' - puts "Getting web page failed:" - case response.code - when '401' - puts 'Probably because the username and password are incorrect' - #Looks like the modem response with 200 when you try to access a page that does not exist >_> - #when '404' - # puts 'Looks like the page this plugin needs isn\'t available...' - # puts 'Check your modem make/model/version' - end - puts s - exit 1 + puts "Getting web page failed:" + case response.code + when '401' + puts 'Probably because the username and password are incorrect' + # Looks like the modem response with 200 when you try to access a page that does not exist >_> + # when '404' + # puts 'Looks like the page this plugin needs isn\'t available...' + # puts 'Check your modem make/model/version' + end + puts s + exit 1 end # Apply voodoo regex to the result HTML to get the data we want. case stat - when 'syncrate' - a = s.scan(/.*share\.curstate.*\n.*share\.downstr[^0-9]*([0-9]+).*share\.upstr[^0-9]*([0-9]+).*$/) - b,c = a[0] - puts 'down.value '+ (b.to_i*1000).to_s + "\n" + 'up.value '+ (c.to_i*1000).to_s - exit 0 - when 'attenuation' - a = s.scan(/.*share\.lineatt.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) - b,c = a[0] - puts 'down.value '+ (b.to_i).to_s + "\n" + 'up.value '+ (c.to_i).to_s - exit 0 - when 'margin','noise' - a = s.scan(/.*share\.noise.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) - b,c = a[0] - puts 'down.value '+ (b.to_i).to_s + "\n" + 'up.value '+ (c.to_i).to_s - exit 0 - else - puts 'Statistic ' + stat.to_s + ' not known, would you like me to fabricate it for you?' - exit 1 +when 'syncrate' + a = s.scan(/.*share\.curstate.*\n.*share\.downstr[^0-9]*([0-9]+).*share\.upstr[^0-9]*([0-9]+).*$/) + b, c = a[0] + puts 'down.value ' + (b.to_i * 1000).to_s + "\n" + 'up.value ' + (c.to_i * 1000).to_s + exit 0 +when 'attenuation' + a = s.scan(/.*share\.lineatt.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) + b, c = a[0] + puts 'down.value ' + (b.to_i).to_s + "\n" + 'up.value ' + (c.to_i).to_s + exit 0 +when 'margin', 'noise' + a = s.scan(/.*share\.noise.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) + b, c = a[0] + puts 'down.value ' + (b.to_i).to_s + "\n" + 'up.value ' + (c.to_i).to_s + exit 0 +else + puts 'Statistic ' + stat.to_s + ' not known, would you like me to fabricate it for you?' + exit 1 end diff --git a/plugins/router/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin index adc0afff..b8712d49 100755 --- a/plugins/router/d-link-dir-655-router-statistics-plugin +++ b/plugins/router/d-link-dir-655-router-statistics-plugin @@ -31,7 +31,6 @@ require 'mechanize' require 'digest/md5' require 'nokogiri' - def output nics = Hash.new nics["LAN"] = Hash.new @@ -80,7 +79,7 @@ def output j = 0 doc = Nokogiri::XML(clients_xml.to_s) doc.xpath('//assoc').each do |assoc| - j+=1 + j += 1 end puts "wifi_assoc.value " + j.to_s @@ -89,7 +88,7 @@ def output j = 0 doc = Nokogiri::XML(clients_xml.to_s) doc.xpath('//client').each do |client| - j+=1 + j += 1 end puts "dhcp_clients.value " + j.to_s @@ -98,31 +97,31 @@ def output clients_xml = agent.get("#{router_path}/wan_connection_status.xml").body doc = Nokogiri::XML(clients_xml.to_s) uptime = doc.children.search('wan_interface_up_time_0')[0].text - puts "uptime.value " + sprintf( "%.2f", (Float(uptime)/86400) ) + puts "uptime.value " + sprintf("%.2f", (Float(uptime) / 86400)) # graph overall interface packets transferred per interval puts "multigraph if_packets" - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_recv.value " + nics[i]["packets_received"] puts "#{i}_send.value " + nics[i]["packets_sent"] end # graph overall interface dropped packets per interval puts "multigraph if_drop" - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_recv.value " + nics[i]["rx_dropped"] puts "#{i}_send.value " + nics[i]["tx_dropped"] end # graph overall interface collisions & errors per interval puts "multigraph if_collerr" - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_coll.value " + nics[i]["tx_collisions"] puts "#{i}_err.value " + nics[i]["rx_errors"] end # graph stats for each interface - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "multigraph if_packets.#{i}" puts "send.value " + nics[i]["packets_sent"] puts "recv.value " + nics[i]["packets_received"] @@ -135,7 +134,6 @@ def output end end - def config # build the configuration for graphs puts "multigraph if_packets" @@ -143,7 +141,7 @@ def config puts 'graph_category network' puts 'graph_order LAN_recv LAN_send WAN_recv WAN_send WLAN_recv WLAN_send' puts 'graph_vlabel packets in (-) / out (+) per ${graph_period}' - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_recv.type DERIVE" puts "#{i}_recv.graph no" puts "#{i}_recv.min 0" @@ -158,7 +156,7 @@ def config puts 'graph_category network' puts 'graph_order LAN_recv LAN_send WAN_recv WAN_send WLAN_recv WLAN_send' puts 'graph_vlabel packets / ${graph_period}' - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_recv.type DERIVE" puts "#{i}_recv.graph no" puts "#{i}_recv.min 0" @@ -173,7 +171,7 @@ def config puts 'graph_category network' puts 'graph_order LAN_coll LAN_err WAN_coll WAN_err WLAN_coll WLAN_coll' puts 'graph_vlabel packets / ${graph_period}' - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "#{i}_coll.label #{i} collisions" puts "#{i}_coll.type DERIVE" puts "#{i}_coll.min 0" @@ -201,7 +199,7 @@ def config puts 'uptime.label uptime' puts 'uptime.draw AREA' - for i in [ "LAN", "WAN", "WLAN" ] do + for i in ["LAN", "WAN", "WLAN"] do puts "multigraph if_packets.#{i}" puts "graph_title D-Link DIR-655 #{i} traffic" puts 'graph_category network' @@ -244,7 +242,6 @@ def config end end - # main if ARGV.length == 1 and ARGV[0] == 'config' config() diff --git a/plugins/snmp/snmp_room_alert_ b/plugins/snmp/snmp_room_alert_ index 8c4ec86e..d2e63dec 100755 --- a/plugins/snmp/snmp_room_alert_ +++ b/plugins/snmp/snmp_room_alert_ @@ -89,6 +89,7 @@ when "config" if not is_vb_valid(vb, subchannel) next end + puts "#{field_name(unit, vb, letter)}.label #{label(unit, vb)}" end end @@ -96,7 +97,6 @@ when "config" exit 0 end - units.each do |unit| SNMP::Manager.open(:Host => unit, :Community => community, @@ -105,6 +105,7 @@ units.each do |unit| if not is_vb_valid(vb, subchannel) next end + puts "#{field_name(unit, vb, letter)}.value #{vb.value.to_f / 100}" end end diff --git a/plugins/ssh/sshd_invalid_countries_ruby b/plugins/ssh/sshd_invalid_countries_ruby index 88cdce40..2664ec78 100755 --- a/plugins/ssh/sshd_invalid_countries_ruby +++ b/plugins/ssh/sshd_invalid_countries_ruby @@ -1,50 +1,56 @@ #!/usr/bin/env ruby -# Plugin to monitor the number of invalid access to sshd per country -# -# Require read permissions for SYSLOG -# ref) ls -l /var/log/secure -# Require geoip rubygem -# ref) http://geoip.rubyforge.org/ -# Require GeoIP-database for searching ip or host for the country -# ref) http://www.maxmind.com/app/geoip_country -# -# Parameters: -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# Revision 1.0 2010/12/25 11:56:12 hirata yoshiyuki -# released. -# -# Magick markers (optional): + +=begin + +Plugin to monitor the number of invalid access to sshd per country + +Require read permissions for SYSLOG + ref) ls -l /var/log/secure +Require geoip rubygem + ref) http://geoip.rubyforge.org/ +Require GeoIP-database for searching ip or host for the country + ref) http://www.maxmind.com/app/geoip_country + +Parameters: + config (required) + autoconf (optional - used by munin-config) + +$Log$ +Revision 1.0 2010/12/25 11:56:12 hirata yoshiyuki + released. + +Magick markers (optional): #%# family=auto #%# capabilities=autoconf -# -# config example for /etc/munin/plugin-conf.d/munin-node -#[sshd_invalid_countries_ruby] -#user root -#group root -#env.logfile /var/log/secure -#env.geoip /home/you/GeoIP.dat -#env.loadpath /usr/local/lib/ruby/gems/1.9.1/gems/geoip-0.8.8/lib/ + +config example for /etc/munin/plugin-conf.d/munin-node +[sshd_invalid_countries_ruby] +user root +group root +env.logfile /var/log/secure +env.geoip /home/you/GeoIP.dat +env.loadpath /usr/local/lib/ruby/gems/1.9.1/gems/geoip-0.8.8/lib/ + +=end + require (ENV['loadpath'] || '') + 'geoip' SYSLOG = ENV['syslog'] || '/var/log/secure' GEOIP_DB = ENV['geoip'] || '/var/www/conf/bbs/GeoIP.dat' AWK_CMD = 'awk \'/sshd\[.*Did not receive identification string/{print $12} ' + - '/sshd\[.*Failed password for (root|ROOT)/{print $11} ' + - '/sshd\[.*Invalid user/{print $10}a\' < ' + SYSLOG + '/sshd\[.*Failed password for (root|ROOT)/{print $11} ' + + '/sshd\[.*Invalid user/{print $10}a\' < ' + SYSLOG def getInvalids - c={} + c = {} wholeips = `#{AWK_CMD}`.split("\n") uniqueips = wholeips.inject({}) do |hash, key| hash.include?(key) ? hash[key] += 1 : hash[key] = 1; hash end geoip = GeoIP.new(GEOIP_DB) - uniqueips.each do |ip,cnt| + uniqueips.each do |ip, cnt| begin country = geoip.country(ip)[5] c[country] = c[country] ? c[country] + cnt : cnt @@ -52,7 +58,7 @@ def getInvalids c['Unknown'] = c['Unknown'] ? c['Unknown'] + cnt : cnt end end - c = c.to_a.sort {|a,b| a[0] <=> b[0]} + c = c.to_a.sort { |a, b| a[0] <=> b[0] } c end @@ -73,8 +79,8 @@ when 'config' puts 'graph_vlabel number of invalid access per country' puts 'graph_category security' puts 'graph_info This graph shows the countries of invalid access to sshd.' - getInvalids.each {|k,v| puts k + '.label ' + k} + getInvalids.each { |k, v| puts k + '.label ' + k } exit 0 else - getInvalids.each {|k,v| puts k + '.value ' + v.to_s} + getInvalids.each { |k, v| puts k + '.value ' + v.to_s } end diff --git a/plugins/thin/thin_memory b/plugins/thin/thin_memory index 0c5aa89d..867e52ce 100755 --- a/plugins/thin/thin_memory +++ b/plugins/thin/thin_memory @@ -1,50 +1,55 @@ #!/usr/bin/env ruby -# thin_memory - A munin plugin for Linux to monitor memory size of each individual thin process -# -# For Linux ONLY ! -# DOES NOT WORK on OSX, Solaris or BSD. -# only linux, because this script relies on proc filesystem -# -# Original author: -# Frederico de Souza Araujo - fred.the.master@gmail.com -# http://www.frederico-araujo.com -# -# Usurper: -# Adam Michel - elfurbe@furbism.com -# http://www.furbism.com -# -# Originally based on: -# thin_process_memory - -# A munin plugin to monitor memory size of -# each individual thin process -# by Ben VandenBos and Avvo, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# + +=begin + +thin_memory - A munin plugin for Linux to monitor memory size of each individual thin process + +For Linux ONLY ! +DOES NOT WORK on OSX, Solaris or BSD. +only linux, because this script relies on proc filesystem + +Original author: + Frederico de Souza Araujo - fred.the.master@gmail.com + http://www.frederico-araujo.com + +Usurper: + Adam Michel - elfurbe@furbism.com + http://www.furbism.com + +Originally based on: + thin_process_memory - + A munin plugin to monitor memory size of + each individual thin process + by Ben VandenBos and Avvo, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + #%# family=auto #%# capabilities=autoconf +=end + + module Munin class ThinProcessMemory - # run main method def run instances = get_pids() instances.each do |instance| pid, port = instance.split("|") - rss = (pid_rss(pid).to_i)/1024 + rss = (pid_rss(pid).to_i) / 1024 puts "thin_#{port}.value #{rss}" end end @@ -67,7 +72,6 @@ module Munin def autoconf get_pids().length > 0 end - end end diff --git a/plugins/thin/thin_threads b/plugins/thin/thin_threads index 055fc657..6be71004 100755 --- a/plugins/thin/thin_threads +++ b/plugins/thin/thin_threads @@ -1,45 +1,50 @@ #!/usr/bin/env ruby -# thin_threads - -# A munin plugin for Linux to monitor how many threads per thin process -# -# For Linux ONLY ! -# DOES NOT WORK on OSX, Solaris or BSD. -# only linux, because this script relies on proc filesystem -# -# Original author: -# Frederico de Souza Araujo - fred.the.master@gmail.com -# http://www.frederico-araujo.com -# -# Usurper: -# Adam Michel - elfurbe@furbism.com -# http://www.furbism.com -# -# Originally based on: -# thin_process_memory - -# A munin plugin to monitor memory size of -# each individual thin process -# by Ben VandenBos and Avvo, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# + +=begin + +thin_threads - + A munin plugin for Linux to monitor how many threads per thin process + +For Linux ONLY ! +DOES NOT WORK on OSX, Solaris or BSD. +only linux, because this script relies on proc filesystem + +Original author: + Frederico de Souza Araujo - fred.the.master@gmail.com + http://www.frederico-araujo.com + +Usurper: + Adam Michel - elfurbe@furbism.com + http://www.furbism.com + +Originally based on: + thin_process_memory - + A munin plugin to monitor memory size of + each individual thin process + by Ben VandenBos and Avvo, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + #%# family=auto #%# capabilities=autoconf +=end + + module Munin class ThinThreads - def run instances = get_pids() instances.each do |instance| @@ -71,7 +76,6 @@ module Munin def autoconf get_pids().length > 0 end - end end diff --git a/plugins/thin/thins_peak_memory b/plugins/thin/thins_peak_memory index bdc0a40f..56d547a6 100755 --- a/plugins/thin/thins_peak_memory +++ b/plugins/thin/thins_peak_memory @@ -1,47 +1,52 @@ #!/usr/bin/env ruby -# thin_peak_memory - -# A munin plugin for Linux to monitor the maximum memory size -# that an each individual thin process has reached -# -# For Linux ONLY ! -# DOES NOT WORK on OSX, Solaris or BSD. -# only linux, because this script relies on proc filesystem -# -# Author: -# Frederico de Souza Araujo - fred.the.master@gmail.com -# http://www.frederico-araujo.com -# -# Based on: -# thin_process_memory - -# A munin plugin to monitor memory size of -# each individual thin process -# by Ben VandenBos and Avvo, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# + +=begin + +thin_peak_memory - + A munin plugin for Linux to monitor the maximum memory size + that an each individual thin process has reached + +For Linux ONLY ! +DOES NOT WORK on OSX, Solaris or BSD. +only linux, because this script relies on proc filesystem + +Author: + Frederico de Souza Araujo - fred.the.master@gmail.com + http://www.frederico-araujo.com + +Based on: + thin_process_memory - + A munin plugin to monitor memory size of + each individual thin process + by Ben VandenBos and Avvo, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + #%# family=auto #%# capabilities=autoconf +=end + + module Munin class ThinPeakMemory - def run instances = get_pids() instances.each do |instance| pid, port = instance.split("|") - hwm = (pid_hwm(pid).to_i)/1024 + hwm = (pid_hwm(pid).to_i) / 1024 puts "thin_#{port}.value #{hwm}" end end @@ -69,7 +74,6 @@ module Munin def autoconf get_pids().length > 0 end - end end diff --git a/plugins/unicorn/unicorn_memory_status b/plugins/unicorn/unicorn_memory_status index a11aa2bf..a32543d9 100755 --- a/plugins/unicorn/unicorn_memory_status +++ b/plugins/unicorn/unicorn_memory_status @@ -32,7 +32,8 @@ module Munin chunks = line.strip.split(/\s+/, 5) pid, pcmd = chunks[0], chunks[4] next if pid !~ /\A\d+\z/ or pcmd !~ /worker/ - result << pid.to_i + + result << pid.to_i end result end @@ -48,7 +49,7 @@ module Munin end def memory_usage - result = { :master => {master_pid => nil}, :worker => {} } + result = { :master => { master_pid => nil }, :worker => {} } ps_output = `ps auxw | grep unicorn` ps_output.split("\n").each do |line| chunks = line.strip.split(/\s+/, 11) diff --git a/plugins/unicorn/unicorn_status b/plugins/unicorn/unicorn_status index 3422f992..7db1911b 100755 --- a/plugins/unicorn/unicorn_status +++ b/plugins/unicorn/unicorn_status @@ -32,7 +32,8 @@ module Munin chunks = line.strip.split(/\s+/, 5) pid, pcmd = chunks[0], chunks[4] next if pid !~ /\A\d+\z/ or pcmd !~ /worker/ - result << pid.to_i + + result << pid.to_i end result end From 809639ab65d5460442c3b88beea0dace7665f416 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 17:06:15 +0200 Subject: [PATCH 680/695] Ruby plugins: apply code changes as suggested by "rubocop --auto-correct" --- .rubocop.yml | 7 + plugins/bsd/netstat_bsd_m_ | 29 +- plugins/ejabberd/ejabberd_scanlog | 36 +- plugins/http/mongrel_memory | 37 +- plugins/http/mongrel_process_memory | 51 +- plugins/icecast/icecast2_simple | 47 +- plugins/minecraft/minecraft-users | 24 +- plugins/moblock/moblock_connections | 49 +- plugins/mssql/microsoft-sql | 53 +- .../microsoft-sql-buffer-cache-hit-ratio | 57 ++- plugins/mssql/microsoft-sql-data-file-sizes | 53 +- plugins/mssql/microsoft-sql-log-file-size | 53 +- plugins/network/http__tp_link | 64 ++- plugins/network/netstat_s_ | 473 +++++++++--------- plugins/oracle/oracle-pga-monitor | 60 ++- plugins/oracle/oracle-sga | 70 ++- plugins/other/port_ | 56 +-- plugins/passenger/passenger_memory | 10 +- plugins/passenger/passenger_processes | 23 +- plugins/passenger/passenger_status | 12 +- plugins/php/php_eaccelerator | 38 +- plugins/puppet/puppet_runtime | 24 +- plugins/router/ag241-adsl | 19 +- .../d-link-dir-655-router-statistics-plugin | 128 ++--- plugins/router/snmp__linksys_poe | 42 +- plugins/snmp/snmp_room_alert_ | 60 +-- plugins/ssh/sshd_invalid_countries_ruby | 15 +- plugins/thin/thin_memory | 38 +- plugins/thin/thin_threads | 40 +- plugins/thin/thins_peak_memory | 38 +- plugins/unicorn/unicorn_memory_status | 31 +- plugins/unicorn/unicorn_status | 25 +- plugins/voldemort/voldemort | 52 +- 33 files changed, 904 insertions(+), 910 deletions(-) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..31bda550 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,7 @@ +# Allow "=begin"/"=end" multiline comments in order to protect munin's +# magic markers ("#%#"). +Style/BlockComments: + Enabled: false + +AllCops: + NewCops: enable diff --git a/plugins/bsd/netstat_bsd_m_ b/plugins/bsd/netstat_bsd_m_ index 82b79fa1..2e359a23 100755 --- a/plugins/bsd/netstat_bsd_m_ +++ b/plugins/bsd/netstat_bsd_m_ @@ -9,24 +9,22 @@ This plugin shows various statistics from 'netstat -m' Required privileges: none OS: - Supposed: BSD - Tested: FreeBSD 8.2 + Supposed: BSD + Tested: FreeBSD 8.2 Author: Artem Sheremet - #%# family=auto #%# capabilities=autoconf suggest =end - # original filename -PLUGIN_NAME = 'netstat_bsd_m_' +PLUGIN_NAME = 'netstat_bsd_m_'.freeze class String def escape - self.gsub /[^\w]/, '_' + gsub(/[^\w]/, '_') end unless method_defined? :start_with? @@ -37,17 +35,20 @@ class String end def netstat_m(filter = nil) - Hash[`netstat -m`.split($/).map { |line| - if line =~ /^([\d\/K]+) (.*) \(([\w\/+]+)\)$/ + Hash[`netstat -m`.split($/).map do |line| + if line =~ %r{^([\d/K]+) (.*) \(([\w/+]+)\)$} # 7891K/22385K/30276K bytes allocated to network (current/cache/total) - values, desc, names = $1, $2, $3 - [desc, names.split('/').zip(values.split '/')] if filter.nil? or desc.escape == filter + values = Regexp.last_match(1) + desc = Regexp.last_match(2) + names = Regexp.last_match(3) + [desc, names.split('/').zip(values.split('/'))] if filter.nil? || (desc.escape == filter) elsif line =~ /^(\d+) (.*)$/ # 12327 requests for I/O initiated by sendfile - value, desc = $1, $2 - [desc, [[:value, value]]] if filter.nil? or desc.escape == filter + value = Regexp.last_match(1) + desc = Regexp.last_match(2) + [desc, [[:value, value]]] if filter.nil? || (desc.escape == filter) end - }.compact] + end.compact] end stat_name = File.basename($0, '.*').escape @@ -74,7 +75,7 @@ when 'config' CONFIG puts values.map { |name, _| esc_name = name.to_s.escape - "#{esc_name}.draw " + if %w(total max).include? name + "#{esc_name}.draw " + if %w[total max].include? name 'LINE' elsif stack if first diff --git a/plugins/ejabberd/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog index c46f6869..a07b8266 100755 --- a/plugins/ejabberd/ejabberd_scanlog +++ b/plugins/ejabberd/ejabberd_scanlog @@ -21,9 +21,9 @@ require 'yaml' # LOG_FILE = ENV['log'] || '/var/log/ejabberd/ejabberd.log' -CACHE_FILE = '/tmp/ejabberd_scanlog_cache' # cache file position +CACHE_FILE = '/tmp/ejabberd_scanlog_cache'.freeze # cache file position -DEFAULT_CACHE = { :start => 0 } +DEFAULT_CACHE = { start: 0 }.freeze $debug_mode = ARGV.first == 'debug' @@ -32,7 +32,7 @@ if $debug_mode else begin log_info = YAML.load IO.read(CACHE_FILE) - rescue + rescue StandardError log_info = DEFAULT_CACHE end @@ -43,7 +43,7 @@ else end if ARGV.first == 'reset' - log_info = { :start => File.size(LOG_FILE) - 1 } + log_info = { start: File.size(LOG_FILE) - 1 } puts 'Log reset' end @@ -80,8 +80,8 @@ KNOWN_LOG_TYPES = [ ['mysql', 'Failed connecting to']], ['Timeout while running a hook', - ['ejabberd_hooks', - 'timeout']], + %w[ejabberd_hooks + timeout]], ['SQL transaction restarts exceeded', ['SQL transaction restarts exceeded']], ['Unexpected info', @@ -89,8 +89,8 @@ KNOWN_LOG_TYPES = [ ['Other sql_cmd timeout', ['sql_cmd']], ['System limit hit: ports', # check with length(erlang:ports())., set in ejabberdctl config file - ['system_limit', - 'open_port']], + %w[system_limit + open_port]], ['Other system limit hit', # processes? check with erlang:system_info(process_count)., erlang:system_info(process_limit)., set in ejabberdctl cfg ['system_limit']], ['Generic server terminating', @@ -115,20 +115,22 @@ KNOWN_LOG_TYPES = [ ['mysql_conn: init failed receiving data']], ['TCP Error', ['Failed TCP']] -] +].freeze def log_type(text) - KNOWN_LOG_TYPES.find_index { |entry| + KNOWN_LOG_TYPES.find_index do |entry| entry[1].all? { |substr| text.include? substr } - } + end end -new_data.split("\n=").each { |report| +new_data.split("\n=").each do |report| next if report.empty? report =~ /\A(\w+) REPORT==== (.*) ===\n(.*)\z/m - type, time, text = $1, $2, $3 - next unless type and time and text + type = Regexp.last_match(1) + time = Regexp.last_match(2) + text = Regexp.last_match(3) + next unless type && time && text log_info[type] = (log_info[type] || 0) + 1 if sub_type = log_type(text) @@ -136,7 +138,7 @@ new_data.split("\n=").each { |report| elsif $debug_mode warn "Unparsed log entry #{type}: #{text} at #{time}" end -} +end log_info[:start] += new_data.size File.open(CACHE_FILE, 'w') { |f| f.write log_info.to_yaml } unless $debug_mode @@ -150,7 +152,7 @@ if ARGV.first == 'config' CONFIG end -(KNOWN_LOG_TYPES + %w(ERROR WARNING INFO DEBUG)).each.with_index { |log_type, index| +(KNOWN_LOG_TYPES + %w[ERROR WARNING INFO DEBUG]).each.with_index do |log_type, index| label, index = if log_type.is_a? Array [log_type.first, index] else @@ -162,4 +164,4 @@ end else puts "T#{index}.value #{log_info[index] or 0}" end -} +end diff --git a/plugins/http/mongrel_memory b/plugins/http/mongrel_memory index 3fb70303..9e3b2798 100755 --- a/plugins/http/mongrel_memory +++ b/plugins/http/mongrel_memory @@ -34,33 +34,32 @@ Contributors: Adam Jacob () =end - module Munin class MongrelProcessMemory def run - pid_port_map = get_pids() - port_list = Hash.new + pid_port_map = get_pids + port_list = {} pid_port_map.sort.each do |pid, port| - rss = `pmap -x #{pid} | grep total`.split(" ")[3] + rss = `pmap -x #{pid} | grep total`.split(' ')[3] puts "mongrel_#{port}.value #{rss}" end end def get_pids - h = Hash.new + h = {} pids = [] pids += `pgrep mongrel_rails`.split("\n") pids += `pgrep ruby`.split("\n") - pids.each { |pid| + pids.each do |pid| l = `pargs -l #{pid}` l =~ /-p (\d+)/ - h[pid] = $1 if $1 - } + h[pid] = Regexp.last_match(1) if Regexp.last_match(1) + end h end def autoconf - get_pids().length > 0 + get_pids.length > 0 end end end @@ -68,25 +67,25 @@ end mpm = Munin::MongrelProcessMemory.new case ARGV[0] -when "config" - puts "graph_title Mongrel Memory" - puts "graph_vlabel RSS" - puts "graph_category memory" - puts "graph_args --base 1024 -l 0" - puts "graph_scale yes" - puts "graph_info Tracks the size of individual mongrel processes" +when 'config' + puts 'graph_title Mongrel Memory' + puts 'graph_vlabel RSS' + puts 'graph_category memory' + puts 'graph_args --base 1024 -l 0' + puts 'graph_scale yes' + puts 'graph_info Tracks the size of individual mongrel processes' mpm.get_pids.values.sort.each do |port| puts "mongrel_#{port}.label mongrel_#{port}" puts "mongrel_#{port}.info Process memory" puts "mongrel_#{port}.type GAUGE" puts "mongrel_#{port}.min 0" end -when "autoconf" +when 'autoconf' if mpm.autoconf - puts "yes" + puts 'yes' exit 0 end - puts "no" + puts 'no' exit 0 else mpm.run diff --git a/plugins/http/mongrel_process_memory b/plugins/http/mongrel_process_memory index fae85415..89030874 100755 --- a/plugins/http/mongrel_process_memory +++ b/plugins/http/mongrel_process_memory @@ -28,27 +28,24 @@ Contributors: Adam Jacob () =end - module Munin class MongrelProcessMemory def run - h = get_pids() - ps_output = "" + h = get_pids + ps_output = '' # I have no doubt that this is a terrible way of doing this. - h.each do |k, v| - ps_output = ps_output + `ps --no-heading l #{k}` + h.each do |k, _v| + ps_output += `ps --no-heading l #{k}` end if ps_output - port_list = Hash.new + port_list = {} ps_output.each_line do |l| - if l =~ /-p (\d+)/ - port = $1 - l_ary = l.split(/\s+/) - if l_ary.length > 6 - port_list[port] = l_ary[7].to_i * 1024 - end - end + next unless l =~ /-p (\d+)/ + + port = Regexp.last_match(1) + l_ary = l.split(/\s+/) + port_list[port] = l_ary[7].to_i * 1024 if l_ary.length > 6 end port_list.sort.each do |port| puts "mongrel_#{port[0]}.value #{port[1]}" @@ -58,14 +55,14 @@ module Munin end def get_pids - h = Hash.new + h = {} pids = [] pids = `pgrep mongrel_rails` - pids.each { |p| + pids.each do |p| l = `ps #{p}` l =~ /-p (\d+)/ - h[p] = $1 - } + h[p] = Regexp.last_match(1) + end h end @@ -78,25 +75,25 @@ end mpm = Munin::MongrelProcessMemory.new case ARGV[0] -when "config" - puts "graph_title Mongrel Memory" - puts "graph_vlabel RSS" - puts "graph_category memory" - puts "graph_args --base 1024 -l 0" - puts "graph_scale yes" - puts "graph_info Tracks the size of individual mongrel processes" +when 'config' + puts 'graph_title Mongrel Memory' + puts 'graph_vlabel RSS' + puts 'graph_category memory' + puts 'graph_args --base 1024 -l 0' + puts 'graph_scale yes' + puts 'graph_info Tracks the size of individual mongrel processes' mpm.get_pids.values.sort.each do |port| puts "mongrel_#{port}.label mongrel_#{port}" puts "mongrel_#{port}.info Process memory" puts "mongrel_#{port}.type GAUGE" puts "mongrel_#{port}.min 0" end -when "autoconf" +when 'autoconf' if mpm.autoconf - puts "yes" + puts 'yes' exit 0 end - puts "no" + puts 'no' exit 0 else mpm.run diff --git a/plugins/icecast/icecast2_simple b/plugins/icecast/icecast2_simple index 543078e5..86c2b38b 100755 --- a/plugins/icecast/icecast2_simple +++ b/plugins/icecast/icecast2_simple @@ -18,10 +18,10 @@ require 'open-uri' def get_conf # Default values - conf = { :host => '127.0.0.1', :port => 8000, - :username => 'admin', :password => 'hackme' } + conf = { host: '127.0.0.1', port: 8000, + username: 'admin', password: 'hackme' } conf.keys.each do |key| - env_key = sprintf('icecast_%s', key) + env_key = format('icecast_%s', key) conf[key] = ENV[env_key] if ENV.has_key?(env_key) end conf @@ -29,12 +29,12 @@ end def get_data(conf) begin - data = Hpricot(open(sprintf('http://%s:%s/admin/stats', - conf[:host], conf[:port]), - :http_basic_authentication => [conf[:username], - conf[:password]])) + data = Hpricot(open(format('http://%s:%s/admin/stats', + conf[:host], conf[:port]), + http_basic_authentication: [conf[:username], + conf[:password]])) rescue OpenURI::HTTPError - puts "Cannot connect: HTTP connection error" + puts 'Cannot connect: HTTP connection error' exit 1 end data @@ -42,13 +42,13 @@ end def get_values(data) vals = {} - [:sources, :clients].each do |key| + %i[sources clients].each do |key| elem = data / key - if elem.nil? - vals[key] = 0 - else - vals[key] = elem.innerHTML - end + vals[key] = if elem.nil? + 0 + else + elem.innerHTML + end end vals end @@ -56,15 +56,16 @@ end data = get_data(get_conf) vals = get_values(data) -if ARGV[0] == 'autoconf' +case ARGV[0] +when 'autoconf' puts 'yes' -elsif ARGV[0] == 'config' - puts "graph_title Total sources and clients for Icecast" - puts "graph_vlabel listeners" - puts "graph_category streaming" - puts "sources.label Total number of sources" - puts "clients.label Total number of clients" +when 'config' + puts 'graph_title Total sources and clients for Icecast' + puts 'graph_vlabel listeners' + puts 'graph_category streaming' + puts 'sources.label Total number of sources' + puts 'clients.label Total number of clients' else - puts "sources.value " + vals[:sources] - puts "clients.value " + vals[:clients] + puts 'sources.value ' + vals[:sources] + puts 'clients.value ' + vals[:clients] end diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index 045937d5..c61d0aa8 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -8,29 +8,29 @@ require 'socket' if ARGV[0] == 'config' - puts "graph_title Connected players" - puts "graph_vlabel players" - puts "players.label players" - puts "graph_info Number of players connected to Minecraft" - puts "graph_category games" + puts 'graph_title Connected players' + puts 'graph_vlabel players' + puts 'players.label players' + puts 'graph_info Number of players connected to Minecraft' + puts 'graph_category games' exit end host = ENV['host'] -host = 'localhost' unless host +host ||= 'localhost' port = ENV['port'] -port = '25566' unless port +port ||= '25566' socket = TCPSocket.new(host, port) -socket.puts "QUERY" +socket.puts 'QUERY' response = socket.read response = response.split("\n") -server_port = response[0].split(" ", 2)[1].to_i -player_count = response[1].split(" ", 2)[1].to_i -max_players = response[2].split(" ", 2)[1].to_i -player_list = response[3].split(" ", 2)[1].chomp[1..-2] +server_port = response[0].split(' ', 2)[1].to_i +player_count = response[1].split(' ', 2)[1].to_i +max_players = response[2].split(' ', 2)[1].to_i +player_list = response[3].split(' ', 2)[1].chomp[1..-2] puts "players.value #{player_count}" diff --git a/plugins/moblock/moblock_connections b/plugins/moblock/moblock_connections index 0c68f2ef..9489e953 100755 --- a/plugins/moblock/moblock_connections +++ b/plugins/moblock/moblock_connections @@ -24,44 +24,43 @@ Magic markers =end - # # Initialize vars # -$logfile = ENV['logfile'] || "/var/log/moblock.log" +$logfile = ENV['logfile'] || '/var/log/moblock.log' # # Configure generated graph # def config - puts "graph_args --base 1000 -r --lower-limit 0" - puts "graph_title Moblock" - puts "graph_vlabel Blocked Connections" - puts "graph_category fw" - puts "graph_info This graph shows the number of connections blocked by Moblock" + puts 'graph_args --base 1000 -r --lower-limit 0' + puts 'graph_title Moblock' + puts 'graph_vlabel Blocked Connections' + puts 'graph_category fw' + puts 'graph_info This graph shows the number of connections blocked by Moblock' - puts "blocked_in.label Blocked In" - puts "blocked_in.draw LINE1" - puts "blocked_in.info Number of blocked incoming connections" - puts "blocked_in.type GAUGE" + puts 'blocked_in.label Blocked In' + puts 'blocked_in.draw LINE1' + puts 'blocked_in.info Number of blocked incoming connections' + puts 'blocked_in.type GAUGE' - puts "blocked_out.label Blocked Out" - puts "blocked_out.draw LINE1" - puts "blocked_out.info Number of blocked outgoing connections" - puts "blocked_out.type GAUGE" + puts 'blocked_out.label Blocked Out' + puts 'blocked_out.draw LINE1' + puts 'blocked_out.info Number of blocked outgoing connections' + puts 'blocked_out.type GAUGE' - puts "blocked_total.label Total Blocked" - puts "blocked_total.draw LINE1" - puts "blocked_total.info Total Number of blocked connections" - puts "blocked_total.type GAUGE" + puts 'blocked_total.label Total Blocked' + puts 'blocked_total.draw LINE1' + puts 'blocked_total.info Total Number of blocked connections' + puts 'blocked_total.type GAUGE' end # # Grep moblock logs for stats # -def fetch(debug = false) - num_in = %x{cat #{$logfile} | grep --extended-regexp 'IN: ' | wc -l} - num_out = %x{cat #{$logfile} | grep --extended-regexp 'OUT: ' | wc -l} +def fetch(_debug = false) + num_in = `cat #{$logfile} | grep --extended-regexp 'IN: ' | wc -l` + num_out = `cat #{$logfile} | grep --extended-regexp 'OUT: ' | wc -l` num_total = num_in.to_i + num_out.to_i puts "blocked_in.value #{num_in}" @@ -73,11 +72,11 @@ end # If moblock executable on path then allow autoconfiguration # def autoconf - moblock_path = %x{which moblock} + moblock_path = `which moblock` if moblock_path.index('moblock') - puts "yes" + puts 'yes' else - puts "no" + puts 'no' end end diff --git a/plugins/mssql/microsoft-sql b/plugins/mssql/microsoft-sql index 42e12d48..e52348e3 100755 --- a/plugins/mssql/microsoft-sql +++ b/plugins/mssql/microsoft-sql @@ -21,34 +21,32 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) /etc/odbc.ini and /etc/freetds.conf - 2) rubygems - 3) ruby-dbi + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/mssql_transaction.rb /etc/munin/plugins/mssql_transaction.rb Parameters: - autoconf - config (required) + autoconf + config (required) Config variables: - sqluser : mssql user who has view server state privilege - sqlpass : password for the mssql user - dsn : datasource name as defined in /etc/odbc.ini - instance: instance to monitor + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'dbi' @@ -74,7 +72,7 @@ transaction_query = "select cntr_value from sys.dm_os_performance_counters and object_name = 'SQLServer:Databases' and instance_name = ?" -all_instance_names = Array.new +all_instance_names = [] sth = dbh.execute(instance_name_query) sth.fetch do |row| all_instance_names.push(row[0].strip) @@ -84,25 +82,26 @@ sth.finish # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" - puts "graph_title MSSQL Transactions/s" - puts "graph_category db" - puts "graph_info This graph shows transactions/s" - puts "graph_vlabel transactions/s" - puts "graph_scale no" - puts "graph_period second" +when 'config' + puts 'graph_args --base 1000 -r --lower-limit 0' + puts 'graph_title MSSQL Transactions/s' + puts 'graph_category db' + puts 'graph_info This graph shows transactions/s' + puts 'graph_vlabel transactions/s' + puts 'graph_scale no' + puts 'graph_period second' all_instance_names.sort.each do |s| puts "#{s}.label #{s}" @@ -122,7 +121,7 @@ all_instance_names.sort.each do |k| sth.execute(k) sth.fetch do |row| # since type is DERIVE, need to convert value to integer then to string - puts "#{k.to_s}.value #{row[0].to_i.to_s}" + puts "#{k}.value #{row[0].to_i}" end end sth.finish diff --git a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio index 8e225617..b7b83d89 100755 --- a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +++ b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio @@ -21,33 +21,31 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) /etc/odbc.ini and /etc/freetds.conf - 2) rubygems - 3) ruby-dbi + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/mssql_buffercachehitratio.rb /etc/munin/plugins/mssql_buffercachehitratio.rb Parameters: - autoconf - config (required) + autoconf + config (required) Config variables: - sqluser : mssql user who has view server state privilege - sqlpass : password for the mssql user - dsn : datasource name as defined in /etc/odbc.ini + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'dbi' @@ -74,37 +72,38 @@ buffercachehitratio_query = "select (a.cntr_value * 1.0 / b.cntr_value) * 100.0 # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1000 -r --lower-limit 0" - puts "graph_title MSSQL Buffer Cache Hit Ratio " - puts "graph_category db" - puts "graph_info This graph shows Buffer Cache Hit Ratio" - puts "graph_vlabel %" - puts "graph_scale no" - puts "graph_period second" +when 'config' + puts 'graph_args --base 1000 -r --lower-limit 0' + puts 'graph_title MSSQL Buffer Cache Hit Ratio ' + puts 'graph_category db' + puts 'graph_info This graph shows Buffer Cache Hit Ratio' + puts 'graph_vlabel %' + puts 'graph_scale no' + puts 'graph_period second' - puts "bc_hitratio.label BufferCacheHitRatio" - puts "bc_hitratio.info BufferCacheHitRatio" - puts "bc_hitratio.type GAUGE" - puts "bc_hitratio.draw LINE1" + puts 'bc_hitratio.label BufferCacheHitRatio' + puts 'bc_hitratio.info BufferCacheHitRatio' + puts 'bc_hitratio.type GAUGE' + puts 'bc_hitratio.draw LINE1' exit 0 end sth = dbh.execute(buffercachehitratio_query) sth.fetch do |row| - puts "bc_hitratio.value #{row[0].strip.to_s}" + puts "bc_hitratio.value #{row[0].strip}" end sth.finish dbh.disconnect diff --git a/plugins/mssql/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes index 4d4b78db..915ebc2d 100755 --- a/plugins/mssql/microsoft-sql-data-file-sizes +++ b/plugins/mssql/microsoft-sql-data-file-sizes @@ -21,34 +21,32 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) /etc/odbc.ini and /etc/freetds.conf - 2) rubygems - 3) ruby-dbi + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/mssql_datafilesizes.rb /etc/munin/plugins/mssql_datafilesizes.rb Parameters: - autoconf - config (required) + autoconf + config (required) Config variables: - sqluser : mssql user who has view server state privilege - sqlpass : password for the mssql user - dsn : datasource name as defined in /etc/odbc.ini - instance: instance to monitor + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'dbi' @@ -72,7 +70,7 @@ transaction_query = "select cntr_value/1024.0 from sys.dm_os_performance_counter and object_name = 'SQLServer:Databases' and instance_name = ?" -all_instance_names = Array.new +all_instance_names = [] sth = dbh.execute(instance_name_query) sth.fetch do |row| all_instance_names.push(row[0].strip) @@ -82,25 +80,26 @@ sth.finish # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title MSSQL DB File Sizes" - puts "graph_category db" - puts "graph_info This graph shows DB File Sizes (MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" +when 'config' + puts 'graph_args --base 1024k -r --lower-limit 0' + puts 'graph_title MSSQL DB File Sizes' + puts 'graph_category db' + puts 'graph_info This graph shows DB File Sizes (MB)' + puts 'graph_vlabel MB' + puts 'graph_scale no' + puts 'graph_period second' all_instance_names.sort.each do |s| puts "#{s}.label #{s}" @@ -119,7 +118,7 @@ sth = dbh.prepare(transaction_query) all_instance_names.sort.each do |k| sth.execute(k) sth.fetch do |row| - puts "#{k.to_s}.value #{row[0].to_s}" + puts "#{k}.value #{row[0]}" end end sth.finish diff --git a/plugins/mssql/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size index 46e777e1..813fac01 100755 --- a/plugins/mssql/microsoft-sql-log-file-size +++ b/plugins/mssql/microsoft-sql-log-file-size @@ -21,34 +21,32 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) /etc/odbc.ini and /etc/freetds.conf - 2) rubygems - 3) ruby-dbi + 1) /etc/odbc.ini and /etc/freetds.conf + 2) rubygems + 3) ruby-dbi Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/mssql_logfilesizes.rb /etc/munin/plugins/mssql_logfilesizes.rb Parameters: - autoconf - config (required) + autoconf + config (required) Config variables: - sqluser : mssql user who has view server state privilege - sqlpass : password for the mssql user - dsn : datasource name as defined in /etc/odbc.ini - instance: instance to monitor + sqluser : mssql user who has view server state privilege + sqlpass : password for the mssql user + dsn : datasource name as defined in /etc/odbc.ini + instance: instance to monitor #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'dbi' @@ -72,7 +70,7 @@ logfilesize_query = "SELECT cntr_value/1024.0 from sys.dm_os_performance_counter AND object_name = 'SQLServer:Databases' AND instance_name = ?" -all_instance_names = Array.new +all_instance_names = [] sth = dbh.execute(instance_name_query) sth.fetch do |row| all_instance_names.push(row[0].strip) @@ -82,25 +80,26 @@ sth.finish # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if all_instance_names.length > 1 && sqluser.length > 1 && sqlpass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" - puts "graph_title MSSQL DB Log File Sizes" - puts "graph_category db" - puts "graph_info This graph shows DB Log File Sizes (MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" +when 'config' + puts 'graph_args --base 1024k -r --lower-limit 0' + puts 'graph_title MSSQL DB Log File Sizes' + puts 'graph_category db' + puts 'graph_info This graph shows DB Log File Sizes (MB)' + puts 'graph_vlabel MB' + puts 'graph_scale no' + puts 'graph_period second' all_instance_names.sort.each do |s| puts "#{s}.label #{s}" @@ -116,7 +115,7 @@ sth = dbh.prepare(logfilesize_query) all_instance_names.sort.each do |k| sth.execute(k) sth.fetch do |row| - puts "#{k.to_s}.value #{row[0].to_s}" + puts "#{k}.value #{row[0]}" end end sth.finish diff --git a/plugins/network/http__tp_link b/plugins/network/http__tp_link index 2214b8e6..06fa489b 100755 --- a/plugins/network/http__tp_link +++ b/plugins/network/http__tp_link @@ -1,42 +1,40 @@ #!/usr/bin/env ruby -if $0 =~ /^(?:|.*\/)http_([^_]+)_/ - host = $1 -end +host = Regexp.last_match(1) if $0 =~ %r{^(?:|.*/)http_([^_]+)_} abort "# Error: couldn't understand what I'm supposed to monitor." unless host user = ENV['user'] || 'user' password = ENV['password'] || 'user' -if (ARGV[0] == 'config') +if ARGV[0] == 'config' puts "host_name #{host}" unless host == 'localhost' - puts "multigraph dsl_rate" - puts "graph_title DSL line speed" - puts "graph_args --base 1000 -l 0" - puts "graph_vlabel bps" - puts "graph_category network" - puts "downstream.label downstream" - puts "downstream.type GAUGE" - puts "downstream.min 0" - puts "downstream.cdef downstream,1000,*" - puts "upstream.label upstream" - puts "upstream.type GAUGE" - puts "upstream.min 0" - puts "upstream.cdef upstream,1000,*" + puts 'multigraph dsl_rate' + puts 'graph_title DSL line speed' + puts 'graph_args --base 1000 -l 0' + puts 'graph_vlabel bps' + puts 'graph_category network' + puts 'downstream.label downstream' + puts 'downstream.type GAUGE' + puts 'downstream.min 0' + puts 'downstream.cdef downstream,1000,*' + puts 'upstream.label upstream' + puts 'upstream.type GAUGE' + puts 'upstream.min 0' + puts 'upstream.cdef upstream,1000,*' - puts "multigraph dsl_snr" - puts "graph_title DSL SNR" - puts "graph_args --base 1000 -l 0" - puts "graph_vlabel dB" - puts "graph_scale no" - puts "graph_category network" - puts "downstream.label downstream" - puts "downstream.type GAUGE" - puts "downstream.min 0" - puts "upstream.label upstream" - puts "upstream.type GAUGE" - puts "upstream.min 0" + puts 'multigraph dsl_snr' + puts 'graph_title DSL SNR' + puts 'graph_args --base 1000 -l 0' + puts 'graph_vlabel dB' + puts 'graph_scale no' + puts 'graph_category network' + puts 'downstream.label downstream' + puts 'downstream.type GAUGE' + puts 'downstream.min 0' + puts 'upstream.label upstream' + puts 'upstream.type GAUGE' + puts 'upstream.min 0' exit 0 end @@ -56,21 +54,21 @@ class TPAdslStats def field_values(label) if @html =~ />#{label}.*?([0-9.]+).*?([0-9.]+)/m - [$1, $2] + [Regexp.last_match(1), Regexp.last_match(2)] else - ['U', 'U'] + %w[U U] end end end stats = TPAdslStats.new(host, user, password) -puts "multigraph dsl_rate" +puts 'multigraph dsl_rate' rate = stats.field_values('Rate') puts "downstream.value #{rate[0]}" puts "upstream.value #{rate[1]}" -puts "multigraph dsl_snr" +puts 'multigraph dsl_snr' snr = stats.field_values('SNR') puts "downstream.value #{snr[0]}" puts "upstream.value #{snr[1]}" diff --git a/plugins/network/netstat_s_ b/plugins/network/netstat_s_ index d5764ef7..2aa209de 100755 --- a/plugins/network/netstat_s_ +++ b/plugins/network/netstat_s_ @@ -9,27 +9,26 @@ This plugin shows various statistics from 'netstat -s' Required privileges: none OS: - Supposed: BSD, Linux (only a few items, see netstat_multi for more) - Tested: FreeBSD: 8.2, 8.3, 9.1 - Linux : Debian 6 (kernel 2.6.32), Arch (kernel 3.11.6), CentOS 6 + Supposed: BSD, Linux (only a few items, see netstat_multi for more) + Tested: FreeBSD: 8.2, 8.3, 9.1 + Linux : Debian 6 (kernel 2.6.32), Arch (kernel 3.11.6), CentOS 6 Author: Artem Sheremet - #%# family=auto #%# capabilities=autoconf suggest =end # original filename -PLUGIN_NAME = 'netstat_s_' +PLUGIN_NAME = 'netstat_s_'.freeze $os = `uname -s`.strip.downcase.to_sym $debug_mode = ARGV.first == 'debug' class String def escape - self.gsub /[^\w]/, '_' + gsub(/[^\w]/, '_') end unless method_defined? :start_with? @@ -40,14 +39,16 @@ class String unless method_defined? :lines def lines - self.split($/).to_enum + split($/).to_enum end end end class Graph def initialize(name, protocol, parse_expr) - @name, @protocol, @parse_expr = name, protocol, parse_expr + @name = name + @protocol = protocol + @parse_expr = parse_expr end def config @@ -56,31 +57,31 @@ class Graph # first, build a list of multigraphs (one graph per unit) # Hash key is unit, and the value is array of labels multigraphs = {} - @parse_expr.each { |expr, descr| + @parse_expr.each do |_expr, descr| next unless descr # no label - skip this entry - descr.each { |entry| + descr.each do |entry| labels_array = (multigraphs[entry[0]] ||= []) labels_array.push [entry[1], entry[2]] - } - } + end + end - multigraphs.each_pair { |unit, labels_and_negatives| + multigraphs.each_pair do |unit, labels_and_negatives| # now just add options to the config config_options.concat [ "multigraph #{name(unit)}", "graph_title Netstat: #{@protocol}: #{@name}#{" (#{unit})" if multigraphs.size > 1}", - "graph_category network", + 'graph_category network', "graph_order #{labels_and_negatives.map { |label, _negative| label.escape }.join(' ')}" ] - config_options.push "graph_args --base 1024" if unit == :bytes + config_options.push 'graph_args --base 1024' if unit == :bytes has_negatives = false - labels_and_negatives.each { |label, negative| + labels_and_negatives.each do |label, negative| label_esc = label.escape - has_negatives = true unless negative == nil + has_negatives = true unless negative.nil? if negative == true # the value has no opposite and is negative @@ -114,10 +115,10 @@ class Graph "#{negative_esc}.graph no" ] end - } + end - config_options.push "graph_vlabel per second#{" in (-) / out (+)" if has_negatives}" - } + config_options.push "graph_vlabel per second#{' in (-) / out (+)' if has_negatives}" + end config_options end @@ -128,25 +129,25 @@ class Graph # first build a set of multigraphs, one per unit. # Hash key is unit, and the value is a hash of 'escaped label' => 'value' multigraphs = {} - @parse_expr.each { |expr, descr| + @parse_expr.each do |expr, descr| next unless descr # no label - skip this entry index = data.index { |line| line =~ expr } if index data.delete_at index - $~[1..-1].zip(descr).each { |value, info| + $~[1..-1].zip(descr).each do |value, info| unit, label = info (multigraphs[unit] ||= {})[label.escape] = value - } + end else warn "no line found for #{expr}, #{descr}" if $debug_mode end - } + end - multigraphs.each_pair { |unit, values| + multigraphs.each_pair do |unit, values| output_data.push "multigraph #{name(unit)}" output_data += values.map { |label, value| "#{label}.value #{value}" } - } + end output_data end @@ -161,214 +162,230 @@ def graphs_for(protocol) # Order of the graps in each section is important for parsing. # At the same time, it is not important for munin, so we are OK placing it in parsing order here. when 'tcp' - $os == :linux ? [ - Graph.new('sent', protocol, [ - # Description of the elements of arrays below: - # 0: regexp to parse the line - # 1: Array for each matching group in the regular expression. - # 0: unit name - # 1: label - # 2 (optional): negative label - # It could be reasonable to add more elements as warning and critical values. + if $os == :linux + [ + Graph.new('sent', protocol, [ + # Description of the elements of arrays below: + # 0: regexp to parse the line + # 1: Array for each matching group in the regular expression. + # 0: unit name + # 1: label + # 2 (optional): negative label + # It could be reasonable to add more elements as warning and critical values. - [/(\d+) segments send out$/, [[:segments, 'total']]], - [/(\d+) segments retransmitted$/, [[:segments, 'retransmitted']]] - ]), + [/(\d+) segments send out$/, [[:segments, 'total']]], + [/(\d+) segments retransmitted$/, [[:segments, 'retransmitted']]] + ]), - Graph.new('received', protocol, [ - [/(\d+) segments received$/, [[:segments, 'total']]], - [/(\d+) bad segments received.$/, [[:segments, 'bad']]] - ]), + Graph.new('received', protocol, [ + [/(\d+) segments received$/, [[:segments, 'total']]], + [/(\d+) bad segments received.$/, [[:segments, 'bad']]] + ]), - Graph.new('connections', protocol, [ - [/(\d+) active connections openings$/, [[:connections, 'active openings']]], - [/(\d+) passive connection openings$/, [[:connections, 'passive openings']]], - [/(\d+) failed connection attempts$/, [[:connections, 'failed attempts']]], - [/(\d+) connection resets received$/, [[:connections, 'RST received']]], - [/(\d+) connections established$/, [[:connections, 'established']]], - [/(\d+) resets sent$/, [[:connections, 'RST sent']]] - ]), + Graph.new('connections', protocol, [ + [/(\d+) active connections openings$/, [[:connections, 'active openings']]], + [/(\d+) passive connection openings$/, [[:connections, 'passive openings']]], + [/(\d+) failed connection attempts$/, [[:connections, 'failed attempts']]], + [/(\d+) connection resets received$/, [[:connections, 'RST received']]], + [/(\d+) connections established$/, [[:connections, 'established']]], + [/(\d+) resets sent$/, [[:connections, 'RST sent']]] + ]), - Graph.new('timeouts', protocol, [ - [/(\d+) timeouts after SACK recovery$/, [[:segments, 'after SACK recovery']]], - [/(\d+) other TCP timeouts$/, [[:segments, 'other TCP']]], - [/(\d+) timeouts in loss state$/, [[:segments, 'in a loss state']]] - ]) - ] : [ - Graph.new('sent', protocol, [ - [/(\d+) packets sent$/, [[:packets, 'total']]], - [/(\d+) data packets \((\d+) bytes\)$/, [[:packets, 'data'], [:bytes, 'data']]], - [/(\d+) data packets \((\d+) bytes\) retransmitted$/, [[:packets, 'retransmitted'], [:bytes, 'retransmitted']]], - [/(\d+) data packets unnecessarily retransmitted$/, [[:packets, 'unnecessarily retransmitted']]], - [/(\d+) resends initiated by MTU discovery$/, [[:packets, 'resends initiated by MTU discovery']]], - [/(\d+) ack-only packets \((\d+) delayed\)$/, [[:packets, 'ack-only'], [:packets, 'ack-only delayed']]], - [/(\d+) URG only packets$/, [[:packets, 'URG only']]], - [/(\d+) window probe packets$/, [[:packets, 'window probe']]], - [/(\d+) window update packets$/, [[:packets, 'window update']]], - [/(\d+) control packets$/, [[:packets, 'control']]] - ]), + Graph.new('timeouts', protocol, [ + [/(\d+) timeouts after SACK recovery$/, [[:segments, 'after SACK recovery']]], + [/(\d+) other TCP timeouts$/, [[:segments, 'other TCP']]], + [/(\d+) timeouts in loss state$/, [[:segments, 'in a loss state']]] + ]) + ] + else + [ + Graph.new('sent', protocol, [ + [/(\d+) packets sent$/, [[:packets, 'total']]], + [/(\d+) data packets \((\d+) bytes\)$/, [[:packets, 'data'], [:bytes, 'data']]], + [/(\d+) data packets \((\d+) bytes\) retransmitted$/, [[:packets, 'retransmitted'], [:bytes, 'retransmitted']]], + [/(\d+) data packets unnecessarily retransmitted$/, [[:packets, 'unnecessarily retransmitted']]], + [/(\d+) resends initiated by MTU discovery$/, [[:packets, 'resends initiated by MTU discovery']]], + [/(\d+) ack-only packets \((\d+) delayed\)$/, [[:packets, 'ack-only'], [:packets, 'ack-only delayed']]], + [/(\d+) URG only packets$/, [[:packets, 'URG only']]], + [/(\d+) window probe packets$/, [[:packets, 'window probe']]], + [/(\d+) window update packets$/, [[:packets, 'window update']]], + [/(\d+) control packets$/, [[:packets, 'control']]] + ]), - Graph.new('received', protocol, [ - [/(\d+) packets received$/, [[:packets, 'total']]], - [/(\d+) acks \(for (\d+) bytes\)$/, [[:packets, 'acks'], [:bytes, 'acks']]], - [/(\d+) duplicate acks$/, [[:packets, 'duplicate acks']]], - [/(\d+) acks for unsent data$/, [[:packets, 'acks for unsent data']]], - [/(\d+) packets \((\d+) bytes\) received in-sequence$/, [[:packets, 'in-sequence'], [:bytes, 'in-sequence']]], - [/(\d+) completely duplicate packets \((\d+) bytes\)$/, [[:packets, 'completely duplicate'], [:bytes, 'completely duplicate']]], - [/(\d+) old duplicate packets$/, [[:packets, 'old duplicate']]], - [/(\d+) packets with some dup\. data \((\d+) bytes duped\)$/, [[:packets, 'some dup. data'], [:bytes, 'partial dups']]], - [/(\d+) out-of-order packets \((\d+) bytes\)$/, [[:packets, 'out-of-order'], [:bytes, 'out-of-order']]], - [/(\d+) packets \((\d+) bytes\) of data after window$/, [[:packets, 'data after window'], [:bytes, 'data after window']]], - [/(\d+) window probes$/, [[:packets, 'window probes']]], - [/(\d+) window update packets$/, [[:packets, 'window update']]], - [/(\d+) packets received after close$/, [[:packets, 'after close']]], - [/(\d+) discarded for bad checksums$/, [[:packets, 'bad checksums']]], - [/(\d+) discarded for bad header offset fields?$/, [[:packets, 'bad header offset flds']]], - [/(\d+) discarded because packet too short$/, [[:packets, 'too short']]], - [/(\d+) discarded due to memory problems$/, [[:packets, 'discarded: memory problems']]], - [/(\d+) ignored RSTs in the windows$/, [[:packets, 'ignored RSTs in windows']]], - [/(\d+) segments updated rtt \(of (\d+) attempts\)$/, [[:packets, 'RTT: updated'], [:packets, 'RTT: attempts to update']]] - ]), + Graph.new('received', protocol, [ + [/(\d+) packets received$/, [[:packets, 'total']]], + [/(\d+) acks \(for (\d+) bytes\)$/, [[:packets, 'acks'], [:bytes, 'acks']]], + [/(\d+) duplicate acks$/, [[:packets, 'duplicate acks']]], + [/(\d+) acks for unsent data$/, [[:packets, 'acks for unsent data']]], + [/(\d+) packets \((\d+) bytes\) received in-sequence$/, [[:packets, 'in-sequence'], [:bytes, 'in-sequence']]], + [/(\d+) completely duplicate packets \((\d+) bytes\)$/, [[:packets, 'completely duplicate'], [:bytes, 'completely duplicate']]], + [/(\d+) old duplicate packets$/, [[:packets, 'old duplicate']]], + [/(\d+) packets with some dup\. data \((\d+) bytes duped\)$/, [[:packets, 'some dup. data'], [:bytes, 'partial dups']]], + [/(\d+) out-of-order packets \((\d+) bytes\)$/, [[:packets, 'out-of-order'], [:bytes, 'out-of-order']]], + [/(\d+) packets \((\d+) bytes\) of data after window$/, [[:packets, 'data after window'], [:bytes, 'data after window']]], + [/(\d+) window probes$/, [[:packets, 'window probes']]], + [/(\d+) window update packets$/, [[:packets, 'window update']]], + [/(\d+) packets received after close$/, [[:packets, 'after close']]], + [/(\d+) discarded for bad checksums$/, [[:packets, 'bad checksums']]], + [/(\d+) discarded for bad header offset fields?$/, [[:packets, 'bad header offset flds']]], + [/(\d+) discarded because packet too short$/, [[:packets, 'too short']]], + [/(\d+) discarded due to memory problems$/, [[:packets, 'discarded: memory problems']]], + [/(\d+) ignored RSTs in the windows$/, [[:packets, 'ignored RSTs in windows']]], + [/(\d+) segments updated rtt \(of (\d+) attempts\)$/, [[:packets, 'RTT: updated'], [:packets, 'RTT: attempts to update']]] + ]), - Graph.new('connections', protocol, [ - [/(\d+) connection requests$/, [[:connections, 'requests']]], - [/(\d+) connection accepts$/, [[:connections, 'accepts']]], - [/(\d+) bad connection attempts$/, [[:connections, 'bad attempts']]], - [/(\d+) listen queue overflows$/, [[:connections, 'listen queue overflows']]], - [/(\d+) connections established \(including accepts\)$/, [[:connections, 'established']]], - [/(\d+) connections closed \(including (\d+) drops\)$/, [[:connections, 'closed'], [:connections, 'dropped']]], - [/(\d+) connections updated cached RTT on close$/, [[:connections, 'closed & upd cached RTT']]], - [/(\d+) connections updated cached RTT variance on close$/, [[:connections, 'closed & upd cached RTT variance']]], - [/(\d+) connections updated cached ssthresh on close$/, [[:connections, 'closed & upd cached ssthresh']]], - [/(\d+) embryonic connections dropped$/, [[:connections, 'embryonic dropped']]] - ]), + Graph.new('connections', protocol, [ + [/(\d+) connection requests$/, [[:connections, 'requests']]], + [/(\d+) connection accepts$/, [[:connections, 'accepts']]], + [/(\d+) bad connection attempts$/, [[:connections, 'bad attempts']]], + [/(\d+) listen queue overflows$/, [[:connections, 'listen queue overflows']]], + [/(\d+) connections established \(including accepts\)$/, [[:connections, 'established']]], + [/(\d+) connections closed \(including (\d+) drops\)$/, [[:connections, 'closed'], [:connections, 'dropped']]], + [/(\d+) connections updated cached RTT on close$/, [[:connections, 'closed & upd cached RTT']]], + [/(\d+) connections updated cached RTT variance on close$/, [[:connections, 'closed & upd cached RTT variance']]], + [/(\d+) connections updated cached ssthresh on close$/, [[:connections, 'closed & upd cached ssthresh']]], + [/(\d+) embryonic connections dropped$/, [[:connections, 'embryonic dropped']]] + ]), - Graph.new('timeouts', protocol, [ - [/(\d+) retransmit timeouts$/, [[:connections, 'retransmit']]], - [/(\d+) connections dropped by rexmit timeout$/, [[:connections, 'retransmit: dropped']]], - [/(\d+) persist timeouts$/, [[:connections, 'persist']]], - [/(\d+) connections dropped by persist timeout$/, [[:connections, 'persist: dropped']]], - [/(\d+) Connections \(fin_wait_2\) dropped because of timeout$/, [[:connections, 'fin_wait_2: dropped']]], - [/(\d+) keepalive timeouts$/, [[:connections, 'keepalive']]], - [/(\d+) keepalive probes sent$/, [[:connections, 'keepalive: probes sent']]], - [/(\d+) connections dropped by keepalive$/, [[:connections, 'keepalive: dropped']]] - ]), + Graph.new('timeouts', protocol, [ + [/(\d+) retransmit timeouts$/, [[:connections, 'retransmit']]], + [/(\d+) connections dropped by rexmit timeout$/, [[:connections, 'retransmit: dropped']]], + [/(\d+) persist timeouts$/, [[:connections, 'persist']]], + [/(\d+) connections dropped by persist timeout$/, [[:connections, 'persist: dropped']]], + [/(\d+) Connections \(fin_wait_2\) dropped because of timeout$/, [[:connections, 'fin_wait_2: dropped']]], + [/(\d+) keepalive timeouts$/, [[:connections, 'keepalive']]], + [/(\d+) keepalive probes sent$/, [[:connections, 'keepalive: probes sent']]], + [/(\d+) connections dropped by keepalive$/, [[:connections, 'keepalive: dropped']]] + ]), - Graph.new('correct predictions', protocol, [ - [/(\d+) correct ACK header predictions$/, [[:predictions, 'ACK header']]], - [/(\d+) correct data packet header predictions$/, [[:predictions, 'data packet header']]] - ]), + Graph.new('correct predictions', protocol, [ + [/(\d+) correct ACK header predictions$/, [[:predictions, 'ACK header']]], + [/(\d+) correct data packet header predictions$/, [[:predictions, 'data packet header']]] + ]), - Graph.new('SYN', protocol, [ - [/(\d+) syncache entries added$/, [[:entries, 'cache added']]], - [/(\d+) cookies sent$/, [[:entries, 'cookies sent']]], - [/(\d+) cookies received$/, [[:entries, 'cookies received']]], - [/(\d+) retransmitted$/, [[:entries, 'retransmitted']]], - [/(\d+) dupsyn$/, [[:entries, 'duplicates']]], - [/(\d+) dropped$/, [[:entries, 'dropped']]], - [/(\d+) completed$/, [[:entries, 'completed']]], - [/(\d+) bucket overflow$/, [[:entries, 'bucket overflow']]], - [/(\d+) cache overflow$/, [[:entries, 'cache overflow']]], - [/(\d+) reset$/, [[:entries, 'reset']]], - [/(\d+) stale$/, [[:entries, 'stale']]], - [/(\d+) aborted$/, [[:entries, 'aborted']]], - [/(\d+) badack$/, [[:entries, 'bad ACK']]], - [/(\d+) unreach$/, [[:entries, 'unreachable']]], - [/(\d+) zone failures$/, [[:entries, 'zone failures']]], - [/(\d+) hostcache entries added$/, [[:entries, 'hostcache added']]], - [/(\d+) bucket overflow$/, [[:entries, 'hostcache overflow']]] - ]), + Graph.new('SYN', protocol, [ + [/(\d+) syncache entries added$/, [[:entries, 'cache added']]], + [/(\d+) cookies sent$/, [[:entries, 'cookies sent']]], + [/(\d+) cookies received$/, [[:entries, 'cookies received']]], + [/(\d+) retransmitted$/, [[:entries, 'retransmitted']]], + [/(\d+) dupsyn$/, [[:entries, 'duplicates']]], + [/(\d+) dropped$/, [[:entries, 'dropped']]], + [/(\d+) completed$/, [[:entries, 'completed']]], + [/(\d+) bucket overflow$/, [[:entries, 'bucket overflow']]], + [/(\d+) cache overflow$/, [[:entries, 'cache overflow']]], + [/(\d+) reset$/, [[:entries, 'reset']]], + [/(\d+) stale$/, [[:entries, 'stale']]], + [/(\d+) aborted$/, [[:entries, 'aborted']]], + [/(\d+) badack$/, [[:entries, 'bad ACK']]], + [/(\d+) unreach$/, [[:entries, 'unreachable']]], + [/(\d+) zone failures$/, [[:entries, 'zone failures']]], + [/(\d+) hostcache entries added$/, [[:entries, 'hostcache added']]], + [/(\d+) bucket overflow$/, [[:entries, 'hostcache overflow']]] + ]), - Graph.new('SACK', protocol, [ - [/(\d+) SACK recovery episodes$/, [[:packets, 'recovery episodes']]], - [/(\d+) segment rexmits in SACK recovery episodes$/, [[:packets, 'segment rexmits']]], - [/(\d+) byte rexmits in SACK recovery episodes$/, [[:bytes, 'bytes rexmitted']]], - [/(\d+) SACK options \(SACK blocks\) received$/, [[:packets, 'options blocks rcvd']]], - [/(\d+) SACK options \(SACK blocks\) sent$/, [[:packets, 'options blocks sent']]], - [/(\d+) SACK scoreboard overflow$/, [[:packets, 'scoreboard overflow']]] - ]), + Graph.new('SACK', protocol, [ + [/(\d+) SACK recovery episodes$/, [[:packets, 'recovery episodes']]], + [/(\d+) segment rexmits in SACK recovery episodes$/, [[:packets, 'segment rexmits']]], + [/(\d+) byte rexmits in SACK recovery episodes$/, [[:bytes, 'bytes rexmitted']]], + [/(\d+) SACK options \(SACK blocks\) received$/, [[:packets, 'options blocks rcvd']]], + [/(\d+) SACK options \(SACK blocks\) sent$/, [[:packets, 'options blocks sent']]], + [/(\d+) SACK scoreboard overflow$/, [[:packets, 'scoreboard overflow']]] + ]), - Graph.new('ECN', protocol, [ - [/(\d+) packets with ECN CE bit set$/, [[:packets, 'CE bit']]], - [/(\d+) packets with ECN ECT\(0\) bit set$/, [[:packets, 'ECT(0) bit']]], - [/(\d+) packets with ECN ECT\(1\) bit set$/, [[:packets, 'ECT(1) bit']]], - [/(\d+) successful ECN handshakes$/, [[:packets, 'successful handshakes']]], - [/(\d+) times ECN reduced the congestion window$/, [[:packets, 'congestion window reduced']]] - ]) - ] + Graph.new('ECN', protocol, [ + [/(\d+) packets with ECN CE bit set$/, [[:packets, 'CE bit']]], + [/(\d+) packets with ECN ECT\(0\) bit set$/, [[:packets, 'ECT(0) bit']]], + [/(\d+) packets with ECN ECT\(1\) bit set$/, [[:packets, 'ECT(1) bit']]], + [/(\d+) successful ECN handshakes$/, [[:packets, 'successful handshakes']]], + [/(\d+) times ECN reduced the congestion window$/, [[:packets, 'congestion window reduced']]] + ]) + ] +end when 'udp' - $os == :linux ? [] : [ - Graph.new('received', protocol, [ - [/(\d+) datagrams received$/, [[:packets, 'total']]], - [/(\d+) with incomplete header$/, [[:packets, 'incomplete header']]], - [/(\d+) with bad data length field$/, [[:packets, 'bad data length field']]], - [/(\d+) with bad checksum$/, [[:packets, 'bad checksum']]], - [/(\d+) with no checksum$/, [[:packets, 'no checksum']]], - [/(\d+) dropped due to no socket$/, [[:packets, 'dropped: no socket']]], - [/(\d+) broadcast\/multicast datagrams undelivered$/, [[:packets, '*cast undelivered']]], - [/(\d+) dropped due to full socket buffers$/, [[:packets, 'dropped: no buffers']]], - [/(\d+) not for hashed pcb$/, [[:packets, 'not for hashed pcb']]], - [/(\d+) delivered$/, [[:packets, 'delivered']]] - ]), + if $os == :linux + [] + else + [ + Graph.new('received', protocol, [ + [/(\d+) datagrams received$/, [[:packets, 'total']]], + [/(\d+) with incomplete header$/, [[:packets, 'incomplete header']]], + [/(\d+) with bad data length field$/, [[:packets, 'bad data length field']]], + [/(\d+) with bad checksum$/, [[:packets, 'bad checksum']]], + [/(\d+) with no checksum$/, [[:packets, 'no checksum']]], + [/(\d+) dropped due to no socket$/, [[:packets, 'dropped: no socket']]], + [%r{(\d+) broadcast/multicast datagrams undelivered$}, [[:packets, '*cast undelivered']]], + [/(\d+) dropped due to full socket buffers$/, [[:packets, 'dropped: no buffers']]], + [/(\d+) not for hashed pcb$/, [[:packets, 'not for hashed pcb']]], + [/(\d+) delivered$/, [[:packets, 'delivered']]] + ]), - Graph.new('sent', protocol, [ - [/(\d+) datagrams output$/, [[:packets, 'total']]], - [/(\d+) times multicast source filter matched$/, [[:packets, 'multicast src filter match']]] - ]) - ] + Graph.new('sent', protocol, [ + [/(\d+) datagrams output$/, [[:packets, 'total']]], + [/(\d+) times multicast source filter matched$/, [[:packets, 'multicast src filter match']]] + ]) + ] +end when 'ip' - $os == :linux ? [] : [ - Graph.new('received', protocol, [ - [/(\d+) total packets received$/, [[:packets, 'total']]], - [/(\d+) bad header checksums$/, [[:packets, 'bad header checksum']]], - [/(\d+) with size smaller than minimum$/, [[:packets, 'size smaller than min']]], - [/(\d+) with data size < data length$/, [[:packets, 'data size < data length']]], - [/(\d+) with ip length > max ip packet size$/, [[:packets, 'ip length > max ip packet sz']]], - [/(\d+) with header length < data size$/, [[:packets, 'header length < data size']]], - [/(\d+) with data length < header length$/, [[:packets, 'data length < header length']]], - [/(\d+) with bad options$/, [[:packets, 'bad options']]], - [/(\d+) with incorrect version number$/, [[:packets, 'incorrect version']]], - [/(\d+) fragments? received$/, [[:packets, 'fragments']]], - [/(\d+) fragments? dropped \(dup or out of space\)$/, [[:packets, 'frags dropped: dup/out of spc']]], - [/(\d+) fragments? dropped after timeout$/, [[:packets, 'frags dropped: timeout']]], - [/(\d+) packets? reassembled ok$/, [[:packets, 'reassembled ok']]], - [/(\d+) packets? for this host$/, [[:packets, 'for this host']]], - [/(\d+) packets? for unknown\/unsupported protocol$/, [[:packets, 'for unknown/unsup protocol']]], - [/(\d+) packets? forwarded \((\d+) packets fast forwarded\)$/, [[:packets, 'forwarded'], [:packets, 'fast forwarded']]], - [/(\d+) packets? not forwardable$/, [[:packets, 'not forwardable']]], - [/(\d+) packets? received for unknown multicast group$/, [[:packets, 'unknown multicast grp']]] - ]), + if $os == :linux + [] + else + [ + Graph.new('received', protocol, [ + [/(\d+) total packets received$/, [[:packets, 'total']]], + [/(\d+) bad header checksums$/, [[:packets, 'bad header checksum']]], + [/(\d+) with size smaller than minimum$/, [[:packets, 'size smaller than min']]], + [/(\d+) with data size < data length$/, [[:packets, 'data size < data length']]], + [/(\d+) with ip length > max ip packet size$/, [[:packets, 'ip length > max ip packet sz']]], + [/(\d+) with header length < data size$/, [[:packets, 'header length < data size']]], + [/(\d+) with data length < header length$/, [[:packets, 'data length < header length']]], + [/(\d+) with bad options$/, [[:packets, 'bad options']]], + [/(\d+) with incorrect version number$/, [[:packets, 'incorrect version']]], + [/(\d+) fragments? received$/, [[:packets, 'fragments']]], + [/(\d+) fragments? dropped \(dup or out of space\)$/, [[:packets, 'frags dropped: dup/out of spc']]], + [/(\d+) fragments? dropped after timeout$/, [[:packets, 'frags dropped: timeout']]], + [/(\d+) packets? reassembled ok$/, [[:packets, 'reassembled ok']]], + [/(\d+) packets? for this host$/, [[:packets, 'for this host']]], + [%r{(\d+) packets? for unknown/unsupported protocol$}, [[:packets, 'for unknown/unsup protocol']]], + [/(\d+) packets? forwarded \((\d+) packets fast forwarded\)$/, [[:packets, 'forwarded'], [:packets, 'fast forwarded']]], + [/(\d+) packets? not forwardable$/, [[:packets, 'not forwardable']]], + [/(\d+) packets? received for unknown multicast group$/, [[:packets, 'unknown multicast grp']]] + ]), - Graph.new('sent', protocol, [ - [/(\d+) packets? sent from this host$/, [[:packets, 'total']]], - [/(\d+) redirects? sent$/, [[:packets, 'redirect']]], - [/(\d+) packets? sent with fabricated ip header$/, [[:packets, 'fabricated IP head']]], - [/(\d+) output packets? dropped due to no bufs, etc\.$/, [[:packets, 'dropped: no bufs, etc']]], - [/(\d+) output packets? discarded due to no route$/, [[:packets, 'discarded: no route']]], - [/(\d+) output datagrams? fragmented$/, [[:packets, 'fragmented']]], - [/(\d+) fragments? created$/, [[:packets, 'fragments created']]], - [/(\d+) datagrams? that can't be fragmented$/, [[:packets, "can't be fragmented"]]], - [/(\d+) tunneling packets? that can't find gif$/, [[:packets, 'tunneling, gif not found']]], - [/(\d+) datagrams? with bad address in header$/, [[:packets, 'bad address in header']]] - ]) - ] + Graph.new('sent', protocol, [ + [/(\d+) packets? sent from this host$/, [[:packets, 'total']]], + [/(\d+) redirects? sent$/, [[:packets, 'redirect']]], + [/(\d+) packets? sent with fabricated ip header$/, [[:packets, 'fabricated IP head']]], + [/(\d+) output packets? dropped due to no bufs, etc\.$/, [[:packets, 'dropped: no bufs, etc']]], + [/(\d+) output packets? discarded due to no route$/, [[:packets, 'discarded: no route']]], + [/(\d+) output datagrams? fragmented$/, [[:packets, 'fragmented']]], + [/(\d+) fragments? created$/, [[:packets, 'fragments created']]], + [/(\d+) datagrams? that can't be fragmented$/, [[:packets, "can't be fragmented"]]], + [/(\d+) tunneling packets? that can't find gif$/, [[:packets, 'tunneling, gif not found']]], + [/(\d+) datagrams? with bad address in header$/, [[:packets, 'bad address in header']]] + ]) + ] +end when 'arp' - $os == :linux ? [] : [ - Graph.new('packets', protocol, [ - # This is just a total, so ignore the value but keep regexp to avoid 'not parsed' warning. - [/(\d+) ARP packets? received$/], - [/(\d+) ARP requests? received$/, [[:packets, 'requests received']]], - [/(\d+) ARP repl(?:y|ies) received$/, [[:packets, 'replies received']]], - [/(\d+) ARP requests? sent$/, [[:packets, 'requests', 'requests received']]], - [/(\d+) ARP repl(?:y|ies) sent$/, [[:packets, 'replies', 'replies received']]], - [/(\d+) total packets? dropped due to no ARP entry$/, [[:packets, 'no entry']]] - ]), + if $os == :linux + [] + else + [ + Graph.new('packets', protocol, [ + # This is just a total, so ignore the value but keep regexp to avoid 'not parsed' warning. + [/(\d+) ARP packets? received$/], + [/(\d+) ARP requests? received$/, [[:packets, 'requests received']]], + [/(\d+) ARP repl(?:y|ies) received$/, [[:packets, 'replies received']]], + [/(\d+) ARP requests? sent$/, [[:packets, 'requests', 'requests received']]], + [/(\d+) ARP repl(?:y|ies) sent$/, [[:packets, 'replies', 'replies received']]], + [/(\d+) total packets? dropped due to no ARP entry$/, [[:packets, 'no entry']]] + ]), - Graph.new('entries', protocol, [ - [/(\d+) ARP entrys? timed out$/, [[:entries, 'timed out']]], - [/(\d+) Duplicate IPs seen$/, [[:entries, 'duplicate IPs seen']]] - ]) - ] + Graph.new('entries', protocol, [ + [/(\d+) ARP entrys? timed out$/, [[:entries, 'timed out']]], + [/(\d+) Duplicate IPs seen$/, [[:entries, 'duplicate IPs seen']]] + ]) + ] +end end end @@ -379,9 +396,11 @@ proto_name = 'tcp' if proto_name.empty? def netstat_s(protocol) if $os == :linux - %w(tcp udp).include?(protocol) ? - `netstat -s --#{protocol}` : + if %w[tcp udp].include?(protocol) + `netstat -s --#{protocol}` + else `netstat -s --raw` +end else `netstat -sp #{protocol}` end.lines.reject { |line| line =~ /^\w+:/ } @@ -389,20 +408,22 @@ end case ARGV.first when 'autoconf' - puts [:linux, :freebsd].include?($os) ? 'yes' : 'no' + puts %i[linux freebsd].include?($os) ? 'yes' : 'no' when 'suggest' - puts $os == :linux ? %w(tcp) : %w(tcp udp ip arp) + puts $os == :linux ? %w[tcp] : %w[tcp udp ip arp] when 'config' - graphs_for(proto_name).each { |graph| + graphs_for(proto_name).each do |graph| puts graph.config.join $/ - } + end else data = netstat_s(proto_name) - graphs_for(proto_name).each { |graph| + graphs_for(proto_name).each do |graph| puts graph.fetch(data).join $/ - } + end - warn "not parsed:\n#{data.join}" unless data.empty? if $debug_mode + if $debug_mode + warn "not parsed:\n#{data.join}" unless data.empty? +end end # awful performance when scrolling through those regexps above diff --git a/plugins/oracle/oracle-pga-monitor b/plugins/oracle/oracle-pga-monitor index a6929ad8..90920a37 100755 --- a/plugins/oracle/oracle-pga-monitor +++ b/plugins/oracle/oracle-pga-monitor @@ -21,39 +21,36 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) env.ORACLE_HOME set in munin-node - 2) rubygems - 3) oci8 - DBI gem for connecting to Oracle - * instruction of installing oci8 is available here: - http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html + 1) env.ORACLE_HOME set in munin-node + 2) rubygems + 3) oci8 - DBI gem for connecting to Oracle + * instruction of installing oci8 is available here: + http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/oracle__pga.rb /etc/munin/plugins/oracle__pga.rb ** replace with your oralce sid Parameters: - autoconf - config (required) + autoconf + config (required) Configurable variables: - orauser : oracle user who has select privilege to query v$pgastat view - orapass : password for the oracle user - dbport : port used by the monitored instance (notice: numeric value) - dbname : database to be monitored - dbhost : host or ip address of db instance - + orauser : oracle user who has select privilege to query v$pgastat view + orapass : password for the oracle user + dbport : port used by the monitored instance (notice: numeric value) + dbname : database to be monitored + dbhost : host or ip address of db instance #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'oci8' @@ -69,7 +66,7 @@ tnsname = "(DESCRIPTION = def runQuery(name, query) rows = $conn.exec(query) - puts "#{name}.value #{rows.fetch().to_s}" + puts "#{name}.value #{rows.fetch}" rows.close end @@ -82,31 +79,32 @@ pga_target_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024) pga_query = "SELECT TO_CHAR(ROUND(decode(unit,'bytes',(value)/(1024*1024),value),2)) pga from V$PGASTAT where name = 'total PGA inuse'" -pga_components = { "pga_target" => pga_target_query, - "pga_in_use" => pga_query } +pga_components = { 'pga_target' => pga_target_query, + 'pga_in_use' => pga_query } # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" +when 'config' + puts 'graph_args --base 1024k -r --lower-limit 0' puts "graph_title Oracle PGA from #{dbname}" - puts "graph_category db" - puts "graph_info This graph shows the PGA memory usage (in MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts 'graph_category db' + puts 'graph_info This graph shows the PGA memory usage (in MB)' + puts 'graph_vlabel MB' + puts 'graph_scale no' + puts 'graph_period second' pga_components.keys.each do |p| puts "#{p}.label #{p}" diff --git a/plugins/oracle/oracle-sga b/plugins/oracle/oracle-sga index c1aa5d12..3fc21f03 100755 --- a/plugins/oracle/oracle-sga +++ b/plugins/oracle/oracle-sga @@ -21,38 +21,35 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - Prerequistes: - 1) env.ORACLE_HOME set in munin-node - 2) rubygems - 3) oci8 - DBI gem for connecting to Oracle - * instruction of installing oci8 is available here: - http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html + 1) env.ORACLE_HOME set in munin-node + 2) rubygems + 3) oci8 - DBI gem for connecting to Oracle + * instruction of installing oci8 is available here: + http://ruby-oci8.rubyforge.org/en/InstallBinaryPackage.html Usage: - 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) - 2) chmod to allow executable to others - 3) create symbolic link in /etc/munin/plugins + 1) copy this script to the munin install plugins directory (e.g. /usr/share/munin/plugins) + 2) chmod to allow executable to others + 3) create symbolic link in /etc/munin/plugins ln -s /usr/share/munin/plugins/oracle_orcl_sga.rb /etc/munin/plugins/oracle_orcl_sga.rb Parameters: - autoconf - config (required) + autoconf + config (required) Configurable variables: - orauser : oracle user who has select privilege to query v$sgastat view - orapass : password for the oracle user - dbport : port used by the monitored instance (notice: numeric value) - dbname : database to be monitored - dbhost : host or ip address of db instance - + orauser : oracle user who has select privilege to query v$sgastat view + orapass : password for the oracle user + dbport : port used by the monitored instance (notice: numeric value) + dbname : database to be monitored + dbhost : host or ip address of db instance #%# family=auto #%# capabilities=autoconf =end - require 'rubygems' require 'oci8' @@ -68,7 +65,7 @@ tnsname = "(DESCRIPTION = def runQuery(name, query) rows = $conn.exec(query) - puts "#{name}.value #{rows.fetch().to_s}" + puts "#{name}.value #{rows.fetch}" rows.close end @@ -102,35 +99,36 @@ log_buffer_query = "SELECT TO_CHAR(ROUND(SUM(decode(pool, NULL, decode(name, 'log_buffer', (bytes)/(1024*1024),0),0)),2)) sga_lbuffer from V$SGASTAT" -memory_components = { "fixed_area" => fixed_area_query, - "buffer_cache" => buffer_cache_query, - "java_pool" => java_pool_query, - "large_pool" => large_pool_query, - "log_buffer" => log_buffer_query, - "shared_pool" => shared_pool_query } +memory_components = { 'fixed_area' => fixed_area_query, + 'buffer_cache' => buffer_cache_query, + 'java_pool' => java_pool_query, + 'large_pool' => large_pool_query, + 'log_buffer' => log_buffer_query, + 'shared_pool' => shared_pool_query } # # autoconf # -if ARGV[0] == "autoconf" +case ARGV[0] +when 'autoconf' if tnsname.length > 1 && orauser.length > 1 && orapass.length > 1 - puts "yes" + puts 'yes' else - puts "no" + puts 'no' puts "Usage: #{__FILE__} autoconf|conf" end exit 0 # # config definition # -elsif ARGV[0] == "config" - puts "graph_args --base 1024k -r --lower-limit 0" +when 'config' + puts 'graph_args --base 1024k -r --lower-limit 0' puts "graph_title Oracle SGA from #{dbname}" - puts "graph_category db" - puts "graph_info This graph shows the SGA memory usage (in MB)" - puts "graph_vlabel MB" - puts "graph_scale no" - puts "graph_period second" + puts 'graph_category db' + puts 'graph_info This graph shows the SGA memory usage (in MB)' + puts 'graph_vlabel MB' + puts 'graph_scale no' + puts 'graph_period second' memory_components.keys.each do |m| puts "#{m}.label #{m}" @@ -138,7 +136,7 @@ elsif ARGV[0] == "config" puts "#{m}.type GAUGE" # make sure fixed_area is at the bottom of the stack - if (m == 'fixed_area') + if m == 'fixed_area' puts "#{m}.draw AREA" else puts "#{m}.draw STACK" diff --git a/plugins/other/port_ b/plugins/other/port_ index 585166f5..81fd7f47 100755 --- a/plugins/other/port_ +++ b/plugins/other/port_ @@ -16,53 +16,53 @@ require 'rubygems' require 'munin' SERVICE = $0.split('_').last -SERVICE_F = '/etc/services' -PORT = /^[\d]+(\.[\d]+){0,1}$/ === SERVICE ? SERVICE : %x[grep #{SERVICE} #{SERVICE_F}].split("\t\t")[1].split('/')[0] +SERVICE_F = '/etc/services'.freeze +PORT = SERVICE =~ /^\d+(\.\d+){0,1}$/ ? SERVICE : `grep #{SERVICE} #{SERVICE_F}`.split("\t\t")[1].split('/')[0] class PortMonit < Munin::Plugin graph_attributes "#{SERVICE} port usage, known as #{PORT}", - :category => 'network', - :info => 'This graph shows connection split by the state of the socket.', - :vlabel => 'Current connections' + category: 'network', + info: 'This graph shows connection split by the state of the socket.', + vlabel: 'Current connections' declare_field :ESTABLISHED, - :label => 'Established', :draw => :AREA, - :type => :GAUGE, :min => 0 + label: 'Established', draw: :AREA, + type: :GAUGE, min: 0 declare_field :CLOSE_WAIT, - :label => 'Waiting close', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Waiting close', draw: :STACK, + type: :GAUGE, min: 0 declare_field :TIME_WAIT, - :label => 'Waiting after close', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Waiting after close', draw: :STACK, + type: :GAUGE, min: 0 declare_field :CLOSING, - :label => 'Closing', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Closing', draw: :STACK, + type: :GAUGE, min: 0 declare_field :LAST_ACK, - :label => 'Waiting for acknowledgement', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Waiting for acknowledgement', draw: :STACK, + type: :GAUGE, min: 0 declare_field :FIN_WAIT_1, - :label => 'Socket closed, connection shutting down', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Socket closed, connection shutting down', draw: :STACK, + type: :GAUGE, min: 0 declare_field :FIN_WAIT_2, - :label => 'Connection closed, Socket still waiting', :draw => :STACK, - :type => :GAUGE, :min => 0 + label: 'Connection closed, Socket still waiting', draw: :STACK, + type: :GAUGE, min: 0 def retrieve_values - @_netstat = %x[netstat -n -P tcp | egrep "\.#{PORT} "].split("\n") + @_netstat = `netstat -n -P tcp | egrep "\.#{PORT} "`.split("\n") - { :ESTABLISHED => count(@_netstat, 'ESTABLISHED'), - :CLOSE_WAIT => count(@_netstat, 'CLOSE_WAIT'), - :CLOSING => count(@_netstat, 'CLOSING'), - :LAST_ACK => count(@_netstat, 'LAST_ACK'), - :FIN_WAIT_1 => count(@_netstat, 'FIN_WAIT_1'), - :FIN_WAIT_2 => count(@_netstat, 'FIN_WAIT_2'), - :TIME_WAIT => count(@_netstat, 'TIME_WAIT') } + { ESTABLISHED: count(@_netstat, 'ESTABLISHED'), + CLOSE_WAIT: count(@_netstat, 'CLOSE_WAIT'), + CLOSING: count(@_netstat, 'CLOSING'), + LAST_ACK: count(@_netstat, 'LAST_ACK'), + FIN_WAIT_1: count(@_netstat, 'FIN_WAIT_1'), + FIN_WAIT_2: count(@_netstat, 'FIN_WAIT_2'), + TIME_WAIT: count(@_netstat, 'TIME_WAIT') } end private @@ -72,7 +72,7 @@ class PortMonit < Munin::Plugin source.each { |obj| @_result += 1 if obj.match(regex) } - return @_result + @_result end end diff --git a/plugins/passenger/passenger_memory b/plugins/passenger/passenger_memory index c1138457..171db640 100755 --- a/plugins/passenger/passenger_memory +++ b/plugins/passenger/passenger_memory @@ -11,11 +11,11 @@ memory_stats_command = ENV['memory_stats_command'] || '/opt/ruby-enterprise-1.8.6-20080810/bin/passenger-memory-stats' if ARGV.length > 0 && ARGV[0] == 'config' - puts "graph_title Passenger Memory Usage" - puts "graph_category webserver" - puts "graph_vlabel MB" - puts "apache_rss.label Apache Dirty RSS" - puts "passenger_rss.label Passenger Dirty RSS" + puts 'graph_title Passenger Memory Usage' + puts 'graph_category webserver' + puts 'graph_vlabel MB' + puts 'apache_rss.label Apache Dirty RSS' + puts 'passenger_rss.label Passenger Dirty RSS' exit(0) end diff --git a/plugins/passenger/passenger_processes b/plugins/passenger/passenger_processes index 1af4e27b..8b4b5a4b 100755 --- a/plugins/passenger/passenger_processes +++ b/plugins/passenger/passenger_processes @@ -8,13 +8,13 @@ process_stats_command = ENV['process_stats_command'] || '/opt/ruby-enterprise-1.8.6-20080810/bin/passenger-status' if ARGV.length > 0 && ARGV[0] == 'config' - puts "graph_title Passenger Processes" - puts "graph_category webserver" - puts "graph_vlabel processes" - puts "max.label Max processes" - puts "count.label Total processes" - puts "active.label Active processes" - puts "queued.label Queued requests" + puts 'graph_title Passenger Processes' + puts 'graph_category webserver' + puts 'graph_vlabel processes' + puts 'max.label Max processes' + puts 'count.label Total processes' + puts 'active.label Active processes' + puts 'queued.label Queued requests' exit(0) end @@ -24,13 +24,14 @@ active = nil queued = nil `#{process_stats_command}`.each_line do |line| - if /max\s+=\s+(\d+)/.match(line) + case line + when /max\s+=\s+(\d+)/ max = $~[1] - elsif /count\s+=\s+(\d+)/.match(line) + when /count\s+=\s+(\d+)/ count = $~[1] - elsif /^active\s+=\s+(\d+)/.match(line) + when /^active\s+=\s+(\d+)/ active = $~[1] - elsif /Waiting on global queue\s+=\s+(\d+)/.match(line) + when /Waiting on global queue\s+=\s+(\d+)/ queued = $~[1] end end diff --git a/plugins/passenger/passenger_status b/plugins/passenger/passenger_status index 8c992c54..9ac5738a 100755 --- a/plugins/passenger/passenger_status +++ b/plugins/passenger/passenger_status @@ -28,27 +28,27 @@ end def output_values status = `sudo passenger-status` unless $?.success? - $stderr.puts "failed executing passenger-status" + warn 'failed executing passenger-status' exit 1 end status =~ /max\s+=\s+(\d+)/ - puts "max.value #{$1}" + puts "max.value #{Regexp.last_match(1)}" status =~ /count\s+=\s+(\d+)/ - puts "running.value #{$1}" + puts "running.value #{Regexp.last_match(1)}" status =~ /active\s+=\s+(\d+)/ - puts "active.value #{$1}" + puts "active.value #{Regexp.last_match(1)}" status =~ /Waiting on global queue:\s+(\d+)/ - puts "waiting.value #{$1}" + puts "waiting.value #{Regexp.last_match(1)}" total_sessions = 0 status.scan(/Sessions: (\d+)/).flatten.each { |count| total_sessions += count.to_i } puts "sessions.value #{total_sessions}" end -if ARGV[0] == "config" +if ARGV[0] == 'config' output_config else output_values diff --git a/plugins/php/php_eaccelerator b/plugins/php/php_eaccelerator index 1d89df71..e5e93354 100755 --- a/plugins/php/php_eaccelerator +++ b/plugins/php/php_eaccelerator @@ -31,7 +31,7 @@ user = ENV['user'] || 'user' pwd = ENV['password'] || 'password' url = ENV['url'] || 'http://127.0.0.1/control.php' -if ARGV[0] == "config" +if ARGV[0] == 'config' print "EAccelerator Monitoring\n" print "graph_title PHP Eaccelerator\n" print "graph_category webserver\n" @@ -49,54 +49,48 @@ end one_liners = 0 three_liners = 0 -key = "" +key = '' -open(url, :http_basic_authentication => [user, pwd]) do |f| +open(url, http_basic_authentication: [user, pwd]) do |f| f.each do |line| if three_liners > 0 - three_liners = three_liners + 1 + three_liners += 1 - if three_liners == 2 - print "Memoryusagepercentage.value " - end + print 'Memoryusagepercentage.value ' if three_liners == 2 - if three_liners == 3 - print "Memoryusage.value " - end + print 'Memoryusage.value ' if three_liners == 3 - if three_liners == 4 - print "Memorymax.value " - end + print 'Memorymax.value ' if three_liners == 4 - print line.gsub!(/[^0-9.]/s, "") + print line.gsub!(/[^0-9.]/s, '') print "\n" end if one_liners > 0 - one_liners = one_liners + 1 + one_liners += 1 print "#{key}.value " - print line.gsub!(/[^0-9.]/s, "") + print line.gsub!(/[^0-9.]/s, '') print "\n" end if one_liners > 1 - line = "" + line = '' one_liners = 0 end if three_liners > 3 - line = "" + line = '' three_liners = 0 end if line =~ /Memory usage/ - key = line.gsub!(/(<[^>]*>)|\n|\t| /s, "") - three_liners = three_liners + 1 + key = line.gsub!(/(<[^>]*>)|\n|\t| /s, '') + three_liners += 1 end if line =~ /Free memory/ || line =~ /Cached scripts/ || line =~ /Removed scripts/ || line =~ /Cached keys/ - key = line.gsub!(/(<[^>]*>)|\n|\t| /s, "") - one_liners = one_liners + 1 + key = line.gsub!(/(<[^>]*>)|\n|\t| /s, '') + one_liners += 1 end end end diff --git a/plugins/puppet/puppet_runtime b/plugins/puppet/puppet_runtime index 620ac06e..30720582 100755 --- a/plugins/puppet/puppet_runtime +++ b/plugins/puppet/puppet_runtime @@ -15,29 +15,29 @@ # reports how long the puppet agent took to apply the catalog def get_runtime - logfile = ENV["puppet_logfile"] || "/var/log/messages" + logfile = ENV['puppet_logfile'] || '/var/log/messages' t = Time.now - dateformat = ENV["puppet_logformat"] || "^%b %d" + dateformat = ENV['puppet_logformat'] || '^%b %d' today = t.strftime(dateformat) File.open(logfile).grep(/#{today}/).grep(/Finished catalog run in/).reverse_each do |line| if line =~ /in (.*) seconds/ - puts "runtime.value #{$1}" + puts "runtime.value #{Regexp.last_match(1)}" exit 0 end end end case ARGV[0] -when "config" - puts "graph_category other" - puts "graph_args --base 1000 -l 0" - puts "graph_scale no" - puts "graph_title puppet catalog run time" - puts "graph_vlabel Seconds" - puts "runtime.label Catalog application time" +when 'config' + puts 'graph_category other' + puts 'graph_args --base 1000 -l 0' + puts 'graph_scale no' + puts 'graph_title puppet catalog run time' + puts 'graph_vlabel Seconds' + puts 'runtime.label Catalog application time' exit 0 -when "autoconf" - puts "yes" +when 'autoconf' + puts 'yes' exit 0 else get_runtime diff --git a/plugins/router/ag241-adsl b/plugins/router/ag241-adsl index 1aba2300..e4063276 100755 --- a/plugins/router/ag241-adsl +++ b/plugins/router/ag241-adsl @@ -35,7 +35,6 @@ Some magical munin foo... =end - # Require this module, it is part of the standard ruby lib AFAIK require 'net/http' @@ -49,7 +48,7 @@ stat = nil # Check executable "name" for parameter count params = $0.split('_') if params.size != 3 - puts "Incorrect number of parameters" + puts 'Incorrect number of parameters' exit 1 end @@ -59,11 +58,11 @@ host = params[1] stat = params[2] unless ENV['debug'].nil? - puts "user = " + user - puts "pass = " + pass - puts "host = " + host - puts "port = " + port - puts "stat = " + stat + puts 'user = ' + user + puts 'pass = ' + pass + puts 'host = ' + host + puts 'port = ' + port + puts 'stat = ' + stat end # Dump the graph configuration data @@ -101,7 +100,7 @@ s = response.body # Make sure we got the page successfully if response.code != '200' - puts "Getting web page failed:" + puts 'Getting web page failed:' case response.code when '401' puts 'Probably because the username and password are incorrect' @@ -124,12 +123,12 @@ when 'syncrate' when 'attenuation' a = s.scan(/.*share\.lineatt.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) b, c = a[0] - puts 'down.value ' + (b.to_i).to_s + "\n" + 'up.value ' + (c.to_i).to_s + puts 'down.value ' + b.to_i.to_s + "\n" + 'up.value ' + c.to_i.to_s exit 0 when 'margin', 'noise' a = s.scan(/.*share\.noise.*\n.*share\.down[^0-9]*([0-9]+).*share\.up[^0-9]*([0-9]+).*$/) b, c = a[0] - puts 'down.value ' + (b.to_i).to_s + "\n" + 'up.value ' + (c.to_i).to_s + puts 'down.value ' + b.to_i.to_s + "\n" + 'up.value ' + c.to_i.to_s exit 0 else puts 'Statistic ' + stat.to_s + ' not known, would you like me to fabricate it for you?' diff --git a/plugins/router/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin index b8712d49..af8705a5 100755 --- a/plugins/router/d-link-dir-655-router-statistics-plugin +++ b/plugins/router/d-link-dir-655-router-statistics-plugin @@ -32,13 +32,13 @@ require 'digest/md5' require 'nokogiri' def output - nics = Hash.new - nics["LAN"] = Hash.new - nics["WAN"] = Hash.new - nics["WLAN"] = Hash.new - password = ENV['router_password'] || "" - router_path = ENV['router_ip_address'] || "10.0.0.1" - router_path = "http://" + router_path + nics = {} + nics['LAN'] = {} + nics['WAN'] = {} + nics['WLAN'] = {} + password = ENV['router_password'] || '' + router_path = ENV['router_ip_address'] || '10.0.0.1' + router_path = 'http://' + router_path agent = Mechanize.new x = agent.get(router_path) salt = x.body.match(/salt = "(.*)"/)[1] @@ -48,7 +48,7 @@ def output padded_password = password + "\x01" * pad_size # pad it the rest of the way, length 64 for user - salted_password = salt + padded_password + ("\x01" * (63 - salt.length - padded_password.length)) + "U" + salted_password = salt + padded_password + ("\x01" * (63 - salt.length - padded_password.length)) + 'U' login_hash = salt + Digest::MD5.hexdigest(salted_password) # authenticate against the router using the hash that we just built @@ -61,87 +61,87 @@ def output doc.xpath('//interface').each do |interface| children = interface.children name = children.search('name')[0].text - nics[name]["packets_sent"] = children.search('packets_sent')[0].text - nics[name]["packets_received"] = children.search('packets_received')[0].text - nics[name]["tx_dropped"] = children.search('tx_dropped')[0].text + nics[name]['packets_sent'] = children.search('packets_sent')[0].text + nics[name]['packets_received'] = children.search('packets_received')[0].text + nics[name]['tx_dropped'] = children.search('tx_dropped')[0].text begin - nics[name]["tx_collisions"] = children.search('tx_collisions')[0].text + nics[name]['tx_collisions'] = children.search('tx_collisions')[0].text rescue Exception - nics[name]["tx_collisions"] = "0" + nics[name]['tx_collisions'] = '0' end - nics[name]["rx_dropped"] = children.search('rx_dropped')[0].text - nics[name]["rx_errors"] = children.search('rx_errors')[0].text + nics[name]['rx_dropped'] = children.search('rx_dropped')[0].text + nics[name]['rx_errors'] = children.search('rx_errors')[0].text end # get wifi associations and print out info for munin graph - puts "multigraph clients" + puts 'multigraph clients' clients_xml = agent.get("#{router_path}/wifi_assoc.xml").body j = 0 doc = Nokogiri::XML(clients_xml.to_s) - doc.xpath('//assoc').each do |assoc| + doc.xpath('//assoc').each do |_assoc| j += 1 end - puts "wifi_assoc.value " + j.to_s + puts 'wifi_assoc.value ' + j.to_s # get dhcp clients and print out info for munin graph clients_xml = agent.get("#{router_path}/dhcp_clients.xml").body j = 0 doc = Nokogiri::XML(clients_xml.to_s) - doc.xpath('//client').each do |client| + doc.xpath('//client').each do |_client| j += 1 end - puts "dhcp_clients.value " + j.to_s + puts 'dhcp_clients.value ' + j.to_s - puts "multigraph uptime" + puts 'multigraph uptime' # get uptime of connection clients_xml = agent.get("#{router_path}/wan_connection_status.xml").body doc = Nokogiri::XML(clients_xml.to_s) uptime = doc.children.search('wan_interface_up_time_0')[0].text - puts "uptime.value " + sprintf("%.2f", (Float(uptime) / 86400)) + puts 'uptime.value ' + format('%.2f', (Float(uptime) / 86_400)) # graph overall interface packets transferred per interval - puts "multigraph if_packets" - for i in ["LAN", "WAN", "WLAN"] do - puts "#{i}_recv.value " + nics[i]["packets_received"] - puts "#{i}_send.value " + nics[i]["packets_sent"] + puts 'multigraph if_packets' + %w[LAN WAN WLAN].each do |i| + puts "#{i}_recv.value " + nics[i]['packets_received'] + puts "#{i}_send.value " + nics[i]['packets_sent'] end # graph overall interface dropped packets per interval - puts "multigraph if_drop" - for i in ["LAN", "WAN", "WLAN"] do - puts "#{i}_recv.value " + nics[i]["rx_dropped"] - puts "#{i}_send.value " + nics[i]["tx_dropped"] + puts 'multigraph if_drop' + %w[LAN WAN WLAN].each do |i| + puts "#{i}_recv.value " + nics[i]['rx_dropped'] + puts "#{i}_send.value " + nics[i]['tx_dropped'] end # graph overall interface collisions & errors per interval - puts "multigraph if_collerr" - for i in ["LAN", "WAN", "WLAN"] do - puts "#{i}_coll.value " + nics[i]["tx_collisions"] - puts "#{i}_err.value " + nics[i]["rx_errors"] + puts 'multigraph if_collerr' + %w[LAN WAN WLAN].each do |i| + puts "#{i}_coll.value " + nics[i]['tx_collisions'] + puts "#{i}_err.value " + nics[i]['rx_errors'] end # graph stats for each interface - for i in ["LAN", "WAN", "WLAN"] do + %w[LAN WAN WLAN].each do |i| puts "multigraph if_packets.#{i}" - puts "send.value " + nics[i]["packets_sent"] - puts "recv.value " + nics[i]["packets_received"] + puts 'send.value ' + nics[i]['packets_sent'] + puts 'recv.value ' + nics[i]['packets_received'] puts "multigraph if_drop.#{i}" - puts "send.value " + nics[i]["tx_dropped"] - puts "recv.value " + nics[i]["rx_dropped"] + puts 'send.value ' + nics[i]['tx_dropped'] + puts 'recv.value ' + nics[i]['rx_dropped'] puts "multigraph if_collerr.#{i}" - puts "coll.value " + nics[i]["tx_collisions"] - puts "err.value " + nics[i]["rx_errors"] + puts 'coll.value ' + nics[i]['tx_collisions'] + puts 'err.value ' + nics[i]['rx_errors'] end end def config # build the configuration for graphs - puts "multigraph if_packets" + puts 'multigraph if_packets' puts 'graph_title D-Link DIR-655 interface traffic' puts 'graph_category network' puts 'graph_order LAN_recv LAN_send WAN_recv WAN_send WLAN_recv WLAN_send' puts 'graph_vlabel packets in (-) / out (+) per ${graph_period}' - for i in ["LAN", "WAN", "WLAN"] do + %w[LAN WAN WLAN].each do |i| puts "#{i}_recv.type DERIVE" puts "#{i}_recv.graph no" puts "#{i}_recv.min 0" @@ -151,12 +151,12 @@ def config puts "#{i}_send.min 0" end - puts "multigraph if_drop" + puts 'multigraph if_drop' puts 'graph_title D-Link DIR-655 interface drops' puts 'graph_category network' puts 'graph_order LAN_recv LAN_send WAN_recv WAN_send WLAN_recv WLAN_send' puts 'graph_vlabel packets / ${graph_period}' - for i in ["LAN", "WAN", "WLAN"] do + %w[LAN WAN WLAN].each do |i| puts "#{i}_recv.type DERIVE" puts "#{i}_recv.graph no" puts "#{i}_recv.min 0" @@ -166,12 +166,12 @@ def config puts "#{i}_send.min 0" end - puts "multigraph if_collerr" + puts 'multigraph if_collerr' puts 'graph_title D-Link DIR-655 interface collisions & errors' puts 'graph_category network' puts 'graph_order LAN_coll LAN_err WAN_coll WAN_err WLAN_coll WLAN_coll' puts 'graph_vlabel packets / ${graph_period}' - for i in ["LAN", "WAN", "WLAN"] do + %w[LAN WAN WLAN].each do |i| puts "#{i}_coll.label #{i} collisions" puts "#{i}_coll.type DERIVE" puts "#{i}_coll.min 0" @@ -180,26 +180,26 @@ def config puts "#{i}_err.min 0" end - puts "multigraph clients" - puts "graph_title D-Link DIR-655 client information" - puts "graph_category system" - puts "graph_order dhcp_clients wifi_assoc" - puts "graph_vlabel number of clients" - puts "dhcp_clients.label DHCP clients" - puts "dhcp_clients.type GAUGE" - puts "dhcp_clients.min 0" - puts "wifi_assoc.label wifi clients" - puts "wifi_assoc.type GAUGE" - puts "wifi_assoc.min 0" + puts 'multigraph clients' + puts 'graph_title D-Link DIR-655 client information' + puts 'graph_category system' + puts 'graph_order dhcp_clients wifi_assoc' + puts 'graph_vlabel number of clients' + puts 'dhcp_clients.label DHCP clients' + puts 'dhcp_clients.type GAUGE' + puts 'dhcp_clients.min 0' + puts 'wifi_assoc.label wifi clients' + puts 'wifi_assoc.type GAUGE' + puts 'wifi_assoc.min 0' - puts "multigraph uptime" - puts "graph_title Uptime" + puts 'multigraph uptime' + puts 'graph_title Uptime' puts 'graph_vlabel uptime in days' puts 'graph_category system' puts 'uptime.label uptime' puts 'uptime.draw AREA' - for i in ["LAN", "WAN", "WLAN"] do + %w[LAN WAN WLAN].each do |i| puts "multigraph if_packets.#{i}" puts "graph_title D-Link DIR-655 #{i} traffic" puts 'graph_category network' @@ -243,8 +243,8 @@ def config end # main -if ARGV.length == 1 and ARGV[0] == 'config' - config() +if (ARGV.length == 1) && (ARGV[0] == 'config') + config else - output() + output end diff --git a/plugins/router/snmp__linksys_poe b/plugins/router/snmp__linksys_poe index a79a27ea..72d6e0bb 100755 --- a/plugins/router/snmp__linksys_poe +++ b/plugins/router/snmp__linksys_poe @@ -65,45 +65,43 @@ rights to this plugin are waived. Do with it as you wish. require 'snmp' -idx_oid = "enterprises.3955.89.108.1.1.2" -max_oid = "enterprises.3955.89.108.1.1.6" -cur_oid = "enterprises.3955.89.108.1.1.5" +idx_oid = 'enterprises.3955.89.108.1.1.2' +max_oid = 'enterprises.3955.89.108.1.1.6' +cur_oid = 'enterprises.3955.89.108.1.1.5' -community = ENV['community'] || "public" +community = ENV['community'] || 'public' version = ENV['version'] == '1' ? :SNMPv1 : :SNMPv2c case ARGV[0] -when "snmpconf" - puts "require 1.3.6.1.4.1.3955.89.108.1.1.2.1. [0-9]" - puts "require 1.3.6.1.4.1.3955.89.108.1.1.5.1. [0-9]" - puts "require 1.3.6.1.4.1.3955.89.108.1.1.6.1. [0-9]" - exit 0; -when "config" +when 'snmpconf' + puts 'require 1.3.6.1.4.1.3955.89.108.1.1.2.1. [0-9]' + puts 'require 1.3.6.1.4.1.3955.89.108.1.1.5.1. [0-9]' + puts 'require 1.3.6.1.4.1.3955.89.108.1.1.6.1. [0-9]' + exit 0 +when 'config' host = $0.match('^(?:|.*\/)snmp_([^_]+)')[1] puts "host_name #{host}" - puts "graph_title PoE Power Usage" - puts "graph_vlabel Watts" - puts "graph_category sensors" + puts 'graph_title PoE Power Usage' + puts 'graph_vlabel Watts' + puts 'graph_category sensors' max_current = 0 - SNMP::Manager.open(:Host => host, - :Community => community, - :Version => version) do |manager| + SNMP::Manager.open(Host: host, + Community: community, + Version: version) do |manager| manager.walk([idx_oid, max_oid]) do |row| puts "iface_#{row[0].value}.label Port #{row[0].value}" puts "iface_#{row[0].value}.cdef iface_#{row[0].value},1000,/" puts "iface_#{row[0].value}.line #{row[1].value.to_f / 1000}" - if row[1].value > max_current - max_current = row[1].value - end + max_current = row[1].value if row[1].value > max_current end end puts "graph_args --upper-limit #{max_current.to_f / 1000}" exit 0 else host = $0.match('^(?:|.*\/)snmp_([^_]+)')[1] - SNMP::Manager.open(:Host => host, - :Community => community, - :Version => version) do |manager| + SNMP::Manager.open(Host: host, + Community: community, + Version: version) do |manager| manager.walk([idx_oid, cur_oid]) do |row| puts "iface_#{row[0].value}.value #{row[1].value}" end diff --git a/plugins/snmp/snmp_room_alert_ b/plugins/snmp/snmp_room_alert_ index d2e63dec..a1822add 100755 --- a/plugins/snmp/snmp_room_alert_ +++ b/plugins/snmp/snmp_room_alert_ @@ -1,6 +1,4 @@ #!/usr/bin/env ruby -# encoding: utf-8 - # Plugin to monitor Room Alert 11E environmental units. # Requires ruby and the ruby SNMP library. # @@ -34,31 +32,31 @@ require 'snmp' -base_oid = "enterprises.20916.1.3.1" +base_oid = 'enterprises.20916.1.3.1' case $0.match('[^_]+$')[0] -when "temperature" +when 'temperature' subchannel = 1 - name = "temperature" - label = "°C" - letter = "t" -when "humidity" + name = 'temperature' + label = '°C' + letter = 't' +when 'humidity' subchannel = 3 - name = "humidity" - label = "% Relative Humidity" - letter = "h" + name = 'humidity' + label = '% Relative Humidity' + letter = 'h' else exit 1 end def is_vb_valid(vb, subchannel) - return (vb.name[-1] == 0 and vb.name[-2] == subchannel and vb.value > 1) + (vb.name[-1] == 0 and vb.name[-2] == subchannel and vb.value > 1) end def field_name(unit, vb, letter) clean_unit = unit.gsub(/[.-]/, '_') sensor = vb.name[-3].to_s - return "#{clean_unit}_#{letter}#{sensor}" + "#{clean_unit}_#{letter}#{sensor}" end def label(unit, vb) @@ -67,28 +65,24 @@ def label(unit, vb) label = "#{unit} " + (ENV["label_#{clean_unit}_#{sensor}"] || "sensor #{sensor}") end -units = (ENV['units'] || "").split(/\s+/) -community = ENV['community'] || "public" +units = (ENV['units'] || '').split(/\s+/) +community = ENV['community'] || 'public' case ARGV[0] -when "autoconf" - puts "no" +when 'autoconf' + puts 'no' exit 0 -when "config" +when 'config' puts "graph_title Room Alert 11E units (#{name} probes)" puts "graph_vlabel #{label}" - puts "graph_category sensors" - if name == "humidity" - puts "graph_args --lower-limit 0 --upper-limit 100" - end + puts 'graph_category sensors' + puts 'graph_args --lower-limit 0 --upper-limit 100' if name == 'humidity' units.each do |unit| - SNMP::Manager.open(:Host => unit, - :Community => community, - :Version => :SNMPv1) do |manager| + SNMP::Manager.open(Host: unit, + Community: community, + Version: :SNMPv1) do |manager| manager.walk(base_oid) do |vb| - if not is_vb_valid(vb, subchannel) - next - end + next unless is_vb_valid(vb, subchannel) puts "#{field_name(unit, vb, letter)}.label #{label(unit, vb)}" end @@ -98,13 +92,11 @@ when "config" end units.each do |unit| - SNMP::Manager.open(:Host => unit, - :Community => community, - :Version => :SNMPv1) do |manager| + SNMP::Manager.open(Host: unit, + Community: community, + Version: :SNMPv1) do |manager| manager.walk(base_oid) do |vb| - if not is_vb_valid(vb, subchannel) - next - end + next unless is_vb_valid(vb, subchannel) puts "#{field_name(unit, vb, letter)}.value #{vb.value.to_f / 100}" end diff --git a/plugins/ssh/sshd_invalid_countries_ruby b/plugins/ssh/sshd_invalid_countries_ruby index 2664ec78..040a79d8 100755 --- a/plugins/ssh/sshd_invalid_countries_ruby +++ b/plugins/ssh/sshd_invalid_countries_ruby @@ -33,7 +33,6 @@ env.loadpath /usr/local/lib/ruby/gems/1.9.1/gems/geoip-0.8.8/lib/ =end - require (ENV['loadpath'] || '') + 'geoip' SYSLOG = ENV['syslog'] || '/var/log/secure' @@ -45,28 +44,26 @@ AWK_CMD = 'awk \'/sshd\[.*Did not receive identification string/{print $12} ' + def getInvalids c = {} wholeips = `#{AWK_CMD}`.split("\n") - uniqueips = wholeips.inject({}) do |hash, key| - hash.include?(key) ? hash[key] += 1 : hash[key] = 1; - hash + uniqueips = wholeips.each_with_object({}) do |key, hash| + hash.include?(key) ? hash[key] += 1 : hash[key] = 1 end geoip = GeoIP.new(GEOIP_DB) uniqueips.each do |ip, cnt| begin country = geoip.country(ip)[5] c[country] = c[country] ? c[country] + cnt : cnt - rescue + rescue StandardError c['Unknown'] = c['Unknown'] ? c['Unknown'] + cnt : cnt end end - c = c.to_a.sort { |a, b| a[0] <=> b[0] } - c + c.to_a.sort { |a, b| a[0] <=> b[0] } end case ARGV[0] when 'autoconf' begin fh = open(SYSLOG, 'r') - rescue + rescue StandardError puts 'no' exit 0 else @@ -79,7 +76,7 @@ when 'config' puts 'graph_vlabel number of invalid access per country' puts 'graph_category security' puts 'graph_info This graph shows the countries of invalid access to sshd.' - getInvalids.each { |k, v| puts k + '.label ' + k } + getInvalids.each { |k, _v| puts k + '.label ' + k } exit 0 else getInvalids.each { |k, v| puts k + '.value ' + v.to_s } diff --git a/plugins/thin/thin_memory b/plugins/thin/thin_memory index 867e52ce..f1557fc4 100755 --- a/plugins/thin/thin_memory +++ b/plugins/thin/thin_memory @@ -35,21 +35,19 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - #%# family=auto #%# capabilities=autoconf =end - module Munin class ThinProcessMemory # run main method def run - instances = get_pids() + instances = get_pids instances.each do |instance| - pid, port = instance.split("|") - rss = (pid_rss(pid).to_i) / 1024 + pid, port = instance.split('|') + rss = pid_rss(pid).to_i / 1024 puts "thin_#{port}.value #{rss}" end end @@ -57,10 +55,10 @@ module Munin # only get the memory for each pid def pid_rss(pid) res = `grep "VmRSS" /proc/#{pid}/status`.split[1] - if res.match("cannot access") - return nil + if res.match('cannot access') + nil else - return res + res end end @@ -70,7 +68,7 @@ module Munin end def autoconf - get_pids().length > 0 + get_pids.length > 0 end end end @@ -78,26 +76,26 @@ end mpm = Munin::ThinProcessMemory.new case ARGV[0] -when "config" - puts "graph_title Thin Memory" - puts "graph_vlabel RSS" - puts "graph_category webserver" - puts "graph_args --base 1024 -l 0" - puts "graph_scale yes" - puts "graph_info Tracks the size of individual thin processes" +when 'config' + puts 'graph_title Thin Memory' + puts 'graph_vlabel RSS' + puts 'graph_category webserver' + puts 'graph_args --base 1024 -l 0' + puts 'graph_scale yes' + puts 'graph_info Tracks the size of individual thin processes' mpm.get_pids.each do |instance| - pid, port = instance.split("|") + pid, port = instance.split('|') puts "thin_#{port}.label thin_#{port}" puts "thin_#{port}.info Process memory" puts "thin_#{port}.type GAUGE" puts "thin_#{port}.min 0" end -when "autoconf" +when 'autoconf' if mpm.autoconf - puts "yes" + puts 'yes' exit 0 end - puts "no" + puts 'no' exit 0 else mpm.run diff --git a/plugins/thin/thin_threads b/plugins/thin/thin_threads index 6be71004..224edbf2 100755 --- a/plugins/thin/thin_threads +++ b/plugins/thin/thin_threads @@ -36,20 +36,18 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - #%# family=auto #%# capabilities=autoconf =end - module Munin class ThinThreads def run - instances = get_pids() + instances = get_pids instances.each do |instance| - pid, port = instance.split("|") - rss = (get_threads(pid).to_i) + pid, port = instance.split('|') + rss = get_threads(pid).to_i puts "thin_#{port}.value #{rss}" end end @@ -60,11 +58,11 @@ module Munin # TODO: make this work on OSX and Solaris, # so the whole unix gang is happy ;) def get_threads(pid) - res = `grep "Threads" /proc/#{pid}/status | cut -d ":" -f2`.gsub(/\s+/, "") - if res.match("cannot access") - return nil + res = `grep "Threads" /proc/#{pid}/status | cut -d ":" -f2`.gsub(/\s+/, '') + if res.match('cannot access') + nil else - return res + res end end @@ -74,7 +72,7 @@ module Munin end def autoconf - get_pids().length > 0 + get_pids.length > 0 end end end @@ -82,26 +80,26 @@ end mpm = Munin::ThinThreads.new case ARGV[0] -when "config" - puts "graph_title Thin Threads" - puts "graph_vlabel Threads" - puts "graph_category webserver" - puts "graph_args -l 0" - puts "graph_scale yes" - puts "graph_info Tracks how many threads per thin processes" +when 'config' + puts 'graph_title Thin Threads' + puts 'graph_vlabel Threads' + puts 'graph_category webserver' + puts 'graph_args -l 0' + puts 'graph_scale yes' + puts 'graph_info Tracks how many threads per thin processes' mpm.get_pids.each do |instance| - pid, port = instance.split("|") + pid, port = instance.split('|') puts "thin_#{port}.label thin_#{port}" puts "thin_#{port}.info Threads per Thin process" puts "thin_#{port}.type GAUGE" puts "thin_#{port}.min 0" end -when "autoconf" +when 'autoconf' if mpm.autoconf - puts "yes" + puts 'yes' exit 0 end - puts "no" + puts 'no' exit 0 else mpm.run diff --git a/plugins/thin/thins_peak_memory b/plugins/thin/thins_peak_memory index 56d547a6..ee289d8a 100755 --- a/plugins/thin/thins_peak_memory +++ b/plugins/thin/thins_peak_memory @@ -33,20 +33,18 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - #%# family=auto #%# capabilities=autoconf =end - module Munin class ThinPeakMemory def run - instances = get_pids() + instances = get_pids instances.each do |instance| - pid, port = instance.split("|") - hwm = (pid_hwm(pid).to_i) / 1024 + pid, port = instance.split('|') + hwm = pid_hwm(pid).to_i / 1024 puts "thin_#{port}.value #{hwm}" end end @@ -59,10 +57,10 @@ module Munin # so the whole unix gang is happy ;) def pid_hwm(pid) res = `grep "VmHWM" /proc/#{pid}/status`.split[1] - if res.match("cannot access") - return nil + if res.match('cannot access') + nil else - return res + res end end @@ -72,7 +70,7 @@ module Munin end def autoconf - get_pids().length > 0 + get_pids.length > 0 end end end @@ -80,26 +78,26 @@ end mpm = Munin::ThinPeakMemory.new case ARGV[0] -when "config" - puts "graph_title Thin Peak Memory (High Water Mark)" - puts "graph_vlabel HWM" - puts "graph_category webserver" - puts "graph_args -l 0" - puts "graph_scale yes" - puts "graph_info Tracks the peak memory of thin processes, aka High Water Mark." +when 'config' + puts 'graph_title Thin Peak Memory (High Water Mark)' + puts 'graph_vlabel HWM' + puts 'graph_category webserver' + puts 'graph_args -l 0' + puts 'graph_scale yes' + puts 'graph_info Tracks the peak memory of thin processes, aka High Water Mark.' mpm.get_pids.each do |instance| - pid, port = instance.split("|") + pid, port = instance.split('|') puts "thin_#{port}.label thin_#{port}" puts "thin_#{port}.info Peak Memory" puts "thin_#{port}.type GAUGE" puts "thin_#{port}.min 0" end -when "autoconf" +when 'autoconf' if mpm.autoconf - puts "yes" + puts 'yes' exit 0 end - puts "no" + puts 'no' exit 0 else mpm.run diff --git a/plugins/unicorn/unicorn_memory_status b/plugins/unicorn/unicorn_memory_status index a32543d9..01f08953 100755 --- a/plugins/unicorn/unicorn_memory_status +++ b/plugins/unicorn/unicorn_memory_status @@ -8,10 +8,10 @@ # # set path to your rails app -RAILS_ROOT = "/path/to/rails/app" +RAILS_ROOT = '/path/to/rails/app'.freeze # set name to your unicorn.pid -PID_NAME = "unicorn.pid" +PID_NAME = 'unicorn.pid'.freeze module Munin class UnicornMemoryStatus @@ -30,8 +30,9 @@ module Munin ps_output = `ps w --ppid #{master_pid}` ps_output.split("\n").each do |line| chunks = line.strip.split(/\s+/, 5) - pid, pcmd = chunks[0], chunks[4] - next if pid !~ /\A\d+\z/ or pcmd !~ /worker/ + pid = chunks[0] + pcmd = chunks[4] + next if pid !~ /\A\d+\z/ || pcmd !~ /worker/ result << pid.to_i end @@ -42,18 +43,18 @@ module Munin result = 0 memory = memory_usage result += memory[:master][master_pid] - memory[:worker].each do |pid, worker_memory| + memory[:worker].each do |_pid, worker_memory| result += worker_memory end result end def memory_usage - result = { :master => { master_pid => nil }, :worker => {} } + result = { master: { master_pid => nil }, worker: {} } ps_output = `ps auxw | grep unicorn` ps_output.split("\n").each do |line| chunks = line.strip.split(/\s+/, 11) - pid, pmem_rss, _ = chunks.values_at(1, 5, 10) + pid, pmem_rss, = chunks.values_at(1, 5, 10) pmem = pmem_rss.to_i * 1024 pid = pid.to_i @@ -69,15 +70,15 @@ module Munin end case ARGV[0] -when "autoconf" - puts "yes" -when "config" +when 'autoconf' + puts 'yes' +when 'config' puts "graph_title Unicorn [#{File.basename(__FILE__).gsub(/^unicorn_memory_status_/, '')}] - Memory usage" - puts "graph_args --base 1024 -l 0" - puts "graph_vlabel bytes" - puts "graph_category webserver" - puts "total_memory.label total_memory" - puts "total_memory.draw LINE2" + puts 'graph_args --base 1024 -l 0' + puts 'graph_vlabel bytes' + puts 'graph_category webserver' + puts 'total_memory.label total_memory' + puts 'total_memory.draw LINE2' else m = Munin::UnicornMemoryStatus.new(ENV['rails_root'] || RAILS_ROOT, ENV['pid_name'] || PID_NAME) puts "total_memory.value #{m.total_memory}" diff --git a/plugins/unicorn/unicorn_status b/plugins/unicorn/unicorn_status index 7db1911b..547116d3 100755 --- a/plugins/unicorn/unicorn_status +++ b/plugins/unicorn/unicorn_status @@ -8,10 +8,10 @@ # # set path to your rails app -RAILS_ROOT = "/path/to/rails/app" +RAILS_ROOT = '/path/to/rails/app'.freeze # set name to your unicorn.pid -PID_NAME = "unicorn.pid" +PID_NAME = 'unicorn.pid'.freeze module Munin class UnicornStatus @@ -30,8 +30,9 @@ module Munin ps_output = `ps w --ppid #{master_pid}` ps_output.split("\n").each do |line| chunks = line.strip.split(/\s+/, 5) - pid, pcmd = chunks[0], chunks[4] - next if pid !~ /\A\d+\z/ or pcmd !~ /worker/ + pid = chunks[0] + pcmd = chunks[4] + next if pid !~ /\A\d+\z/ || pcmd !~ /worker/ result << pid.to_i end @@ -67,15 +68,15 @@ module Munin end case ARGV[0] -when "autoconf" - puts "yes" -when "config" +when 'autoconf' + puts 'yes' +when 'config' puts "graph_title Unicorn [#{File.basename(__FILE__).gsub(/^unicorn_status_/, '')}] - Status" - puts "graph_args -l 0" - puts "graph_vlabel number of workers" - puts "graph_category webserver" - puts "total_worker.label total_workers" - puts "idle_worker.label idle_workers" + puts 'graph_args -l 0' + puts 'graph_vlabel number of workers' + puts 'graph_category webserver' + puts 'total_worker.label total_workers' + puts 'idle_worker.label idle_workers' else m = Munin::UnicornStatus.new(ENV['rails_root'] || RAILS_ROOT, ENV['pid_name'] || PID_NAME) puts "total_worker.value #{m.worker_count}" diff --git a/plugins/voldemort/voldemort b/plugins/voldemort/voldemort index c11f9469..4c279f84 100755 --- a/plugins/voldemort/voldemort +++ b/plugins/voldemort/voldemort @@ -6,39 +6,37 @@ require 'rubygems' require 'jmx4r' - -#%# family=auto -#%# capabilities=autoconf +# %# family=auto +# %# capabilities=autoconf # friendly name => result of listPerfStatsKeys via JMX keys = { - "Throughput" => { "vlabel" => "rate", - "type" => "ABSOLUTE", - "values" => ["all_operation_throughput","delete_throughput", "get_all_throughput", "get_throughput", "put_throughput"] - }, - "Number of Calls" => { "vlabel" => "counts", - "type" => "COUNTER", - "values" => ["number_of_calls_to_delete","number_of_calls_to_get", "number_of_calls_to_get_all", - "number_of_calls_to_put", "number_of_exceptions"] - } + 'Throughput' => { 'vlabel' => 'rate', + 'type' => 'ABSOLUTE', + 'values' => %w[all_operation_throughput delete_throughput get_all_throughput get_throughput put_throughput] }, + 'Number of Calls' => { 'vlabel' => 'counts', + 'type' => 'COUNTER', + 'values' => %w[number_of_calls_to_delete number_of_calls_to_get number_of_calls_to_get_all + number_of_calls_to_put number_of_exceptions] } } -if ARGV[0] == "config" +case ARGV[0] +when 'config' keys.each_key do |key| - puts "multigraph voldemort_#{key.gsub(" ", "_")}" + puts "multigraph voldemort_#{key.gsub(' ', '_')}" puts "graph_title #{key}" - puts "graph_scale no" - puts "graph_category search" + puts 'graph_scale no' + puts 'graph_category search' puts "graph_vlabel #{keys[key]['vlabel']}" - for data in keys[key]['values'] do + keys[key]['values'].each do |data| puts "#{data}.type #{keys[key]['type']}" - puts "#{data}.label #{data.gsub("_", " ")}" + puts "#{data}.label #{data.gsub('_', ' ')}" end puts end exit 0 -elsif ARGV[0] == "autoconf" - puts "yes" +when 'autoconf' + puts 'yes' exit 0 else @@ -49,16 +47,18 @@ else # Make the platform MBean server able to work with JBossAS MBeans # JAVA_OPTS="$JAVA_OPTS -Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl" # JBOSS_CLASSPATH="/opt/webtrends/jboss/bin/mbean" - JMX::MBean.establish_connection :port => 5400 - vs = JMX::MBean.find_by_name "voldemort.store.stats.aggregate:type=aggregate-perf" + JMX::MBean.establish_connection port: 5400 + vs = JMX::MBean.find_by_name 'voldemort.store.stats.aggregate:type=aggregate-perf' keys.each_key do |key| - puts "multigraph voldemort_#{key.gsub(" ", "_")}" + puts "multigraph voldemort_#{key.gsub(' ', '_')}" for data in keys[key]['values'] do - puts "#{data}.value #{begin vs.send("#{data}") rescue 0 end}" + puts "#{data}.value #{begin begin + vs.send(data.to_s) + rescue StandardError + 0 + end end}" end puts end end - - From 758c3ecdd7c6a709af449e2fc78571e28c7fe508 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 17:41:46 +0200 Subject: [PATCH 681/695] Tests: add ruby style and code checks via rubocop At the moment all plugins fail the test out of various reasons. Thus they are exempted from causing test failures. But future (new) plugins will fail the test and thus indicate the relevant steps to fix issues before a plugin is included. --- .travis.yml | 1 + t/test-exception-wrapper.expected-failures | 33 ++++++++++++++++++++++ t/test.t | 21 ++++++++++---- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index eafd3de9..abad2157 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ addons: - zsh - pylint - shellcheck + - rubocop - pkg-config - libdb-dev - libvirt-dev diff --git a/t/test-exception-wrapper.expected-failures b/t/test-exception-wrapper.expected-failures index 5f298af6..b0bdca69 100644 --- a/t/test-exception-wrapper.expected-failures +++ b/t/test-exception-wrapper.expected-failures @@ -30,6 +30,7 @@ plugins/bind/bind9_socket_stats plugins/bsd/df_abs_bsd plugins/bsd/df_bsd plugins/bsd/freebsd_hdd_power_state +plugins/bsd/netstat_bsd_m_ plugins/bsd/spamd-blacklist-bsd plugins/bsd/spamd-tarpit-bsd plugins/cacti/cacti-host @@ -90,6 +91,7 @@ plugins/dspam/dspam_activity plugins/dvb/2wcomdsr_ plugins/dvb/femon plugins/ejabberd/ejabberd_resources_ +plugins/ejabberd/ejabberd_scanlog plugins/emc/emc_vnx_block_lun_perfdata plugins/emc/emc_vnx_file_ plugins/fan/dell_fans @@ -148,11 +150,14 @@ plugins/harddisks/powered_up_ plugins/horde/horde plugins/http/http_pagespeed plugins/http/http_responsecode +plugins/http/mongrel_memory +plugins/http/mongrel_process_memory plugins/http/vhost_requests_ plugins/http/wget_page plugins/i2p/i2p_ plugins/icecast/icecast_ plugins/icecast/icecast2 +plugins/icecast/icecast2_simple plugins/icecast/icecast2_stats_ plugins/imapproxy/imapproxy_multi plugins/ip6/ip6_ @@ -187,7 +192,9 @@ plugins/memory/kmemsum plugins/memory/multimemory plugins/memory/proc_mem plugins/memory/proc_memory_status +plugins/minecraft/minecraft-users plugins/mixminion/mixminion +plugins/moblock/moblock_connections plugins/mod_jk/mod_jk plugins/moinmoin/moinoin_pages plugins/mongodb/mongo_btree @@ -197,6 +204,10 @@ plugins/mongodb/mongo_lag plugins/mongodb/mongo_lock plugins/mongodb/mongo_mem plugins/mongodb/mongo_ops +plugins/mssql/microsoft-sql +plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +plugins/mssql/microsoft-sql-data-file-sizes +plugins/mssql/microsoft-sql-log-file-size plugins/mumble/mumble_users plugins/mumble/murmur-stats plugins/munin/healthcheck_log @@ -220,6 +231,7 @@ plugins/network/fwbuilder_ plugins/network/hfsc plugins/network/hfsc_sep plugins/network/host_traffic +plugins/network/http__tp_link plugins/network/if1sec_ plugins/network/ifem_ plugins/network/if_uptime @@ -235,6 +247,7 @@ plugins/network/mtr100_ plugins/network/netatalk plugins/network/netatalk3 plugins/network/net_hosts_ +plugins/network/netstat_s_ plugins/network/nsd3 plugins/network/olsrd plugins/network/packetloss @@ -270,20 +283,27 @@ plugins/ntp/ntp_pool_score_ plugins/openntpd/openntp_offset plugins/openvz/openvz_ plugins/oracle/oracle_connections +plugins/oracle/oracle-pga-monitor +plugins/oracle/oracle-sga plugins/oracle/oracle_sysstat plugins/ossec/ossec_active_response plugins/ossec/ossec_agents plugins/ossec/ossec_alerts plugins/other/beanstalkd plugins/other/cm2 +plugins/other/delayed_jobs_queue_size plugins/other/earthquakes-stronger-than-m4-and-m5-plus-solar-act plugins/other/foldingathome_activecpu plugins/other/globesurfer plugins/other/hookbox plugins/other/listeners plugins/other/pid +plugins/other/port_ plugins/other/s9y plugins/other/services +plugins/passenger/passenger_memory +plugins/passenger/passenger_processes +plugins/passenger/passenger_status plugins/pdns/pdns_errors plugins/pdns/pdns_queries plugins/pdns/pdns_rec_qtypes @@ -298,6 +318,7 @@ plugins/php/eaccelerator-python plugins/php/php5-fpm_status plugins/php/php_apc_ plugins/php/php-cgi +plugins/php/php_eaccelerator plugins/php/php_errors_ plugins/php/php_opcache plugins/php/php_sessions @@ -354,6 +375,7 @@ plugins/printer/toshiba_5520c_scan_ plugins/printer/xerox-wc3220 plugins/printer/xerox-wc7232-consumables plugins/puma/puma_ +plugins/puppet/puppet_runtime plugins/qmail/qmailconn plugins/qmail/queuestats plugins/qmail/spamdyke @@ -373,17 +395,20 @@ plugins/rethinkdb/rethinkdb_node_io plugins/riak/riak_fsm_time_95 plugins/riak/riak_memory plugins/riak/riak_node +plugins/router/ag241-adsl plugins/router/arris-tm502g_ plugins/router/bbox plugins/router/cisco-epc3010_ plugins/router/conexant_adsl plugins/router/dartybox +plugins/router/d-link-dir-655-router-statistics-plugin plugins/router/dsl-connection-speed plugins/router/dsl-stats plugins/router/freeboxuptime plugins/router/motorola_sb6141 plugins/router/snmp__juniper plugins/router/snmp__juniper_spu +plugins/router/snmp__linksys_poe plugins/router/speedport_300 plugins/rsync/rsyncd_bytes plugins/rsync/rsyncd_count @@ -405,6 +430,7 @@ plugins/smstools/smstools_ plugins/snmp/snmp___bri_se_ plugins/snmp/snmp__brocade_ifs plugins/snmp/snmp__fn +plugins/snmp/snmp_room_alert_ plugins/snmp/snmp__webthermometer plugins/solaris/io_disk plugins/solaris/zones_cpu @@ -414,6 +440,7 @@ plugins/sphinx/sphinx_documents plugins/squid/squid_efficiency plugins/squid/squid_times plugins/ssh/openssh-denyhosts +plugins/ssh/sshd_invalid_countries_ruby plugins/ssh/sshd_log plugins/streaming/packetship_ plugins/swift/swift-async_ @@ -426,6 +453,9 @@ plugins/system/file_length_ plugins/tarsnap/tarsnap plugins/tcp/tcp-retransmissions plugins/tcp/tcp-states +plugins/thin/thin_memory +plugins/thin/thins_peak_memory +plugins/thin/thin_threads plugins/tinydns/tinydns plugins/tinydns/tinydns_err plugins/tor/tor_traffic @@ -433,6 +463,8 @@ plugins/trafic_ro/trafic_ro_24h plugins/tv/hdhomerun_ plugins/twemproxy/nutcracker_requests_ plugins/unicorn/unicorn_ +plugins/unicorn/unicorn_memory_status +plugins/unicorn/unicorn_status plugins/user/membyuser plugins/user/multipsu plugins/user/system_users @@ -442,6 +474,7 @@ plugins/vmware/esxcli_env_ plugins/vmware/esxi plugins/vmware/esxi__sensors plugins/vmware/vm_cpu_load +plugins/voldemort/voldemort plugins/vpn/openvpn_as_mtime plugins/vpn/openvpn_as_traffic plugins/vpn/openvpn_as_ttime diff --git a/t/test.t b/t/test.t index 205ac5d4..45182f66 100644 --- a/t/test.t +++ b/t/test.t @@ -192,12 +192,21 @@ sub process_file { ); } elsif ( $interpreter =~ m{j?ruby} ) { - run_check( - { command => [ 'ruby', '-cw', $file ], - description => 'ruby syntax check', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'ruby', '-cw', $file ], + description => 'ruby syntax check', + filename => $filename + } + ); + run_check( + { command => [ 'rubocop', $file ], + description => 'ruby style and syntax check', + filename => $filename + } + ); + } } elsif ( $interpreter =~ m{gawk} ) { run_check( From 3143c2c5768cecd17417c2ee387adce9382726e2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Tue, 25 Aug 2020 17:49:51 +0200 Subject: [PATCH 682/695] Tests: apply the exceptions to ruby-based code checks --- t/test.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/test.t b/t/test.t index 45182f66..a65c1220 100644 --- a/t/test.t +++ b/t/test.t @@ -201,7 +201,7 @@ sub process_file { } ); run_check( - { command => [ 'rubocop', $file ], + { command => [ 't/test-exception-wrapper', $file, 'rubocop' ], description => 'ruby style and syntax check', filename => $filename } From 6f0e91f8507f26415d8f1b8ce17fa37a7c632537 Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Tue, 25 Aug 2020 20:21:19 +0200 Subject: [PATCH 683/695] Add perldoc-style header. --- plugins/disk/btrfs_device_stats | 36 ++++++++++++++++++++++++++++++++- plugins/disk/btrfs_device_usage | 36 ++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats index 8555c280..c055d119 100755 --- a/plugins/disk/btrfs_device_stats +++ b/plugins/disk/btrfs_device_stats @@ -1,5 +1,39 @@ #!/usr/bin/env python3 -# + + +""" +=pod + +=head1 NAME + +btrfs_device_stats - Script to monitor btrfs device statistics + +=head1 CONFIGURATION + +Simply create a symlink in your plugins directory like with any other plugin. +No configuration needed. + +=head2 DEFAULT CONFIGURATION + +=head1 BUGS + +=head1 AUTHOR + +2019, HaseHarald + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 LICENSE + +LGPLv3 + +=cut +""" + + # This file contains a munin-plugin to gather btrfs statistics per device. # # This is free software: you can redistribute it and/or modify diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage index e7265de8..ab683e0d 100755 --- a/plugins/disk/btrfs_device_usage +++ b/plugins/disk/btrfs_device_usage @@ -1,5 +1,39 @@ #!/usr/bin/env python3 -# + + +""" +=pod + +=head1 NAME + +btrfs_device_usage - Script to monitor usage of btrfs devices + +=head1 CONFIGURATION + +Simply create a symlink in your plugins directory like with any other plugin. +No configuration needed. + +=head2 DEFAULT CONFIGURATION + +=head1 BUGS + +=head1 AUTHOR + +2019, HaseHarald + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 LICENSE + +LGPLv3 + +=cut +""" + + # This file contains a munin-plugin to gather btrfs statistics per device. # # This is free software: you can redistribute it and/or modify From 3f92394b23b0a0a56e7cb0511eed8f01be1357ab Mon Sep 17 00:00:00 2001 From: HaseHarald Date: Tue, 25 Aug 2020 22:01:19 +0200 Subject: [PATCH 684/695] Touchups on configuration. --- plugins/disk/btrfs_device_stats | 7 +++++-- plugins/disk/btrfs_device_usage | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/plugins/disk/btrfs_device_stats b/plugins/disk/btrfs_device_stats index c055d119..0959381e 100755 --- a/plugins/disk/btrfs_device_stats +++ b/plugins/disk/btrfs_device_stats @@ -11,7 +11,10 @@ btrfs_device_stats - Script to monitor btrfs device statistics =head1 CONFIGURATION Simply create a symlink in your plugins directory like with any other plugin. -No configuration needed. +Must be run as root. + +[btrfs_device_stats] +user root =head2 DEFAULT CONFIGURATION @@ -24,7 +27,7 @@ No configuration needed. =head1 MAGIC MARKERS #%# family=auto - #%# capabilities=autoconf suggest + #%# capabilities=autoconf =head1 LICENSE diff --git a/plugins/disk/btrfs_device_usage b/plugins/disk/btrfs_device_usage index ab683e0d..c5c7238f 100755 --- a/plugins/disk/btrfs_device_usage +++ b/plugins/disk/btrfs_device_usage @@ -11,7 +11,10 @@ btrfs_device_usage - Script to monitor usage of btrfs devices =head1 CONFIGURATION Simply create a symlink in your plugins directory like with any other plugin. -No configuration needed. +Must be run as root. + +[btrfs_device_usage] +user root =head2 DEFAULT CONFIGURATION @@ -24,7 +27,7 @@ No configuration needed. =head1 MAGIC MARKERS #%# family=auto - #%# capabilities=autoconf suggest + #%# capabilities=autoconf =head1 LICENSE From bd872f7af1e3076376fcdcac2dec3c63403fb573 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 26 Aug 2020 03:24:37 +0200 Subject: [PATCH 685/695] btrfs_*_usage: add example graphs Provided by HaseHarald. See https://github.com/munin-monitoring/contrib/pull/1114. --- .../example-graphs/btrfs_device_usage-day.png | Bin 0 -> 15186 bytes .../example-graphs/btrfs_subvol_usage-day.png | Bin 0 -> 22858 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/disk/example-graphs/btrfs_device_usage-day.png create mode 100644 plugins/disk/example-graphs/btrfs_subvol_usage-day.png diff --git a/plugins/disk/example-graphs/btrfs_device_usage-day.png b/plugins/disk/example-graphs/btrfs_device_usage-day.png new file mode 100644 index 0000000000000000000000000000000000000000..587894479266f04cfa7c75ff45c447e967fcf948 GIT binary patch literal 15186 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 Date: Fri, 28 Aug 2020 14:36:55 +0200 Subject: [PATCH 686/695] README: whitespace adjustments --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fa10af06..34bd92c2 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,10 @@ It should serves as a base for small editions that can be resynced in SVN trunk, * don't copy the whole template * directly edit files in this directory + # contrib/tools/ - 3rd-party tools -Here, you can put just any kind of tool. Please use this directory instead of a random place on the internet. +Here, you can put just any kind of tool. Please use this directory instead of a random place on the internet. It makes things way more easy to search for others. And, it serves as an incubator of SVN `trunk/contrib` :-) @@ -35,33 +36,36 @@ And, it serves as an incubator of SVN `trunk/contrib` :-) This serves as a repository for examples of various configs. You know, the ''learn by example'' way of doing things. + ## Notes to contributors ### Commits, Comments & Pull requests -We like to have _elementary_ commits as it is much easier to manage for reviewing and debugging. +We like to have _elementary_ commits as it is much easier to manage for reviewing and debugging. So please **don't** be afraid to make **as many** commits as needed. Merging many commits is as easy as merging one, if not easier. -A good rationale is that each commit shall have a one-liner commit comment as its first line. +A good rationale is that each commit shall have a one-liner commit comment as its first line. Ideally that first line has a prefix that shows the part the commit is about. It makes it very easy to see grouped changes, and it enable avoiding to look at the `--stat`. To know the prefix you should use, you can have a look at already existing commits. Next lines are optional and should only -explain the _why_ it is done this particular way. +explain the _why_ it is done this particular way. On the other side, pull requests can regroup many commits at once. Just try to explain in the pull comment the ''why'' we should merge it (if it's not obvious). Tim Pope wrote a [very nice tuto](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) on making good commit comments. + ### Licenses All the code here is licensed with the same terms as munin itself (GPLv2), unless specified otherwise inside a file. In all cases the code shall have an OSI-compatible license. Asking for a pull implies that you agree with that fact. -This change was made on Jun 1st 2012. If you wrote some code earlier and you do not agree to the new licensing default, you can : +This change was made on Jun 1st 2012. If you wrote some code earlier and you do not agree to the new licensing default, you can: - submit a licensing change pull -- submit a removal pull +- submit a removal pull + # Building status From 84e0858ba8b961552193c3c4abefa12fa8deab03 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 28 Aug 2020 14:37:08 +0200 Subject: [PATCH 687/695] README: remove references for subversion --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 34bd92c2..c6a6bdaf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -This is the repository for all user contributed stuff +This is the repository for all user contributed stuff related to +[munin](http://munin-monitoring.org). + # contrib/plugins/ - 3rd-party plugins @@ -18,8 +20,8 @@ Feel free to update templates here, or even to create new ones. Bonus points for mobile-friendly ones :) -Note that the one named `official` is a loose-synced copy of the one in SVN trunk. -It should serves as a base for small editions that can be resynced in SVN trunk, so for that : +Note that the one named `official` is a loose-synced copy of the one distributed with munin. +It should serves as a base for small editions that can be merged into munin, so for that: * don't copy the whole template * directly edit files in this directory @@ -30,7 +32,6 @@ It should serves as a base for small editions that can be resynced in SVN trunk, Here, you can put just any kind of tool. Please use this directory instead of a random place on the internet. It makes things way more easy to search for others. -And, it serves as an incubator of SVN `trunk/contrib` :-) # contrib/samples/ - 3rd-party examples of configs From 7a00a8b53af0244ba38eda6304120f4c7565da74 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 28 Aug 2020 14:38:45 +0200 Subject: [PATCH 688/695] plugins/README: mention more style check tools --- plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/README.md b/plugins/README.md index 7ddc08fd..2885e46a 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -25,7 +25,7 @@ Please note, that this repository is not supposed to be a dumping site for rando * please avoid code copies - they are a maintenance burden 2. add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) 3. pick a suitable [category](http://guide.munin-monitoring.org/en/latest/reference/graph-category.html) -5. use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) +5. use style check tools for the language of the plugin (e.g. perl: `perlcritic`, shell: `shellcheck`, python: `flake8`, ruby: `rubocop`) 6. pick a suitable [name and location](#Plugin_name_and_location) 7. bonus: * use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins From 6a1a9c7c469292f357d85577619a8a6e6fe855dd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 30 Aug 2020 00:54:53 +0200 Subject: [PATCH 689/695] Fix wrong "example-directory" names --- .../fronius-week.png | Bin .../wunderground_STATION-week.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/solar/{example_graphs => example-graphs}/fronius-week.png (100%) rename plugins/weather/{example_graphs => example-graphs}/wunderground_STATION-week.png (100%) diff --git a/plugins/solar/example_graphs/fronius-week.png b/plugins/solar/example-graphs/fronius-week.png similarity index 100% rename from plugins/solar/example_graphs/fronius-week.png rename to plugins/solar/example-graphs/fronius-week.png diff --git a/plugins/weather/example_graphs/wunderground_STATION-week.png b/plugins/weather/example-graphs/wunderground_STATION-week.png similarity index 100% rename from plugins/weather/example_graphs/wunderground_STATION-week.png rename to plugins/weather/example-graphs/wunderground_STATION-week.png From 897de1bbbac8bee0bb098c9b0f73185ad09e2d93 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 31 Aug 2020 12:55:40 +1000 Subject: [PATCH 690/695] squash! [plugins/transmission] Fixups for consistency Only show host in title when not localhost --- plugins/network/transmission | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/network/transmission b/plugins/network/transmission index e87613ef..d07a8e18 100755 --- a/plugins/network/transmission +++ b/plugins/network/transmission @@ -84,11 +84,12 @@ host = os.getenv('host','localhost') port = os.getenv('port',9091) user = os.getenv('user') passwd = os.getenv('pass') +title_host = '' if host in ['localhost', '127.0.0.1', '::1'] else ' on ' + host def config(): conf = Template("""multigraph ${plugin_name}_throughput -graph_title Transmission throughput +graph_title Transmission throughput${title_host} graph_vlabel bytes/${graph_period} in (-) / out (+) graph_args --base 1000 graph_category network @@ -105,7 +106,7 @@ up.draw AREA up.min 0 multigraph ${plugin_name}_activity -graph_title Transmission activity +graph_title Transmission activity${title_host} graph_vlabel torrents graph_args --base 1000 graph_category network @@ -119,7 +120,7 @@ paused.draw STACK paused.min 0 paused.colour COLOUR8 """) - print conf.safe_substitute(plugin_name=plugin_name, host=host) + print conf.safe_substitute(plugin_name=plugin_name, host=host, title_host=title_host) sys.exit(0) From 92483a045ba48d3bd669666b77c9ec23f11ae557 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 31 Aug 2020 12:56:14 +1000 Subject: [PATCH 691/695] [plugins/transmission] Lint Signed-off-by: Olivier Mehani --- plugins/network/transmission | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/plugins/network/transmission b/plugins/network/transmission index d07a8e18..e2d252f1 100755 --- a/plugins/network/transmission +++ b/plugins/network/transmission @@ -76,12 +76,13 @@ find this plugin on github at http://github.com/VolatileMesh/munin-plugins __version__ = '1.1' -import os, sys +import os +import sys from string import Template -plugin_name=list(os.path.split(sys.argv[0]))[1] -host = os.getenv('host','localhost') -port = os.getenv('port',9091) +plugin_name = list(os.path.split(sys.argv[0]))[1] +host = os.getenv('host', 'localhost') +port = os.getenv('port', 9091) user = os.getenv('user') passwd = os.getenv('pass') title_host = '' if host in ['localhost', '127.0.0.1', '::1'] else ' on ' + host @@ -132,7 +133,6 @@ def autoconf(): print 'no python module \'transmissionrpc\' missing' - def fetch(): import transmissionrpc @@ -173,7 +173,6 @@ def print_values_throughput(stats): print "up.value U" - def dumpstats(): import transmissionrpc try: @@ -186,14 +185,14 @@ def dumpstats(): if __name__ == '__main__': - if len(sys.argv)>1 : - if sys.argv[1]=="dumpstats" : - dumpstats() - elif sys.argv[1]=="config" : + if len(sys.argv) > 1 : + if sys.argv[1] == "dumpstats" : + dumpstats() + elif sys.argv[1] == "config" : config() - elif sys.argv[1]=="autoconf" : + elif sys.argv[1] == "autoconf" : autoconf() - elif sys.argv[1]!="": + elif sys.argv[1] != "": raise ValueError, "unknown parameter '%s'" % sys.argv[1] fetch() From 1a614fb00d5e4531b85ee9feade0e541bb0799d4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 4 Sep 2020 01:55:06 +0200 Subject: [PATCH 692/695] Plugin acpi_batt_: improve perldoc formatting --- plugins/battery/acpi_batt_ | 66 +++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/plugins/battery/acpi_batt_ b/plugins/battery/acpi_batt_ index d88804a2..24d5d36e 100755 --- a/plugins/battery/acpi_batt_ +++ b/plugins/battery/acpi_batt_ @@ -3,7 +3,7 @@ =head1 NAME -acpi_batt_ Munin plugin to monitor the (note|net)book battery states through procfs +acpi_batt_ - monitor the (note|net)book battery states through procfs =head1 APPLICABLE SYSTEMS @@ -11,37 +11,59 @@ Notebooks and netbooks with available /proc/acpi/battery =head1 CONFIGURATION -Configured change the name of symbolic link +The name of the plugin (or its symbolic link) determines the retrieved data. + +=over + +=item acpi_batt_X_capacity: chart of Design capacity, Last full capacity, Design capacity low, Design capacity warning, Capacity granularity 1, Capacity granularity 2, Remaining capacity, Present rate (mA) + +=item acpi_batt_X_percents: percentage chart of Current voltage, Current capacity, Full capacity (of design) + +=item acpi_batt_X_voltage: chart of Design voltage, Present voltage + +=back -acpi_batt_X_capacity - chart of Design capacity, Last full capacity, Design capacity low, - Design capacity warning, Capacity granularity 1, Capacity granularity 2, - Remaining capacity, Present rate (mA) -acpi_batt_X_percents - percentage chart of Current voltage, Current capacity, Full capacity (of design) -acpi_batt_X_voltage - chart of Design voltage, Present voltage Where X is the number of battery from /proc/acpi/battery/BATX =head1 INTERPRETATION The plugin shows: - Design capacity - Last full capacity - Design capacity low - Design capacity warning - Capacity granularity 1 - Capacity granularity 2 - Remaining capacity - Present rate (mA) - Percentage Current/design voltage - Percentage Full/current capacity - Percentage Design/full capacity - Design voltage - Present voltage + +=over + +=item Design capacity + +=item Last full capacity + +=item Design capacity low + +=item Design capacity warning + +=item Capacity granularity 1 + +=item Capacity granularity 2 + +=item Remaining capacity + +=item Present rate (mA) + +=item Percentage Current/design voltage + +=item Percentage Full/current capacity + +=item Percentage Design/full capacity + +=item Design voltage + +=item Present voltage + +=back + =head1 MAGIC MARKERS -#%# family=power + #%# family=power -=head1 VERSION =head1 BUGS None known. From bba98f95b3aecdd62a9c88c0cc5b8bd49d2ca206 Mon Sep 17 00:00:00 2001 From: Martin Schobert Date: Sun, 6 Sep 2020 22:19:12 +0200 Subject: [PATCH 693/695] ssl-certificate-expiry: Feature added: checking intermediate certs as well (#1088) --- plugins/ssl/ssl-certificate-expiry | 34 ++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry index c757c8a9..41f6fce3 100755 --- a/plugins/ssl/ssl-certificate-expiry +++ b/plugins/ssl/ssl-certificate-expiry @@ -57,9 +57,11 @@ uncached updates after the cache file is older than an hour. * Pactrick Domack (ssl_) * Olivier Mehani (ssl-certificate-expiry) - + * Martin Schobert (check for intermediate certs) + * Copyright (C) 2013 Patrick Domack * Copyright (C) 2017, 2019 Olivier Mehani + * Copyright (C) 2020 Martin Schobert =head1 LICENSE @@ -90,6 +92,7 @@ parse_valid_days_from_certificate() { local now_epoch local input_data input_data=$(cat) + if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ | grep "^notAfter=" | cut -f 2 -d "=") @@ -122,11 +125,34 @@ print_expire_days() { local s_client_args= [ -n "$starttls" ] && s_client_args="-starttls $starttls" + # We extract and check the server certificate, + # but the end date also depends on intermediate certs. Therefore + # we want to check intermediate certs as well. + # + # The following cryptic lines do: + # - invoke openssl and connect to a port + # - print certs, not only the server cert + # - extract each certificate as a single line + # - pipe each cert to the parse_valid_days_from_certificate + # function, which basically is 'openssl x509 -enddate' + # - get a list of the parse_valid_days_from_certificate + # results and sort them + # shellcheck disable=SC2086 echo "" | openssl s_client \ - -servername "$host" -connect "${host}:${port}" \ - $s_client_args 2>/dev/null \ - | parse_valid_days_from_certificate + -servername "$host" -connect "${host}:${port}" \ + -showcerts \ + $s_client_args 2>/dev/null | \ + awk '{ + if ($0 == "-----BEGIN CERTIFICATE-----") cert="" + else if ($0 == "-----END CERTIFICATE-----") print cert + else cert=cert$0 + }' | \ + while read -r CERT; do + (printf '\n-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n' "$CERT") | \ + parse_valid_days_from_certificate + done | sort -n | head -n 1 + } main() { From 322644634867d5717014a6c6a0635e8ba77665c4 Mon Sep 17 00:00:00 2001 From: ScratchBuild Date: Mon, 7 Sep 2020 11:11:22 +0900 Subject: [PATCH 694/695] Fix Fix `printf` and `sprintf` output numbers from %s to %d --- plugins/battery/acpi_batt_ | 38 ++++++++++++++-------------------- plugins/battery/acpi_sys_batt_ | 26 +++++++++++------------ 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/plugins/battery/acpi_batt_ b/plugins/battery/acpi_batt_ index 24d5d36e..b96578b7 100755 --- a/plugins/battery/acpi_batt_ +++ b/plugins/battery/acpi_batt_ @@ -142,8 +142,8 @@ if ($ARGV[0] and $ARGV[0] eq "config") { my $batt_name = sprintf("%s %s %s", $batt_data->{'info'}{'OEM info'}, $batt_data->{'info'}{'battery type'}, $batt_data->{'info'}{'model number'}); print ("graph_args --base 1000\n"); - printf ("graph_title Battery %s (%s) %s\n" , $batt_num, $batt_name, $graph_type); - printf ("graph_info This graph shows battery %s (%s) %s\n" , $batt_num, $batt_name, $graph_type); + printf ("graph_title Battery %d (%s) %s\n" , $batt_num, $batt_name, $graph_type); + printf ("graph_info This graph shows battery %d (%s) %s\n" , $batt_num, $batt_name, $graph_type); print ("graph_category sensors\n"); if ($graph_type eq "capacity") { @@ -182,31 +182,23 @@ sub percent if ($graph_type eq "capacity") { - printf ("dc.value %s\n", $batt_data->{'info'}{'design capacity'}); - printf ("lfc.value %s\n", $batt_data->{'info'}{'last full capacity'}); - printf ("dcl.value %s\n", $batt_data->{'info'}{'design capacity low'}); - printf ("dcw.value %s\n", $batt_data->{'info'}{'design capacity warning'}); - printf ("cg1.value %s\n", $batt_data->{'info'}{'capacity granularity 1'}); - printf ("cg2.value %s\n", $batt_data->{'info'}{'capacity granularity 2'}); - printf ("rc.value %s\n", $batt_data->{'state'}{'remaining capacity'}); - printf ("pr.value %s\n", $batt_data->{'state'}{'present rate'}); + printf ("dc.value %d\n", $batt_data->{'info'}{'design capacity'}); + printf ("lfc.value %d\n", $batt_data->{'info'}{'last full capacity'}); + printf ("dcl.value %d\n", $batt_data->{'info'}{'design capacity low'}); + printf ("dcw.value %d\n", $batt_data->{'info'}{'design capacity warning'}); + printf ("cg1.value %d\n", $batt_data->{'info'}{'capacity granularity 1'}); + printf ("cg2.value %d\n", $batt_data->{'info'}{'capacity granularity 2'}); + printf ("rc.value %d\n", $batt_data->{'state'}{'remaining capacity'}); + printf ("pr.value %d\n", $batt_data->{'state'}{'present rate'}); } elsif ($graph_type eq "voltage") { - printf ("d.value %s\n", $batt_data->{'info'}{'design voltage'}); - printf ("p.value %s\n", $batt_data->{'state'}{'present voltage'}); + printf ("d.value %d\n", $batt_data->{'info'}{'design voltage'}); + printf ("p.value %d\n", $batt_data->{'state'}{'present voltage'}); } elsif ($graph_type eq "percents") { - printf ("cv.value %s\n", percent($batt_data->{'info'}{'design voltage'},$batt_data->{'state'}{'present voltage'})); - printf ("cc.value %s\n", percent($batt_data->{'info'}{'design capacity'},$batt_data->{'state'}{'remaining capacity'})); - printf ("fc.value %s\n", percent($batt_data->{'info'}{'design capacity'},$batt_data->{'info'}{'last full capacity'})); + printf ("cv.value %d\n", percent($batt_data->{'info'}{'design voltage'},$batt_data->{'state'}{'present voltage'})); + printf ("cc.value %d\n", percent($batt_data->{'info'}{'design capacity'},$batt_data->{'state'}{'remaining capacity'})); + printf ("fc.value %d\n", percent($batt_data->{'info'}{'design capacity'},$batt_data->{'info'}{'last full capacity'})); } - - - - - - - - diff --git a/plugins/battery/acpi_sys_batt_ b/plugins/battery/acpi_sys_batt_ index 9f91f0ca..60c87c3b 100755 --- a/plugins/battery/acpi_sys_batt_ +++ b/plugins/battery/acpi_sys_batt_ @@ -63,7 +63,7 @@ if ($0 =~ /^(?:|.*\/)acpi_sys_batt_([^_]+)_(.+)$/) elsif (!defined($batt_num) or !defined($graph_type)) { die "# Error: couldn't understand what I'm supposed to monitor."; } -my $sys_path=sprintf("/sys/class/power_supply/BAT%s", $batt_num); +my $sys_path=sprintf("/sys/class/power_supply/BAT%d", $batt_num); #print "$batt_num, $graph_type \n"; @@ -97,8 +97,8 @@ if ($ARGV[0] and $ARGV[0] eq "config") my $batt_name = sprintf("%s %s %s %s (sn: %s)", cat_file('technology'), cat_file('type'), cat_file('manufacturer'), cat_file('model_name'), cat_file('serial_number')); print ("graph_args --base 1000\n"); - printf ("graph_title Battery %s (%s) %s\n" , $batt_num, $batt_name, $graph_type); - printf ("graph_info This graph shows battery %s (%s) %s\n" , $batt_num, $batt_name, $graph_type); + printf ("graph_title Battery %d (%s) %s\n" , $batt_num, $batt_name, $graph_type); + printf ("graph_info This graph shows battery %d (%s) %s\n" , $batt_num, $batt_name, $graph_type); print ("graph_category sensors\n"); if ($graph_type eq "capacity") { @@ -144,24 +144,22 @@ sub percent if ($graph_type eq "capacity") { - printf ("cfd.value %s\n", cat_file('charge_full_design')); - printf ("cf.value %s\n", cat_file('charge_full')); - printf ("cn.value %s\n", cat_file('charge_now')); + printf ("cfd.value %d\n", cat_file('charge_full_design')); + printf ("cf.value %d\n", cat_file('charge_full')); + printf ("cn.value %d\n", cat_file('charge_now')); } elsif ($graph_type eq "voltage") { - printf ("vmd.value %s\n", cat_file('voltage_min_design')); - printf ("vn.value %s\n", cat_file('voltage_now')); + printf ("vmd.value %d\n", cat_file('voltage_min_design')); + printf ("vn.value %d\n", cat_file('voltage_now')); } elsif ($graph_type eq "current") { - printf ("currn.value %s\n", cat_file('current_now')); + printf ("currn.value %d\n", cat_file('current_now')); } elsif ($graph_type eq "percents") { - printf ("cv.value %s\n", percent(cat_file('voltage_min_design'),cat_file('voltage_now'))); - printf ("cc.value %s\n", percent(cat_file('charge_full'),cat_file('charge_now'))); - printf ("fc.value %s\n", percent(cat_file('charge_full_design'),cat_file('charge_full'))); + printf ("cv.value %d\n", percent(cat_file('voltage_min_design'),cat_file('voltage_now'))); + printf ("cc.value %d\n", percent(cat_file('charge_full'),cat_file('charge_now'))); + printf ("fc.value %d\n", percent(cat_file('charge_full_design'),cat_file('charge_full'))); } - - From 254eb56dde59b86ab081895e9a81ca659093b4f6 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 3 Sep 2020 22:45:41 +1000 Subject: [PATCH 695/695] [docker_] Fake functools.cached_property for Python<3.8 Fixes: #1116 Signed-off-by: Olivier Mehani --- plugins/docker/docker_ | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/docker/docker_ b/plugins/docker/docker_ index f2d951b5..7bfe94dd 100755 --- a/plugins/docker/docker_ +++ b/plugins/docker/docker_ @@ -77,7 +77,13 @@ This section has been reverse-engineered from git logs import os import sys import re -from functools import cached_property +try: + from functools import cached_property +except ImportError: + # If cached_property is not available, + # just use the property decorator, without caching + # This is for backward compatibility with Python<3.8 + cached_property = property from multiprocessing import Process, Queue