diff options
author | Ilya <ilyamaschenko@gmail.com> | 2017-05-21 00:08:15 +0900 |
---|---|---|
committer | Ilya <ilyamaschenko@gmail.com> | 2017-05-21 00:08:15 +0900 |
commit | 5496a1e1111c84fc1d455c3b8a74f491c5fae735 (patch) | |
tree | 53a6107773bd4fcdc23902afa9ff7daa2e6b779c /python.d | |
parent | a413d1f10525d6234c5293db473a048836a68af4 (diff) |
haproxy plugin: "idle percent" chart added; #2189
Diffstat (limited to 'python.d')
-rw-r--r-- | python.d/haproxy.chart.py | 196 |
1 files changed, 110 insertions, 86 deletions
diff --git a/python.d/haproxy.chart.py b/python.d/haproxy.chart.py index 779e060a95..4dcb4584bd 100644 --- a/python.d/haproxy.chart.py +++ b/python.d/haproxy.chart.py @@ -3,6 +3,12 @@ # Author: l2isbad from base import UrlService, SocketService +from collections import defaultdict +from re import compile as re_compile +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # default module values (can be overridden per job in `config`) # update_every = 2 @@ -10,7 +16,8 @@ priority = 60000 retries = 60 # charts order (can be overridden if you want less charts, or different order) -ORDER = ['fbin', 'fbout', 'fscur', 'fqcur', 'bbin', 'bbout', 'bscur', 'bqcur', 'health_sdown', 'health_bdown'] +ORDER = ['fbin', 'fbout', 'fscur', 'fqcur', 'bbin', 'bbout', 'bscur', 'bqcur', + 'health_sdown', 'health_bdown', 'health_idle'] CHARTS = { 'fbin': { 'options': [None, "Kilobytes In", "KB/s", 'frontend', 'haproxy_f.bin', 'line'], @@ -50,105 +57,92 @@ CHARTS = { 'lines': [ ]}, 'health_bdown': { - 'options': [None, "Is backend alive? 1 = DOWN", "failed backend", 'health', 'haproxy_hb.down', 'line'], + 'options': [None, "Is Backend Alive? 1 = DOWN", "failed backend", 'health', 'haproxy_hb.down', 'line'], 'lines': [ + ]}, + 'health_idle': { + 'options': [None, "The Ratio Of Polling Time Vs Total Time", "percent", 'health', 'haproxy.idle', 'line'], + 'lines': [ + ['idle', None, 'absolute'] ]} } +METRICS = ('bin', 'bout', 'scur', 'qcur') +REGEX = dict(url=re_compile(r'idle = (?P<idle>[0-9]+)'), socket=re_compile(r'Idle_pct: (?P<idle>[0-9]+)')) + class Service(UrlService, SocketService): def __init__(self, configuration=None, name=None): if 'socket' in configuration: SocketService.__init__(self, configuration=configuration, name=name) - self.poll_method = SocketService - self.request = 'show stat\n' + self.poll = SocketService + self.options_ = dict(regex=REGEX['socket'], stat='show stat\n', info='show info\n') else: UrlService.__init__(self, configuration=configuration, name=name) - self.poll_method = UrlService + self.poll = UrlService + self.options_ = dict(regex=REGEX['url'], stat=self.url, info=url_remove_params(self.url)) self.order = ORDER self.definitions = CHARTS - self.order_front = [_ for _ in ORDER if _.startswith('f')] - self.order_back = [_ for _ in ORDER if _.startswith('b')] - self.charts = True def check(self): - - if self.poll_method.check(self): - self.info('Plugin was started successfully. We are using %s.' % self.poll_method.__name__) + if self.poll.check(self): + self.create_charts() + self.info('We are using %s.' % self.poll.__name__) return True - else: - return False - - def create_charts(self, front_ends, back_ends): - for _ in enumerate(front_ends): - idx = _[0] - self.definitions['fbin']['lines'].append(['_'.join(['fbin', front_ends[idx]['# pxname']]), - front_ends[idx]['# pxname'], 'incremental', 1, 1024]) - self.definitions['fbout']['lines'].append(['_'.join(['fbout', front_ends[idx]['# pxname']]), - front_ends[idx]['# pxname'], 'incremental', 1, 1024]) - self.definitions['fscur']['lines'].append(['_'.join(['fscur', front_ends[idx]['# pxname']]), - front_ends[idx]['# pxname'], 'absolute']) - self.definitions['fqcur']['lines'].append(['_'.join(['fqcur', front_ends[idx]['# pxname']]), - front_ends[idx]['# pxname'], 'absolute']) - - for _ in enumerate(back_ends): - idx = _[0] - self.definitions['bbin']['lines'].append(['_'.join(['bbin', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'incremental', 1, 1024]) - self.definitions['bbout']['lines'].append(['_'.join(['bbout', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'incremental', 1, 1024]) - self.definitions['bscur']['lines'].append(['_'.join(['bscur', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'absolute']) - self.definitions['bqcur']['lines'].append(['_'.join(['bqcur', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'absolute']) - self.definitions['health_sdown']['lines'].append(['_'.join(['hsdown', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'absolute']) - self.definitions['health_bdown']['lines'].append(['_'.join(['hbdown', back_ends[idx]['# pxname']]), - back_ends[idx]['# pxname'], 'absolute']) + return False def _get_data(self): + to_netdata = dict() + self.request, self.url = self.options_['stat'], self.options_['stat'] + stat_data = self._get_stat_data() + self.request, self.url = self.options_['info'], self.options_['info'] + info_data = self._get_info_data(regex=self.options_['regex']) + + to_netdata.update(stat_data) + to_netdata.update(info_data) + return to_netdata or None + + def _get_stat_data(self): """ - Format data received from http request :return: dict """ - raw_data = self.poll_method._get_raw_data(self) + raw_data = self.poll._get_raw_data(self) if not raw_data: - return None - else: - raw_data = raw_data.splitlines() - - all_instances = [dict(zip(raw_data[0].split(','), - raw_data[_].split(','))) for _ in range(1, len(raw_data))] - - back_ends = list(filter(is_backend, all_instances)) - front_ends = list(filter(is_frontend, all_instances)) - servers = list(filter(is_server, all_instances)) - - if self.charts: - self.create_charts(front_ends, back_ends) - self.charts = False - - to_netdata = dict() - - for frontend in front_ends: - for idx in self.order_front: - to_netdata.update({'_'.join([idx, frontend['# pxname']]): - int(frontend[idx[1:]]) if frontend.get(idx[1:]) else 0}) - - for backend in back_ends: - for idx in self.order_back: - to_netdata.update({'_'.join([idx, backend['# pxname']]): - int(backend[idx[1:]]) if backend.get(idx[1:]) else 0}) - - for _ in enumerate(back_ends): - idx = _[0] - to_netdata.update({'_'.join(['hsdown', back_ends[idx]['# pxname']]): - len([server for server in servers if is_server_down(server, back_ends, idx)])}) - to_netdata.update({'_'.join(['hbdown', back_ends[idx]['# pxname']]): - 1 if is_backend_down(back_ends, idx) else 0}) + return dict() + + raw_data = raw_data.splitlines() + self.data = parse_data_([dict(zip(raw_data[0].split(','), raw_data[_].split(','))) + for _ in range(1, len(raw_data))]) + if not self.data: + return dict() + + stat_data = dict() + + for frontend in self.data['frontend']: + for metric in METRICS: + idx = frontend['# pxname'].replace('.', '_') + stat_data['_'.join(['frontend', metric, idx])] = frontend.get(metric) or 0 + + for backend in self.data['backend']: + name, idx = backend['# pxname'], backend['# pxname'].replace('.', '_') + stat_data['hsdown_' + idx] = len([server for server in self.data['servers'] + if server_down(server, name)]) + stat_data['hbdown_' + idx] = 1 if backend.get('status') == 'DOWN' else 0 + for metric in METRICS: + stat_data['_'.join(['backend', metric, idx])] = backend.get(metric) or 0 + return stat_data + + def _get_info_data(self, regex): + """ + :return: dict + """ + raw_data = self.poll._get_raw_data(self) + if not raw_data: + return dict() - return to_netdata + match = regex.search(raw_data) + return match.groupdict() if match else dict() @staticmethod def _check_raw_data(data): @@ -159,22 +153,52 @@ class Service(UrlService, SocketService): """ return not bool(data) - -def is_backend(backend): + def create_charts(self): + for front in self.data['frontend']: + name, idx = front['# pxname'], front['# pxname'].replace('.', '_') + for metric in METRICS: + self.definitions['f' + metric]['lines'].append(['_'.join(['frontend', metric, idx]), + name, 'incremental', 1, 1024]) + for back in self.data['backend']: + name, idx = back['# pxname'], back['# pxname'].replace('.', '_') + for metric in METRICS: + self.definitions['b' + metric]['lines'].append(['_'.join(['backend', metric, idx]), + name, 'incremental', 1, 1024]) + self.definitions['health_sdown']['lines'].append(['hsdown_' + idx, name, 'absolute']) + self.definitions['health_bdown']['lines'].append(['hbdown_' + idx, name, 'absolute']) + + +def parse_data_(data): + def is_backend(backend): return backend.get('svname') == 'BACKEND' and backend.get('# pxname') != 'stats' - -def is_frontend(frontend): + def is_frontend(frontend): return frontend.get('svname') == 'FRONTEND' and frontend.get('# pxname') != 'stats' - -def is_server(server): + def is_server(server): return not server.get('svname', '').startswith(('FRONTEND', 'BACKEND')) + if not data: + return None + + result = defaultdict(list) + for elem in data: + if is_backend(elem): + result['backend'].append(elem) + continue + elif is_frontend(elem): + result['frontend'].append(elem) + continue + elif is_server(elem): + result['servers'].append(elem) + + return result or None + -def is_server_down(server, back_ends, idx): - return server.get('# pxname') == back_ends[idx].get('# pxname') and server.get('status') == 'DOWN' +def server_down(server, backend_name): + return server.get('# pxname') == backend_name and server.get('status') == 'DOWN' -def is_backend_down(back_ends, idx): - return back_ends[idx].get('status') == 'DOWN' +def url_remove_params(url): + parsed = urlparse(url or str()) + return '%s://%s%s' % (parsed.scheme, parsed.netloc, parsed.path) |