0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-05-09 11:30:25 +00:00
netdata_netdata/collectors/python.d.plugin/postgres/postgres.chart.py
Ilya Mashchenko 5286dae8eb python.d.plugin update ()
<!--
Describe the change in summary section, including rationale and degin decisions.
Include "Fixes #nnn" if you are fixing an existing issue.

In "Component Name" section write which component is changed in this PR. This
will help us review your PR quicker.

If you have more information you want to add, write them in "Additional
Information" section. This is usually used to help others understand your
motivation behind this change. A step-by-step reproduction of the problem is
helpful if there is no related issue.
-->

##### Summary
Fix: 

`python.d.plugin` updates:
  * remove `retries` option 
  * make `penalty` configurable (enabled by default, max is 10 minutes)

> penalty indicates whether to apply penalty to update_every in case of failures.
> Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes.
> penalty: yes

##### Component Name

`python.d.plugin`

##### Additional Information
2018-12-07 10:15:03 +01:00

823 lines
27 KiB
Python

# -*- coding: utf-8 -*-
# Description: example netdata python.d module
# Authors: facetoe, dangtranhoang
# SPDX-License-Identifier: GPL-3.0-or-later
from copy import deepcopy
try:
import psycopg2
from psycopg2 import extensions
from psycopg2.extras import DictCursor
from psycopg2 import OperationalError
PSYCOPG2 = True
except ImportError:
PSYCOPG2 = False
from bases.FrameworkServices.SimpleService import SimpleService
# default module values
update_every = 1
priority = 60000
METRICS = {
'DATABASE': [
'connections',
'xact_commit',
'xact_rollback',
'blks_read',
'blks_hit',
'tup_returned',
'tup_fetched',
'tup_inserted',
'tup_updated',
'tup_deleted',
'conflicts',
'temp_files',
'temp_bytes',
'size'
],
'BACKENDS': [
'backends_active',
'backends_idle'
],
'INDEX_STATS': [
'index_count',
'index_size'
],
'TABLE_STATS': [
'table_size',
'table_count'
],
'WAL': [
'written_wal',
'recycled_wal',
'total_wal'
],
'WAL_WRITES': [
'wal_writes'
],
'ARCHIVE': [
'ready_count',
'done_count',
'file_count'
],
'BGWRITER': [
'checkpoint_scheduled',
'checkpoint_requested',
'buffers_checkpoint',
'buffers_clean',
'maxwritten_clean',
'buffers_backend',
'buffers_alloc',
'buffers_backend_fsync'
],
'LOCKS': [
'ExclusiveLock',
'RowShareLock',
'SIReadLock',
'ShareUpdateExclusiveLock',
'AccessExclusiveLock',
'AccessShareLock',
'ShareRowExclusiveLock',
'ShareLock',
'RowExclusiveLock'
],
'AUTOVACUUM': [
'analyze',
'vacuum_analyze',
'vacuum',
'vacuum_freeze',
'brin_summarize'
],
'STANDBY_DELTA': [
'sent_delta',
'write_delta',
'flush_delta',
'replay_delta'
],
'REPSLOT_FILES': [
'replslot_wal_keep',
'replslot_files'
]
}
QUERIES = {
'WAL': """
SELECT
count(*) as total_wal,
count(*) FILTER (WHERE type = 'recycled') AS recycled_wal,
count(*) FILTER (WHERE type = 'written') AS written_wal
FROM
(SELECT
wal.name,
pg_{0}file_name(
CASE pg_is_in_recovery()
WHEN true THEN NULL
ELSE pg_current_{0}_{1}()
END ),
CASE
WHEN wal.name > pg_{0}file_name(
CASE pg_is_in_recovery()
WHEN true THEN NULL
ELSE pg_current_{0}_{1}()
END ) THEN 'recycled'
ELSE 'written'
END AS type
FROM pg_catalog.pg_ls_dir('pg_{0}') AS wal(name)
WHERE name ~ '^[0-9A-F]{{24}}$'
ORDER BY
(pg_stat_file('pg_{0}/'||name)).modification,
wal.name DESC) sub;
""",
'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_{0}/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 table_size,
count(1) AS table_count
FROM pg_class
WHERE relkind IN ('r', 't');
""",
'INDEX_STATS': """
SELECT
((sum(relpages) * 8) * 1024) AS index_size,
count(1) AS index_count
FROM pg_class
WHERE relkind = 'i';
""",
'DATABASE': """
SELECT
datname AS database_name,
numbackends AS connections,
xact_commit AS xact_commit,
xact_rollback AS xact_rollback,
blks_read AS blks_read,
blks_hit AS blks_hit,
tup_returned AS tup_returned,
tup_fetched AS tup_fetched,
tup_inserted AS tup_inserted,
tup_updated AS tup_updated,
tup_deleted AS tup_deleted,
conflicts AS conflicts,
pg_database_size(datname) AS size,
temp_files AS temp_files,
temp_bytes AS temp_bytes
FROM pg_stat_database
WHERE datname IN %(databases)s ;
""",
'BGWRITER': """
SELECT
checkpoints_timed AS checkpoint_scheduled,
checkpoints_req AS checkpoint_requested,
buffers_checkpoint * current_setting('block_size')::numeric buffers_checkpoint,
buffers_clean * current_setting('block_size')::numeric buffers_clean,
maxwritten_clean,
buffers_backend * current_setting('block_size')::numeric buffers_backend,
buffers_alloc * current_setting('block_size')::numeric buffers_alloc,
buffers_backend_fsync
FROM pg_stat_bgwriter;
""",
'LOCKS': """
SELECT
pg_database.datname as database_name,
mode,
count(mode) AS locks_count
FROM pg_locks
INNER JOIN pg_database
ON pg_database.oid = pg_locks.database
GROUP BY datname, mode
ORDER BY datname, mode;
""",
'FIND_DATABASES': """
SELECT
datname
FROM pg_stat_database
WHERE
has_database_privilege(
(SELECT current_user), datname, 'connect')
AND NOT datname ~* '^template\d ';
""",
'FIND_STANDBY': """
SELECT
application_name
FROM pg_stat_replication
WHERE application_name IS NOT NULL
GROUP BY application_name;
""",
'FIND_REPLICATION_SLOT': """
SELECT slot_name
FROM pg_replication_slots;
""",
'STANDBY_DELTA': """
SELECT
application_name,
pg_{0}_{1}_diff(
CASE pg_is_in_recovery()
WHEN true THEN pg_last_{0}_receive_{1}()
ELSE pg_current_{0}_{1}()
END,
sent_{1}) AS sent_delta,
pg_{0}_{1}_diff(
CASE pg_is_in_recovery()
WHEN true THEN pg_last_{0}_receive_{1}()
ELSE pg_current_{0}_{1}()
END,
write_{1}) AS write_delta,
pg_{0}_{1}_diff(
CASE pg_is_in_recovery()
WHEN true THEN pg_last_{0}_receive_{1}()
ELSE pg_current_{0}_{1}()
END,
flush_{1}) AS flush_delta,
pg_{0}_{1}_diff(
CASE pg_is_in_recovery()
WHEN true THEN pg_last_{0}_receive_{1}()
ELSE pg_current_{0}_{1}()
END,
replay_{1}) AS replay_delta
FROM pg_stat_replication
WHERE application_name IS NOT NULL;
""",
'REPSLOT_FILES': """
WITH wal_size AS (
SELECT
current_setting('wal_block_size')::INT * setting::INT AS val
FROM pg_settings
WHERE name = 'wal_segment_size'
)
SELECT
slot_name,
slot_type,
replslot_wal_keep,
count(slot_file) AS replslot_files
FROM
(SELECT
slot.slot_name,
CASE
WHEN slot_file <> 'state' THEN 1
END AS slot_file ,
slot_type,
COALESCE (
floor(
(pg_wal_lsn_diff(pg_current_wal_lsn (),slot.restart_lsn)
- (pg_walfile_name_offset (restart_lsn)).file_offset) / (s.val)
),0) AS replslot_wal_keep
FROM pg_replication_slots slot
LEFT JOIN (
SELECT
slot2.slot_name,
pg_ls_dir('pg_replslot/' || slot2.slot_name) AS slot_file
FROM pg_replication_slots slot2
) files (slot_name, slot_file)
ON slot.slot_name = files.slot_name
CROSS JOIN wal_size s
) AS d
GROUP BY
slot_name,
slot_type,
replslot_wal_keep;
""",
'IF_SUPERUSER': """
SELECT current_setting('is_superuser') = 'on' AS is_superuser;
""",
'DETECT_SERVER_VERSION': """
SHOW server_version_num;
""",
'AUTOVACUUM': """
SELECT
count(*) FILTER (WHERE query LIKE 'autovacuum: ANALYZE%%') AS analyze,
count(*) FILTER (WHERE query LIKE 'autovacuum: VACUUM ANALYZE%%') AS vacuum_analyze,
count(*) FILTER (WHERE query LIKE 'autovacuum: VACUUM%%'
AND query NOT LIKE 'autovacuum: VACUUM ANALYZE%%'
AND query NOT LIKE '%%to prevent wraparound%%') AS vacuum,
count(*) FILTER (WHERE query LIKE '%%to prevent wraparound%%') AS vacuum_freeze,
count(*) FILTER (WHERE query LIKE 'autovacuum: BRIN summarize%%') AS brin_summarize
FROM pg_stat_activity
WHERE query NOT LIKE '%%pg_stat_activity%%';
""",
'DIFF_LSN': """
SELECT
pg_{0}_{1}_diff(
CASE pg_is_in_recovery()
WHEN true THEN pg_last_{0}_receive_{1}()
ELSE pg_current_{0}_{1}()
END,
'0/0') as wal_writes ;
"""
}
QUERY_STATS = {
QUERIES['DATABASE']: METRICS['DATABASE'],
QUERIES['BACKENDS']: METRICS['BACKENDS'],
QUERIES['LOCKS']: METRICS['LOCKS']
}
ORDER = [
'db_stat_temp_files',
'db_stat_temp_bytes',
'db_stat_blks',
'db_stat_tuple_returned',
'db_stat_tuple_write',
'db_stat_transactions',
'db_stat_connections',
'database_size',
'backend_process',
'index_count',
'index_size',
'table_count',
'table_size',
'wal',
'wal_writes',
'archive_wal',
'checkpointer',
'stat_bgwriter_alloc',
'stat_bgwriter_checkpoint',
'stat_bgwriter_backend',
'stat_bgwriter_backend_fsync',
'stat_bgwriter_bgwriter',
'stat_bgwriter_maxwritten',
'replication_slot',
'standby_delta',
'autovacuum'
]
CHARTS = {
'db_stat_transactions': {
'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions',
'line'],
'lines': [
['xact_commit', 'committed', 'incremental'],
['xact_rollback', 'rolled back', 'incremental']
]
},
'db_stat_connections': {
'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections',
'line'],
'lines': [
['connections', 'connections', 'absolute']
]
},
'db_stat_blks': {
'options': [None, 'Disk blocks reads from db', 'reads/s', 'db statistics', 'postgres.db_stat_blks', 'line'],
'lines': [
['blks_read', 'disk', 'incremental'],
['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': [
['tup_returned', 'sequential', 'incremental'],
['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': [
['tup_inserted', 'inserted', 'incremental'],
['tup_updated', 'updated', 'incremental'],
['tup_deleted', 'deleted', 'incremental'],
['conflicts', 'conflicts', 'incremental']
]
},
'db_stat_temp_bytes': {
'options': [None, 'Temp files written to disk', 'KB/s', 'db statistics', 'postgres.db_stat_temp_bytes',
'line'],
'lines': [
['temp_bytes', 'size', 'incremental', 1, 1024]
]
},
'db_stat_temp_files': {
'options': [None, 'Temp files written to disk', 'files', 'db statistics', 'postgres.db_stat_temp_files',
'line'],
'lines': [
['temp_files', 'files', 'incremental']
]
},
'database_size': {
'options': [None, 'Database size', 'MB', 'database size', 'postgres.db_size', 'stacked'],
'lines': [
]
},
'backend_process': {
'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process',
'line'],
'lines': [
['backends_active', 'active', 'absolute'],
['backends_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 Logs', 'files', 'wal', 'postgres.wal', 'line'],
'lines': [
['written_wal', 'written', 'absolute'],
['recycled_wal', 'recycled', 'absolute'],
['total_wal', 'total', 'absolute']
]
},
'wal_writes': {
'options': [None, 'Write-Ahead Logs', 'kilobytes/s', 'wal_writes', 'postgres.wal_writes', 'line'],
'lines': [
['wal_writes', 'writes', 'incremental', 1, 1024]
]
},
'archive_wal': {
'options': [None, 'Archive Write-Ahead Logs', 'files/s', 'archive wal', 'postgres.archive_wal', 'line'],
'lines': [
['file_count', 'total', 'incremental'],
['ready_count', 'ready', 'incremental'],
['done_count', 'done', 'incremental']
]
},
'checkpointer': {
'options': [None, 'Checkpoints', 'writes', 'checkpointer', 'postgres.checkpointer', 'line'],
'lines': [
['checkpoint_scheduled', 'scheduled', 'incremental'],
['checkpoint_requested', 'requested', 'incremental']
]
},
'stat_bgwriter_alloc': {
'options': [None, 'Buffers allocated', 'kilobytes/s', 'bgwriter', 'postgres.stat_bgwriter_alloc', 'line'],
'lines': [
['buffers_alloc', 'alloc', 'incremental', 1, 1024]
]
},
'stat_bgwriter_checkpoint': {
'options': [None, 'Buffers written during checkpoints', 'kilobytes/s', 'bgwriter',
'postgres.stat_bgwriter_checkpoint', 'line'],
'lines': [
['buffers_checkpoint', 'checkpoint', 'incremental', 1, 1024]
]
},
'stat_bgwriter_backend': {
'options': [None, 'Buffers written directly by a backend', 'kilobytes/s', 'bgwriter',
'postgres.stat_bgwriter_backend', 'line'],
'lines': [
['buffers_backend', 'backend', 'incremental', 1, 1024]
]
},
'stat_bgwriter_backend_fsync': {
'options': [None, 'Fsync by backend', 'times', 'bgwriter', 'postgres.stat_bgwriter_backend_fsync', 'line'],
'lines': [
['buffers_backend_fsync', 'backend fsync', 'incremental']
]
},
'stat_bgwriter_bgwriter': {
'options': [None, 'Buffers written by the background writer', 'kilobytes/s', 'bgwriter',
'postgres.bgwriter_bgwriter', 'line'],
'lines': [
['buffers_clean', 'clean', 'incremental', 1, 1024]
]
},
'stat_bgwriter_maxwritten': {
'options': [None, 'Too many buffers written', 'times', 'bgwriter', 'postgres.stat_bgwriter_maxwritten',
'line'],
'lines': [
['maxwritten_clean', 'maxwritten', 'incremental']
]
},
'autovacuum': {
'options': [None, 'Autovacuum workers', 'workers', 'autovacuum', 'postgres.autovacuum', 'line'],
'lines': [
['analyze', 'analyze', 'absolute'],
['vacuum', 'vacuum', 'absolute'],
['vacuum_analyze', 'vacuum analyze', 'absolute'],
['vacuum_freeze', 'vacuum freeze', 'absolute'],
['brin_summarize', 'brin summarize', 'absolute']
]
},
'standby_delta': {
'options': [None, 'Standby delta', 'kilobytes', 'replication delta', 'postgres.standby_delta', 'line'],
'lines': [
['sent_delta', 'sent delta', 'absolute', 1, 1024],
['write_delta', 'write delta', 'absolute', 1, 1024],
['flush_delta', 'flush delta', 'absolute', 1, 1024],
['replay_delta', 'replay delta', 'absolute', 1, 1024]
]
},
'replication_slot': {
'options': [None, 'Replication slot files', 'files', 'replication slot', 'postgres.replication_slot', 'line'],
'lines': [
['replslot_wal_keep', 'wal keeped', 'absolute'],
['replslot_files', 'pg_replslot files', 'absolute']
]
}
}
class Service(SimpleService):
def __init__(self, configuration=None, name=None):
SimpleService.__init__(self, configuration=configuration, name=name)
self.order = ORDER[:]
self.definitions = deepcopy(CHARTS)
self.table_stats = configuration.pop('table_stats', False)
self.index_stats = configuration.pop('index_stats', False)
self.database_poll = configuration.pop('database_poll', None)
self.configuration = configuration
self.connection = False
self.server_version = None
self.data = dict()
self.locks_zeroed = dict()
self.databases = list()
self.secondaries = list()
self.replication_slots = list()
self.queries = QUERY_STATS.copy()
def _connect(self):
params = dict(user='postgres',
database=None,
password=None,
host=None,
port=5432)
params.update(self.configuration)
if not self.connection:
try:
self.connection = psycopg2.connect(**params)
self.connection.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT)
self.connection.set_session(readonly=True)
except OperationalError as error:
return False, str(error)
return True, True
def check(self):
if not PSYCOPG2:
self.error('\'python-psycopg2\' module is needed to use postgres.chart.py')
return False
result, error = self._connect()
if not result:
conf = dict((k, (lambda k, v: v if k != 'password' else '*****')(k, v))
for k, v in self.configuration.items())
self.error('Failed to connect to %s. Error: %s' % (str(conf), error))
return False
try:
cursor = self.connection.cursor()
self.databases = discover_databases_(cursor, QUERIES['FIND_DATABASES'])
is_superuser = check_if_superuser_(cursor, QUERIES['IF_SUPERUSER'])
self.secondaries = discover_secondaries_(cursor, QUERIES['FIND_STANDBY'])
self.server_version = detect_server_version(cursor, QUERIES['DETECT_SERVER_VERSION'])
if self.server_version >= 94000:
self.replication_slots = discover_replication_slots_(cursor, QUERIES['FIND_REPLICATION_SLOT'])
cursor.close()
if self.database_poll and isinstance(self.database_poll, str):
self.databases = [dbase for dbase in self.databases if dbase in self.database_poll.split()] \
or self.databases
self.locks_zeroed = populate_lock_types(self.databases)
self.add_additional_queries_(is_superuser)
self.create_dynamic_charts_()
return True
except Exception as error:
self.error(str(error))
return False
def add_additional_queries_(self, is_superuser):
if self.server_version >= 100000:
wal = 'wal'
lsn = 'lsn'
else:
wal = 'xlog'
lsn = 'location'
self.queries[QUERIES['BGWRITER']] = METRICS['BGWRITER']
self.queries[QUERIES['DIFF_LSN'].format(wal, lsn)] = METRICS['WAL_WRITES']
self.queries[QUERIES['STANDBY_DELTA'].format(wal, lsn)] = METRICS['STANDBY_DELTA']
if self.index_stats:
self.queries[QUERIES['INDEX_STATS']] = METRICS['INDEX_STATS']
if self.table_stats:
self.queries[QUERIES['TABLE_STATS']] = METRICS['TABLE_STATS']
if is_superuser:
self.queries[QUERIES['ARCHIVE'].format(wal)] = METRICS['ARCHIVE']
if self.server_version >= 90400:
self.queries[QUERIES['WAL'].format(wal, lsn)] = METRICS['WAL']
if self.server_version >= 100000:
self.queries[QUERIES['REPSLOT_FILES']] = METRICS['REPSLOT_FILES']
if self.server_version >= 90400:
self.queries[QUERIES['AUTOVACUUM']] = METRICS['AUTOVACUUM']
def create_dynamic_charts_(self):
for database_name in self.databases[::-1]:
self.definitions['database_size']['lines'].append(
[database_name + '_size', database_name, 'absolute', 1, 1024 * 1024])
for chart_name in [name for name in self.order if name.startswith('db_stat')]:
add_database_stat_chart_(order=self.order, definitions=self.definitions,
name=chart_name, database_name=database_name)
add_database_lock_chart_(order=self.order, definitions=self.definitions, database_name=database_name)
for application_name in self.secondaries[::-1]:
add_replication_delta_chart_(
order=self.order,
definitions=self.definitions,
name='standby_delta',
application_name=application_name)
for slot_name in self.replication_slots[::-1]:
add_replication_slot_chart_(
order=self.order,
definitions=self.definitions,
name='replication_slot',
slot_name=slot_name)
def _get_data(self):
result, _ = self._connect()
if result:
cursor = self.connection.cursor(cursor_factory=DictCursor)
try:
self.data.update(self.locks_zeroed)
for query, metrics in self.queries.items():
self.query_stats_(cursor, query, metrics)
except OperationalError:
self.connection = False
cursor.close()
return None
else:
cursor.close()
return self.data
else:
return None
def query_stats_(self, cursor, query, metrics):
cursor.execute(query, dict(databases=tuple(self.databases)))
for row in cursor:
for metric in metrics:
if 'database_name' in row:
dimension_id = '_'.join([row['database_name'], metric])
elif 'application_name' in row:
dimension_id = '_'.join([row['application_name'], metric])
elif 'slot_name' in row:
dimension_id = '_'.join([row['slot_name'], metric])
else:
dimension_id = metric
if metric in row:
if row[metric] is not None:
self.data[dimension_id] = int(row[metric])
elif 'locks_count' in row:
if metric == row['mode']:
self.data[dimension_id] = row['locks_count']
def discover_databases_(cursor, query):
cursor.execute(query)
result = list()
for db in [database[0] for database in cursor]:
if db not in result:
result.append(db)
return result
def discover_secondaries_(cursor, query):
cursor.execute(query)
result = list()
for sc in [standby[0] for standby in cursor]:
if sc not in result:
result.append(sc)
return result
def discover_replication_slots_(cursor, query):
cursor.execute(query)
result = list()
for slot in [replication_slot[0] for replication_slot in cursor]:
if slot not in result:
result.append(slot)
return result
def check_if_superuser_(cursor, query):
cursor.execute(query)
return cursor.fetchone()[0]
def detect_server_version(cursor, query):
cursor.execute(query)
return int(cursor.fetchone()[0])
def populate_lock_types(databases):
result = dict()
for database in databases:
for lock_type in METRICS['LOCKS']:
key = '_'.join([database, lock_type])
result[key] = 0
return result
def add_database_lock_chart_(order, definitions, database_name):
def create_lines(database):
result = list()
for lock_type in METRICS['LOCKS']:
dimension_id = '_'.join([database, lock_type])
result.append([dimension_id, lock_type, 'absolute'])
return result
chart_name = database_name + '_locks'
order.insert(-1, chart_name)
definitions[chart_name] = {
'options':
[None, 'Locks on db: ' + database_name, 'locks', 'db ' + database_name, 'postgres.db_locks', 'line'],
'lines': create_lines(database_name)
}
def add_database_stat_chart_(order, definitions, name, database_name):
def create_lines(database, lines):
result = list()
for line in lines:
new_line = ['_'.join([database, line[0]])] + line[1:]
result.append(new_line)
return result
chart_template = CHARTS[name]
chart_name = '_'.join([database_name, name])
order.insert(0, chart_name)
name, title, units, _, context, chart_type = chart_template['options']
definitions[chart_name] = {
'options': [name, title + ': ' + database_name, units, 'db ' + database_name, context, chart_type],
'lines': create_lines(database_name, chart_template['lines'])}
def add_replication_delta_chart_(order, definitions, name, application_name):
def create_lines(standby, lines):
result = list()
for line in lines:
new_line = ['_'.join([standby, line[0]])] + line[1:]
result.append(new_line)
return result
chart_template = CHARTS[name]
chart_name = '_'.join([application_name, name])
position = order.index('database_size')
order.insert(position, chart_name)
name, title, units, family, context, chart_type = chart_template['options']
definitions[chart_name] = {
'options': [name, title + ': ' + application_name, units, 'replication delta', context, chart_type],
'lines': create_lines(application_name, chart_template['lines'])}
def add_replication_slot_chart_(order, definitions, name, slot_name):
def create_lines(slot, lines):
result = list()
for line in lines:
new_line = ['_'.join([slot, line[0]])] + line[1:]
result.append(new_line)
return result
chart_template = CHARTS[name]
chart_name = '_'.join([slot_name, name])
position = order.index('database_size')
order.insert(position, chart_name)
name, title, units, family, context, chart_type = chart_template['options']
definitions[chart_name] = {
'options': [name, title + ': ' + slot_name, units, 'replication slot files', context, chart_type],
'lines': create_lines(slot_name, chart_template['lines'])}