6 import importlib.machinery
9 class PythonCharts(object):
14 modules_path='../python.d/',
15 modules_configs='../conf.d/'):
17 self.interval = interval
18 # check if plugin directory exists
19 if not os.path.isdir(modules_path):
20 debug("cannot find charts directory ", modules_path)
21 sys.stdout.write("DISABLE\n")
23 self.configs = modules_configs
30 modules_path + m + ".chart.py"))
32 self.load_modules(modules_path)
33 if len(self.modules) == 0:
34 debug("cannot find modules directory", modules_path)
35 sys.stdout.write("DISABLE\n")
40 # set last execution and execution frequency dict
42 for m in self.modules:
44 interval = m.update_every
45 interval = int(interval)
46 except AttributeError:
47 interval = self.interval
49 interval = self.interval
51 ": wrong value of 'update_every' setting to " +
53 # ensure module update frequency is not less than execution of this program
54 if interval < self.interval:
55 interval = self.interval
56 # charts updates are twice as frequent as user specified
57 self.timetable[m.__name__] = [0, interval/2]
59 def import_plugin(self, path, name=None):
61 name = path.split('/')[-1]
62 if name[-9:] != ".chart.py":
66 return importlib.machinery.SourceFileLoader(name, path).load_module()
67 except Exception as e:
71 def load_modules(self, path):
72 names = os.listdir(path)
74 m = self.import_plugin(path + mod)
76 debug("loading chart: '" + path + mod + "'")
77 self.modules.append(m)
79 def load_configs(self):
80 for m in self.modules:
81 configfile = self.configs + m.__name__ + ".conf"
82 if os.path.isfile(configfile):
83 debug("loading chart options: '" + configfile + "'")
84 for k, v in read_config(configfile).items():
88 ": configuration file '" +
90 "' not found. Using defaults.")
92 def disable_module(self, mod, reason=None):
93 self.modules.remove(mod)
94 del self.timetable[mod.__name__]
97 elif reason[:3] == "no ":
100 "' does not seem to have " +
102 "() function. Disabling it.")
103 elif reason[:7] == "failed ":
107 "() function. reports failure.")
108 elif reason[:13] == "configuration":
110 "configuration file '" +
113 ".conf' not found. Using defaults.")
114 elif reason[:11] == "misbehaving":
115 debug(mod.__name__, "is misbeaving. Disabling it")
118 for mod in self.modules:
121 self.disable_module(mod, "failed check")
122 except AttributeError:
123 self.disable_module(mod, "no check")
124 except UnboundLocalError:
125 self.disable_module(mod, "misbehaving")
128 for mod in self.modules:
131 self.disable_module(mod, "failed create")
135 "CHART netdata.plugin_pythond_" +
137 " '' 'Execution time for " +
139 " plugin' 'milliseconds / run' python.d netdata.plugin_python area 145000 " +
140 str(self.timetable[mod.__name__][1]) +
142 sys.stdout.write("DIMENSION run_time 'run time' absolute 1 1\n\n")
144 except AttributeError:
145 self.disable_module(mod, "no create")
146 except UnboundLocalError:
147 self.disable_module(mod, "misbehaving")
149 def update_module(self, mod):
151 # check if it is time to execute module update() function
152 if (t1 - self.timetable[mod.__name__][0]) < self.timetable[mod.__name__][1]:
155 if not self.first_run:
158 t=int((t1- self.timetable[mod.__name__][0]) * 1000000)
159 if not mod.update(t):
160 self.disable_module(mod, "update failed")
162 except AttributeError:
163 self.disable_module(mod, "no update")
165 except UnboundLocalError:
166 self.disable_module(mod, "misbehaving")
172 dt = int((t2 - self.timetable[mod.__name__][0]) * 1000000)
173 sys.stdout.write("BEGIN netdata.plugin_pythond_"+mod.__name__+" "+str(dt)+'\n')
174 sys.stdout.write("SET run_time = " + str(int((t2 - t1) * 1000)) + '\n')
175 sys.stdout.write("END\n")
177 self.timetable[mod.__name__][0] = t1
181 t_begin = time.time()
182 for mod in self.modules:
183 self.update_module(mod)
184 time.sleep((self.interval - (time.time() - t_begin)) / 2)
185 self.first_run = False
188 def read_config(path):
189 config = configparser.ConfigParser()
192 with open(path, 'r', encoding="utf_8") as f:
193 config_str = '[config]\n' + f.read()
194 except IsADirectoryError:
195 debug(str(path), "is a directory")
197 config.read_string(config_str)
198 return dict(config.items('config'))
204 sys.stderr.write(PROGRAM + ":")
206 sys.stderr.write(" " + str(i))
207 sys.stderr.write("\n")
211 def parse_cmdline(directory, *commands):
212 # TODO number -> interval
213 global DEBUG_FLAG, PROGRAM
218 for cmd in commands[1:]:
221 elif cmd == "debug" or cmd == "all":
223 # redirect stderr to stdout?
224 elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd):
226 mods.append(cmd.replace(".chart.py", ""))
233 PROGRAM = commands[0].split('/')[-1].split('.plugin')[0]
234 debug("started from", commands[0], "with options:", *commands[1:])
236 return {'interval': interval,
240 # if __name__ == '__main__':
242 # parse env variables
243 # https://github.com/firehol/netdata/wiki/External-Plugins#environment-variables
244 main_dir = os.getenv('NETDATA_PLUGINS_DIR',
245 os.path.abspath(__file__).strip("python.d.plugin.py"))
246 config_dir = os.getenv('NETDATA_CONFIG_DIR', "/etc/netdata/")
247 interval = int(os.getenv('NETDATA_UPDATE_EVERY', 1))
249 # read configuration file
250 if config_dir[-1] != '/':
251 configfile = config_dir + '/' + "python.d.conf"
253 configfile = config_dir + "python.d.conf"
256 conf = read_config(configfile)
258 if str(conf['enable']) == "no":
259 debug("disabled in configuration file")
260 sys.stdout.write("DISABLE\n")
262 except (KeyError, TypeError):
265 modules_conf = conf['plugins_config_dir']
266 except (KeyError, TypeError):
267 modules_conf = config_dir # default configuration directory
269 modules_dir = conf['plugins_dir']
270 except (KeyError, TypeError):
271 modules_dir = main_dir.replace("plugins.d", "python.d")
273 interval = int(conf['interval'])
274 except (KeyError, TypeError):
275 pass # default interval
278 DEBUG_FLAG = bool(conf['debug'])
279 except (KeyError, TypeError):
281 except FileNotFoundError:
282 modules_conf = config_dir
283 modules_dir = main_dir.replace("plugins.d", "python.d")
285 # directories should end with '/'
286 if modules_dir[-1] != '/':
288 if modules_conf[-1] != '/':
291 # parse passed command line arguments
292 out = parse_cmdline(modules_dir, *sys.argv)
293 modules = out['modules']
294 if out['interval'] is not None:
295 interval = out['interval']
298 charts = PythonCharts(interval, modules, modules_dir, modules_conf)
303 if __name__ == '__main__':