# _create is called once, to create the charts
nginx_create() {
cat <<EOF
-CHART nginx.connections '' "nginx Active Connections" "connections" nginx nginx.connections line $((nginx_priority + 1)) $nginx_update_every
+CHART nginx_local.connections '' "nginx Active Connections" "connections" nginx nginx.connections line $((nginx_priority + 1)) $nginx_update_every
DIMENSION active '' absolute 1 1
-CHART nginx.requests '' "nginx Requests" "requests/s" nginx nginx.requests line $((nginx_priority + 2)) $nginx_update_every
+CHART nginx_local.requests '' "nginx Requests" "requests/s" nginx nginx.requests line $((nginx_priority + 2)) $nginx_update_every
DIMENSION requests '' incremental 1 1
-CHART nginx.connections_status '' "nginx Active Connections by Status" "connections" nginx nginx.connections.status line $((nginx_priority + 3)) $nginx_update_every
+CHART nginx_local.connections_status '' "nginx Active Connections by Status" "connections" nginx nginx.connections.status line $((nginx_priority + 3)) $nginx_update_every
DIMENSION reading '' absolute 1 1
DIMENSION writing '' absolute 1 1
DIMENSION waiting idle absolute 1 1
-CHART nginx.connect_rate '' "nginx Connections Rate" "connections/s" nginx nginx.connections.rate line $((nginx_priority + 4)) $nginx_update_every
+CHART nginx_local.connect_rate '' "nginx Connections Rate" "connections/s" nginx nginx.connections.rate line $((nginx_priority + 4)) $nginx_update_every
DIMENSION accepts accepted incremental 1 1
DIMENSION handled '' incremental 1 1
EOF
# write the result of the work.
cat <<VALUESEOF
-BEGIN nginx.connections $1
+BEGIN nginx_local.connections $1
SET active = $((nginx_active_connections))
END
-BEGIN nginx.requests $1
+BEGIN nginx_local.requests $1
SET requests = $((nginx_requests))
END
-BEGIN nginx.connections_status $1
+BEGIN nginx_local.connections_status $1
SET reading = $((nginx_reading))
SET writing = $((nginx_writing))
SET waiting = $((nginx_waiting))
END
-BEGIN nginx.connect_rate $1
+BEGIN nginx_local.connect_rate $1
SET accepts = $((nginx_accepts))
SET handled = $((nginx_handled))
END
python.d/nginx.conf \
python.d/phpfpm.conf \
python.d/postfix.conf \
+ python.d/redis.conf \
python.d/sensors.conf \
python.d/squid.conf \
python.d/tomcat.conf \
# Modules can be disabled with setting "module_name = no"
apache: yes
apache_cache: yes
+cpufreq: yes
example: yes
+exim: yes
hddtemp: yes
mysql: yes
nginx: yes
phpfpm: yes
+postfix: yes
+redis: yes
+sensors: yes
squid: yes
+tomcat: yes
# Example configuration of exim.chart.py
# YAML format
-update_every : 2
+update_every : 8 # executing `exim -bpc` can be very slow
--- /dev/null
+# Example configuration of redis.chart.py
+# YAML format
+
+update_every: 1
+retries: 10
+
+socket:
+ name : 'local'
+ socket : '/tmp/redis.sock'
+
+tcp:
+ name : 'local'
+ host : 'localhost'
+ port : '6379'
\ No newline at end of file
-#!/usr/bin/env python
+#!/usr/bin/env bash
+'''':; exec "$(command -v python2 || command -v python3 || echo "ERROR python IS NOT AVAILABLE IN THIS SYSTEM")" "$0" "$@" # '''
# -*- coding: utf-8 -*-
# Description: netdata python modules supervisor
:param config: dict
:return: dict
"""
+ if config is None:
+ config = {}
# get default values
defaults = {}
msg.debug(module.__name__ + ": reading configuration")
:param reason: str
"""
prefix = job.__module__
- if job.name is not None:
+ if job.name is not None and len(job.name) != 0:
prefix += "/" + job.name
prefix += ": "
-
- self.jobs.remove(job)
+ try:
+ self.jobs.remove(job)
+ except Exception as e:
+ msg.debug("This shouldn't happen. NO " + prefix + " IN LIST:" + str(self.jobs))
if reason is None:
return
elif reason[:3] == "no ":
try:
if job.override_name is not None:
job.name = job.override_name
- msg.debug(job.chart_name + " changing chart name to: " + job.__module__ + job.name)
- job.chart_name = job.__module__ + job.name
+ msg.debug(job.chart_name + " changing chart name to: " + job.__module__ + '_' + job.name)
+ job.chart_name = job.__module__ + '_' + job.name
overridden.append(job.name)
except Exception:
pass
DEBUG_FLAG = True
# redirect stderr to stdout?
elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd):
- DEBUG_FLAG = True
+ #DEBUG_FLAG = True
mods.append(cmd.replace(".chart.py", ""))
else:
try:
nginx.chart.py \
phpfpm.chart.py \
postfix.chart.py \
+ redis.chart.py \
sensors.chart.py \
squid.chart.py \
tomcat.chart.py \
:return: dict
"""
try:
- raw = self._get_raw_data()[-1].split(' ')
- return {'emails': raw[4],
- 'size': raw[1]}
+ return {'emails': int(self._get_raw_data()[0])}
except (ValueError, AttributeError):
return None
# Description: hddtemp netdata python.d module
# Author: Pawel Krupa (paulfantom)
-from base import NetSocketService
+from base import SocketService
# default module values (can be overridden per job in `config`)
#update_every = 2
}
-class Service(NetSocketService):
+class Service(SocketService):
def __init__(self, configuration=None, name=None):
- NetSocketService.__init__(self, configuration=configuration, name=name)
+ SocketService.__init__(self, configuration=configuration, name=name)
self.request = ""
self.host = "127.0.0.1"
self.port = 7634
import sys
import os
import socket
+import resource
try:
import urllib.request as urllib2
except ImportError:
t_start = time.time()
# check if it is time to execute job update() function
if self.timetable['next'] > t_start:
- msg.debug(self.chart_name + " will be run in " +
- str(int((self.timetable['next'] - t_start) * 1000)) + " ms")
+ #msg.debug(self.chart_name + " will be run in " +
+ # str(int((self.timetable['next'] - t_start) * 1000)) + " ms")
+ msg.debug(self.chart_name,"will be run in", str(int((self.timetable['next'] - t_start) * 1000)), "ms")
return True
since_last = int((t_start - self.timetable['last']) * 1000000)
- msg.debug(self.chart_name +
- " ready to run, after " + str(int((t_start - self.timetable['last']) * 1000)) +
- " ms (update_every: " + str(self.timetable['freq'] * 1000) +
- " ms, latency: " + str(int((t_start - self.timetable['next']) * 1000)) + " ms)")
+ #msg.debug(self.chart_name +
+ # " ready to run, after " + str(int((t_start - self.timetable['last']) * 1000)) +
+ # " ms (update_every: " + str(self.timetable['freq'] * 1000) +
+ # " ms, latency: " + str(int((t_start - self.timetable['next']) * 1000)) + " ms)")
+ msg.debug(self.chart_name,
+ "ready to run, after", str(int((t_start - self.timetable['last']) * 1000)),
+ "ms (update_every:", str(self.timetable['freq'] * 1000),
+ "ms, latency:", str(int((t_start - self.timetable['next']) * 1000)), "ms")
if not self.update(since_last):
return False
t_end = time.time()
self.timetable['next'] = t_end - (t_end % self.timetable['freq']) + self.timetable['freq']
-
# draw performance graph
run_time = str(int((t_end - t_start) * 1000))
- run_time_chart = "BEGIN netdata.plugin_pythond_" + self.chart_name + " " + str(since_last) + '\n'
- run_time_chart += "SET run_time = " + run_time + '\n'
- run_time_chart += "END\n"
- sys.stdout.write(run_time_chart)
- msg.debug(self.chart_name + " updated in " + str(run_time) + " ms")
+ #run_time_chart = "BEGIN netdata.plugin_pythond_" + self.chart_name + " " + str(since_last) + '\n'
+ #run_time_chart += "SET run_time = " + run_time + '\n'
+ #run_time_chart += "END\n"
+ #sys.stdout.write(run_time_chart)
+ sys.stdout.write("BEGIN netdata.plugin_pythond_%s %s\nSET run_time = %s\nEND\n" % \
+ (self.chart_name, str(since_last), run_time))
+
+ #msg.debug(self.chart_name + " updated in " + str(run_time) + " ms")
+ msg.debug(self.chart_name, "updated in", str(run_time), "ms")
self.timetable['last'] = t_start
return True
else:
time.sleep(self.timetable['freq'])
+ def _format(self, *args):
+ params = []
+ append = params.append
+ for p in args:
+ if p is None:
+ append(p)
+ continue
+ if type(p) is not str:
+ p = str(p)
+ if ' ' in p:
+ p = "'" + p + "'"
+ append(p)
+ return params
+
def _line(self, instruction, *params):
"""
Converts *params to string and joins them with one space between every one.
:param params: str/int/float
"""
- self._data_stream += instruction
- for p in params:
- if p is None:
- p = ""
- else:
- p = str(p)
- if len(p) == 0:
- p = "''"
- if ' ' in p:
- p = "'" + p + "'"
- self._data_stream += " " + p
- self._data_stream += "\n"
+ #self._data_stream += instruction
+ tmp = list(map((lambda x: "''" if x is None or len(x) == 0 else x), params))
+
+ self._data_stream += "%s %s\n" % (instruction, str(" ".join(tmp)))
+
+ # self.error(str(" ".join(tmp)))
+ # for p in params:
+ # if p is None:
+ # p = ""
+ # else:
+ # p = str(p)
+ # if len(p) == 0:
+ # p = "''"
+ # if ' ' in p:
+ # p = "'" + p + "'"
+ # self._data_stream += " " + p
+ #self._data_stream += "\n"
def chart(self, type_id, name="", title="", units="", family="",
category="", charttype="line", priority="", update_every=""):
:param update_every: int/str
"""
self._charts.append(type_id)
- self._line("CHART", type_id, name, title, units, family, category, charttype, priority, update_every)
+ #self._line("CHART", type_id, name, title, units, family, category, charttype, priority, update_every)
+
+ p = self._format(type_id, name, title, units, family, category, charttype, priority, update_every)
+ self._line("CHART", *p)
+
+
def dimension(self, id, name=None, algorithm="absolute", multiplier=1, divisor=1, hidden=False):
"""
self._dimensions.append(id)
if hidden:
- self._line("DIMENSION", id, name, algorithm, multiplier, divisor, "hidden")
+ p = self._format(id, name, algorithm, multiplier, divisor, "hidden")
+ #self._line("DIMENSION", id, name, algorithm, str(multiplier), str(divisor), "hidden")
else:
- self._line("DIMENSION", id, name, algorithm, multiplier, divisor)
+ p = self._format(id, name, algorithm, multiplier, divisor)
+ #self._line("DIMENSION", id, name, algorithm, str(multiplier), str(divisor))
+
+ self._line("DIMENSION", *p)
def begin(self, type_id, microseconds=0):
"""
self.error("malformed begin statement: microseconds are not a number:", microseconds)
microseconds = ""
- self._line("BEGIN", type_id, microseconds)
+ self._line("BEGIN", type_id, str(microseconds))
return True
def set(self, id, value):
except TypeError:
self.error("cannot set non-numeric value:", value)
return False
- self._line("SET", id, "=", value)
+ self._line("SET", id, "=", str(value))
return True
def end(self):
return False
-class NetSocketService(SimpleService):
+class SocketService(SimpleService):
def __init__(self, configuration=None, name=None):
self.host = "localhost"
self.port = None
self.sock = None
+ self.unix_socket = None
self.request = ""
SimpleService.__init__(self, configuration=configuration, name=name)
"""
if self.sock is None:
try:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.settimeout(self.update_every)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.connect((self.host, self.port))
+ if self.unix_socket is None:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ #sock.settimeout(self.update_every)
+ sock.connect((self.host, self.port))
+ else:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ #sock.settimeout(self.update_every)
+ sock.connect(self.unix_socket)
+
except Exception as e:
self.error(e)
self.sock = None
self.sock = None
return None
- data = sock.recv(1024)
+ data = sock.recv(2)
try:
while True:
- buf = sock.recv(1024)
- if not buf:
+ try:
+ buf = sock.recv(1024, 0x40) # get 1024 bytes in NON-BLOCKING mode
+ except socket.error:
+ break
+
+ if len(buf) == 0:
break
else:
data += buf
else:
self.name = str(self.name)
try:
- self.host = str(self.configuration['host'])
+ self.unix_socket = str(self.configuration['socket'])
except (KeyError, TypeError):
- self.error("No host specified. Using: '" + self.host + "'")
- try:
- self.port = int(self.configuration['port'])
- except (KeyError, TypeError):
- self.error("No port specified. Using: '" + str(self.port) + "'")
+ self.error("No unix socket specified. Trying TCP/IP socket.")
+ try:
+ self.host = str(self.configuration['host'])
+ except (KeyError, TypeError):
+ self.error("No host specified. Using: '" + self.host + "'")
+ try:
+ self.port = int(self.configuration['port'])
+ except (KeyError, TypeError):
+ self.error("No port specified. Using: '" + str(self.port) + "'")
try:
self.request = str(self.configuration['request'])
except (KeyError, TypeError):
class ExecutableService(SimpleService):
command_whitelist = ['exim', 'postqueue']
+ bad_substrings = ('&', '|', ';', '>', '<')
def __init__(self, configuration=None, name=None):
self.command = ""
return None
data = []
for line in p.stdout.readlines():
- data.append(line)
+ data.append(str(line.decode()))
return data
# except (KeyError, TypeError):
# self.error("No command specified. Using: '" + self.command + "'")
self.command = self.command.split(' ')
- for i in self.command:
- if i.startswith('-') or i in self.command_whitelist:
- pass
- else:
- self.error("Wrong command. Probably not on whitelist.")
+ if self.command[0] not in self.command_whitelist:
+ self.error("Command is not whitelisted.")
+ return False
+
+ for arg in self.command[1:]:
+ if any(st in arg for st in self.bad_substrings):
+ self.error("Bad command argument:" + " ".join(self.command[1:]))
return False
# test command and search for it in /usr/sbin or /sbin when failed
base = self.command[0]
if self._get_raw_data() is None:
for prefix in ['/sbin/', '/usr/sbin/']:
self.command[0] = prefix + base
- if self._get_raw_data() is not None:
+ if os.path.isfile(self.command[0]):
break
+ #if self._get_raw_data() is not None:
+ # break
if self._get_data() is None or len(self._get_data()) == 0:
return False
Print message on stderr.
:param msg_type: str
"""
- msg = PROGRAM + " " + str(msg_type) + ":"
- for i in args:
- msg += " "
- msg += str(i)
+ msg = "%s %s: %s" % (PROGRAM, str(msg_type), " ".join(args))
+
sys.stderr.write(msg + "\n")
sys.stderr.flush()
--- /dev/null
+# -*- coding: utf-8 -*-
+# Description: redis netdata python.d module
+# Author: Pawel Krupa (paulfantom)
+
+from base import SocketService
+
+# default module values (can be overridden per job in `config`)
+#update_every = 2
+priority = 60000
+retries = 5
+
+# default job configuration (overridden by python.d.plugin)
+# config = {'local': {
+# 'update_every': update_every,
+# 'retries': retries,
+# 'priority': priority,
+# 'host': 'localhost',
+# 'port': 6379,
+# 'unix_socket': None
+# }}
+
+ORDER = ['operations', 'hit_rate', 'memory', 'keys', 'clients', 'slaves']
+
+CHARTS = {
+ 'operations': {
+ 'options': [None, 'Operations', 'operations/s', 'Statistics', 'redis.statistics', 'line'],
+ 'lines': [
+ ['instantaneous_ops_per_sec', 'operations', 'absolute']
+ ]},
+ 'hit_rate': {
+ 'options': [None, 'Hit rate', 'percent', 'Statistics', 'redis.statistics', 'line'],
+ 'lines': [
+ ['hit_rate', 'rate', 'absolute']
+ ]},
+ 'memory': {
+ 'options': [None, 'Memory utilization', 'kilobytes', 'Memory', 'redis.memory', 'line'],
+ 'lines': [
+ ['used_memory', 'total', 'absolute', 1, 1024],
+ ['used_memory_lua', 'lua', 'absolute', 1, 1024]
+ ]},
+ 'keys': {
+ 'options': [None, 'Database keys', 'keys', 'Keys', 'redis.keys', 'line'],
+ 'lines': [
+ # lines are created dynamically in `check()` method
+ ]},
+ 'clients': {
+ 'options': [None, 'Clients', 'clients', 'Clients', 'redis.clients', 'line'],
+ 'lines': [
+ ['connected_clients', 'connected', 'absolute'],
+ ['blocked_clients', 'blocked', 'absolute']
+ ]},
+ 'slaves': {
+ 'options': [None, 'Slaves', 'slaves', 'Replication', 'redis.replication', 'line'],
+ 'lines': [
+ ['connected_slaves', 'connected', 'absolute']
+ ]}
+}
+
+
+class Service(SocketService):
+ def __init__(self, configuration=None, name=None):
+ SocketService.__init__(self, configuration=configuration, name=name)
+ self.request = "INFO\r\n"
+ self.host = "localhost"
+ self.port = 6379
+ self.unix_socket = None
+ self.order = ORDER
+ self.definitions = CHARTS
+
+ def _get_data(self):
+ """
+ Get data from socket
+ :return: dict
+ """
+ try:
+ raw = self._get_raw_data().split("\n")
+ except AttributeError:
+ return None
+ data = {}
+ for line in raw:
+ if line.startswith(('instantaneous', 'keyspace', 'used_memory', 'connected', 'blocked')):
+ try:
+ t = line.split(':')
+ data[t[0]] = int(t[1])
+ except (IndexError, ValueError):
+ pass
+ elif line.startswith('db'):
+ tmp = line.split(',')[0].replace('keys=', '')
+ record = tmp.split(':')
+ data[record[0]] = int(record[1])
+ try:
+ data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100)
+ except:
+ data['hit_rate'] = 0
+
+ return data
+
+ def check(self):
+ """
+ Parse configuration, check if redis is available, and dynamically create chart lines data
+ :return: boolean
+ """
+ self._parse_config()
+ if self.name == "":
+ self.name = "local"
+ self.chart_name += "_" + self.name
+ data = self._get_data()
+ if data is None:
+ self.error("No data received")
+ return False
+
+ for name in data:
+ if name.startswith('db'):
+ self.definitions['keys']['lines'].append([name.decode(), None, 'absolute'])
+
+ return True
# Description: squid netdata python.d module
# Author: Pawel Krupa (paulfantom)
-from base import NetSocketService
+from base import SocketService
# default module values (can be overridden per job in `config`)
# update_every = 2
CHARTS = {
'clients_net': {
- 'options': [None, "Squid Client Bandwidth", "kilobits/s", "clients", "squid.clients.net" "area"],
+ 'options': [None, "Squid Client Bandwidth", "kilobits/s", "clients", "squid.clients.net", "area"],
'lines': [
["client_http_kbytes_in", "in", "incremental", 8, 1],
["client_http_kbytes_out", "out", "incremental", -8, 1],
'clients_requests': {
'options': [None, "Squid Client Requests", "requests/s", "clients", "squid.clients.requests", 'line'],
'lines': [
- ["client_http_requests", "requests"],
- ["client_http_hits", "hits"],
+ ["client_http_requests", "requests", "incremental"],
+ ["client_http_hits", "hits", "incremental"],
["client_http_errors", "errors", "incremental", -1, 1]
]},
'servers_net': {
- 'options': [None, "Squid Server Bandwidth", "kilobits/s", "servers", "squid.servers.net" "area"],
+ 'options': [None, "Squid Server Bandwidth", "kilobits/s", "servers", "squid.servers.net", "area"],
'lines': [
["server_all_kbytes_in", "in", "incremental", 8, 1],
["server_all_kbytes_out", "out", "incremental", -8, 1]
}
-class Service(NetSocketService):
+class Service(SocketService):
def __init__(self, configuration=None, name=None):
- NetSocketService.__init__(self, configuration=configuration, name=name)
+ SocketService.__init__(self, configuration=configuration, name=name)
self.request = ""
self.host = "localhost"
self.port = 3128
Get data via http request
:return: dict
"""
+ data = {}
try:
- raw = self._get_raw_data().split('\n')
- if "200 OK" not in raw[0]:
+ raw = self._get_raw_data().split('\r\n')[-1]
+ if raw.startswith('<'):
return None
- data = {}
- for row in raw:
+ for row in raw.split('\n'):
if row.startswith(("client", "server.all")):
tmp = row.split("=")
data[tmp[0].replace('.', '_').strip(' ')] = int(tmp[1])
+ except (ValueError, AttributeError, TypeError):
+ return None
- return data
- except (ValueError, AttributeError):
+ if len(data) == 0:
return None
+ else:
+ return data
def check(self):
"""
if not req.endswith(" HTTP/1.0\r\n\r\n"):
req += " HTTP/1.0\r\n\r\n"
self.request = req.encode()
- #
- # # autodetect squid
- # if type(self.port) is tuple:
- # ports = self.port
- # for port in ports:
- # self.port = port
- # urls = ["cache_object://" + self.host + ":" + str(port) + "/counters",
- # "/squid-internal-mgr/counters"]
- # for url in urls:
- # tmp = "GET " + url + " HTTP/1.0\r\n\r\n"
- # self.request = tmp.encode()
- # if self._get_data() is not None:
- # return True
- # else:
- if True:
- if self._get_data() is not None:
- return True
- else:
- return False
-
-
-
-
-
-
-
-
+ if self._get_data() is not None:
+ return True
+ else:
+ self.error("No data returned")
+ return False
info: undefined
},
+ 'redis': {
+ title: 'Redis',
+ info: undefined
+ },
+
'phpfpm': {
title: 'PHP-FPM',
info: undefined,
},
-/* 'nginx': {
+ 'nginx': {
title: 'nginx',
info: undefined,
},
-
+/*
'apache': {
title: 'Apache',
info: undefined,
height: 0.5
},
- 'nginx.connections': {
+ 'nginx_local.connections': {
colors: NETDATA.colors[4],
mainheads: [
gaugeChart('Connections', '12%', '', NETDATA.colors[4])
]
},
- 'nginx.requests': {
+ 'nginx_local.requests': {
colors: NETDATA.colors[0],
mainheads: [
gaugeChart('Requests', '12%', '', NETDATA.colors[0])
break;
case 'mysql':
+ case 'redis':
case 'phpfpm':
-/* case 'nginx':
- case 'apache':*/
+ case 'nginx':
+/* case 'apache':*/
case 'named':
case 'cgroup':
chart.menu = chart.type;