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 self.override_name = None
37 if configuration is None:
38 self.error("BaseService: no configuration parameters supplied. Cannot create Service.")
41 self._extract_base_config(configuration)
43 self.create_timetable()
46 def _extract_base_config(self, config):
48 Get basic parameters to run service
50 config = {'update_every':1,
56 self.override_name = config.pop('override_name')
59 self.update_every = int(config.pop('update_every'))
60 self.priority = int(config.pop('priority'))
61 self.retries = int(config.pop('retries'))
62 self.retries_left = self.retries
63 self.configuration = config
65 def create_timetable(self, freq=None):
67 Create service timetable.
70 timetable = {'last': 1466370091.3767564,
76 freq = self.update_every
78 self.timetable = {'last': now,
79 'next': now - (now % freq) + freq,
84 Executes self.update(interval) and draws run time chart.
85 Return value presents exit status of update()
89 # check if it is time to execute job update() function
90 if self.timetable['next'] > t_start:
91 msg.debug(self.chart_name + " will be run in " +
92 str(int((self.timetable['next'] - t_start) * 1000)) + " ms")
95 since_last = int((t_start - self.timetable['last']) * 1000000)
96 msg.debug(self.chart_name +
97 " ready to run, after " + str(int((t_start - self.timetable['last']) * 1000)) +
98 " ms (update_every: " + str(self.timetable['freq'] * 1000) +
99 " ms, latency: " + str(int((t_start - self.timetable['next']) * 1000)) + " ms)")
100 if not self.update(since_last):
103 self.timetable['next'] = t_end - (t_end % self.timetable['freq']) + self.timetable['freq']
105 # draw performance graph
106 run_time = str(int((t_end - t_start) * 1000))
107 run_time_chart = "BEGIN netdata.plugin_pythond_" + self.chart_name + " " + str(since_last) + '\n'
108 run_time_chart += "SET run_time = " + run_time + '\n'
109 run_time_chart += "END\n"
110 sys.stdout.write(run_time_chart)
111 msg.debug(self.chart_name + " updated in " + str(run_time) + " ms")
112 self.timetable['last'] = t_start
117 Runs job in thread. Handles retries.
118 Exits when job failed or timed out.
121 self.timetable['last'] = time.time()
124 status = self._run_once()
125 except Exception as e:
126 msg.error("Something wrong: " + str(e))
129 time.sleep(self.timetable['next'] - time.time())
130 self.retries_left = self.retries
133 if self.retries_left <= 0:
134 msg.error("no more retries. Exiting")
137 time.sleep(self.timetable['freq'])
144 msg.error("Service " + str(self.__module__) + "doesn't implement check() function")
152 msg.error("Service " + str(self.__module__) + "doesn't implement create() function?")
155 def update(self, interval):
161 msg.error("Service " + str(self.__module__) + "doesn't implement update() function")
165 class UrlService(BaseService):
166 def __init__(self, configuration=None, name=None):
168 # charts definitions in format:
170 # 'chart_name_in_netdata': {
171 # 'options': "parameters defining chart (passed to CHART statement)",
173 # { 'name': 'dimension_name',
174 # 'options': 'dimension parameters (passed to DIMENSION statement)"
179 self.definitions = {}
180 # definitions are created dynamically in create() method based on 'charts' dictionary. format:
182 # 'chart_name_in_netdata' : [ charts['chart_name_in_netdata']['lines']['name'] ]
185 BaseService.__init__(self, configuration=configuration, name=name)
189 Get raw data from http request
194 f = urlopen(self.url, timeout=self.update_every)
195 raw = f.read().decode('utf-8')
196 except Exception as e:
197 msg.error(self.__module__, str(e))
205 def _formatted_data(self):
207 Format data received from http request
214 Format configuration data and try to connect to server
217 if self.name is None or self.name == str(None):
220 self.name = str(self.name)
222 self.url = str(self.configuration['url'])
223 except (KeyError, TypeError):
226 if self._formatted_data() is not None:
236 for name in self.order:
237 if name not in self.charts:
239 self.definitions[name] = []
240 for line in self.charts[name]['lines']:
241 self.definitions[name].append(line['name'])
244 data = self._formatted_data()
248 for name in self.order:
249 header = "CHART " + \
250 self.__module__ + "_" + \
253 self.charts[name]['options'] + " " + \
254 str(self.priority + idx) + " " + \
255 str(self.update_every)
257 # check if server has this datapoint
258 for line in self.charts[name]['lines']:
259 if line['name'] in data:
260 content += "\nDIMENSION " + line['name'] + " " + line['options']
263 data_stream += header + content + "\n"
272 def update(self, interval):
278 data = self._formatted_data()
283 for chart, dimensions in self.definitions.items():
284 header = "BEGIN " + self.__module__ + "_" + str(self.name) + "." + chart + " " + str(interval)
286 for dim in dimensions:
288 c += "\nSET " + dim + " = " + str(data[dim])
292 data_stream += header + c + "\nEND\n"