-#!/usr/bin/python3 -u
+#!/usr/bin/env bash
+'''':; exec "$(command -v python || command -v python3 || command -v python2 || echo "ERROR python IS NOT AVAILABLE IN THIS SYSTEM")" "$0" "$@" # '''
+# -*- coding: utf-8 -*-
+
+# Description: netdata python modules supervisor
+# Author: Pawel Krupa (paulfantom)
import os
import sys
import time
-import importlib.machinery
-import configparser
+import threading
+from re import sub
+
+# -----------------------------------------------------------------------------
+# globals & environment setup
+# https://github.com/firehol/netdata/wiki/External-Plugins#environment-variables
+MODULE_EXTENSION = ".chart.py"
+BASE_CONFIG = {'update_every': os.getenv('NETDATA_UPDATE_EVERY', 1),
+ 'priority': 90000,
+ 'retries': 10}
+
+MODULES_DIR = os.path.abspath(os.getenv('NETDATA_PLUGINS_DIR',
+ os.path.dirname(__file__)) + "/../python.d") + "/"
+CONFIG_DIR = os.getenv('NETDATA_CONFIG_DIR', "/etc/netdata/")
+# directories should end with '/'
+if CONFIG_DIR[-1] != "/":
+ CONFIG_DIR += "/"
+sys.path.append(MODULES_DIR + "python_modules")
+
+PROGRAM = os.path.basename(__file__).replace(".plugin", "")
+DEBUG_FLAG = False
+TRACE_FLAG = False
+OVERRIDE_UPDATE_EVERY = False
+
+# -----------------------------------------------------------------------------
+# custom, third party and version specific python modules management
+import msg
+
+try:
+ assert sys.version_info >= (3, 1)
+ import importlib.machinery
+ PY_VERSION = 3
+ # change this hack below if we want PY_VERSION to be used in modules
+ # import builtins
+ # builtins.PY_VERSION = 3
+ msg.info('Using python v3')
+except (AssertionError, ImportError):
+ try:
+ import imp
+
+ # change this hack below if we want PY_VERSION to be used in modules
+ # import __builtin__
+ # __builtin__.PY_VERSION = 2
+ PY_VERSION = 2
+ msg.info('Using python v2')
+ except ImportError:
+ msg.fatal('Cannot start. No importlib.machinery on python3 or lack of imp on python2')
+# try:
+# import yaml
+# except ImportError:
+# msg.fatal('Cannot find yaml library')
+try:
+ if PY_VERSION == 3:
+ import pyyaml3 as yaml
+ else:
+ import pyyaml2 as yaml
+except ImportError:
+ msg.fatal('Cannot find yaml library')
+
+try:
+ from collections import OrderedDict
+ ORDERED = True
+ DICT = OrderedDict
+ msg.info('YAML output is ordered')
+except ImportError:
+ try:
+ from ordereddict import OrderedDict
+ ORDERED = True
+ DICT = OrderedDict
+ msg.info('YAML output is ordered')
+ except ImportError:
+ ORDERED = False
+ DICT = dict
+ msg.info('YAML output is unordered')
+if ORDERED:
+ def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
+ class OrderedLoader(Loader):
+ pass
+
+ def construct_mapping(loader, node):
+ loader.flatten_mapping(node)
+ return object_pairs_hook(loader.construct_pairs(node))
+ OrderedLoader.add_constructor(
+ yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
+ construct_mapping)
+ return yaml.load(stream, OrderedLoader)
+
class PythonCharts(object):
- def __init__(self,interval=1,modules=[],modules_path='../python.d/',modules_configs='../conf.d/'):
- self.interval = interval
- # check if plugin directory exists
- if not os.path.isdir(modules_path):
- debug("cannot find charts directory ",modules_path)
- sys.exit(1)
- self.configs=modules_configs
-
- self.modules = []
- if len(modules) > 0:
- for m in modules:
- self.modules.append(self.import_plugin(modules_path+m+".chart.py"))
- else:
- self.load_modules(modules_path)
- if len(self.modules) == 0:
- debug("cannot find charts directory",modules_path)
-# sys.stdout.write("DISABLE")
- sys.exit(1)
+ """
+ Main class used to control every python module.
+ """
+
+ def __init__(self,
+ modules=None,
+ modules_path='../python.d/',
+ modules_configs='../conf.d/',
+ modules_disabled=None,
+ modules_enabled=None,
+ default_run=None):
+ """
+ :param modules: list
+ :param modules_path: str
+ :param modules_configs: str
+ :param modules_disabled: list
+ :param modules_enabled: list
+ :param default_run: bool
+ """
+
+ if modules is None:
+ modules = []
+ if modules_disabled is None:
+ modules_disabled = []
- self.load_configs()
+ self.first_run = True
+ # set configuration directory
+ self.configs = modules_configs
- # set last execution dict
- self.timetable = {}
- t = time.time()
- for m in self.modules:
- self.timetable[m.__name__] = t
+ # load modules
+ loaded_modules = self._load_modules(modules_path, modules, modules_disabled, modules_enabled, default_run)
+
+ # load configuration files
+ configured_modules = self._load_configs(loaded_modules)
+
+ # good economy and prosperity:
+ self.jobs = self._create_jobs(configured_modules) # type <list>
+
+ # enable timetable override like `python.d.plugin mysql debug 1`
+ if DEBUG_FLAG and OVERRIDE_UPDATE_EVERY:
+ for job in self.jobs:
+ job.create_timetable(BASE_CONFIG['update_every'])
+
+ @staticmethod
+ def _import_module(path, name=None):
+ """
+ Try to import module using only its path.
+ :param path: str
+ :param name: str
+ :return: object
+ """
- def import_plugin(self,path,name=None):
if name is None:
name = path.split('/')[-1]
- if name[-9:] != ".chart.py":
- return None
- name=name[:-9]
+ if name[-len(MODULE_EXTENSION):] != MODULE_EXTENSION:
+ return None
+ name = name[:-len(MODULE_EXTENSION)]
try:
- return importlib.machinery.SourceFileLoader(name,path).load_module()
- except FileNotFoundError:
+ if PY_VERSION == 3:
+ return importlib.machinery.SourceFileLoader(name, path).load_module()
+ else:
+ return imp.load_source(name, path)
+ except Exception as e:
+ msg.error("Problem loading", name, str(e))
return None
- def load_modules(self,path):
- names = os.listdir(path)
- for mod in names:
- m = self.import_plugin(path+mod)
- if m is not None:
- debug("loading chart: '"+path+mod+"'")
- self.modules.append(m)
-
- def load_configs(self):
- for m in self.modules:
- if os.path.isfile(self.configs+m.__name__+".conf"):
- debug("loading chart options: '"+self.configs+m.__name__+".conf'")
- for k,v in read_config(self.configs+m.__name__+".conf").items():
- setattr(m,k,v)
+ def _load_modules(self, path, modules, disabled, enabled, default_run):
+ """
+ Load modules from 'modules' list or dynamically every file from 'path' (only .chart.py files)
+ :param path: str
+ :param modules: list
+ :param disabled: list
+ :return: list
+ """
+
+ # check if plugin directory exists
+ if not os.path.isdir(path):
+ msg.fatal("cannot find charts directory ", path)
+
+ # load modules
+ loaded = []
+ if len(modules) > 0:
+ for m in modules:
+ if m in disabled:
+ continue
+ mod = self._import_module(path + m + MODULE_EXTENSION)
+ if mod is not None:
+ loaded.append(mod)
+ else: # exit if plugin is not found
+ msg.fatal('no modules found.')
+ else:
+ # scan directory specified in path and load all modules from there
+ if default_run is False:
+ names = [module for module in os.listdir(path) if module[:-9] in enabled]
+ else:
+ names = os.listdir(path)
+ for mod in names:
+ if mod.replace(MODULE_EXTENSION, "") in disabled:
+ msg.error(mod + ": disabled module ", mod.replace(MODULE_EXTENSION, ""))
+ continue
+ m = self._import_module(path + mod)
+ if m is not None:
+ msg.debug(mod + ": loading module '" + path + mod + "'")
+ loaded.append(m)
+ return loaded
+
+ def _load_configs(self, modules):
+ """
+ Append configuration in list named `config` to every module.
+ For multi-job modules `config` list is created in _parse_config,
+ otherwise it is created here based on BASE_CONFIG prototype with None as identifier.
+ :param modules: list
+ :return: list
+ """
+ for mod in modules:
+ configfile = self.configs + mod.__name__ + ".conf"
+ if os.path.isfile(configfile):
+ msg.debug(mod.__name__ + ": loading module configuration: '" + configfile + "'")
+ try:
+ if not hasattr(mod, 'config'):
+ mod.config = {}
+ setattr(mod,
+ 'config',
+ self._parse_config(mod, read_config(configfile)))
+ except Exception as e:
+ msg.error(mod.__name__ + ": cannot parse configuration file '" + configfile + "':", str(e))
else:
- debug(m.__name__+": configuration file '"+self.configs+m.__name__+".conf' not found. Using defaults.")
+ msg.error(mod.__name__ + ": configuration file '" + configfile + "' not found. Using defaults.")
+ # set config if not found
+ if not hasattr(mod, 'config'):
+ msg.debug(mod.__name__ + ": setting configuration for only one job")
+ mod.config = {None: {}}
+ for var in BASE_CONFIG:
+ try:
+ mod.config[None][var] = getattr(mod, var)
+ except AttributeError:
+ mod.config[None][var] = BASE_CONFIG[var]
+ return modules
+
+ @staticmethod
+ def _parse_config(module, config):
+ """
+ Parse configuration file or extract configuration from module file.
+ Example of returned dictionary:
+ config = {'name': {
+ 'update_every': 2,
+ 'retries': 3,
+ 'priority': 30000
+ 'other_val': 123}}
+ :param module: object
+ :param config: dict
+ :return: dict
+ """
+ if config is None:
+ config = {}
+ # get default values
+ defaults = {}
+ msg.debug(module.__name__ + ": reading configuration")
+ for key in BASE_CONFIG:
+ try:
+ # get defaults from module config
+ defaults[key] = int(config.pop(key))
+ except (KeyError, ValueError):
+ try:
+ # get defaults from module source code
+ defaults[key] = getattr(module, key)
+ except (KeyError, ValueError, AttributeError):
+ # if above failed, get defaults from global dict
+ defaults[key] = BASE_CONFIG[key]
+
+ # check if there are dict in config dict
+ many_jobs = False
+ for name in config:
+ if isinstance(config[name], DICT):
+ many_jobs = True
+ break
+
+ # assign variables needed by supervisor to every job configuration
+ if many_jobs:
+ for name in config:
+ for key in defaults:
+ if key not in config[name]:
+ config[name][key] = defaults[key]
+ # if only one job is needed, values doesn't have to be in dict (in YAML)
+ else:
+ config = {None: config.copy()}
+ config[None].update(defaults)
+
+ # return dictionary of jobs where every job has BASE_CONFIG variables
+ return config
- def disable_module(self,mod,reason=None):
- self.modules.remove(mod)
- del self.timetable[mod.__name__]
+ @staticmethod
+ def _create_jobs(modules):
+ """
+ Create jobs based on module.config dictionary and module.Service class definition.
+ :param modules: list
+ :return: list
+ """
+ jobs = []
+ for module in modules:
+ for name in module.config:
+ # register a new job
+ conf = module.config[name]
+ try:
+ job = module.Service(configuration=conf, name=name)
+ except Exception as e:
+ msg.error(module.__name__ +
+ ("/" + str(name) if name is not None else "") +
+ ": cannot start job: '" +
+ str(e))
+ return None
+ else:
+ # set chart_name (needed to plot run time graphs)
+ job.chart_name = module.__name__
+ if name is not None:
+ job.chart_name += "_" + name
+ jobs.append(job)
+ msg.debug(module.__name__ + ("/" + str(name) if name is not None else "") + ": job added")
+
+ return [j for j in jobs if j is not None]
+
+ def _stop(self, job, reason=None):
+ """
+ Stop specified job and remove it from self.jobs list
+ Also notifies user about job failure if DEBUG_FLAG is set
+ :param job: object
+ :param reason: str
+ """
+ prefix = job.__module__
+ if job.name is not None and len(job.name) != 0:
+ prefix += "/" + job.name
+ try:
+ msg.error("DISABLED:", prefix)
+ self.jobs.remove(job)
+ except Exception as e:
+ msg.debug("This shouldn't happen. NO " + prefix + " IN LIST:" + str(self.jobs) + " ERROR: " + str(e))
+
+ # TODO remove section below and remove `reason`.
+ prefix += ": "
if reason is None:
return
elif reason[:3] == "no ":
- debug("chart '"+ mod.__name__,"' does not seem to have "+reason[3:]+"() function. Disabling it.")
+ msg.error(prefix +
+ "does not seem to have " +
+ reason[3:] +
+ "() function. Disabling it.")
elif reason[:7] == "failed ":
- debug("chart '"+mod.__name__+reason[3:]+"() function. reports failure.")
+ msg.error(prefix +
+ reason[7:] +
+ "() function reports failure.")
elif reason[:13] == "configuration":
- debug(mod.__name__,"configuration file '"+self.configs+mod.__name__+".conf' not found. Using defaults.")
+ msg.error(prefix +
+ "configuration file '" +
+ self.configs +
+ job.__module__ +
+ ".conf' not found. Using defaults.")
elif reason[:11] == "misbehaving":
- debug(mod.__name__,"is misbeaving. Disabling it")
- #sys.stdout.write('DISABLE')
-
+ msg.error(prefix + "is " + reason)
def check(self):
- for mod in self.modules:
+ """
+ Tries to execute check() on every job.
+ This cannot fail thus it is catching every exception
+ If job.check() fails job is stopped
+ """
+ i = 0
+ overridden = []
+ msg.debug("all job objects", str(self.jobs))
+ while i < len(self.jobs):
+ job = self.jobs[i]
try:
- if not mod.check():
- self.disable_module(mod,"failed check")
- except AttributeError:
- self.disable_module(mod,"no check")
- except UnboundLocalError:
- self.disable_module(mod,"misbehaving")
+ if not job.check():
+ msg.error(job.chart_name, "check() failed - disabling job")
+ self._stop(job)
+ else:
+ msg.info("CHECKED OK:", job.chart_name)
+ i += 1
+ try:
+ if job.override_name is not None:
+ new_name = job.__module__ + '_' + sub(r'\s+', '_', job.override_name)
+ if new_name in overridden:
+ msg.info("DROPPED:", job.name, ", job '" + job.override_name +
+ "' is already served by another job.")
+ self._stop(job)
+ i -= 1
+ else:
+ job.name = job.override_name
+ msg.info("RENAMED:", new_name, ", from " + job.chart_name)
+ job.chart_name = new_name
+ overridden.append(job.chart_name)
+ except Exception:
+ pass
+ except AttributeError as e:
+ self._stop(job)
+ msg.error(job.chart_name, "cannot find check() function or it thrown unhandled exception.")
+ msg.debug(str(e))
+ except (UnboundLocalError, Exception) as e:
+ msg.error(job.chart_name, str(e))
+ self._stop(job)
+ msg.debug("overridden job names:", str(overridden))
+ msg.debug("all remaining job objects:", str(self.jobs))
def create(self):
- for mod in self.modules:
+ """
+ Tries to execute create() on every job.
+ This cannot fail thus it is catching every exception.
+ If job.create() fails job is stopped.
+ This is also creating job run time chart.
+ """
+ i = 0
+ while i < len(self.jobs):
+ job = self.jobs[i]
try:
- if not mod.create():
- self.disable_module(mod,"failed create")
+ if not job.create():
+ msg.error(job.chart_name, "create function failed.")
+ self._stop(job)
else:
- chart=mod.__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.interval)+'\n')
+ chart = job.chart_name
+ sys.stdout.write(
+ "CHART netdata.plugin_pythond_" +
+ chart +
+ " '' 'Execution time for " +
+ chart +
+ " plugin' 'milliseconds / run' python.d netdata.plugin_python area 145000 " +
+ str(job.timetable['freq']) +
+ '\n')
sys.stdout.write("DIMENSION run_time 'run time' absolute 1 1\n\n")
- sys.stdout.flush()
+ msg.debug("created charts for", job.chart_name)
+ # sys.stdout.flush()
+ i += 1
except AttributeError:
- self.disable_module(mod,"no create")
- except UnboundLocalError:
- self.disable_module(mod,"misbehaving")
-
- def update_module(self,mod):
- t1 = time.time()
- try:
-# t=int((t1- self.timetable[mod.__name__]) * 1000000)
-# if t == 0: t=""
- t = "" #FIXME
- if not mod.update(t):
-# if not mod.update(int((t1- self.timetable[mod.__name__]) * 1000)):
- self.disable_module(mod, "update failed")
- return
- except AttributeError:
- self.disable_module(mod, "no update")
- return
- except UnboundLocalError:
- self.disable_module(mod,"misbehaving")
- return
- t2 = time.time()
-# dt = int((t2 - self.timetable[mod.__name__]) * 1000)
-# if dt == 0: dt=""
- dt="" #FIXME
- sys.stdout.write("BEGIN netdata.plugin_pythond_"+mod.__name__+" "+str(dt)+'\n')
- sys.stdout.write("SET run_time = "+str(int((t2 - t1))*1000)+'\n')
- sys.stdout.write("END\n")
- sys.stdout.flush()
- self.timetable[mod.__name__] = t1
+ msg.error(job.chart_name, "cannot find create() function or it thrown unhandled exception.")
+ self._stop(job)
+ except (UnboundLocalError, Exception) as e:
+ msg.error(job.chart_name, str(e))
+ self._stop(job)
def update(self):
+ """
+ Creates and supervises every job thread.
+ This will stay forever and ever and ever forever and ever it'll be the one...
+ """
+ for job in self.jobs:
+ job.start()
+
while True:
- t_begin = time.time()
- for mod in self.modules:
- self.update_module(mod)
- time.sleep(self.interval - (time.time()-t_begin))
-
+ if threading.active_count() <= 1:
+ msg.fatal("no more jobs")
+ time.sleep(1)
+
def read_config(path):
- #TODO make it bulletproof
- config = configparser.ConfigParser()
- config_str = ""
+ """
+ Read YAML configuration from specified file
+ :param path: str
+ :return: dict
+ """
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
- config.read_string(config_str)
- return dict(config.items('config'))
-
-def debug(*args):
- if not DEBUG_FLAG:
- return
- sys.stderr.write(PROGRAM+":")
- for i in args:
- sys.stderr.write(" "+str(i))
- sys.stderr.write("\n")
- sys.stderr.flush()
-
-def parse_cmdline(directory,*commands):
- global DEBUG_FLAG, PROGRAM
- DEBUG_FLAG=False
+ with open(path, 'r') as stream:
+ if ORDERED:
+ config = ordered_load(stream, yaml.SafeLoader)
+ else:
+ config = yaml.load(stream)
+ except (OSError, IOError):
+ msg.error(str(path), "is not a valid configuration file")
+ return None
+ except yaml.YAMLError as e:
+ msg.error(str(path), "is malformed:", e)
+ return None
+ return config
+
+def parse_cmdline(directory, *commands):
+ """
+ Parse parameters from command line.
+ :param directory: str
+ :param commands: list of str
+ :return: dict
+ """
+ global DEBUG_FLAG, TRACE_FLAG
+ global OVERRIDE_UPDATE_EVERY
+ global BASE_CONFIG
+
+ changed_update = False
mods = []
for cmd in commands[1:]:
if cmd == "check":
pass
elif cmd == "debug" or cmd == "all":
- DEBUG_FLAG=True
+ DEBUG_FLAG = True
# redirect stderr to stdout?
- elif os.path.isfile(directory+cmd+".chart.py") or os.path.isfile(directory+cmd):
- DEBUG_FLAG=True
- mods.append(cmd.replace(".chart.py",""))
+ elif cmd == "trace" or cmd == "all":
+ TRACE_FLAG = True
+ elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd):
+ # DEBUG_FLAG = True
+ mods.append(cmd.replace(".chart.py", ""))
+ else:
+ try:
+ BASE_CONFIG['update_every'] = int(cmd)
+ changed_update = True
+ except ValueError:
+ pass
+ if changed_update and DEBUG_FLAG:
+ OVERRIDE_UPDATE_EVERY = True
+ msg.debug(PROGRAM, "overriding update interval to", str(BASE_CONFIG['update_every']))
- PROGRAM = commands[0].split('/')[-1].split('.plugin')[0]
- debug("started from",commands[0],"with options:",*commands[1:])
+ msg.debug("started from", commands[0], "with options:", *commands[1:])
return mods
-#if __name__ == '__main__':
+
+# if __name__ == '__main__':
def run():
- # parse env variables
- # https://github.com/firehol/netdata/wiki/External-Plugins#environment-variables
- main_dir = os.getenv('NETDATA_PLUGINS_DIR',os.path.abspath(__file__).strip("python.d.plugin.py"))
- config_dir = os.getenv('NETDATA_CONFIG_DIR',"/etc/netdata/")
- # logs_dir = os.getenv('NETDATA_LOGS_DIR',None) #TODO logging?
- interval = int(os.getenv('NETDATA_UPDATE_EVERY',1))
+ """
+ Main program.
+ """
+ global DEBUG_FLAG, TRACE_FLAG, BASE_CONFIG
# read configuration file
- if config_dir[-1] != '/':
- configfile = config_dir + '/' + "python.d.conf"
- else:
- configfile = config_dir + "python.d.conf"
+ disabled = ['nginx_log', 'gunicorn_log']
+ enabled = list()
+ default_run = True
+ configfile = CONFIG_DIR + "python.d.conf"
+ msg.PROGRAM = PROGRAM
+ msg.info("reading configuration file:", configfile)
+ log_throttle = 200
+ log_interval = 3600
+
+ conf = read_config(configfile)
+ if conf is not None:
+ try:
+ # exit the whole plugin when 'enabled: no' is set in 'python.d.conf'
+ if conf['enabled'] is False:
+ msg.fatal('disabled in configuration file.\n')
+ except (KeyError, TypeError):
+ pass
+
+ try:
+ for param in BASE_CONFIG:
+ BASE_CONFIG[param] = conf[param]
+ except (KeyError, TypeError):
+ pass # use default update_every from NETDATA_UPDATE_EVERY
- try:
- conf = read_config(configfile)
try:
- if str(conf['enable']) == "no":
- debug("disabled in configuration file")
- sys.exit(1)
- except (KeyError,TypeError): pass
- try: modules_conf = conf['plugins_config_dir']
- except (KeyError,TypeError): modules_conf = config_dir # default configuration directory
- try: modules_dir = conf['plugins_dir']
- except (KeyError,TypeError): modules_dir = main_dir.replace("plugins.d","python.d")
- try: interval = int(conf['interval'])
- except (KeyError,TypeError): pass # default interval
- global DEBUG_FLAG
- try: DEBUG_FLAG = bool(conf['debug'])
- except (KeyError,TypeError): pass
- except FileNotFoundError:
- modules_conf = config_dir
- modules_dir = main_dir.replace("plugins.d","python.d")
-
- # directories should end with '/'
- if modules_dir[-1] != '/': modules_dir+="/"
- if modules_conf[-1] != '/': modules_conf+="/"
+ DEBUG_FLAG = conf['debug']
+ except (KeyError, TypeError):
+ pass
+
+ try:
+ TRACE_FLAG = conf['trace']
+ except (KeyError, TypeError):
+ pass
+ try:
+ log_throttle = conf['logs_per_interval']
+ except (KeyError, TypeError):
+ pass
+
+ try:
+ log_interval = conf['log_interval']
+ except (KeyError, TypeError):
+ pass
+
+ default_run = True if ('default_run' not in conf or conf.get('default_run')) else False
+
+ for k, v in conf.items():
+ if k in ("update_every", "debug", "enabled", "default_run"):
+ continue
+ if default_run:
+ if v is False:
+ disabled.append(k)
+ else:
+ if v is True:
+ enabled.append(k)
# parse passed command line arguments
- modules = parse_cmdline(modules_dir,*sys.argv)
+ modules = parse_cmdline(MODULES_DIR, *sys.argv)
+ msg.DEBUG_FLAG = DEBUG_FLAG
+ msg.TRACE_FLAG = TRACE_FLAG
+ msg.LOG_THROTTLE = log_throttle
+ msg.LOG_INTERVAL = log_interval
+ msg.LOG_COUNTER = 0
+ msg.LOG_NEXT_CHECK = 0
+ msg.info("MODULES_DIR='" + MODULES_DIR +
+ "', CONFIG_DIR='" + CONFIG_DIR +
+ "', UPDATE_EVERY=" + str(BASE_CONFIG['update_every']) +
+ ", ONLY_MODULES=" + str(modules))
# run plugins
- charts = PythonCharts(interval,modules,modules_dir,modules_conf)
+ charts = PythonCharts(modules, MODULES_DIR, CONFIG_DIR + "python.d/", disabled, enabled, default_run)
charts.check()
charts.create()
charts.update()
+ msg.fatal("finished")
+
if __name__ == '__main__':
run()