0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-28 06:32:30 +00:00
netdata_netdata/collectors/python.d.plugin/unbound/unbound.chart.py
Ilya Mashchenko 28e0d7526d
python loaders cleanup ()
<!--
Describe the change in summary section, including rationale and degin decisions.
Include "Fixes #nnn" if you are fixing an existing issue.

In "Component Name" section write which component is changed in this PR. This
will help us review your PR quicker.

If you have more information you want to add, write them in "Additional
Information" section. This is usually used to help others understand your
motivation behind this change. A step-by-step reproduction of the problem is
helpful if there is no related issue.
-->

##### Summary
python loaders module cleanup
 - move `load_module` to `python.d.plugin`
 - use `load_config` in unbound module instead of YamlOrderedLoad class
 - remove all classes from `loaders.py` - no longer used

##### Component Name
[`collectors/python.d.plugin/python_modules/bases/loaders`](https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/python_modules/bases/loaders.py)
##### Additional Information
2019-03-11 16:58:00 +03:00

283 lines
12 KiB
Python

# -*- coding: utf-8 -*-
# Description: unbound netdata python.d module
# Author: Austin S. Hemmelgarn (Ferroin)
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import sys
from copy import deepcopy
from bases.FrameworkServices.SocketService import SocketService
from bases.loaders import load_config
PRECISION = 1000
ORDER = [
'queries',
'recursion',
'reqlist',
]
CHARTS = {
'queries': {
'options': [None, 'Queries Processed', 'queries', 'Unbound', 'unbound.queries', 'line'],
'lines': [
['ratelimit', 'ratelimited', 'absolute', 1, 1],
['cachemiss', 'cache_miss', 'absolute', 1, 1],
['cachehit', 'cache_hit', 'absolute', 1, 1],
['expired', 'expired', 'absolute', 1, 1],
['prefetch', 'prefetched', 'absolute', 1, 1],
['recursive', 'recursive', 'absolute', 1, 1]
]
},
'recursion': {
'options': [None, 'Recursion Timings', 'seconds', 'Unbound', 'unbound.recursion', 'line'],
'lines': [
['recursive_avg', 'average', 'absolute', 1, PRECISION],
['recursive_med', 'median', 'absolute', 1, PRECISION]
]
},
'reqlist': {
'options': [None, 'Request List', 'items', 'Unbound', 'unbound.reqlist', 'line'],
'lines': [
['reqlist_avg', 'average_size', 'absolute', 1, 1],
['reqlist_max', 'maximum_size', 'absolute', 1, 1],
['reqlist_overwritten', 'overwritten_requests', 'absolute', 1, 1],
['reqlist_exceeded', 'overruns', 'absolute', 1, 1],
['reqlist_current', 'current_size', 'absolute', 1, 1],
['reqlist_user', 'user_requests', 'absolute', 1, 1]
]
}
}
# These get added too if we are told to use extended stats.
EXTENDED_ORDER = ['cache']
EXTENDED_CHARTS = {
'cache': {
'options': [None, 'Cache Sizes', 'items', 'Unbound', 'unbound.cache', 'stacked'],
'lines': [
['cache_message', 'message_cache', 'absolute', 1, 1],
['cache_rrset', 'rrset_cache', 'absolute', 1, 1],
['cache_infra', 'infra_cache', 'absolute', 1, 1],
['cache_key', 'dnssec_key_cache', 'absolute', 1, 1],
['cache_dnscss', 'dnscrypt_Shared_Secret_cache', 'absolute', 1, 1],
['cache_dnscn', 'dnscrypt_Nonce_cache', 'absolute', 1, 1]
]
}
}
# This is used as a templates for the per-thread charts.
PER_THREAD_CHARTS = {
'_queries': {
'options': [None, '{longname} Queries Processed', 'queries', 'Queries Processed',
'unbound.threads.queries', 'line'],
'lines': [
['{shortname}_ratelimit', 'ratelimited', 'absolute', 1, 1],
['{shortname}_cachemiss', 'cache_miss', 'absolute', 1, 1],
['{shortname}_cachehit', 'cache_hit', 'absolute', 1, 1],
['{shortname}_expired', 'expired', 'absolute', 1, 1],
['{shortname}_prefetch', 'prefetched', 'absolute', 1, 1],
['{shortname}_recursive', 'recursive', 'absolute', 1, 1]
]
},
'_recursion': {
'options': [None, '{longname} Recursion Timings', 'seconds', 'Recursive Timings',
'unbound.threads.recursion', 'line'],
'lines': [
['{shortname}_recursive_avg', 'average', 'absolute', 1, PRECISION],
['{shortname}_recursive_med', 'median', 'absolute', 1, PRECISION]
]
},
'_reqlist': {
'options': [None, '{longname} Request List', 'items', 'Request List', 'unbound.threads.reqlist', 'line'],
'lines': [
['{shortname}_reqlist_avg', 'average_size', 'absolute', 1, 1],
['{shortname}_reqlist_max', 'maximum_size', 'absolute', 1, 1],
['{shortname}_reqlist_overwritten', 'overwritten_requests', 'absolute', 1, 1],
['{shortname}_reqlist_exceeded', 'overruns', 'absolute', 1, 1],
['{shortname}_reqlist_current', 'current_size', 'absolute', 1, 1],
['{shortname}_reqlist_user', 'user_requests', 'absolute', 1, 1]
]
}
}
# This maps the Unbound stat names to our names and precision requiremnets.
STAT_MAP = {
'total.num.queries_ip_ratelimited': ('ratelimit', 1),
'total.num.cachehits': ('cachehit', 1),
'total.num.cachemiss': ('cachemiss', 1),
'total.num.zero_ttl': ('expired', 1),
'total.num.prefetch': ('prefetch', 1),
'total.num.recursivereplies': ('recursive', 1),
'total.requestlist.avg': ('reqlist_avg', 1),
'total.requestlist.max': ('reqlist_max', 1),
'total.requestlist.overwritten': ('reqlist_overwritten', 1),
'total.requestlist.exceeded': ('reqlist_exceeded', 1),
'total.requestlist.current.all': ('reqlist_current', 1),
'total.requestlist.current.user': ('reqlist_user', 1),
'total.recursion.time.avg': ('recursive_avg', PRECISION),
'total.recursion.time.median': ('recursive_med', PRECISION),
'msg.cache.count': ('cache_message', 1),
'rrset.cache.count': ('cache_rrset', 1),
'infra.cache.count': ('cache_infra', 1),
'key.cache.count': ('cache_key', 1),
'dnscrypt_shared_secret.cache.count': ('cache_dnscss', 1),
'dnscrypt_nonce.cache.count': ('cache_dnscn', 1)
}
# Same as above, but for per-thread stats.
PER_THREAD_STAT_MAP = {
'{shortname}.num.queries_ip_ratelimited': ('{shortname}_ratelimit', 1),
'{shortname}.num.cachehits': ('{shortname}_cachehit', 1),
'{shortname}.num.cachemiss': ('{shortname}_cachemiss', 1),
'{shortname}.num.zero_ttl': ('{shortname}_expired', 1),
'{shortname}.num.prefetch': ('{shortname}_prefetch', 1),
'{shortname}.num.recursivereplies': ('{shortname}_recursive', 1),
'{shortname}.requestlist.avg': ('{shortname}_reqlist_avg', 1),
'{shortname}.requestlist.max': ('{shortname}_reqlist_max', 1),
'{shortname}.requestlist.overwritten': ('{shortname}_reqlist_overwritten', 1),
'{shortname}.requestlist.exceeded': ('{shortname}_reqlist_exceeded', 1),
'{shortname}.requestlist.current.all': ('{shortname}_reqlist_current', 1),
'{shortname}.requestlist.current.user': ('{shortname}_reqlist_user', 1),
'{shortname}.recursion.time.avg': ('{shortname}_recursive_avg', PRECISION),
'{shortname}.recursion.time.median': ('{shortname}_recursive_med', PRECISION)
}
# Used to actually generate per-thread charts.
def _get_perthread_info(thread):
sname = 'thread{0}'.format(thread)
lname = 'Thread {0}'.format(thread)
charts = dict()
order = []
statmap = dict()
for item in PER_THREAD_CHARTS:
cname = '{0}{1}'.format(sname, item)
chart = deepcopy(PER_THREAD_CHARTS[item])
chart['options'][1] = chart['options'][1].format(longname=lname)
for index, line in enumerate(chart['lines']):
chart['lines'][index][0] = line[0].format(shortname=sname)
order.append(cname)
charts[cname] = chart
for key, value in PER_THREAD_STAT_MAP.items():
statmap[key.format(shortname=sname)] = (value[0].format(shortname=sname), value[1])
return charts, order, statmap
class Service(SocketService):
def __init__(self, configuration=None, name=None):
# The unbound control protocol is always TLS encapsulated
# unless it's used over a UNIX socket, so enable TLS _before_
# doing the normal SocketService initialization.
configuration['tls'] = True
self.port = 8935
SocketService.__init__(self, configuration, name)
self.ext = self.configuration.get('extended', None)
self.ubconf = self.configuration.get('ubconf', None)
self.perthread = self.configuration.get('per_thread', False)
self.threads = None
self.order = deepcopy(ORDER)
self.definitions = deepcopy(CHARTS)
self.request = 'UBCT1 stats\n'
self.statmap = deepcopy(STAT_MAP)
self._parse_config()
self._auto_config()
self.debug('Extended stats: {0}'.format(self.ext))
self.debug('Per-thread stats: {0}'.format(self.perthread))
if self.ext:
self.order = self.order + EXTENDED_ORDER
self.definitions.update(EXTENDED_CHARTS)
if self.unix_socket:
self.debug('Using unix socket: {0}'.format(self.unix_socket))
else:
self.debug('Connecting to: {0}:{1}'.format(self.host, self.port))
self.debug('Using key: {0}'.format(self.key))
self.debug('Using certificate: {0}'.format(self.cert))
def _auto_config(self):
if self.ubconf and os.access(self.ubconf, os.R_OK):
self.debug('Unbound config: {0}'.format(self.ubconf))
conf = dict()
try:
conf = load_config(self.ubconf)
except Exception as error:
self.error("error on loading '{0}' : {1}".format(self.ubconf, error))
if self.ext is None:
if 'extended-statistics' in conf['server']:
self.ext = conf['server']['extended-statistics']
if 'remote-control' in conf:
if conf['remote-control'].get('control-use-cert', False):
self.key = self.key or conf['remote-control'].get('control-key-file')
self.cert = self.cert or conf['remote-control'].get('control-cert-file')
self.port = self.port or conf['remote-control'].get('control-port')
else:
self.unix_socket = self.unix_socket or conf['remote-control'].get('control-interface')
else:
self.debug('Unbound configuration not found.')
if not self.key:
self.key = '/etc/unbound/unbound_control.key'
if not self.cert:
self.cert = '/etc/unbound/unbound_control.pem'
if not self.port:
self.port = 8953
def _generate_perthread_charts(self):
tmporder = list()
for thread in range(0, self.threads):
charts, order, statmap = _get_perthread_info(thread)
tmporder.extend(order)
self.definitions.update(charts)
self.statmap.update(statmap)
self.order.extend(sorted(tmporder))
def check(self):
# Check if authentication is working.
self._connect()
result = bool(self._sock)
self._disconnect()
# If auth works, and we need per-thread charts, query the server
# to see how many threads it's using. This somewhat abuses the
# SocketService API to get the data we need.
if result and self.perthread:
tmp = self.request
if sys.version_info[0] < 3:
self.request = 'UBCT1 status\n'
else:
self.request = b'UBCT1 status\n'
raw = self._get_raw_data()
for line in raw.splitlines():
if line.startswith('threads'):
self.threads = int(line.split()[1])
self._generate_perthread_charts()
break
if self.threads is None:
self.info('Unable to auto-detect thread counts, disabling per-thread stats.')
self.perthread = False
self.request = tmp
return result
@staticmethod
def _check_raw_data(data):
# The server will close the connection when it's done sending
# data, so just keep looping until that happens.
return False
def _get_data(self):
raw = self._get_raw_data()
data = dict()
tmp = dict()
for line in raw.splitlines():
stat = line.split('=')
tmp[stat[0]] = stat[1]
for item in self.statmap:
if item in tmp:
data[self.statmap[item][0]] = float(tmp[item]) * self.statmap[item][1]
return data