-#!/usr/bin/python3 -u
+#!/usr/bin/env python3
import os
import sys
import time
+import json
try:
assert sys.version_info >= (3,1)
- import configparser
import importlib.machinery
except AssertionError:
sys.stderr.write('Not supported python version. Needed python >= 3.1\n')
modules_configs='../conf.d/',
modules_disabled=[]):
self.first_run = True
- self.default_priority = 90000
# set configuration directory
self.configs = modules_configs
# load configuration files
self._load_configs()
+ # good economy and prosperity:
self.jobs = self._create_jobs(self.modules)
-
- # set timetable dict (last execution, next execution and frequency)
- # set priorities
- self.timetable = {}
- freq = 1
- for job in self.jobs:
- try:
- job.priority = int(job.priority)
- except (AttributeError,ValueError):
- job.priority = self.default_priority
-
- if interval is None:
- try:
- freq = int(job.update_every)
- except (AttributeError, ValueError):
- freq = 1
-
- now = time.time()
- self.timetable[job.__name__] = {'last' : now,
- 'next' : now - (now % freq) + freq,
- 'freq' : freq}
def _create_jobs(self,modules):
- # modules are a list of modules to load
- # module store a definition of Service class
- # module store configuration in module.config
- # configs are list of dicts or a dict
- # one dict is one service
- # iterate over list of modules and inside one loop iterate over configs
+ # module store a definition of Service class
+ # module store configuration in module.config
+ # configs are list of dicts or a dict
+ # one dict is one service
+ # iterate over list of modules and inside one loop iterate over configs
jobs = []
for module in modules:
if type(module.config) is dict:
def _job(self,module,conf):
- update = 0
- prio = 0
- retries = 0
- try:
- update = int(conf['update_every'])
- except (KeyError,ValueError):
- update = module.update_every
- try:
- priority = int(conf['priority'])
- except (KeyError,ValueError):
- priority = module.priority
- try:
- retries = int(conf['retries'])
- except (KeyError,ValueError):
- retries = module.retries
+ # register a new job
+ # default job settings:
+ settings = {'update_every' : 11,
+ 'priority' : 12345,
+ 'retries' : 0}
+ # ensure job has basic variables
+ # first check in job config
+ # next check in module
+ # at last assign defaults
+ for key in settings:
+ try:
+ settings[key] = int(conf[key])
+ except (KeyError,ValueError):
+ try:
+ settings[key] = int(getattr(module, key))
+ except (AttributeError,ValueError):
+ pass
try:
- return module.Service(configuration=conf,
- update_every=update,
- priority=prio,
- retries=retries)
+ job = module.Service(configuration=conf)
except Exception as e:
- print(e)
- #TODO print some error
+ sys.stderr.write(module.__name__ +
+ ": Couldn't start one of jobs " +
+ str(e))
return None
+ else:
+ # set execution_name (needed to plot run time graphs)
+ try:
+ job.execution_name = module.__name__ + \
+ "_" + \
+ str(conf['name'])
+ except KeyError:
+ job.execution_name = module.__name__
-
- def _import_plugin(self, path, name=None):
+ def _import_module(self, path, name=None):
# try to import module using only its path
if name is None:
name = path.split('/')[-1]
loaded = []
if len(modules) > 0:
for m in modules:
- mod = self._import_plugin(path + m + ".chart.py")
+ mod = self._import_module(path + m + ".chart.py")
if mod is not None:
loaded.append(mod)
else:
# scan directory specified in path and load all modules from there
names = os.listdir(path)
for mod in names:
- m = self._import_plugin(path + mod)
+ m = self._import_module(path + mod)
if m is not None:
debug("loading chart: '" + path + mod + "'")
loaded.append(m)
def _load_configs(self):
# function modifies every loaded module in self.modules
- # TODO ensure module has update_every, priority and retries variables
for m in self.modules:
configfile = self.configs + m.__name__ + ".conf"
if os.path.isfile(configfile):
configfile +
"' not found. Using defaults.")
- def _disable_module(self, mod, reason=None):
- # modifies self.modules
- self.modules.remove(mod)
- del self.timetable[mod.__name__]
+ def _stop(self, job, reason=None): #FIXME test if Service has __name__
+ # modifies self.jobs
+ self.jobs.remove(job)
if reason is None:
return
elif reason[:3] == "no ":
debug("chart '" +
- mod.__name__,
+ job.__name__,
"' does not seem to have " +
reason[3:] +
"() function. Disabling it.")
elif reason[:7] == "failed ":
debug("chart '" +
- mod.__name__ + "' " +
+ job.__name__ + "' " +
reason[7:] +
"() function reports failure.")
elif reason[:13] == "configuration":
- debug(mod.__name__,
+ debug(job.__name__,
"configuration file '" +
self.configs +
- mod.__name__ +
+ job.__name__ +
".conf' not found. Using defaults.")
elif reason[:11] == "misbehaving":
- debug(mod.__name__, "is "+reason)
+ debug(job.__name__, "is "+reason)
def check(self):
- # try to execute check() on every loaded module
- for mod in self.modules:
+ # try to execute check() on every job
+ for job in self.jobs:
try:
- if not mod.check():
- self._disable_module(mod, "failed check")
+ if not job.check():
+ self._stop(job, "failed check")
except AttributeError:
- self._disable_module(mod, "no check")
+ self._stop(job, "no check")
except (UnboundLocalError, Exception) as e:
- self._disable_module(mod, "misbehaving. Reason: " + str(e))
+ self._stop(job, "misbehaving. Reason: " + str(e))
def create(self):
- # try to execute create() on every loaded module
- for mod in self.modules:
+ # try to execute create() on every job
+ for job in self.jobs:
try:
- if not mod.create():
- self._disable_module(mod, "failed create")
+ if not job.create():
+ self._stop(job, "failed create")
else:
- chart = mod.__name__
+ chart = job.execution_name
sys.stdout.write(
"CHART netdata.plugin_pythond_" +
chart +
" '' 'Execution time for " +
chart +
" plugin' 'milliseconds / run' python.d netdata.plugin_python area 145000 " +
- str(self.timetable[mod.__name__]['freq']) +
+ str(job.timetable['freq']) +
'\n')
sys.stdout.write("DIMENSION run_time 'run time' absolute 1 1\n\n")
sys.stdout.flush()
except AttributeError:
- self._disable_module(mod, "no create")
+ self._stop(job, "no create")
except (UnboundLocalError, Exception) as e:
- self._disable_module(mod, "misbehaving. Reason: " + str(e))
+ self._stop(job, "misbehaving. Reason: " + str(e))
- def _update_module(self, mod):
- # try to execute update() on every module and draw run time graph
+ def _update_job(self, job):
+ # try to execute update() on every job and draw run time graph
t_start = time.time()
- # check if it is time to execute module update() function
- if self.timetable[mod.__name__]['next'] > t_start:
+ # check if it is time to execute job update() function
+ if job.timetable['next'] > t_start:
return
try:
if self.first_run:
since_last = 0
else:
- since_last = int((t_start - self.timetable[mod.__name__]['last']) * 1000000)
- if not mod.update(since_last):
- self._disable_module(mod, "update failed")
+ since_last = int((t_start - job.timetable['last']) * 1000000)
+ if not job.update(since_last):
+ self._stop(job, "update failed")
return
except AttributeError:
- self._disable_module(mod, "no update")
+ self._stop(job, "no update")
return
except (UnboundLocalError, Exception) as e:
- self._disable_module(mod, "misbehaving. Reason: " + str(e))
+ self._stop(job, "misbehaving. Reason: " + str(e))
return
t_end = time.time()
- self.timetable[mod.__name__]['next'] = t_end - (t_end % self.timetable[mod.__name__]['freq']) + self.timetable[mod.__name__]['freq']
+ job.timetable['next'] = t_end - (t_end % job.timetable['freq']) + job.timetable['freq']
# draw performance graph
if self.first_run:
dt = 0
else:
- dt = int((t_end - self.timetable[mod.__name__]['last']) * 1000000)
- sys.stdout.write("BEGIN netdata.plugin_pythond_"+mod.__name__+" "+str(since_last)+'\n')
+ dt = int((t_end - job.timetable['last']) * 1000000)
+ sys.stdout.write("BEGIN netdata.plugin_pythond_"+job.execution_name+" "+str(since_last)+'\n')
sys.stdout.write("SET run_time = " + str(int((t_end - t_start) * 1000)) + '\n')
sys.stdout.write("END\n")
sys.stdout.flush()
- self.timetable[mod.__name__]['last'] = t_start
+ job.timetable['last'] = t_start
self.first_run = False
def update(self):
while True:
t_begin = time.time()
next_runs = []
- for mod in self.modules:
- self._update_module(mod)
+ for job in self.jobs:
+ self._update_job(job)
try:
- next_runs.append(self.timetable[mod.__name__]['next'])
+ next_runs.append(job.timetable['next'])
except KeyError:
pass
if len(next_runs) == 0:
def read_config(path):
- config = configparser.ConfigParser()
- config_str = ""
- try:
- with open(path, 'r', encoding="utf_8") as f:
- config_str = '[config]\n' + f.read()
- except IsADirectoryError:
- debug(str(path), "is a directory")
- return
- try:
- config.read_string(config_str)
- except configparser.ParsingError as e:
- debug("Malformed configuration file: "+str(e))
- return
- return dict(config.items('config'))
+ # FIXME move to JSON configurations
+# try:
+# with open(path, 'r') as f:
+# config_str = '[config]\n' + f.read()
+# except IsADirectoryError:
+# debug(str(path), "is a directory")
+# return
+# try:
+# debug("Malformed configuration file: "+str(e))
+# return
+ return None
def debug(*args):
# Description: base for netdata python.d plugins
# Author: Pawel Krupa (paulfantom)
+from time import time
+
+
class BaseService(object):
- def __init__(self,configuration=None,update_every=None,priority=None,retries=None):
- if None in (configuration,update_every,priority,retries):
+ def __init__(self,configuration=None):
+ if configuration is None:
# use defaults
self.error("BaseService: no configuration parameters supplied. Cannot create Service.")
raise RuntimeError
else:
- self._parse_base_config(configuration,update_every,priority,retries)
-
- def _parse_base_config(self,config,update_every,priority,retries):
- # parse configuration options to run this Service
- try:
- self.update_every = int(config['update_every'])
- except (KeyError, ValueError):
- self.update_every = update_every
- try:
- self.priority = int(config['priority'])
- except (KeyError, ValueError):
- self.priority = priority
- try:
- self.retries = int(config['retries'])
- except (KeyError, ValueError):
- self.retries = retries
+ self._extract_base_config(configuration)
+ self._create_timetable()
+ self.execution_name = ""
+
+ def _extract_base_config(self,config):
+ self.update_every = int(config['update_every'])
+ self.priority = int(config['priority'])
+ self.retries = int(config['retries'])
self.retries_left = self.retries
+ def _create_timetable(self):
+ now = time()
+ self.timetable = {'last' : now,
+ 'next' : now - (now % self.update_every) + self.update_every,
+ 'freq' : self.update_every}
+
+
def error(self, msg, exception=""):
if exception != "":
exception = " " + str(exception).replace("\n"," ")