summaryrefslogtreecommitdiffstats
path: root/python.d
diff options
context:
space:
mode:
authorliu lei <leolovenet@gmail.com>2016-11-17 03:23:49 +0800
committerliu lei <leolovenet@gmail.com>2016-11-17 03:23:49 +0800
commit0acd15dd4aae24a0366fafce03679c51feed779d (patch)
tree538f308b83d2a2332fd943a09639d0ed58ffc645 /python.d
parent7d5c114516ae6cce23803807d5893d7a617ae243 (diff)
parent2ba7db38468400e1b1e8365e30ee8f3b776b9215 (diff)
Merge remote-tracking branch 'upstream/master'
# Conflicts: # python.d/python_modules/base.py # python.d/redis.chart.py
Diffstat (limited to 'python.d')
-rw-r--r--python.d/Makefile.am61
-rw-r--r--python.d/dovecot.chart.py46
-rw-r--r--python.d/hddtemp.chart.py2
-rw-r--r--python.d/memcached.chart.py88
-rw-r--r--python.d/nginx_log.chart.py5
-rw-r--r--python.d/postgres.chart.py389
-rw-r--r--python.d/python_modules/base.py4
-rw-r--r--python.d/python_modules/msg.py54
-rw-r--r--python.d/redis.chart.py3
-rw-r--r--python.d/squid.chart.py19
10 files changed, 559 insertions, 112 deletions
diff --git a/python.d/Makefile.am b/python.d/Makefile.am
index d769e31386..cc4a37bc88 100644
--- a/python.d/Makefile.am
+++ b/python.d/Makefile.am
@@ -1,46 +1,47 @@
MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
CLEANFILES = \
- python-modules-installer.sh \
- $(NULL)
+ python-modules-installer.sh \
+ $(NULL)
include $(top_srcdir)/build/subst.inc
SUFFIXES = .in
dist_python_SCRIPTS = \
- apache.chart.py \
- apache_cache.chart.py \
- cpufreq.chart.py \
- dovecot.chart.py \
- example.chart.py \
- exim.chart.py \
- hddtemp.chart.py \
- ipfs.chart.py \
- memcached.chart.py \
- mysql.chart.py \
- nginx.chart.py \
- nginx_log.chart.py \
- phpfpm.chart.py \
- postfix.chart.py \
- redis.chart.py \
- retroshare.chart.py \
- sensors.chart.py \
- squid.chart.py \
- tomcat.chart.py \
- python-modules-installer.sh \
- $(NULL)
+ apache.chart.py \
+ apache_cache.chart.py \
+ cpufreq.chart.py \
+ dovecot.chart.py \
+ example.chart.py \
+ exim.chart.py \
+ hddtemp.chart.py \
+ ipfs.chart.py \
+ memcached.chart.py \
+ mysql.chart.py \
+ nginx.chart.py \
+ nginx_log.chart.py \
+ phpfpm.chart.py \
+ postfix.chart.py \
+ postgres.chart.py \
+ redis.chart.py \
+ retroshare.chart.py \
+ sensors.chart.py \
+ squid.chart.py \
+ tomcat.chart.py \
+ python-modules-installer.sh \
+ $(NULL)
dist_python_DATA = \
- README.md \
- $(NULL)
+ README.md \
+ $(NULL)
pythonmodulesdir=$(pythondir)/python_modules
dist_pythonmodules_DATA = \
- python_modules/__init__.py \
- python_modules/base.py \
- python_modules/msg.py \
- python_modules/lm_sensors.py \
- $(NULL)
+ python_modules/__init__.py \
+ python_modules/base.py \
+ python_modules/msg.py \
+ python_modules/lm_sensors.py \
+ $(NULL)
pythonyaml2dir=$(pythonmodulesdir)/pyyaml2
dist_pythonyaml2_DATA = \
diff --git a/python.d/dovecot.chart.py b/python.d/dovecot.chart.py
index c05cb0c2e9..60e8bf6ef7 100644
--- a/python.d/dovecot.chart.py
+++ b/python.d/dovecot.chart.py
@@ -13,75 +13,75 @@ retries = 60
ORDER = ['sessions', 'logins', 'commands',
'faults',
'context_switches',
- 'disk', 'bytes', 'syscalls',
+ 'io', 'net', 'syscalls',
'lookup', 'cache',
'auth', 'auth_cache']
CHARTS = {
'sessions': {
- 'options': [None, "active sessions", 'number', 'IMAP', 'dovecot.sessions', 'line'],
+ 'options': [None, "Dovecot Active Sessions", 'number', 'sessions', 'dovecot.sessions', 'line'],
'lines': [
['num_connected_sessions', 'active sessions', 'absolute']
]},
'logins': {
- 'options': [None, "logins", 'number', 'IMAP', 'dovecot.logins', 'line'],
+ 'options': [None, "Dovecot Logins", 'number', 'logins', 'dovecot.logins', 'line'],
'lines': [
['num_logins', 'logins', 'absolute']
]},
'commands': {
- 'options': [None, "commands", "commands", 'IMAP', 'dovecot.commands', 'line'],
+ 'options': [None, "Dovecot Commands", "commands", 'commands', 'dovecot.commands', 'line'],
'lines': [
['num_cmds', 'commands', 'absolute']
]},
'faults': {
- 'options': [None, "faults", "faults", 'Faults', 'dovecot.faults', 'line'],
+ 'options': [None, "Dovecot Page Faults", "faults", 'page faults', 'dovecot.faults', 'line'],
'lines': [
['min_faults', 'minor', 'absolute'],
['maj_faults', 'major', 'absolute']
]},
'context_switches': {
- 'options': [None, "context switches", '', 'Context Switches', 'dovecot.context_switches', 'line'],
+ 'options': [None, "Dovecot Context Switches", '', 'context switches', 'dovecot.context_switches', 'line'],
'lines': [
['vol_cs', 'volountary', 'absolute'],
['invol_cs', 'involountary', 'absolute']
]},
- 'disk': {
- 'options': [None, "disk", 'bytes/s', 'Reads and Writes', 'dovecot.disk', 'line'],
+ 'io': {
+ 'options': [None, "Dovecot Disk I/O", 'kilobytes/s', 'disk', 'dovecot.io', 'area'],
'lines': [
- ['disk_input', 'read', 'incremental'],
- ['disk_output', 'write', 'incremental']
+ ['disk_input', 'read', 'incremental', 1, 1024],
+ ['disk_output', 'write', 'incremental', -1, 1024]
]},
- 'bytes': {
- 'options': [None, "bytes", 'bytes/s', 'Reads and Writes', 'dovecot.bytes', 'line'],
+ 'net': {
+ 'options': [None, "Dovecot Network Bandwidth", 'kilobits/s', 'network', 'dovecot.net', 'area'],
'lines': [
- ['read_bytes', 'read', 'incremental'],
- ['write_bytes', 'write', 'incremental']
+ ['read_bytes', 'read', 'incremental', 8, 1024],
+ ['write_bytes', 'write', 'incremental', -8, 1024]
]},
'syscalls': {
- 'options': [None, "number of syscalls", 'syscalls/s', 'Reads and Writes', 'dovecot.syscalls', 'line'],
+ 'options': [None, "Dovecot Number of SysCalls", 'syscalls/s', 'system', 'dovecot.syscalls', 'line'],
'lines': [
['read_count', 'read', 'incremental'],
['write_count', 'write', 'incremental']
]},
'lookup': {
- 'options': [None, "lookups", 'number/s', 'Mail', 'dovecot.lookup', 'line'],
+ 'options': [None, "Dovecot Lookups", 'number/s', 'lookups', 'dovecot.lookup', 'stacked'],
'lines': [
['mail_lookup_path', 'path', 'incremental'],
['mail_lookup_attr', 'attr', 'incremental']
]},
'cache': {
- 'options': [None, "hits", 'hits/s', 'Mail', 'dovecot.cache', 'line'],
+ 'options': [None, "Dovecot Cache Hits", 'hits/s', 'cache', 'dovecot.cache', 'line'],
'lines': [
['mail_cache_hits', 'hits', 'incremental']
]},
'auth': {
- 'options': [None, "attempts", 'attempts', 'Authentication', 'dovecot.auth', 'stacked'],
+ 'options': [None, "Dovecot Authentications", 'attempts', 'logins', 'dovecot.auth', 'stacked'],
'lines': [
- ['auth_successes', 'success', 'absolute'],
- ['auth_failures', 'failure', 'absolute']
+ ['auth_successes', 'ok', 'absolute'],
+ ['auth_failures', 'failed', 'absolute']
]},
'auth_cache': {
- 'options': [None, "cache", 'number', 'Authentication', 'dovecot.auth_cache', 'stacked'],
+ 'options': [None, "Dovecot Authentication Cache", 'number', 'cache', 'dovecot.auth_cache', 'stacked'],
'lines': [
['auth_cache_hits', 'hit', 'absolute'],
['auth_cache_misses', 'miss', 'absolute']
@@ -110,6 +110,10 @@ class Service(SocketService):
except (ValueError, AttributeError):
return None
+ if raw is None:
+ self.debug("dovecot returned no data")
+ return None
+
data = raw.split('\n')[:2]
desc = data[0].split('\t')
vals = data[1].split('\t')
diff --git a/python.d/hddtemp.chart.py b/python.d/hddtemp.chart.py
index 465bfdfa22..2e7695db54 100644
--- a/python.d/hddtemp.chart.py
+++ b/python.d/hddtemp.chart.py
@@ -23,7 +23,7 @@ ORDER = ['temperatures']
CHARTS = {
'temperatures': {
- 'options': ['disks_temp', 'temperature', 'Celsius', 'Disks temperature', 'hddtemp.temperatures', 'line'],
+ 'options': ['disks_temp', 'Disks Temperatures', 'Celsius', 'temperatures', 'hddtemp.temperatures', 'line'],
'lines': [
# lines are created dynamically in `check()` method
]}
diff --git a/python.d/memcached.chart.py b/python.d/memcached.chart.py
index 5a1400c992..0d6807ba73 100644
--- a/python.d/memcached.chart.py
+++ b/python.d/memcached.chart.py
@@ -20,89 +20,89 @@ retries = 60
# }}
ORDER = ['cache', 'net', 'connections', 'items', 'evicted_reclaimed',
- 'get', 'get_rate', 'set_rate', 'delete', 'cas', 'increment', 'decrement', 'touch', 'touch_rate']
+ 'get', 'get_rate', 'set_rate', 'cas', 'delete', 'increment', 'decrement', 'touch', 'touch_rate']
CHARTS = {
'cache': {
- 'options': [None, 'Cache Size', 'megabytes', 'Cache', 'memcached.cache', 'stacked'],
+ 'options': [None, 'Cache Size', 'megabytes', 'cache', 'memcached.cache', 'stacked'],
'lines': [
- ['used', 'used', 'absolute', 1, 1048576],
- ['avail', 'available', 'absolute', 1, 1048576]
+ ['avail', 'available', 'absolute', 1, 1048576],
+ ['used', 'used', 'absolute', 1, 1048576]
]},
'net': {
- 'options': [None, 'Network', 'kilobytes/s', 'Network', 'memcached.net', 'line'],
+ 'options': [None, 'Network', 'kilobits/s', 'network', 'memcached.net', 'area'],
'lines': [
- ['bytes_read', 'read', 'incremental', 1, 1024],
- ['bytes_written', 'written', 'incremental', 1, 1024]
+ ['bytes_read', 'in', 'incremental', 8, 1024],
+ ['bytes_written', 'out', 'incremental', -8, 1024]
]},
'connections': {
- 'options': [None, 'Connections', 'connections/s', 'Cluster', 'memcached.connections', 'line'],
+ 'options': [None, 'Connections', 'connections/s', 'connections', 'memcached.connections', 'line'],
'lines': [
['curr_connections', 'current', 'incremental'],
['rejected_connections', 'rejected', 'incremental'],
['total_connections', 'total', 'incremental']
]},
'items': {
- 'options': [None, 'Items', 'items', 'Cluster', 'memcached.items', 'line'],
+ 'options': [None, 'Items', 'items', 'items', 'memcached.items', 'line'],
'lines': [
['curr_items', 'current', 'absolute'],
['total_items', 'total', 'absolute']
]},
'evicted_reclaimed': {
- 'options': [None, 'Items', 'items', 'Evicted and Reclaimed', 'memcached.evicted_reclaimed', 'line'],
+ 'options': [None, 'Items', 'items', 'items', 'memcached.evicted_reclaimed', 'line'],
'lines': [
- ['evictions', 'evicted', 'absolute'],
- ['reclaimed', 'reclaimed', 'absolute']
+ ['reclaimed', 'reclaimed', 'absolute'],
+ ['evictions', 'evicted', 'absolute']
]},
'get': {
- 'options': [None, 'Requests', 'requests', 'GET', 'memcached.get', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'get ops', 'memcached.get', 'stacked'],
'lines': [
['get_hits', 'hits', 'percent-of-absolute-row'],
['get_misses', 'misses', 'percent-of-absolute-row']
]},
'get_rate': {
- 'options': [None, 'Rate', 'requests/s', 'GET', 'memcached.get_rate', 'line'],
+ 'options': [None, 'Rate', 'requests/s', 'get ops', 'memcached.get_rate', 'line'],
'lines': [
['cmd_get', 'rate', 'incremental']
]},
'set_rate': {
- 'options': [None, 'Rate', 'requests/s', 'SET', 'memcached.set_rate', 'line'],
+ 'options': [None, 'Rate', 'requests/s', 'set ops', 'memcached.set_rate', 'line'],
'lines': [
['cmd_set', 'rate', 'incremental']
]},
'delete': {
- 'options': [None, 'Requests', 'requests', 'DELETE', 'memcached.delete', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'delete ops', 'memcached.delete', 'stacked'],
'lines': [
['delete_hits', 'hits', 'percent-of-absolute-row'],
['delete_misses', 'misses', 'percent-of-absolute-row'],
]},
'cas': {
- 'options': [None, 'Requests', 'requests', 'CAS', 'memcached.cas', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'check and set ops', 'memcached.cas', 'stacked'],
'lines': [
['cas_hits', 'hits', 'percent-of-absolute-row'],
['cas_misses', 'misses', 'percent-of-absolute-row'],
['cas_badval', 'bad value', 'percent-of-absolute-row']
]},
'increment': {
- 'options': [None, 'Requests', 'requests', 'Increment', 'memcached.increment', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'increment ops', 'memcached.increment', 'stacked'],
'lines': [
['incr_hits', 'hits', 'percent-of-absolute-row'],
['incr_misses', 'misses', 'percent-of-absolute-row']
]},
'decrement': {
- 'options': [None, 'Requests', 'requests', 'Decrement', 'memcached.decrement', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'decrement ops', 'memcached.decrement', 'stacked'],
'lines': [
['decr_hits', 'hits', 'percent-of-absolute-row'],
['decr_misses', 'misses', 'percent-of-absolute-row']
]},
'touch': {
- 'options': [None, 'Requests', 'requests', 'Touch', 'memcached.touch', 'stacked'],
+ 'options': [None, 'Requests', 'requests', 'touch ops', 'memcached.touch', 'stacked'],
'lines': [
['touch_hits', 'hits', 'percent-of-absolute-row'],
['touch_misses', 'misses', 'percent-of-absolute-row']
]},
'touch_rate': {
- 'options': [None, 'Rate', 'requests/s', 'Touch', 'memcached.touch_rate', 'line'],
+ 'options': [None, 'Rate', 'requests/s', 'touch ops', 'memcached.touch_rate', 'line'],
'lines': [
['cmd_touch', 'rate', 'incremental']
]}
@@ -125,44 +125,52 @@ class Service(SocketService):
Get data from socket
:return: dict
"""
+ response = self._get_raw_data()
+ if response is None:
+ # error has already been logged
+ return None
+
+ if response.startswith('ERROR'):
+ self.error("received ERROR")
+ return None
+
try:
- raw = self._get_raw_data().split("\n")
+ parsed = response.split("\n")
except AttributeError:
- self.error("no data received")
- return None
- if raw[0].startswith('ERROR'):
- self.error("Memcached returned ERROR")
+ self.error("response is invalid/empty")
return None
+
+ # split the response
data = {}
- for line in raw:
+ for line in parsed:
if line.startswith('STAT'):
try:
t = line[5:].split(' ')
- data[t[0]] = int(t[1])
+ data[t[0]] = t[1]
except (IndexError, ValueError):
+ self.debug("invalid line received: " + str(line))
pass
- try:
- data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100)
- except:
- data['hit_rate'] = 0
+ if len(data) == 0:
+ self.error("received data doesn't have any records")
+ return None
+
+ # custom calculations
try:
data['avail'] = int(data['limit_maxbytes']) - int(data['bytes'])
- data['used'] = data['bytes']
+ data['used'] = int(data['bytes'])
except:
pass
- if len(data) == 0:
- self.error("received data doesn't have needed records")
- return None
- else:
- return data
+ return data
def _check_raw_data(self, data):
if data.endswith('END\r\n'):
+ self.debug("received full response from memcached")
return True
- else:
- return False
+
+ self.debug("waiting more data from memcached")
+ return False
def check(self):
"""
diff --git a/python.d/nginx_log.chart.py b/python.d/nginx_log.chart.py
index 95fb123d2f..16a9d95713 100644
--- a/python.d/nginx_log.chart.py
+++ b/python.d/nginx_log.chart.py
@@ -54,7 +54,10 @@ class Service(LogService):
regex = self.regex
for line in raw:
code = regex.search(line)
- beginning = code.group(1)[0]
+ try:
+ beginning = code.group(1)[0]
+ except AttributeError:
+ continue
if beginning == '2':
data["2xx"] += 1
diff --git a/python.d/postgres.chart.py b/python.d/postgres.chart.py
new file mode 100644
index 0000000000..cd99bdfc9d
--- /dev/null
+++ b/python.d/postgres.chart.py
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+# Description: example netdata python.d module
+# Authors: facetoe, dangtranhoang
+
+import re
+from copy import deepcopy
+
+import psycopg2
+from psycopg2 import extensions
+from psycopg2._psycopg import DatabaseError
+from psycopg2.extras import DictCursor
+
+from base import SimpleService
+
+# default module values
+update_every = 1
+priority = 90000
+retries = 60
+
+ARCHIVE = """
+SELECT
+ CAST(COUNT(*) AS INT) AS file_count,
+ CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.ready$$r$ as INT)), 0) AS INT) AS ready_count,
+ CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.done$$r$ AS INT)), 0) AS INT) AS done_count
+FROM
+ pg_catalog.pg_ls_dir('pg_xlog/archive_status') AS archive_files (archive_file);
+"""
+
+BACKENDS = """
+SELECT
+ count(*) - (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle') AS backends_active,
+ (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle' ) AS backends_idle
+FROM
+ pg_stat_activity;
+"""
+
+TABLE_STATS = """
+SELECT
+ ((sum(relpages) * 8) * 1024) AS size_relations,
+ count(1) AS relations
+FROM pg_class
+WHERE relkind IN ('r', 't');
+"""
+
+INDEX_STATS = """
+SELECT
+ ((sum(relpages) * 8) * 1024) AS size_indexes,
+ count(1) AS indexes
+FROM pg_class
+WHERE relkind = 'i';"""
+
+DATABASE = """
+SELECT
+ datname AS database_name,
+ sum(numbackends) AS connections,
+ sum(xact_commit) AS xact_commit,
+ sum(xact_rollback) AS xact_rollback,
+ sum(blks_read) AS blks_read,
+ sum(blks_hit) AS blks_hit,
+ sum(tup_returned) AS tup_returned,
+ sum(tup_fetched) AS tup_fetched,
+ sum(tup_inserted) AS tup_inserted,
+ sum(tup_updated) AS tup_updated,
+ sum(tup_deleted) AS tup_deleted,
+ sum(conflicts) AS conflicts
+FROM pg_stat_database
+WHERE NOT datname ~* '^template\d+'
+GROUP BY database_name;
+"""
+
+BGWRITER = 'SELECT * FROM pg_stat_bgwriter;'
+DATABASE_LOCKS = """
+SELECT
+ pg_database.datname as database_name,
+ mode,
+ count(mode) AS count
+FROM pg_locks
+ INNER JOIN pg_database ON pg_database.oid = pg_locks.database
+GROUP BY datname, mode
+ORDER BY datname, mode;
+"""
+REPLICATION = """
+SELECT
+ client_hostname,
+ client_addr,
+ state,
+ sent_offset - (
+ replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag
+FROM (
+ SELECT
+ client_addr, client_hostname, state,
+ ('x' || lpad(split_part(sent_location, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog,
+ ('x' || lpad(split_part(replay_location, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog,
+ ('x' || lpad(split_part(sent_location, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset,
+ ('x' || lpad(split_part(replay_location, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset
+ FROM pg_stat_replication
+) AS s;
+"""
+
+LOCK_TYPES = [
+ 'ExclusiveLock',
+ 'RowShareLock',
+ 'SIReadLock',
+ 'ShareUpdateExclusiveLock',
+ 'AccessExclusiveLock',
+ 'AccessShareLock',
+ 'ShareRowExclusiveLock',
+ 'ShareLock',
+ 'RowExclusiveLock'
+]
+
+ORDER = ['db_stat_transactions', 'db_stat_tuple_read', 'db_stat_tuple_returned', 'db_stat_tuple_write',
+ 'backend_process', 'index_count', 'index_size', 'table_count', 'table_size', 'wal', 'background_writer']
+
+CHARTS = {
+ 'db_stat_transactions': {
+ 'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions', 'line'],
+ 'lines': [
+ ['db_stat_xact_commit', 'committed', 'incremental'],
+ ['db_stat_xact_rollback', 'rolled back', 'incremental']
+ ]},
+ 'db_stat_connections': {
+ 'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections', 'line'],
+ 'lines': [
+ ['db_stat_connections', 'connections', 'absolute']
+ ]},
+ 'db_stat_tuple_read': {
+ 'options': [None, 'Tuple reads from db', 'reads/s', 'db statistics', 'postgres.db_stat_tuple_read', 'line'],
+ 'lines': [
+ ['db_stat_blks_read', 'disk', 'incremental'],
+ ['db_stat_blks_hit', 'cache', 'incremental']
+ ]},
+ 'db_stat_tuple_returned': {
+ 'options': [None, 'Tuples returned from db', 'tuples/s', 'db statistics', 'postgres.db_stat_tuple_returned', 'line'],
+ 'lines': [
+ ['db_stat_tup_returned', 'sequential', 'incremental'],
+ ['db_stat_tup_fetched', 'bitmap', 'incremental']
+ ]},
+ 'db_stat_tuple_write': {
+ 'options': [None, 'Tuples written to db', 'writes/s', 'db statistics', 'postgres.db_stat_tuple_write', 'line'],
+ 'lines': [
+ ['db_stat_tup_inserted', 'inserted', 'incremental'],
+ ['db_stat_tup_updated', 'updated', 'incremental'],
+ ['db_stat_tup_deleted', 'deleted', 'incremental'],
+ ['db_stat_conflicts', 'conflicts', 'incremental']
+ ]},
+ 'backend_process': {
+ 'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process', 'line'],
+ 'lines': [
+ ['backend_process_active', 'active', 'absolute'],
+ ['backend_process_idle', 'idle', 'absolute']
+ ]},
+ 'index_count': {
+ 'options': [None, 'Total indexes', 'index', 'indexes', 'postgres.index_count', 'line'],
+ 'lines': [
+ ['index_count', 'total', 'absolute']
+ ]},
+ 'index_size': {
+ 'options': [None, 'Indexes size', 'MB', 'indexes', 'postgres.index_size', 'line'],
+ 'lines': [
+ ['index_size', 'size', 'absolute', 1, 1024 * 1024]
+ ]},
+ 'table_count': {
+ 'options': [None, 'Total Tables', 'tables', 'tables', 'postgres.table_count', 'line'],
+ 'lines': [
+ ['table_count', 'total', 'absolute']
+ ]},
+ 'table_size': {
+ 'options': [None, 'Tables size', 'MB', 'tables', 'postgres.table_size', 'line'],
+ 'lines': [
+ ['table_size', 'size', 'absolute', 1, 1024 * 1024]
+ ]},
+ 'wal': {
+ 'options': [None, 'Write-Ahead Logging Statistics', 'files/s', 'write ahead log', 'postgres.wal', 'line'],
+ 'lines': [
+ ['wal_total', 'total', 'incremental'],
+ ['wal_ready', 'ready', 'incremental'],
+ ['wal_done', 'done', 'incremental']
+ ]},
+ 'background_writer': {
+ 'options': [None, 'Checkpoints', 'writes/s', 'background writer', 'postgres.background_writer', 'line'],
+ 'lines': [
+ ['background_writer_scheduled', 'scheduled', 'incremental'],
+ ['background_writer_requested', 'requested', 'incremental']
+ ]}
+}
+
+
+class Service(SimpleService):
+ def __init__(self, configuration=None, name=None):
+ super(self.__class__, self).__init__(configuration=configuration, name=name)
+ self.order = ORDER
+ self.definitions = CHARTS
+ self.table_stats = configuration.pop('table_stats', True)
+ self.index_stats = configuration.pop('index_stats', True)
+ self.configuration = configuration
+ self.connection = None
+ self.is_superuser = False
+ self.data = {}
+ self.databases = set()
+
+ def _connect(self):
+ params = dict(user='postgres',
+ database=None,
+ password=None,
+ host='localhost',
+ port=5432)
+ params.update(self.configuration)
+
+ if not self.connection:
+ self.connection = psycopg2.connect(**params)
+ self.connection.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT)
+ self.connection.set_session(readonly=True)
+
+ def check(self):
+ try:
+ self._connect()
+ cursor = self.connection.cursor()
+ self._discover_databases(cursor)
+ self._check_if_superuser(cursor)
+ cursor.close()
+
+ self._create_definitions()
+ return True
+ except DatabaseError:
+ return False
+ except Exception as e:
+ self.error(e)
+ return False
+
+ def _discover_databases(self, cursor):
+ cursor.execute("""
+ SELECT datname
+ FROM pg_stat_database
+ WHERE NOT datname ~* '^template\d+'
+ """)
+ self.databases = set(r[0] for r in cursor)
+
+ def _check_if_superuser(self, cursor):
+ cursor.execute("""
+ SELECT current_setting('is_superuser') = 'on' AS is_superuser;
+ """)
+ self.is_superuser = cursor.fetchone()[0]
+
+ def _create_definitions(self):
+ for database_name in self.databases:
+ for chart_template_name in list(CHARTS):
+ if chart_template_name.startswith('db_stat'):
+ self._add_database_stat_chart(chart_template_name, database_name)
+ self._add_database_lock_chart(database_name)
+
+ def _add_database_stat_chart(self, chart_template_name, database_name):
+ chart_template = CHARTS[chart_template_name]
+ chart_name = "{}_{}".format(database_name, chart_template_name)
+ if chart_name not in self.order:
+ self.order.insert(0, chart_name)
+ name, title, units, family, context, chart_type = chart_template['options']
+ self.definitions[chart_name] = {
+ 'options': [
+ name,
+ title + ': ' + database_name,
+ units,
+ 'db ' + database_name,
+ context,
+ chart_type
+ ]
+ }
+
+ self.definitions[chart_name]['lines'] = []
+ for line in deepcopy(chart_template['lines']):
+ line[0] = "{}_{}".format(database_name, line[0])
+ self.definitions[chart_name]['lines'].append(line)
+
+ def _add_database_lock_chart(self, database_name):
+ chart_name = "{}_locks".format(database_name)
+ if chart_name not in self.order:
+ self.order.insert(-1, chart_name)
+ self.definitions[chart_name] = dict(
+ options=
+ [
+ None,
+ 'Locks on db: ' + database_name,
+ 'locks',
+ 'db ' + database_name,
+ 'postgres.db_locks',
+ 'line'
+ ],
+ lines=[]
+ )
+
+ for lock_type in LOCK_TYPES:
+ lock_id = "{}_{}".format(database_name, lock_type)
+ label = re.sub("([a-z])([A-Z])", "\g<1> \g<2>", lock_type)
+ self.definitions[chart_name]['lines'].append([lock_id, label, 'absolute'])
+
+ def _get_data(self):
+ self._connect()
+
+ cursor = self.connection.cursor(cursor_factory=DictCursor)
+ self.add_stats(cursor)
+
+ cursor.close()
+ return self.data
+
+ def add_stats(self, cursor):
+ self.add_database_stats(cursor)
+ self.add_backend_stats(cursor)
+ if self.index_stats:
+ self.add_index_stats(cursor)
+ if self.table_stats:
+ self.add_table_stats(cursor)
+ self.add_lock_stats(cursor)
+ self.add_bgwriter_stats(cursor)
+
+ # self.add_replication_stats(cursor)
+
+ if self.is_superuser:
+ self.add_wal_stats(cursor)
+
+ def add_database_stats(self, cursor):
+ cursor.execute(DATABASE)
+ for row in cursor:
+ database_name = row.get('database_name')
+ self.data["{}_{}".format(database_name, 'db_stat_xact_commit')] = int(row.get('xact_commit', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_xact_rollback')] = int(row.get('xact_rollback', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_blks_read')] = int(row.get('blks_read', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_blks_hit')] = int(row.get('blks_hit', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_tup_returned')] = int(row.get('tup_returned', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_tup_fetched')] = int(row.get('tup_fetched', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_tup_inserted')] = int(row.get('tup_inserted', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_tup_updated')] = int(row.get('tup_updated', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_tup_deleted')] = int(row.get('tup_deleted', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_conflicts')] = int(row.get('conflicts', 0))
+ self.data["{}_{}".format(database_name, 'db_stat_connections')] = int(row.get('connections', 0))
+
+ def add_backend_stats(self, cursor):
+ cursor.execute(BACKENDS)
+ temp = cursor.fetchone()
+
+ self.data['backend_process_active'] = int(temp.get('backends_active', 0))
+ self.data['backend_process_idle'] = int(temp.get('backends_idle', 0))
+
+ def add_index_stats(self, cursor):
+ cursor.execute(INDEX_STATS)
+ temp = cursor.fetchone()
+ self.data['index_count'] = int(temp.get('indexes', 0))
+ self.data['index_size'] = int(temp.get('size_indexes', 0))
+
+ def add_table_stats(self, cursor):
+ cursor.execute(TABLE_STATS)
+ temp = cursor.fetchone()
+ self.data['table_count'] = int(temp.get('relations', 0))
+ self.data['table_size'] = int(temp.get('size_relations', 0))
+
+ def add_lock_stats(self, cursor):
+ cursor.execute(DATABASE_LOCKS)
+
+ # zero out all current lock values
+ for database_name in self.databases:
+ for lock_type in LOCK_TYPES:
+ self.data["{}_{}".format(database_name, lock_type)] = 0
+
+ # populate those that have current locks
+ for row in cursor:
+ database_name, lock_type, lock_count = row
+ self.data["{}_{}".format(database_name, lock_type)] = lock_count
+
+ def add_wal_stats(self, cursor):
+ cursor.execute(ARCHIVE)
+ temp = cursor.fetchone()
+ self.data['wal_total'] = int(temp.get('file_count', 0))
+ self.data['wal_ready'] = int(temp.get('ready_count', 0))
+ self.data['wal_done'] = int(temp.get('done_count', 0))
+
+ def add_bgwriter_stats(self, cursor):
+ cursor.execute(BGWRITER)
+ temp = cursor.fetchone()
+ self.data['background_writer_scheduled'] = temp.get('checkpoints_timed', 0)
+ self.data['background_writer_requested'] = temp.get('checkpoints_requests', 0)
+
+'''
+ def add_replication_stats(self, cursor):
+ cursor.execute(REPLICATION)
+ temp = cursor.fetchall()
+ for row in temp:
+ self.add_gauge_value('Replication/%s' % row.get('client_addr', 'Unknown'),
+ 'byte_lag',
+ int(row.get('byte_lag', 0)))
+'''
diff --git a/python.d/python_modules/base.py b/python.d/python_modules/base.py
index 8940b8731a..5077e9995e 100644
--- a/python.d/python_modules/base.py
+++ b/python.d/python_modules/base.py
@@ -692,7 +692,11 @@ class SocketService(SimpleService):
break
self.debug("received data:", str(buf))
+<<<<<<< HEAD
data += buf.decode('utf-8', 'ignore')
+=======
+ data += buf.decode(errors='ignore')
+>>>>>>> upstream/master
if self._check_raw_data(data):
break
diff --git a/python.d/python_modules/msg.py b/python.d/python_modules/msg.py
index ecefba0a4f..b835c94045 100644
--- a/python.d/python_modules/msg.py
+++ b/p