Автоматизируем добавление сервера в Zabbix с назначением шаблонов

в 13:43, , рубрики: python, zabbix, zabbix api, системное администрирование, метки: , , , ,

Самый простой способ добавить новый сервер в Zabbix — через веб интерфейс. Засетапили новый сервер, пошли в web-морду, добавили машинку. Но, когда что-то делается руками, всегда можно забыть, особенно когда ввод серверов в строй происходит часто.
После очередного такого случая: что-то грохнулось, а потом выяснилось, что оно и не мониторилось никогда, возникла мысль автоматизировать процесс.

Изучение вопроса показало, что сервера можно автоматически добавлять несколькими способами:

  1. Через network discovery — задаем диапазон адресов, система переодически запускает скан портов и, по результатам обнаружения, позволяет автоматически добавить узел, а так же присвоить тот или иной шаблон на основе полученных данных.
    Этот вариант неплох, но, в моем случае, подсетей много, они меняются, каждый раз добавлять списки в заббикс было бы неудобно. К тому же, не всю информацию о системе можно выяснить, опрашивая порты.
  2. Авторегистрация. Можно настроить zabbix сервер так, чтоб при подключении нового агента (определяется по уникальному имени хоста) происходило добавление узла сети в ту или иную группу. Также, можно добавлять какой-то набор шаблонов.
    В данном случае — мы уходим от сканирования и диапазонов адресов, но получаем отсутствие гибкости в плане шаблонов
  3. Есть варианты — генерировать шаблон на лету, писать информацию прямо в базу и т.п — все как-то сложно.

Так как, при сетапе нового сервера конфиг агента генерируется автоматически, при помощи puppet, было решено использовать второй вариант — авторегистрацию, а потом накатывать нужные шаблоны через API заббикса.
На самом деле, наличие паппета не критично, можно закидывать скрипт на новый хост каким-либо другим путем и использовать что-то иное для определения параметров конфигурации хоста. Я приведу простой вариант, но додумать до более сложной схемы не составит труда.

Итак, алгоритм такой:

  1. При сетапе сервера, на него приезжает zabbix агент, в конфиге которого прописано FQDN имя хоста. Также, приезжает скрипт zabbixapi.py, который умеет работать с zabbix API.
  2. Агент стучится на сервер, при этом срабатывает авто регистрация, хост попадает в специальную группу AutoRegistered, к нему автоматом применяется шаблон «New Server», с единственным элементом данных host.relocate
  3. Zabbix сервер, выполняет опрос ключа host.relocate, чем запускает на выполнение скрипт zabbixapi.py.
  4. Скрипт, в свою очередь, определяет базовые параметры хоста, лезет через API на Zabbix сервер, удаляет там хост из группы AutoRegistered, удаляет шаблон New Server, добавляет в нужные группы и включает нужные шаблоны.

Некоторая сложность с промежуточным шаблоном нужна для того, чтоб уйти от лишнего запуска zabbixapi.py. По сути, сам zabbix сервер перестает его дергать как только хост уезжает из группы AutoRegistered. В любом другом случае пришлось бы или запускать скрипт постоянно с некоторой периодичностью, либо запускать его один раз, не имея возможности проверить результат.

Что нам нужно настроить, чтоб вся система заработала?

  1. Создаем группу узлов AutoRegister сети на сервере Zabbix
  2. Создаем шаблон Autoregister с единственным элементом данных
    host.relocate

    image
  3. Создаем правило обнаружения, присваивающее новому хосту шаблон и добаляющее его в группу AutoRegistered. В Web интерфейсе — Настройка -> Действия, выбираем справа вверху — источник события: Авторегистрация -> Новое дествие
    Host autoregistration

    image
  4. Добавляем в конфиг заббикс агента соответствующий UserParameter:
    UserParameter=host.relocate,/etc/zabbix/bin/zabbixapi.py > /dev/null 2>&1 ;echo $?
  5. Кладем по указаному пути скрипт:
    /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

Источник

Поделиться

* - обязательные к заполнению поля