]> arthur.barton.de Git - netdata.git/blob - python.d/python_modules/base.py
fix some timeout issues
[netdata.git] / python.d / python_modules / base.py
1 # -*- coding: utf-8 -*-
2 # Description: prototypes for netdata python.d modules
3 # Author: Pawel Krupa (paulfantom)
4
5 from time import time
6 import sys
7 try:
8     from urllib.request import urlopen
9 except ImportError:
10     from urllib2 import urlopen
11
12
13 class BaseService(object):
14     """
15     Prototype of Service class.
16     Implemented basic functionality to run jobs by `python.d.plugin`
17     """
18     def __init__(self, configuration=None, name=None):
19         """
20         This needs to be initialized in child classes
21         :param configuration: dict
22         :param name: str
23         """
24         self.name = name
25         if configuration is None:
26             self.error("BaseService: no configuration parameters supplied. Cannot create Service.")
27             raise RuntimeError
28         else:
29             self._extract_base_config(configuration)
30             self.timetable = {}
31             self.create_timetable()
32             self.chart_name = ""
33
34     def _extract_base_config(self, config):
35         """
36         Get basic parameters to run service
37         Minimum config:
38             config = {'update_every':1,
39                       'priority':100000,
40                       'retries':0}
41         :param config: dict
42         """
43         self.update_every = int(config.pop('update_every'))
44         self.priority = int(config.pop('priority'))
45         self.retries = int(config.pop('retries'))
46         self.retries_left = self.retries
47         self.configuration = config
48
49     def create_timetable(self, freq=None):
50         """
51         Create service timetable.
52         `freq` is optional
53         Example:
54             timetable = {'last': 1466370091.3767564,
55                          'next': 1466370092,
56                          'freq': 1}
57         :param freq: int
58         """
59         if freq is None:
60             freq = self.update_every
61         now = time()
62         self.timetable = {'last': now,
63                           'next': now - (now % freq) + freq,
64                           'freq': freq}
65
66     @staticmethod
67     def error(msg, exception=""):
68         if exception != "":
69             exception = " " + str(exception).replace("\n", " ")
70         sys.stderr.write(str(msg)+exception+"\n")
71         sys.stderr.flush()
72
73     def check(self):
74         """
75         check() prototype
76         :return: boolean
77         """
78         self.error("Service " + str(self.__module__) + "doesn't implement check() function")
79         return False
80
81     def create(self):
82         """
83         create() prototype
84         :return: boolean
85         """
86         self.error("Service " + str(self.__module__) + "doesn't implement create() function?")
87         return False
88
89     def update(self, interval):
90         """
91         update() prototype
92         :param interval: int
93         :return: boolean
94         """
95         self.error("Service " + str(self.__module__) + "doesn't implement update() function")
96         return False
97
98
99 class UrlService(BaseService):
100     def __init__(self, configuration=None, name=None):
101         self.charts = {}
102         # charts definitions in format:
103         # charts = {
104         #    'chart_name_in_netdata': {
105         #        'options': "parameters defining chart (passed to CHART statement)",
106         #        'lines': [
107         #           { 'name': 'dimension_name',
108         #             'options': 'dimension parameters (passed to DIMENSION statement)"
109         #           }
110         #        ]}
111         #    }
112         self.order = []
113         self.definitions = {}
114         # definitions are created dynamically in create() method based on 'charts' dictionary. format:
115         # definitions = {
116         #     'chart_name_in_netdata' : [ charts['chart_name_in_netdata']['lines']['name'] ]
117         # }
118         self.url = ""
119         BaseService.__init__(self, configuration=configuration, name=name)
120
121     def _get_data(self):
122         """
123         Get raw data from http request
124         :return: str
125         """
126         raw = None
127         try:
128             f = urlopen(self.url, timeout=self.update_every)
129             raw = f.read().decode('utf-8')
130         except Exception as e:
131             self.error(self.__module__, str(e))
132         finally:
133             try:
134                 f.close()
135             except:
136                 pass
137         return raw
138
139     def _formatted_data(self):
140         """
141         Format data received from http request
142         :return: dict
143         """
144         return {}
145
146     def check(self):
147         """
148         Format configuration data and try to connect to server
149         :return: boolean
150         """
151         if self.name is None:
152             self.name = 'local'
153         else:
154             self.name = str(self.name)
155         try:
156             self.url = str(self.configuration['url'])
157         except (KeyError, TypeError):
158             pass
159
160         if self._formatted_data() is not None:
161             return True
162         else:
163             return False
164
165     def create(self):
166         """
167         Create charts
168         :return: boolean
169         """
170         for name in self.order:
171             if name not in self.charts:
172                 continue
173             self.definitions[name] = []
174             for line in self.charts[name]['lines']:
175                 self.definitions[name].append(line['name'])
176
177         idx = 0
178         data = self._formatted_data()
179         if data is None:
180             return False
181         for name in self.order:
182             header = "CHART " + \
183                      self.__module__ + "_" + \
184                      self.name + "." + \
185                      name + " " + \
186                      self.charts[name]['options'] + " " + \
187                      str(self.priority + idx) + " " + \
188                      str(self.update_every)
189             content = ""
190             # check if server has this datapoint
191             for line in self.charts[name]['lines']:
192                 if line['name'] in data:
193                     content += "DIMENSION " + line['name'] + " " + line['options'] + "\n"
194
195             if len(content) > 0:
196                 print(header)
197                 print(content)
198                 idx += 1
199
200         if idx == 0:
201             return False
202         return True
203
204     def update(self, interval):
205         """
206         Update charts
207         :param interval: int
208         :return: boolean
209         """
210         data = self._formatted_data()
211         if data is None:
212             return False
213
214         for chart, dimensions in self.definitions.items():
215             header = "BEGIN " + self.__module__ + "_" + str(self.name) + "." + chart + " " + str(interval)
216             c = ""
217             for dim in dimensions:
218                 try:
219                     c += "\nSET " + dim + " = " + str(data[dim])
220                 except KeyError:
221                     pass
222             if len(c) != 0:
223                 print(header + c)
224                 print("END")
225
226         return True