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