1 # -*- coding: utf-8 -*-
2 # Description: prototypes for netdata python.d modules
3 # Author: Pawel Krupa (paulfantom)
8 from urllib.request import urlopen
10 from urllib2 import urlopen
16 class BaseService(threading.Thread):
18 Prototype of Service class.
19 Implemented basic functionality to run jobs by `python.d.plugin`
23 def __init__(self, configuration=None, name=None):
25 This needs to be initialized in child classes
26 :param configuration: dict
29 threading.Thread.__init__(self)
33 self.priority = 140000
36 if configuration is None:
37 self.error("BaseService: no configuration parameters supplied. Cannot create Service.")
40 self._extract_base_config(configuration)
42 self.create_timetable()
45 def _extract_base_config(self, config):
47 Get basic parameters to run service
49 config = {'update_every':1,
54 self.update_every = int(config.pop('update_every'))
55 self.priority = int(config.pop('priority'))
56 self.retries = int(config.pop('retries'))
57 self.retries_left = self.retries
58 self.configuration = config
60 def create_timetable(self, freq=None):
62 Create service timetable.
65 timetable = {'last': 1466370091.3767564,
71 freq = self.update_every
73 self.timetable = {'last': now,
74 'next': now - (now % freq) + freq,
79 Executes self.update(interval) and draws run time chart.
80 Return value presents exit status of update()
84 # check if it is time to execute job update() function
85 if self.timetable['next'] > t_start:
86 msg.debug(self.chart_name + " will be run in " +
87 str(int((self.timetable['next'] - t_start) * 1000)) + " ms")
90 since_last = int((t_start - self.timetable['last']) * 1000000)
91 msg.debug(self.chart_name +
92 " ready to run, after " + str(int((t_start - self.timetable['last']) * 1000)) +
93 " ms (update_every: " + str(self.timetable['freq'] * 1000) +
94 " ms, latency: " + str(int((t_start - self.timetable['next']) * 1000)) + " ms)")
95 if not self.update(since_last):
98 self.timetable['next'] = t_end - (t_end % self.timetable['freq']) + self.timetable['freq']
100 # draw performance graph
101 run_time = str(int((t_end - t_start) * 1000))
102 run_time_chart = "BEGIN netdata.plugin_pythond_" + self.chart_name + " " + str(since_last) + '\n'
103 run_time_chart += "SET run_time = " + run_time + '\n'
104 run_time_chart += "END\n"
105 sys.stdout.write(run_time_chart)
106 msg.debug(self.chart_name + " updated in " + str(run_time) + " ms")
107 self.timetable['last'] = t_start
112 Runs job in thread. Handles retries.
113 Exits when job failed or timed out.
116 self.timetable['last'] = time.time()
119 status = self._run_once()
120 except Exception as e:
121 msg.error("Something wrong: " + str(e))
124 time.sleep(self.timetable['next'] - time.time())
125 self.retries_left = self.retries
128 if self.retries_left <= 0:
129 msg.error("no more retries. Exiting")
132 time.sleep(self.timetable['freq'])
139 msg.error("Service " + str(self.__module__) + "doesn't implement check() function")
147 msg.error("Service " + str(self.__module__) + "doesn't implement create() function?")
150 def update(self, interval):
156 msg.error("Service " + str(self.__module__) + "doesn't implement update() function")
160 class UrlService(BaseService):
161 def __init__(self, configuration=None, name=None):
163 # charts definitions in format:
165 # 'chart_name_in_netdata': {
166 # 'options': "parameters defining chart (passed to CHART statement)",
168 # { 'name': 'dimension_name',
169 # 'options': 'dimension parameters (passed to DIMENSION statement)"
174 self.definitions = {}
175 # definitions are created dynamically in create() method based on 'charts' dictionary. format:
177 # 'chart_name_in_netdata' : [ charts['chart_name_in_netdata']['lines']['name'] ]
180 BaseService.__init__(self, configuration=configuration, name=name)
184 Get raw data from http request
189 f = urlopen(self.url, timeout=self.update_every)
190 raw = f.read().decode('utf-8')
191 except Exception as e:
192 msg.error(self.__module__, str(e))
200 def _formatted_data(self):
202 Format data received from http request
209 Format configuration data and try to connect to server
212 if self.name is None or self.name == str(None):
215 self.name = str(self.name)
217 self.url = str(self.configuration['url'])
218 except (KeyError, TypeError):
221 if self._formatted_data() is not None:
231 for name in self.order:
232 if name not in self.charts:
234 self.definitions[name] = []
235 for line in self.charts[name]['lines']:
236 self.definitions[name].append(line['name'])
239 data = self._formatted_data()
243 for name in self.order:
244 header = "CHART " + \
245 self.__module__ + "_" + \
248 self.charts[name]['options'] + " " + \
249 str(self.priority + idx) + " " + \
250 str(self.update_every)
252 # check if server has this datapoint
253 for line in self.charts[name]['lines']:
254 if line['name'] in data:
255 content += "\nDIMENSION " + line['name'] + " " + line['options']
258 data_stream += header + content + "\n"
267 def update(self, interval):
273 data = self._formatted_data()
278 for chart, dimensions in self.definitions.items():
279 header = "BEGIN " + self.__module__ + "_" + str(self.name) + "." + chart + " " + str(interval)
281 for dim in dimensions:
283 c += "\nSET " + dim + " = " + str(data[dim])
287 data_stream += header + c + "\nEND\n"