Самый простой способ добавить новый сервер в Zabbix — через веб интерфейс. Засетапили новый сервер, пошли в web-морду, добавили машинку. Но, когда что-то делается руками, всегда можно забыть, особенно когда ввод серверов в строй происходит часто.
После очередного такого случая: что-то грохнулось, а потом выяснилось, что оно и не мониторилось никогда, возникла мысль автоматизировать процесс.
Изучение вопроса показало, что сервера можно автоматически добавлять несколькими способами:
- Через network discovery — задаем диапазон адресов, система переодически запускает скан портов и, по результатам обнаружения, позволяет автоматически добавить узел, а так же присвоить тот или иной шаблон на основе полученных данных.
Этот вариант неплох, но, в моем случае, подсетей много, они меняются, каждый раз добавлять списки в заббикс было бы неудобно. К тому же, не всю информацию о системе можно выяснить, опрашивая порты. - Авторегистрация. Можно настроить zabbix сервер так, чтоб при подключении нового агента (определяется по уникальному имени хоста) происходило добавление узла сети в ту или иную группу. Также, можно добавлять какой-то набор шаблонов.
В данном случае — мы уходим от сканирования и диапазонов адресов, но получаем отсутствие гибкости в плане шаблонов - Есть варианты — генерировать шаблон на лету, писать информацию прямо в базу и т.п — все как-то сложно.
Так как, при сетапе нового сервера конфиг агента генерируется автоматически, при помощи puppet, было решено использовать второй вариант — авторегистрацию, а потом накатывать нужные шаблоны через API заббикса.
На самом деле, наличие паппета не критично, можно закидывать скрипт на новый хост каким-либо другим путем и использовать что-то иное для определения параметров конфигурации хоста. Я приведу простой вариант, но додумать до более сложной схемы не составит труда.
Итак, алгоритм такой:
- При сетапе сервера, на него приезжает zabbix агент, в конфиге которого прописано FQDN имя хоста. Также, приезжает скрипт zabbixapi.py, который умеет работать с zabbix API.
- Агент стучится на сервер, при этом срабатывает авто регистрация, хост попадает в специальную группу AutoRegistered, к нему автоматом применяется шаблон «New Server», с единственным элементом данных host.relocate
- Zabbix сервер, выполняет опрос ключа host.relocate, чем запускает на выполнение скрипт zabbixapi.py.
- Скрипт, в свою очередь, определяет базовые параметры хоста, лезет через API на Zabbix сервер, удаляет там хост из группы AutoRegistered, удаляет шаблон New Server, добавляет в нужные группы и включает нужные шаблоны.
Некоторая сложность с промежуточным шаблоном нужна для того, чтоб уйти от лишнего запуска zabbixapi.py. По сути, сам zabbix сервер перестает его дергать как только хост уезжает из группы AutoRegistered. В любом другом случае пришлось бы или запускать скрипт постоянно с некоторой периодичностью, либо запускать его один раз, не имея возможности проверить результат.
Что нам нужно настроить, чтоб вся система заработала?
- Создаем группу узлов AutoRegister сети на сервере Zabbix
- Создаем шаблон Autoregister с единственным элементом данных
host.relocate
- Создаем правило обнаружения, присваивающее новому хосту шаблон и добаляющее его в группу AutoRegistered. В Web интерфейсе — Настройка -> Действия, выбираем справа вверху — источник события: Авторегистрация -> Новое дествие
Host autoregistration
- Добавляем в конфиг заббикс агента соответствующий UserParameter:
UserParameter=host.relocate,/etc/zabbix/bin/zabbixapi.py > /dev/null 2>&1 ;echo $?
- Кладем по указаному пути скрипт:
/etc/zabbix/bin/zabbixapi.py
#!/usr/bin/env python import urllib, json, httplib, socket, sys, os def httpRequest(request): body = json.dumps(request) headers = {"Content-type": "application/json"} conn = httplib.HTTPConnection("zabbix.example.com", 80) conn.request("POST", "/api_jsonrpc.php", body, headers) response = conn.getresponse() data = response.read() conn.close() return data me = os.popen('facter --puppet fqdn').read().strip('n') # Авторизуемся на сервере authToken = httpRequest({"jsonrpc": "2.0","method":"user.authenticate","params":{"user":"API_USER","password":"API_PASSWORD"},"auth": None,"id":0}) authToken = json.loads(authToken).get('result', None) # Проверяем, находится дли сервер в группе Autoregistered hostInGroup = httpRequest({"jsonrpc":"2.0","method":"host.get","params":{"output":"extend","filter":{"host":me}, "groupids":"21"},"auth":authToken,"id":1}) hostInGroup = json.loads(hostInGroup).get('result', None) if hostInGroup: hostID = httpRequest({"jsonrpc":"2.0","method":"host.get","params":{"output":"extend","filter":{"host":me}, "groupids":"21"},"auth":authToken,"id":2}) hostID = json.loads(hostID) hostID = hostID['result'][0]['hostid'] print "Host is in AutoRegistered Group" print "HostID is",hostID print "We need to update host info, link templates and move host to proper group" # Используем facter для того, чтоб собрать инфо о системе print "Collecitng host info..." osFamily = os.popen('facter --puppet kernel').read().strip('n') virtualEnv = os.popen('facter --puppet virtual').read().strip('n') print "Base OS is",osFamily if osFamily == "FreeBSD": hostGroups = [{"groupid":"11"}, {"groupid":"6"}] hostTemplates = [{"templateid":"10077"}, {"templateid":"10090"}] if osFamily == "Linux": print "Checking virtualization..." if virtualEnv == "physical": print "Physical host detected, no virtualization" hostGroups = [{"groupid":"11"}, {"groupid":"2"}] hostTemplates = [{"templateid":"10076"}, {"templateid":"10090"}, {"templateid":"10127"}] if virtualEnv == "openvzve" or virtualEnv == "openvz": print "OpenVZ virtualization detected" hostGroups = [{"groupid":"11"}, {"groupid":"14"}] hostTemplates = [{"templateid":"10117"}, {"templateid":"10090"}] print "Detected groups are", hostGroups print "Detected templates are", hostTemplates updateHost = { "jsonrpc":"2.0", "method":"host.update", "params":{ "hostid":hostID, "ip":"127.0.0.1", "groups":hostGroups, "templates":hostTemplates, "templates_clear":"10263" }, "auth":authToken, "id":3 } print "Trying to update host..." httpRequest(updateHost) hostInterfaceID = httpRequest({"jsonrpc":"2.0","method":"hostinterface.get","params":{"output":"extend", "hostids":hostID},"auth":authToken,"id":4}) hostInterfaceID = json.loads(hostInterfaceID) hostInterfaceID = hostInterfaceID['result'][0]['interfaceid'] print "Host Interface ID detected as", hostInterfaceID updateHostInterface = { "jsonrpc":"2.0", "method":"hostinterface.update", "params":{ "interfaceid":hostInterfaceID, "dns": me, "useip": 0 }, "auth":authToken, "id":4 } print "Trying to update host interface to DNS based..." httpRequest(updateHostInterface) sys.exit(0) else: print "Host is absent in AutoRegistered Group" print "Nothing to do..." sys.exit(0)
В приведенном примере мы смотрим операционную систему: Linux или FreeBSD, смотрим наличие виртуализации OpenVZ — это влияет на выбор базовых шаблонов. При необходимости, функционал легко расширить, мознательно публикую простую версию, так как реализация может сильно отличаться от того, что используем мы.
Внимание, в скрипте захардкожены номера групп и шаблонов, в вашей инсталяции они будут другими, не забудьте поправить!
Автор: xenozauros