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