Все мы любим мониторинг, особенно, когда он предоставляет полную информацию о сервисах. Так получилось, что на работе мы используем Graphite, а метрики в него засылаем с помощью collectd.
Иногда предоставленной функциональности collectd не хватает, и тогда на выручку приходят готовые плагины с гитхаба или самописные костыли. Сейчас я на примере простого плагина для сбора метрик freeradius расскажу как написать python плагин для collectd.
Для начала, определим конфиг плагина, например, в конфиге мы укажем, какой тип статистики мы будем собирать (не забудьте настроить радиус отображать статус), порт freeradius и пароль для подключения. Предполагается, что freeradius сервер запущен локально, т.е. там же где и collectd.
Вот пример конфига collectd
<LoadPlugin python>
Globals true
</LoadPlugin>
<Plugin python>
ModulePath "/usr/lib/collectd/plugins/python"
Import "freeradius"
<Module freeradius>
Statistics_Type 1
Host localhost
Port 18121
Secret adminsecret
</Module>
</Plugin>
Теперь создадим сам модуль в /usr/lib/collectd/plugins/python/freeradius.py
Импортируем нужные нам модули
import signal
import re
import subprocess
Создадим функцию, которая прочитает конфиг
config = {
'port': 18121,
'secret': 'adminsecret',
'host': 'localhost',
'timeout': 1,
'stat_type': 1
}
def configer(conf):
for node in conf.children:
if node.key == 'Host':
config['host'] = node.values[0]
if node.key == 'Port':
try:
config['port'] = int(node.values[0])
except:
collectd.warning("can not use provided port {0}"
.format(node.values[0]))
if node.key == 'Secret':
config['secret'] = node.values[0]
if node.key == 'Statistics_Type':
try:
config['stat_type'] = int(node.values[0])
except:
collectd.warning("can not use provided stats type {0}"
.format(node.values[0]))
Напишем основную функцию, которая собирает метрики с помощью radclient
def get_metrics():
# изменяем имена метрик
def _convert_name(name):
return name.replace('FreeRADIUS-Total-', '').replace('-', '_').lower()
# выполняем команду для получения метрик
cmd = ('echo "Message-Authenticator = 0x00, '
'FreeRADIUS-Statistics-Type = {stat_type}, '
'Response-Packet-Type = Access-Accept" | '
'radclient -t {timeout} -x {host}:{port} status {secret}'
.format(
timeout=config['timeout'],
port=config['port'],
secret=config['secret'],
host=config['host'],
stat_type=config['stat_type']))
child = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
stdout, stderr = child.communicate()
metrics = {}
# метрики находятся не во всех строках, выберем нужные
found_metrics = re.findall(r'(FreeRADIUS-Total.*) = (\d*)', stdout)
for metric_couple in found_metrics:
try:
metrics[_convert_name(metric_couple[0])] = int(metric_couple[1])
except:
collectd.warning(
"can not collect couple: {0}".format(', '.join(metric_couple)))
return metrics
Обвязки для основной функции и отправки метрик в collectd. Все метрики у нас будут типа «gauge».
def reader():
metrics = get_metrics()
for type, value in metrics.items():
dispatch_value(type, value)
def dispatch_value(val_type, value, metric_type='gauge'):
val = collectd.Values(plugin='freeradius')
val.type = metric_type
val.type_instance = val_type
val.values = [value]
val.dispatch()
Ну и вызов всего этого хозяйства. Если скрипт запущен вручную, то просто выведем информацию на stdout.
if __name__ == '__main__':
print(get_metrics())
else:
import collectd
# это нужно, чтоб collectd правильно обрабатывал запуск radclient
# http://giovannitorres.me/using-collectd-python-and-graphite-to-graph-slurm-partitions.html
collectd.register_init(signal.signal(signal.SIGCHLD, signal.SIG_DFL))
collectd.register_config(configer)
collectd.register_read(reader)
Перезапускаем collectd для применения изменений:
sudo service collectd restart
Смотрим на метрики:
Стоит заметить, что все метрики — это счетчики, после перезапуска freeradius они обнуляются.
Исходники плагина вы сможете найти здесь: https://github.com/kshcherban/collectd-freeradius