summaryrefslogtreecommitdiffstats
path: root/python.d
diff options
context:
space:
mode:
authorTom Worster <fsb@thefsb.org>2017-11-07 13:23:43 -0500
committerTom Worster <fsb@thefsb.org>2017-11-07 13:23:43 -0500
commit2e28f0e11bf9fe5697caf4faf6f3732fd8e13a44 (patch)
treea383d7139b653daf325397cf5ddc085afe954b78 /python.d
parent48d4cbf54d4fcf112295ddd35b31cad67ca47626 (diff)
parent5609e6dc1b13d2ddd0f26315cdf1a6867a652555 (diff)
Merge branch 'master' of github.com:firehol/netdata
Diffstat (limited to 'python.d')
-rw-r--r--python.d/Makefile.am1
-rw-r--r--python.d/README.md107
-rw-r--r--python.d/beanstalk.chart.py250
-rw-r--r--python.d/bind_rndc.chart.py2
-rw-r--r--python.d/cpufreq.chart.py2
-rw-r--r--python.d/cpuidle.chart.py2
-rw-r--r--python.d/example.chart.py18
-rw-r--r--python.d/freeradius.chart.py2
-rw-r--r--python.d/memcached.chart.py1
-rw-r--r--python.d/mysql.chart.py2
-rw-r--r--python.d/postgres.chart.py4
-rw-r--r--python.d/python_modules/bases/FrameworkServices/SimpleService.py69
-rw-r--r--python.d/python_modules/bases/charts.py136
-rw-r--r--python.d/python_modules/bases/collection.py6
-rw-r--r--python.d/redis.chart.py1
-rw-r--r--python.d/varnish.chart.py199
-rw-r--r--python.d/web_log.chart.py26
17 files changed, 603 insertions, 225 deletions
diff --git a/python.d/Makefile.am b/python.d/Makefile.am
index 2275643366..6eb35c7aee 100644
--- a/python.d/Makefile.am
+++ b/python.d/Makefile.am
@@ -14,6 +14,7 @@ dist_python_SCRIPTS = \
dist_python_DATA = \
README.md \
apache.chart.py \
+ beanstalk.chart.py \
bind_rndc.chart.py \
chrony.chart.py \
couchdb.chart.py \
diff --git a/python.d/README.md b/python.d/README.md
index fe85ec4299..45fede537b 100644
--- a/python.d/README.md
+++ b/python.d/README.md
@@ -125,13 +125,118 @@ If no configuration is given, module will attempt to read log file at `/var/log/
---
+# beanstalk
+
+Module provides server and tube level statistics:
+
+**Requirements:**
+ * `python-beanstalkc`
+ * `python-yaml`
+
+**Server statistics:**
+
+1. **Cpu usage** in cpu time
+ * user
+ * system
+
+2. **Jobs rate** in jobs/s
+ * total
+ * timeouts
+
+3. **Connections rate** in connections/s
+ * connections
+
+4. **Commands rate** in commands/s
+ * put
+ * peek
+ * peek-ready
+ * peek-delayed
+ * peek-buried
+ * reserve
+ * use
+ * watch
+ * ignore
+ * delete
+ * release
+ * bury
+ * kick
+ * stats
+ * stats-job
+ * stats-tube
+ * list-tubes
+ * list-tube-used
+ * list-tubes-watched
+ * pause-tube
+
+5. **Current tubes** in tubes
+ * tubes
+
+6. **Current jobs** in jobs
+ * urgent
+ * ready
+ * reserved
+ * delayed
+ * buried
+
+7. **Current connections** in connections
+ * written
+ * producers
+ * workers
+ * waiting
+
+8. **Binlog** in records/s
+ * written
+ * migrated
+
+9. **Uptime** in seconds
+ * uptime
+
+**Per tube statistics:**
+
+1. **Jobs rate** in jobs/s
+ * jobs
+
+2. **Jobs** in jobs
+ * using
+ * ready
+ * reserved
+ * delayed
+ * buried
+
+3. **Connections** in connections
+ * using
+ * waiting
+ * watching
+
+4. **Commands** in commands/s
+ * deletes
+ * pauses
+
+5. **Pause** in seconds
+ * since
+ * left
+
+
+### configuration
+
+Sample:
+
+```yaml
+host : '127.0.0.1'
+port : 11300
+```
+
+If no configuration is given, module will attempt to connect to beanstalkd on `127.0.0.1:11300` address
+
+---
+
# bind_rndc
Module parses bind dump file to collect real-time performance metrics
**Requirements:**
* Version of bind must be 9.6 +
- * Netdata must have permissions to run `rndc status`
+ * Netdata must have permissions to run `rndc stats`
It produces:
diff --git a/python.d/beanstalk.chart.py b/python.d/beanstalk.chart.py
new file mode 100644
index 0000000000..8880afdd98
--- /dev/null
+++ b/python.d/beanstalk.chart.py
@@ -0,0 +1,250 @@
+# -*- coding: utf-8 -*-
+# Description: beanstalk netdata python.d module
+# Author: l2isbad
+
+try:
+ import beanstalkc
+ BEANSTALKC = True
+except ImportError:
+ BEANSTALKC = False
+
+try:
+ import yaml
+ YAML = True
+except ImportError:
+ YAML = False
+
+from bases.FrameworkServices.SimpleService import SimpleService
+
+# default module values (can be overridden per job in `config`)
+# update_every = 2
+priority = 60000
+retries = 60
+
+ORDER = ['cpu_usage', 'jobs_rate', 'connections_rate', 'commands_rate', 'current_tubes', 'current_jobs',
+ 'current_connections', 'binlog', 'uptime']
+
+CHARTS = {
+ 'cpu_usage': {
+ 'options': [None, 'Cpu Usage', 'cpu time', 'server statistics', 'beanstalk.cpu_usage', 'area'],
+ 'lines': [
+ ['rusage-utime', 'user', 'incremental'],
+ ['rusage-stime', 'system', 'incremental']
+ ]
+ },
+ 'jobs_rate': {
+ 'options': [None, 'Jobs Rate', 'jobs/s', 'server statistics', 'beanstalk.jobs_rate', 'line'],
+ 'lines': [
+ ['total-jobs', 'total', 'incremental'],
+ ['job-timeouts', 'timeouts', 'incremental']
+ ]
+ },
+ 'connections_rate': {
+ 'options': [None, 'Connections Rate', 'connections/s', 'server statistics', 'beanstalk.connections_rate',
+ 'area'],
+ 'lines': [
+ ['total-connections', 'connections', 'incremental']
+ ]
+ },
+ 'commands_rate': {
+ 'options': [None, 'Commands Rate', 'commands/s', 'server statistics', 'beanstalk.commands_rate', 'stacked'],
+ 'lines': [
+ ['cmd-put', 'put', 'incremental'],
+ ['cmd-peek', 'peek', 'incremental'],
+ ['cmd-peek-ready', 'peek-ready', 'incremental'],
+ ['cmd-peek-delayed', 'peek-delayed', 'incremental'],
+ ['cmd-peek-buried', 'peek-buried', 'incremental'],
+ ['cmd-reserve', 'reserve', 'incremental'],
+ ['cmd-use', 'use', 'incremental'],
+ ['cmd-watch', 'watch', 'incremental'],
+ ['cmd-ignore', 'ignore', 'incremental'],
+ ['cmd-delete', 'delete', 'incremental'],
+ ['cmd-release', 'release', 'incremental'],
+ ['cmd-bury', 'bury', 'incremental'],
+ ['cmd-kick', 'kick', 'incremental'],
+ ['cmd-stats', 'stats', 'incremental'],
+ ['cmd-stats-job', 'stats-job', 'incremental'],
+ ['cmd-stats-tube', 'stats-tube', 'incremental'],
+ ['cmd-list-tubes', 'list-tubes', 'incremental'],
+ ['cmd-list-tube-used', 'list-tube-used', 'incremental'],
+ ['cmd-list-tubes-watched', 'list-tubes-watched', 'incremental'],
+ ['cmd-pause-tube', 'pause-tube', 'incremental']
+ ]
+ },
+ 'current_tubes': {
+ 'options': [None, 'Current Tubes', 'tubes', 'server statistics', 'beanstalk.current_tubes', 'area'],
+ 'lines': [
+ ['current-tubes', 'tubes']
+ ]
+ },
+ 'current_jobs': {
+ 'options': [None, 'Current Jobs', 'jobs', 'server statistics', 'beanstalk.current_jobs', 'stacked'],
+ 'lines': [
+ ['current-jobs-urgent', 'urgent'],
+ ['current-jobs-ready', 'ready'],
+ ['current-jobs-reserved', 'reserved'],
+ ['current-jobs-delayed', 'delayed'],
+ ['current-jobs-buried', 'buried']
+ ]
+ },
+ 'current_connections': {
+ 'options': [None, 'Current Connections', 'connections', 'server statistics',
+ 'beanstalk.current_connections', 'line'],
+ 'lines': [
+ ['current-connections', 'written'],
+ ['current-producers', 'producers'],
+ ['current-workers', 'workers'],
+ ['current-waiting', 'waiting']
+ ]
+ },
+ 'binlog': {
+ 'options': [None, 'Binlog', 'records/s', 'server statistics', 'beanstalk.binlog', 'line'],
+ 'lines': [
+ ['binlog-records-written', 'written', 'incremental'],
+ ['binlog-records-migrated', 'migrated', 'incremental']
+ ]
+ },
+ 'uptime': {
+ 'options': [None, 'Uptime', 'seconds', 'server statistics', 'beanstalk.uptime', 'line'],
+ 'lines': [
+ ['uptime'],
+ ]
+ }
+}
+
+
+def tube_chart_template(name):
+ order = ['{0}_jobs_rate'.format(name),
+ '{0}_jobs'.format(name),
+ '{0}_connections'.format(name),
+ '{0}_commands'.format(name),
+ '{0}_pause'.format(name)
+ ]
+ family = 'tube {0}'.format(name)
+
+ charts = {
+ order[0]: {
+ 'options': [None, 'Job Rate', 'jobs/s', family, 'beanstalk.jobs_rate', 'area'],
+ 'lines': [
+ ['_'.join([name, 'total-jobs']), 'jobs', 'incremental']
+ ]},
+ order[1]: {
+ 'options': [None, 'Jobs', 'jobs', family, 'beanstalk.jobs', 'stacked'],
+ 'lines': [
+ ['_'.join([name, 'current-jobs-urgent']), 'urgent'],
+ ['_'.join([name, 'current-jobs-ready']), 'ready'],
+ ['_'.join([name, 'current-jobs-reserved']), 'reserved'],
+ ['_'.join([name, 'current-jobs-delayed']), 'delayed'],
+ ['_'.join([name, 'current-jobs-buried']), 'buried']
+ ]},
+ order[2]: {
+ 'options': [None, 'Connections', 'connections', family, 'beanstalk.connections', 'stacked'],
+ 'lines': [
+ ['_'.join([name, 'current-using']), 'using'],
+ ['_'.join([name, 'current-waiting']), 'waiting'],
+ ['_'.join([name, 'current-watching']), 'watching']
+ ]},
+ order[3]: {
+ 'options': [None, 'Commands', 'commands/s', family, 'beanstalk.commands', 'stacked'],
+ 'lines': [
+ ['_'.join([name, 'cmd-delete']), 'deletes', 'incremental'],
+ ['_'.join([name, 'cmd-pause-tube']), 'pauses', 'incremental']
+ ]},
+ order[4]: {
+ 'options': [None, 'Pause', 'seconds', family, 'beanstalk.pause', 'stacked'],
+ 'lines': [
+ ['_'.join([name, 'pause']), 'since'],
+ ['_'.join([name, 'pause-time-left']), 'left']
+ ]}
+
+ }
+
+ return order, charts
+
+
+class Service(SimpleService):
+ def __init__(self, configuration=None, name=None):
+ SimpleService.__init__(self, configuration=configuration, name=name)
+ self.configuration = configuration
+ self.order = list(ORDER)
+ self.definitions = dict(CHARTS)
+ self.conn = None
+ self.alive = True
+
+ def check(self):
+ if not BEANSTALKC:
+ self.error("'beanstalkc' module is needed to use beanstalk.chart.py")
+ return False
+
+ if not YAML:
+ self.error("'yaml' module is needed to use beanstalk.chart.py")
+ return False
+
+ self.conn = self.connect()
+
+ return True if self.conn else False
+
+ def get_data(self):
+ """
+ :return: dict
+ """
+ if not self.is_alive():
+ return None
+
+ active_charts = self.charts.active_charts()
+ data = dict()
+
+ try:
+ data.update(self.conn.stats())
+
+ for tube in self.conn.tubes():
+ stats = self.conn.stats_tube(tube)
+
+ if tube + '_jobs_rate' not in active_charts:
+ self.create_new_tube_charts(tube)
+
+ for stat in stats:
+ data['_'.join([tube, stat])] = stats[stat]
+
+ except beanstalkc.SocketError:
+ self.alive = False
+ return None
+
+ return data or None
+
+ def create_new_tube_charts(self, tube):
+ order, charts = tube_chart_template(tube)
+
+ for chart_name in order:
+ params = [chart_name] + charts[chart_name]['options']
+ dimensions = charts[chart_name]['lines']
+
+ new_chart = self.charts.add_chart(params)
+ for dimension in dimensions:
+ new_chart.add_dimension(dimension)
+
+ def connect(self):
+ host = self.configuration.get('host', '127.0.0.1')
+ port = self.configuration.get('port', 11300)
+ timeout = self.configuration.get('timeout', 1)
+ try:
+ return beanstalkc.Connection(host=host,
+ port=port,
+ connect_timeout=timeout,
+ parse_yaml=yaml.load)
+ except beanstalkc.SocketError as error:
+ self.error('Connection to {0}:{1} failed: {2}'.format(host, port, error))
+ return None
+
+ def reconnect(self):
+ try:
+ self.conn.reconnect()
+ self.alive = True
+ return True
+ except beanstalkc.SocketError:
+ return False
+
+ def is_alive(self):
+ if not self.alive:
+ return self.reconnect()
+ return True
diff --git a/python.d/bind_rndc.chart.py b/python.d/bind_rndc.chart.py
index 97a33fc891..cc96659b21 100644
--- a/python.d/bind_rndc.chart.py
+++ b/python.d/bind_rndc.chart.py
@@ -165,7 +165,7 @@ class Service(SimpleService):
if dimension_id not in self.data:
dimension = dimension_id.replace(chart_name[:9], '')
if dimension_id not in self.charts[chart_name]:
- self.charts[chart_name].add_dimension_and_push_chart([dimension_id, dimension, 'incremental'])
+ self.charts[chart_name].add_dimension([dimension_id, dimension, 'incremental'])
self.data[dimension_id] = value
diff --git a/python.d/cpufreq.chart.py b/python.d/cpufreq.chart.py
index 9c5355ad62..3abde736ca 100644
--- a/python.d/cpufreq.chart.py
+++ b/python.d/cpufreq.chart.py
@@ -14,7 +14,7 @@ ORDER = ['cpufreq']
CHARTS = {
'cpufreq': {
- 'options': [None, 'CPU Clock', 'MHz', 'cpufreq', 'cpufreq', 'line'],
+ 'options': [None, 'CPU Clock', 'MHz', 'cpufreq', 'cpufreq.cpufreq', 'line'],
'lines': [
# lines are created dynamically in `check()` method
]}
diff --git a/python.d/cpuidle.chart.py b/python.d/cpuidle.chart.py
index 4f732e6eb1..d14c6aaf3d 100644
--- a/python.d/cpuidle.chart.py
+++ b/python.d/cpuidle.chart.py
@@ -122,7 +122,7 @@ class Service(SimpleService):
self.order.append(orderid)
active_name = '%s_active_time' % (cpu,)
self.definitions[orderid] = {
- 'options': [None, 'C-state residency', 'time%', 'cpuidle', 'cpuidle', 'stacked'],
+ 'options': [None, 'C-state residency', 'time%', 'cpuidle', 'cpuidle.cpuidle', 'stacked'],
'lines': [
[active_name, 'C0 (active)', 'percentage-of-incremental-row', 1, 1],
],
diff --git a/python.d/example.chart.py b/python.d/example.chart.py
index f7e4329cb2..ee7ff62fc6 100644
--- a/python.d/example.chart.py
+++ b/python.d/example.chart.py
@@ -2,7 +2,7 @@
# Description: example netdata python.d module
# Author: Pawel Krupa (paulfantom)
-import random
+from random import SystemRandom
from bases.FrameworkServices.SimpleService import SimpleService
@@ -24,22 +24,24 @@ CHARTS = {
class Service(SimpleService):
def __init__(self, configuration=None, name=None):
- super(self.__class__, self).__init__(configuration=configuration, name=name)
+ SimpleService.__init__(self, configuration=configuration, name=name)
self.order = ORDER
self.definitions = CHARTS
- self.data = dict()
+ self.random = SystemRandom()
@staticmethod
def check():
return True
def get_data(self):
- dimension = ''.join(['random', str(random.randint(1, 3))])
+ data = dict()
- if dimension not in self.charts['random']:
- self.charts['random'].add_dimension_and_push_chart([dimension])
+ for i in range(1, 4):
+ dimension_id = ''.join(['random', str(i)])
- self.data[dimension] = random.randint(0, 100)
+ if dimension_id not in self.charts['random']:
+ self.charts['random'].add_dimension([dimension_id])
- return self.data
+ data[dimension_id] = self.random.randint(0, 100)
+ return data
diff --git a/python.d/freeradius.chart.py b/python.d/freeradius.chart.py
index 92818e818f..251582d4f2 100644
--- a/python.d/freeradius.chart.py
+++ b/python.d/freeradius.chart.py
@@ -84,7 +84,7 @@ class Service(SimpleService):
self.sub_echo = [self.echo, RADIUS_MSG]
self.sub_radclient = [self.radclient, '-r', '1', '-t', '1',
':'.join([self.host, self.port]), 'status', self.secret]
-
+
def check(self):
if not all([self.echo, self.radclient]):
self.error('Can\'t locate "radclient" binary or binary is not executable by netdata')
diff --git a/python.d/memcached.chart.py b/python.d/memcached.chart.py
index 59ff154075..4f7adfa233 100644
--- a/python.d/memcached.chart.py
+++ b/python.d/memcached.chart.py
@@ -149,7 +149,6 @@ class Service(SocketService):
data[t[0]] = t[1]
except (IndexError, ValueError):
self.debug("invalid line received: " + str(line))
- pass
if not data:
self.error("received data doesn't have any records")
diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py
index 1f35b4f579..9f323d0cdd 100644
--- a/python.d/mysql.chart.py
+++ b/python.d/mysql.chart.py
@@ -6,7 +6,7 @@ from bases.FrameworkServices.MySQLService import MySQLService
# default module values (can be overridden per job in `config`)
# update_every = 3
-priority = 90000
+priority = 60000
retries = 60
# query executed on MySQL server
diff --git a/python.d/postgres.chart.py b/python.d/postgres.chart.py
index 3241e6e81e..ef69a9c77e 100644
--- a/python.d/postgres.chart.py
+++ b/python.d/postgres.chart.py
@@ -17,7 +17,7 @@ from bases.FrameworkServices.SimpleService import SimpleService
# default module values
update_every = 1
-priority = 90000
+priority = 60000
retries = 60
METRICS = dict(
@@ -225,7 +225,7 @@ CHARTS = {
class Service(SimpleService):
def __init__(self, configuration=None, name=None):
- super(self.__class__, self).__init__(configuration=configuration, name=name)
+ SimpleService.__init__(self, configuration=configuration, name=name)
self.order = ORDER[:]
self.definitions = deepcopy(CHARTS)
self.table_stats = configuration.pop('table_stats', False)
diff --git a/python.d/python_modules/bases/FrameworkServices/SimpleService.py b/python.d/python_modules/bases/FrameworkServices/SimpleService.py
index 65c791f981..14c8391013 100644
--- a/python.d/python_modules/bases/FrameworkServices/SimpleService.py
+++ b/python.d/python_modules/bases/FrameworkServices/SimpleService.py
@@ -14,8 +14,6 @@ from bases.charts import Charts, ChartError, create_runtime_chart
from bases.collection import OldVersionCompatibility, safe_print
from bases.loggers import PythonDLimitedLogger
-CHART_OBSOLETE_PENALTY = 10
-
RUNTIME_CHART_UPDATE = 'BEGIN netdata.runtime_{job_name} {since_last}\n' \
'SET run_time = {elapsed}\n' \
'END\n'
@@ -66,7 +64,9 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
self._runtime_counters = RuntimeCounters(configuration=configuration)
self.charts = Charts(job_name=self.actual_name,
priority=configuration.pop('priority'),
- get_update_every=self.get_update_every)
+ cleanup=configuration.pop('chart_cleanup'),
+ get_update_every=self.get_update_every,
+ module_name=self.module_name)
def __repr__(self):
return '<{cls_bases}: {name}>'.format(cls_bases=', '.join(c.__name__ for c in self.__class__.__bases__),
@@ -149,10 +149,6 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
del self.order
del self.definitions
- # push charts to netdata
- for chart in self.charts:
- safe_print(chart.create())
-
# True if job has at least 1 chart else False
return bool(self.charts)
@@ -209,55 +205,32 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
self.debug('get_data() returned incorrect type data')
return False
- charts_updated = False
-
- for chart in self.charts.penalty_exceeded(penalty_max=CHART_OBSOLETE_PENALTY):
- safe_print(chart.obsolete())
- chart.suppress()
- self.error("chart '{0}' was removed due to non updating".format(chart.name))
+ updated = False
for chart in self.charts:
- if not chart.alive:
+ if chart.flags.obsoleted:
+ continue
+ elif self.charts.cleanup and chart.penalty >= self.charts.cleanup:
+ chart.obsolete()
+ self.error("chart '{0}' was suppressed due to non updating".format(chart.name))
continue
- dimension_updated, variables_updated = str(), str()
-
- for dimension in chart:
- try:
- value = int(data[dimension.id])
- except (KeyError, TypeError):
- continue
- else:
- dimension_updated += dimension.set(value)
- for var in chart.variables:
- try:
- value = int(data[var.id])
- except (KeyError, TypeError):
- continue
- else:
- variables_updated += var.set(value)
-
- if dimension_updated:
- charts_updated = True
- safe_print(''.join([chart.begin(since_last=interval),
- dimension_updated,
- variables_updated,
- 'END\n']))
- else:
- chart.penalty += 1
+ ok = chart.update(data, interval)
+ if ok:
+ updated = True
- if not charts_updated:
- self.debug('none of the charts have been updated')
+ if not updated:
+ self.debug('none of the charts has been updated')
- return charts_updated
+ return updated
def manage_retries(self):
- self._runtime_counters.RETRIES += 1
- if self._runtime_counters.RETRIES % 5 == 0:
- self._runtime_counters.PENALTY = int(self._runtime_counters.RETRIES * self.update_every / 2)
- if self._runtime_counters.RETRIES >= self._runtime_counters.RETRIES_MAX:
- self.error('stopped after {retries_max} data '
- 'collection failures in a row'.format(retries_max=self._runtime_counters.RETRIES_MAX))
+ rc = self._runtime_counters
+ rc.RETRIES += 1
+ if rc.RETRIES % 5 == 0:
+ rc.PENALTY = int(rc.RETRIES * self.update_every / 2)
+ if rc.RETRIES >= rc.RETRIES_MAX:
+ self.error('stopped after {0} data collection failures in a row'.format(rc.RETRIES_MAX))
return False
return True
diff --git a/python.d/python_modules/bases/charts.py b/python.d/python_modules/bases/charts.py
index 731cebdfaf..f6296adc52 100644
--- a/python.d/python_modules/bases/charts.py
+++ b/python.d/python_modules/bases/charts.py
@@ -13,7 +13,7 @@ DIMENSION_ALGORITHMS = ['absolute', 'incremental', 'percentage-of-absolute-row',
CHART_BEGIN = 'BEGIN {type}.{id} {since_last}\n'
CHART_CREATE = "CHART {type}.{id} '{name}' '{title}' '{units}' '{family}' '{context}' " \
- "{chart_type} {priority} {update_every}\n"
+ "{chart_type} {priority} {update_every} '' 'python.d.plugin' '{module_name}'\n"
CHART_OBSOLETE = "CHART {type}.{id} '{name}' '{title}' '{units}' '{family}' '{context}' " \
"{chart_type} {priority} {update_every} 'obsolete'\n"
@@ -71,7 +71,7 @@ class Charts:
All charts stored in a dict.
Chart is a instance of Chart class.
Charts adding must be done using Charts.add_chart() method only"""
- def __init__(self, job_name, priority, get_update_every):
+ def __init__(self, job_name, priority, cleanup, get_update_every, module_name):
"""
:param job_name: <bound method>
:param priority: <int>
@@ -79,7 +79,9 @@ class Charts:
"""
self.job_name = job_name
self.priority = priority
+ self.cleanup = cleanup
self.get_update_every = get_update_every
+ self.module_name = module_name
self.charts = dict()
def __len__(self):
@@ -109,13 +111,6 @@ class Charts:
def __nonzero__(self):
return self.__bool__()
- def penalty_exceeded(self, penalty_max):
- """
- :param penalty_max: <int>
- :return:
- """
- return (chart for chart in self if chart.penalty > penalty_max and chart.alive)
-
def add_chart(self, params):
"""
Create Chart instance and add it to the dict
@@ -125,16 +120,19 @@ class Charts:
:return:
"""
params = [self.job_name()] + params
- chart_id = params[1]
- if chart_id in self.charts:
- raise DuplicateItemError("'{chart}' already in charts".format(chart=chart_id))
- else:
- new_chart = Chart(params)
- new_chart.params['update_every'] = self.get_update_every()
- new_chart.params['priority'] = self.priority
- self.priority += 1
- self.charts[new_chart.id] = new_chart
- return new_chart
+ new_chart = Chart(params)
+
+ new_chart.params['update_every'] = self.get_update_every()
+ new_chart.params['priority'] = self.priority
+ new_chart.params['module_name'] = self.module_name
+
+ self.priority += 1
+ self.charts[new_chart.id] = new_chart
+
+ return new_chart
+
+ def active_charts(self):
+ return [chart.id for chart in self if not chart.flags.obsoleted]
class Chart:
@@ -156,7 +154,7 @@ class Chart:
self.dimensions = list()
self.variables = set()
- self.alive = True
+ self.flags = ChartFlags()
self.penalty = 0
def __getattr__(self, item):
@@ -178,13 +176,6 @@ class Chart:
def __contains__(self, item):
return item in [dimension.id for dimension in self.dimensions]
- def suppress(self):
- self.alive = False
-
- def unsuppress(self):
- self.penalty = 0
- self.alive = True
-
def add_variable(self, variable):
"""
:param variable: <list>
@@ -202,43 +193,62 @@ class Chart:
if dim.id in self:
raise DuplicateItemError("'{dimension}' already in '{chart}' dimensions".format(dimension=dim.id,
chart=self.name))
+ self.refresh()
self.dimensions.append(dim)
return dim
- def add_dimension_and_push_chart(self, dimension):
- """
- :param dimension: <list>
- :return:
- """
- dim = self.add_dimension(dimension)
- self.unsuppress()
- safe_print(self.create(dim))
-
- def create(self, dimension=None):
+ def create(self):
"""
- :param dimension: Dimension
:return:
"""
chart = CHART_CREATE.format(**self.params)
- if not dimension:
- dimensions = ''.join([dimension.create() for dimension in self.dimensions])
- variables = ''.join([var.set(var.value) for var in self.variables if var])
- return chart + dimensions + variables
+ dimensions = ''.join([dimension.create() for dimension in self.dimensions])
+ variables = ''.join([var.set(var.value) for var in self.variables if var])
+
+ self.flags.push = False
+ self.flags.created = True
+
+ safe_print(chart + dimensions + variables)
+
+ def update(self, data, interval):
+ updated_dimensions, updated_variables = str(), str()
+
+ for dim in self.dimensions:
+ value = dim.get_value(data)
+ if value is not None:
+ updated_dimensions += dim.set(value)
+
+ for var in self.variables:
+ value = var.get_value(data)
+ if value is not None:
+ updated_variables += var.set(value)
+
+ if updated_dimensions:
+ since_last = interval if self.flags.updated else 0
+
+ if self.flags.push:
+ self.create()
+
+ chart_begin = CHART_BEGIN.format(type=self.type, id=self.id, since_last=since_last)
+ safe_print(chart_begin, updated_dimensions, updated_variables, 'END\n')
+
+ self.flags.updated = True
+ self.penalty = 0
else:
- dimensions = dimension.create()
- return chart + dimensions
+ self.penalty += 1
+ self.flags.updated = False
- def begin(self, since_last):
- """
- :param since_last: <int>: microseconds
- :return:
- """
- return CHART_BEGIN.format(type=self.type,
- id=self.id,
- since_last=since_last)
+ return bool(updated_dimensions)