diff options
author | liu lei <leolovenet@gmail.com> | 2016-11-17 03:23:49 +0800 |
---|---|---|
committer | liu lei <leolovenet@gmail.com> | 2016-11-17 03:23:49 +0800 |
commit | 0acd15dd4aae24a0366fafce03679c51feed779d (patch) | |
tree | 538f308b83d2a2332fd943a09639d0ed58ffc645 /python.d | |
parent | 7d5c114516ae6cce23803807d5893d7a617ae243 (diff) | |
parent | 2ba7db38468400e1b1e8365e30ee8f3b776b9215 (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.am | 61 | ||||
-rw-r--r-- | python.d/dovecot.chart.py | 46 | ||||
-rw-r--r-- | python.d/hddtemp.chart.py | 2 | ||||
-rw-r--r-- | python.d/memcached.chart.py | 88 | ||||
-rw-r--r-- | python.d/nginx_log.chart.py | 5 | ||||
-rw-r--r-- | python.d/postgres.chart.py | 389 | ||||
-rw-r--r-- | python.d/python_modules/base.py | 4 | ||||
-rw-r--r-- | python.d/python_modules/msg.py | 54 | ||||
-rw-r--r-- | python.d/redis.chart.py | 3 | ||||
-rw-r--r-- | python.d/squid.chart.py | 19 |
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 |