6 import importlib.machinery
9 class PythonCharts(object):
10 def __init__(self,interval=1,modules=[],modules_path='../python.d/',modules_configs='../conf.d/'):
11 self.interval = interval
12 # check if plugin directory exists
13 if not os.path.isdir(modules_path):
14 debug("cannot find charts directory ",modules_path)
16 self.configs=modules_configs
21 self.modules.append(self.import_plugin(modules_path+m+".chart.py"))
23 self.load_modules(modules_path)
24 if len(self.modules) == 0:
25 debug("cannot find charts directory",modules_path)
26 # sys.stdout.write("DISABLE")
31 # set last execution dict
34 for m in self.modules:
35 self.timetable[m.__name__] = t
37 def import_plugin(self,path,name=None):
39 name = path.split('/')[-1]
40 if name[-9:] != ".chart.py":
44 return importlib.machinery.SourceFileLoader(name,path).load_module()
45 except FileNotFoundError:
48 def load_modules(self,path):
49 names = os.listdir(path)
51 m = self.import_plugin(path+mod)
53 debug("loading chart: '"+path+mod+"'")
54 self.modules.append(m)
56 def load_configs(self):
57 for m in self.modules:
58 if os.path.isfile(self.configs+m.__name__+".conf"):
59 debug("loading chart options: '"+self.configs+m.__name__+".conf'")
60 for k,v in read_config(self.configs+m.__name__+".conf").items():
63 debug(m.__name__+": configuration file '"+self.configs+m.__name__+".conf' not found. Using defaults.")
65 def disable_module(self,mod,reason=None):
66 self.modules.remove(mod)
67 del self.timetable[mod.__name__]
70 elif reason[:3] == "no ":
71 debug("chart '"+ mod.__name__,"' does not seem to have "+reason[3:]+"() function. Disabling it.")
72 elif reason[:7] == "failed ":
73 debug("chart '"+mod.__name__+reason[3:]+"() function. reports failure.")
74 elif reason[:13] == "configuration":
75 debug(mod.__name__,"configuration file '"+self.configs+mod.__name__+".conf' not found. Using defaults.")
76 elif reason[:11] == "misbehaving":
77 debug(mod.__name__,"is misbeaving. Disabling it")
78 #sys.stdout.write('DISABLE')
82 for mod in self.modules:
85 self.disable_module(mod,"failed check")
86 except AttributeError:
87 self.disable_module(mod,"no check")
88 except UnboundLocalError:
89 self.disable_module(mod,"misbehaving")
92 for mod in self.modules:
95 self.disable_module(mod,"failed create")
98 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')
99 sys.stdout.write("DIMENSION run_time 'run time' absolute 1 1\n\n")
101 except AttributeError:
102 self.disable_module(mod,"no create")
103 except UnboundLocalError:
104 self.disable_module(mod,"misbehaving")
106 def update_module(self,mod):
109 # t=int((t1- self.timetable[mod.__name__]) * 1000)
112 if not mod.update(t):
113 # if not mod.update(int((t1- self.timetable[mod.__name__]) * 1000)):
114 self.disable_module(mod, "update failed")
116 except AttributeError:
117 self.disable_module(mod, "no update")
119 except UnboundLocalError:
120 self.disable_module(mod,"misbehaving")
123 # dt = int((t2 - self.timetable[mod.__name__]) * 1000)
126 sys.stdout.write("BEGIN netdata.plugin_pythond_"+mod.__name__+" "+str(dt)+'\n')
127 sys.stdout.write("SET run_time = "+str(int((t2 - t1)*1000))+'\n')
128 sys.stdout.write("END\n")
130 self.timetable[mod.__name__] = t1
134 t_begin = time.time()
135 for mod in self.modules:
136 self.update_module(mod)
137 time.sleep(self.interval - (time.time()-t_begin))
140 def read_config(path):
141 #TODO make it bulletproof
142 config = configparser.ConfigParser()
145 with open(path, 'r', encoding="utf_8") as f:
146 config_str = '[config]\n' + f.read()
147 except IsADirectoryError:
148 debug(str(path),"is a directory")
150 config.read_string(config_str)
151 return dict(config.items('config'))
156 sys.stderr.write(PROGRAM+":")
158 sys.stderr.write(" "+str(i))
159 sys.stderr.write("\n")
162 def parse_cmdline(directory,*commands):
163 global DEBUG_FLAG, PROGRAM
167 for cmd in commands[1:]:
170 elif cmd == "debug" or cmd == "all":
172 # redirect stderr to stdout?
173 elif os.path.isfile(directory+cmd+".chart.py") or os.path.isfile(directory+cmd):
175 mods.append(cmd.replace(".chart.py",""))
177 PROGRAM = commands[0].split('/')[-1].split('.plugin')[0]
178 debug("started from",commands[0],"with options:",*commands[1:])
182 #if __name__ == '__main__':
184 # parse env variables
185 # https://github.com/firehol/netdata/wiki/External-Plugins#environment-variables
186 main_dir = os.getenv('NETDATA_PLUGINS_DIR',os.path.abspath(__file__).strip("python.d.plugin.py"))
187 config_dir = os.getenv('NETDATA_CONFIG_DIR',"/etc/netdata/")
188 # logs_dir = os.getenv('NETDATA_LOGS_DIR',None) #TODO logging?
189 interval = int(os.getenv('NETDATA_UPDATE_EVERY',1))
191 # read configuration file
192 if config_dir[-1] != '/':
193 configfile = config_dir + '/' + "python.d.conf"
195 configfile = config_dir + "python.d.conf"
198 conf = read_config(configfile)
200 if str(conf['enable']) == "no":
201 debug("disabled in configuration file")
203 except (KeyError,TypeError): pass
204 try: modules_conf = conf['plugins_config_dir']
205 except (KeyError,TypeError): modules_conf = config_dir # default configuration directory
206 try: modules_dir = conf['plugins_dir']
207 except (KeyError,TypeError): modules_dir = main_dir.replace("plugins.d","python.d")
208 try: interval = int(conf['interval'])
209 except (KeyError,TypeError): pass # default interval
211 try: DEBUG_FLAG = bool(conf['debug'])
212 except (KeyError,TypeError): pass
213 except FileNotFoundError:
214 modules_conf = config_dir
215 modules_dir = main_dir.replace("plugins.d","python.d")
217 # directories should end with '/'
218 if modules_dir[-1] != '/': modules_dir+="/"
219 if modules_conf[-1] != '/': modules_conf+="/"
221 # parse passed command line arguments
222 modules = parse_cmdline(modules_dir,*sys.argv)
225 charts = PythonCharts(interval,modules,modules_dir,modules_conf)
230 if __name__ == '__main__':