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