]> arthur.barton.de Git - netdata.git/blob - python.d/python_modules/base.py
cfb27abf1dfbebcb669e71ad0637d6699fcaf79d
[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         try:
127             f = urlopen(self.url, timeout=self.update_every)
128             raw = f.read().decode('utf-8')
129             f.close()
130         except Exception as e:
131             self.error(self.__module__, str(e))
132             return None
133         return raw
134
135     def _formatted_data(self):
136         """
137         Format data received from http request
138         :return: dict
139         """
140         return {}
141
142     def check(self):
143         """
144         Format configuration data and try to connect to server
145         :return: boolean
146         """
147         if self.name is None:
148             self.name = 'local'
149         else:
150             self.name = str(self.name)
151         try:
152             self.url = str(self.configuration['url'])
153         except (KeyError, TypeError):
154             pass
155
156         if self._formatted_data() is not None:
157             return True
158         else:
159             return False
160
161     def create(self):
162         """
163         Create charts
164         :return: boolean
165         """
166         for name in self.order:
167             if name not in self.charts:
168                 continue
169             self.definitions[name] = []
170             for line in self.charts[name]['lines']:
171                 self.definitions[name].append(line['name'])
172
173         idx = 0
174         data = self._formatted_data()
175         if data is None:
176             return False
177         for name in self.order:
178             header = "CHART " + \
179                      self.__module__ + "_" + \
180                      self.name + "." + \
181                      name + " " + \
182                      self.charts[name]['options'] + " " + \
183                      str(self.priority + idx) + " " + \
184                      str(self.update_every)
185             content = ""
186             # check if server has this datapoint
187             for line in self.charts[name]['lines']:
188                 if line['name'] in data:
189                     content += "DIMENSION " + line['name'] + " " + line['options'] + "\n"
190
191             if len(content) > 0:
192                 print(header)
193                 print(content)
194                 idx += 1
195
196         if idx == 0:
197             return False
198         return True
199
200     def update(self, interval):
201         """
202         Update charts
203         :param interval: int
204         :return: boolean
205         """
206         data = self._formatted_data()
207         if data is None:
208             return False
209
210         for chart, dimensions in self.definitions.items():
211             header = "BEGIN " + self.__module__ + "_" + str(self.name) + "." + chart + " " + str(interval)
212             c = ""
213             for dim in dimensions:
214                 try:
215                     c += "\nSET " + dim + " = " + str(data[dim])
216                 except KeyError:
217                     pass
218             if len(c) != 0:
219                 print(header + c)
220                 print("END")
221
222         return True