7 #import importlib.machinery
9 assert sys.version_info >= (3,1)
11 import importlib.machinery
12 except AssertionError:
13 sys.stderr.write('Not supported python version. Needed python >= 3.1\n')
14 sys.stdout.write('DISABLE\n')
18 class PythonCharts(object):
23 modules_path='../python.d/',
24 modules_configs='../conf.d/'):
26 self.interval = interval
27 self.default_priority = 90000
28 # check if plugin directory exists
29 if not os.path.isdir(modules_path):
30 debug("cannot find charts directory ", modules_path)
31 sys.stdout.write("DISABLE\n")
33 self.configs = modules_configs
38 mod = self.import_plugin(modules_path + m + ".chart.py")
40 self.modules.append(mod)
41 if len(self.modules) == 0:
42 debug("cannot find provided module(s)", modules_path)
43 sys.stdout.write("DISABLE\n")
46 self.load_modules(modules_path)
47 if len(self.modules) == 0:
48 debug("cannot find modules directory", modules_path)
49 sys.stdout.write("DISABLE\n")
54 # set last execution and execution frequency dict
56 for m in self.modules:
58 interval = m.update_every
59 interval = int(interval)
60 except AttributeError:
61 interval = self.interval
63 interval = self.interval
65 ": wrong value of 'update_every' setting to " +
67 # ensure module update frequency is not less than execution of this program
68 if interval < self.interval:
69 interval = self.interval
70 # charts updates are twice as frequent as user specified
71 self.timetable[m.__name__] = [0, interval/2.0]
74 for m in self.modules:
76 m.priority = int(m.priority)
77 except (AttributeError,ValueError):
78 m.priority = self.default_priority
81 def import_plugin(self, path, name=None):
83 name = path.split('/')[-1]
84 if name[-9:] != ".chart.py":
88 return importlib.machinery.SourceFileLoader(name, path).load_module()
89 except Exception as e:
93 def load_modules(self, path):
94 names = os.listdir(path)
96 m = self.import_plugin(path + mod)
98 debug("loading chart: '" + path + mod + "'")
99 self.modules.append(m)
101 def load_configs(self):
102 for m in self.modules:
103 configfile = self.configs + m.__name__ + ".conf"
104 if os.path.isfile(configfile):
105 debug("loading chart options: '" + configfile + "'")
106 for k, v in read_config(configfile).items():
110 ": configuration file '" +
112 "' not found. Using defaults.")
114 def disable_module(self, mod, reason=None):
115 self.modules.remove(mod)
116 del self.timetable[mod.__name__]
119 elif reason[:3] == "no ":
122 "' does not seem to have " +
124 "() function. Disabling it.")
125 elif reason[:7] == "failed ":
129 "() function. reports failure.")
130 elif reason[:13] == "configuration":
132 "configuration file '" +
135 ".conf' not found. Using defaults.")
136 elif reason[:11] == "misbehaving":
137 debug(mod.__name__, "is misbeaving. Disabling it")
140 for mod in self.modules:
143 self.disable_module(mod, "failed check")
144 except AttributeError:
145 self.disable_module(mod, "no check")
146 except UnboundLocalError:
147 self.disable_module(mod, "misbehaving")
150 for mod in self.modules:
153 self.disable_module(mod, "failed create")
157 "CHART netdata.plugin_pythond_" +
159 " '' 'Execution time for " +
161 " plugin' 'milliseconds / run' python.d netdata.plugin_python area 145000 " +
162 str(self.timetable[mod.__name__][1]) +
164 sys.stdout.write("DIMENSION run_time 'run time' absolute 1 1\n\n")
166 except AttributeError:
167 self.disable_module(mod, "no create")
168 except UnboundLocalError:
169 self.disable_module(mod, "misbehaving")
171 def update_module(self, mod):
173 # check if it is time to execute module update() function
174 if (t1 - self.timetable[mod.__name__][0]) < self.timetable[mod.__name__][1]:
177 if not self.first_run:
180 t=int((t1- self.timetable[mod.__name__][0]) * 1000000)
181 if not mod.update(t):
182 self.disable_module(mod, "update failed")
184 except AttributeError:
185 self.disable_module(mod, "no update")
187 except UnboundLocalError:
188 self.disable_module(mod, "misbehaving")
191 if not self.first_run:
194 dt = int((t2 - self.timetable[mod.__name__][0]) * 1000000)
195 sys.stdout.write("BEGIN netdata.plugin_pythond_"+mod.__name__+" "+str(dt)+'\n')
196 sys.stdout.write("SET run_time = " + str(int((t2 - t1) * 1000)) + '\n')
197 sys.stdout.write("END\n")
199 self.timetable[mod.__name__][0] = t1
203 t_begin = time.time()
204 for mod in self.modules:
205 self.update_module(mod)
206 time.sleep((self.interval - (time.time() - t_begin)) / 2)
207 self.first_run = False
210 def read_config(path):
211 config = configparser.ConfigParser()
214 with open(path, 'r', encoding="utf_8") as f:
215 config_str = '[config]\n' + f.read()
216 except IsADirectoryError:
217 debug(str(path), "is a directory")
219 config.read_string(config_str)
220 return dict(config.items('config'))
226 sys.stderr.write(PROGRAM + ":")
228 sys.stderr.write(" " + str(i))
229 sys.stderr.write("\n")
233 def parse_cmdline(directory, *commands):
234 # TODO number -> interval
240 for cmd in commands[1:]:
243 elif cmd == "debug" or cmd == "all":
245 # redirect stderr to stdout?
246 elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd):
248 mods.append(cmd.replace(".chart.py", ""))
255 debug("started from", commands[0], "with options:", *commands[1:])
257 return {'interval': interval,
261 # if __name__ == '__main__':
263 global DEBUG_FLAG, PROGRAM
265 PROGRAM = sys.argv[0].split('/')[-1].split('.plugin')[0]
266 # parse env variables
267 # https://github.com/firehol/netdata/wiki/External-Plugins#environment-variables
268 main_dir = os.getenv('NETDATA_PLUGINS_DIR',
269 os.path.abspath(__file__).strip("python.d.plugin.py"))
270 config_dir = os.getenv('NETDATA_CONFIG_DIR', "/etc/netdata/")
271 interval = int(os.getenv('NETDATA_UPDATE_EVERY', 1))
273 # read configuration file
274 if config_dir[-1] != '/':
275 configfile = config_dir + '/' + "python.d.conf"
277 configfile = config_dir + "python.d.conf"
280 conf = read_config(configfile)
282 if str(conf['enable']) == "no":
283 debug("disabled in configuration file")
284 sys.stdout.write("DISABLE\n")
286 except (KeyError, TypeError):
289 modules_conf = conf['plugins_config_dir']
290 except (KeyError, TypeError):
291 modules_conf = config_dir # default configuration directory
293 modules_dir = conf['plugins_dir']
294 except (KeyError, TypeError):
295 modules_dir = main_dir.replace("plugins.d", "python.d")
297 interval = int(conf['interval'])
298 except (KeyError, TypeError):
299 pass # default interval
301 DEBUG_FLAG = bool(conf['debug'])
302 except (KeyError, TypeError):
304 except FileNotFoundError:
305 modules_conf = config_dir
306 modules_dir = main_dir.replace("plugins.d", "python.d")
308 # directories should end with '/'
309 if modules_dir[-1] != '/':
311 if modules_conf[-1] != '/':
314 # parse passed command line arguments
315 out = parse_cmdline(modules_dir, *sys.argv)
316 modules = out['modules']
317 if out['interval'] is not None:
318 interval = out['interval']
320 # configure environement to run modules
321 #sys.path.append(modules_dir+"python_modules") # append path to directory with modules dependencies
324 charts = PythonCharts(interval, modules, modules_dir, modules_conf)
329 if __name__ == '__main__':