Мониторим клиентские ПК в Microsoft AD с помощью Zabbix. Часть 2 — Шаблон, скрипты и LLD

в 21:02, , рубрики: python, zabbix, системное администрирование

Zabbix обладает достаточно большими возможностями из коробки, но этого иногда может не хватать, и на этот случай есть возможность использовать сторонний скрипт для обработки событий(Report problems to script). К самому скрипту вернемся чуть позже, пока опишу только основную мысль, чтобы было понятно, что и зачем мы добавляем в description триггеров. Скрипт парсит тело письма и ищет строчку MYparsBLOCK:funcname: если находит, то выполняет funcname(), если не находит, — то просто отправляет оповещение. Разумнее всего добавлять это в описание триггеров, поэтому в Actions-Event source –Triggers-operations необходимо добавить в default message — {TRIGGER.DESCRIPTION}.

Готовим template

Необходимо создать шаблон для мониторинга, который будет прикрепляться к новым хостам, с помощью правила авторегистрации. (Action-> Event Source->Auto registration->Link to templates: Win_monitor) Я взял за основу стандартный шаблон zabbix для windows, а также APC Smart UPS Monitoring откуда-то с просторов, выкинул из них все лишнее и добавил то, что нужно мне.

Items
Agent ping
agent.ping
Average disk queue length
perf_counter[234(_Total)1400]
Average disk read queue length
perf_counter[234(_Total)1402]
Average disk write queue length
perf_counter[234(_Total)1404]
CPU Model
wmi.get[ROOTcimv2,SELECT Name FROM Win32_Processor]
CPU Utilization
perf_counter[238(_Total)6]
APC Smart UPS Monitoring: Driver Caption
wmi.get[ROOTcimv2,SELECT Caption FROM Win32_PNPEntity WHERE PNPDeviceID LIKE '%VID_051D&PID_0002%' OR Service LIKE '%hidbatt%']
Free disk space on C:
vfs.fs.size[c:,free]
Free disk space on C: (percentage)
vfs.fs.size[c:,pfree]
Free memory
vm.memory.size[free]
Host name of zabbix_agentd running
agent.hostname
Mainboard Model
wmi.get[ROOTcimv2,SELECT Product FROM Win32_BaseBoard]
System information
system.uname
System uptime
system.uptime
Total disk space on C:
vfs.fs.size[c:,total]
Total memory
vm.memory.size[total]
Used disk space on C:
vfs.fs.size[c:,used]
APC Smart UPS Monitoring: Время работы от батареи
battery.runtime
APC Smart UPS Monitoring: Дата замены батареи
battery.mfr.date
APC Smart UPS Monitoring: Заряд батареи
battery.charge
APC Smart UPS Monitoring: Модель UPS
ups.model
APC Smart UPS Monitoring: Нагрузка
ups.load
APC Smart UPS Monitoring: Напряжение (на вход)
input.voltage
APC Smart UPS Monitoring: Напряжение (на выход)
output.voltage
APC Smart UPS Monitoring: Статус UPS
ups.status
APC Smart UPS Monitoring: Статус бипера
ups.beeper.status
APC Smart UPS Monitoring: Температура батареи
battery.temperature

Triggers

Free disk space is less than 1GB on volume C: {HOST.NAME}
{Win_monitor:vfs.fs.size[c:,free].last(0)}<1073741824
Lack of free memory on {HOST.NAME}
{Win_monitor:vm.memory.size[free].avg(30m)}<10000000
APC Smart UPS Monitoring: robot_ Не приходят данные с ИБП {HOST.NAME}
{Win_monitor:ups.status.str(Error)}=1 and {Win_monitor:wmi.get[ROOTcimv2,SELECT Caption FROM Win32_PNPEntity WHERE PNPDeviceID LIKE '%VID_051D&PID_0002%' OR Service LIKE '%hidbatt%'].strlen()}>1
Zabbix agent on {HOST.NAME} is unreachable for 7 days
{Win_monitor:agent.ping.nodata(7d)}=1
APC Smart UPS Monitoring: Батарея не заряжается на {HOST.NAME}
{Win_monitor:battery.charge.max(#120)}<90
APC Smart UPS Monitoring: Выключен бипер на {HOST.NAME}
{Win_monitor:ups.beeper.status.str(disabled)}=1
APC Smart UPS Monitoring: Низкое время работы от батареи на {HOST.NAME}
{Win_monitor:battery.runtime.last(0)}<5 and {Win_monitor:ups.model.str(Smart)}=1

Я уже писал, что реализация затеи с мониторингом упсов оказалась не такой гладкой, как хотелось бы. Упсы постоянно отваливаются, помогает перезапуск драйвера с помощью утилиты devcon, поэтому добавляем в триггер(в description) «robot_ Не приходят данные с ИБП {HOST.NAME}» наш блок с функцией nutp. Ну и никому не нужны мертвые хосты в мониторинге, потому в триггер «Zabbix agent on {HOST.NAME} is unreachable for 7 days» добавляем функцию remove_offline, которая будет удалять хосты из zabbix:

MYparsBLOCK:nutpt:
HIP:{HOST.DNS}
MYparsBLOCK:remove_offline:
HID:{HOST.NAME}

Low-level discovery

Что касается смартов, то обычные Items и Triggers нам не подойдут, т. к. на разных машинах может быть разное количество смартов. В zabbix есть возможность сделать item и trigger prototype, которые будут создаваться для списка объектов, полученных с помощью low-level discovery rules, подробнее можно почитать здесь. Чтобы правило работало, нам нужно написать скрипт/приложение, которое будет при запуске выдавать список хардов в специальном JSON формате. Сначала я сделал скрипт на powershell, но на части машин скрипт периодически не успевал выполниться за 30 секунд, из-за того, что сам powershell очень долго инициализируется. Пришлось отказаться от powershell и сделать exe приложение на c#(я его не знаю, но он показался достаточно простым, чтобы переписать скрипт). Приложение с помощью smartctl получает список hdd, убирает дублирующиеся(по серийникам) и выводит в нужном нам формате.

hddscan.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace hdd_scan
{
    class Program
    {
        static string[] smartctl(string arg)
        {
            Process p = new Process();
            p.StartInfo.FileName = "C:\Program Files\Zabbix\extra\smart\smartctl.exe";
            p.StartInfo.Arguments = arg;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.Start();
            string output = p.StandardOutput.ReadToEnd();
            string[] list = output.Split('n');
            p.WaitForExit();
            return list;
        }

        static void Main(string[] args)
        {
            try
            {

                string[] hddlist = smartctl("--scan");
                Dictionary<string, string> psarr = new Dictionary<string, string>();
                string pattern = @"^(?<1>/[w]+)/(?<xer>[S]+)s";
                foreach (string hdd in hddlist)
                {
                    var match = Regex.Match(hdd, pattern);
                    if (match.Success)
                    {
                        string shdd = match.Groups["xer"].Value;
                        string[] tmp = smartctl("-a " + shdd);
                        foreach (string line in tmp)
                        {
                            if (line.Contains("Serial") == true)
                            {
                                string[] serials = Regex.Split(line, @"^SerialsNumber:s+");
                                if (serials.Length < 2) continue;
                                string serial = serials[1];

                                if (!psarr.ContainsValue(serial))
                                {
                                    psarr.Add(shdd, serial);
                                }
                            }
                        }
                    }
                }

                //Starting output
                int cnt = 0;
                Console.WriteLine("{n");
                Console.WriteLine("t"data":[nn");
                foreach (KeyValuePair<string, string> kvp in psarr)
                {
                    string[] flist = smartctl("-a "+kvp.Key);
                    string checkstring = "A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.";
                   
                    //
                    bool test= false;
                    for (int i = 0; i < flist.Length; i++)
                    {
                        if (flist[i].Contains(checkstring))
                        {
                            test = true;
                        }
                    }
                    if (!test)
                    {
                        cnt++;
                        if (cnt > 1)
                        {
                            Console.WriteLine("t,n");
                        }
                        Console.WriteLine("t{n");
                        Console.WriteLine("tt"{{#HDDNAME}}":"{0}"n", kvp.Key);
                        Console.WriteLine("t}n");
                    }
                }

                Console.WriteLine("nt]n");
                Console.WriteLine("}n");

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
}
        }
    }
}

Да, на некоторых машинах смарт может быть отключен, поэтому добавим еще одну функцию firstrun, и разместим ее в сообщение правила авторегистрации Action-> Event Source->Auto registration добавляем пункт Send message to ...via script, в тело размещаем:

MYparsBLOCK:firstrun:
HIP:{HOST.IP}

В шаблоне создаем discovey rule — smart.discovery.

Item prototypes

smart_{#HDDNAME}_CRC_Error_Count
smart[{#HDDNAME},crc]
smart_{#HDDNAME}_Current_Pending_Sector
smart[{#HDDNAME},pend]
smart_{#HDDNAME}_Health_Status
smart[{#HDDNAME},health]
smart_{#HDDNAME}_Model
smart[{#HDDNAME},model]
smart_{#HDDNAME}_Reallocated_Sector_Ct
smart[{#HDDNAME},realloc]
smart_{#HDDNAME}_Temperature
smart[{#HDDNAME},temp]

Triggers prototypes
HDD: Current_Pending_Sector на {#HDDNAME} {HOST.NAME} больше 5
{Win_monitor:smart[{#HDDNAME},pend].last()}>5
HDD: Reallocated_Sector_Ct на {#HDDNAME} {HOST.NAME} больше 5
{Win_monitor:smart[{#HDDNAME},realloc].last()}>5
HDD: Температура жесткого диска выше 55 градусов на {#HDDNAME} {HOST.NAME}
{Win_monitor:smart[{#HDDNAME},temp].last()}>55
HDD: зафиксирован рост CRC_Error_Count на {#HDDNAME} {HOST.NAME}
{Win_monitor:smart[{#HDDNAME},crc].change()}>0
HDD: зафиксирован рост Current_Pending_Sector на {#HDDNAME} {HOST.NAME}
{Win_monitor:smart[{#HDDNAME},pend].change()}>0 and {Win_monitor:smart[{#HDDNAME},pend].last()}>6
HDD: зафиксирован рост Reallocated_Sector_Ct на {#HDDNAME} {HOST.NAME}
{Win_monitor:smart[{#HDDNAME},realloc].change()}>0 and {Win_monitor:smart[{#HDDNAME},realloc].last()}>6

В description прототипов добавляем функцию hddsmart, она будет добавлять модель HDD в тело сообщения триггера, чтобы было понятно, о каком именно харде идет речь, т. к. smartctl использует в качестве имени sda, sdb и т. п.

MYparsBLOCK:hddsmart:
HIP:{HOST.DNS}:KKEY:smart[{#HDDNAME},model]

Большинство параметров агент не понимает, поэтому в конфигурации клиента обязательно нужно прописать все UserParameter.

UserParameter

UserParameter=battery.charge,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.charge
UserParameter=battery.charge.low,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.charge.low
UserParameter=battery.charge.warning,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.charge.warning
UserParameter=battery.mfr.date,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.mfr.date
UserParameter=battery.runtime,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.runtime
UserParameter=battery.runtime.low,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.runtime.low
UserParameter=battery.temperature,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.temperature
UserParameter=battery.type,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.type
UserParameter=battery.voltage,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.voltage
UserParameter=battery.voltage.nominal,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost battery.voltage.nominal
UserParameter=input.sensitivity,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost input.sensitivity
UserParameter=input.transfer.high,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost input.transfer.high
UserParameter=input.transfer.low,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost input.transfer.low
UserParameter=input.voltage,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost input.voltage
UserParameter=output.current,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost output.current
UserParameter=output.frequency,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost output.frequency
UserParameter=output.voltage,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost output.voltage
UserParameter=output.voltage.nominal,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost output.voltage.nominal
UserParameter=ups.beeper.status,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.beeper.status
UserParameter=ups.delay.shutdown,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.delay.shutdown
UserParameter=ups.delay.start,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.delay.start
UserParameter=ups.firmware,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.firmware
UserParameter=ups.firmware.aux,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.firmware.aux
UserParameter=ups.load,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.load
UserParameter=ups.mfr,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.mfr
UserParameter=ups.mfr.date,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.mfr.date
UserParameter=ups.model,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.model
UserParameter=ups.productid,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.productid
UserParameter=ups.serial,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.serial
UserParameter=ups.status,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.status
UserParameter=ups.test.result,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.test.result
UserParameter=ups.timer.reboot,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.timer.reboot
UserParameter=ups.timer.shutdown,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.timer.shutdown
UserParameter=ups.timer.start,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.timer.start
UserParameter=ups.vendorid,"c:Program Files (x86)NUTbinupsc.exe" primary@localhost ups.timer.vendorid
UserParameter=smart[*],"C:Program FilesZabbixcmdsmart.cmd" "$1" $2
UserParameter=smart.discovery, "C:Program FilesZabbixcmdhdd_scan.exe"

smart.cmd

@echo off
rem  use smart.cmd <disk> < parameter>   smart.cmd sda health
cd "C:Program FilesZabbixcmd"
if %2==health ("C:Program FilesZabbixextrasmartsmartctl.exe" -H %1 | grep result | awk "{print $6}") 
if %2==model ("C:Program FilesZabbixextrasmartsmartctl.exe" -i %1 | grep "Device Model" |  awk -F"Device Model:" "{print $2}")
if %2==realloc ("C:Program FilesZabbixextrasmartsmartctl.exe" --attributes %1 | grep Reallocated_S | awk "{print $10}")
if %2==crc ("C:Program FilesZabbixextrasmartsmartctl.exe" --attributes %1 | grep CRC | awk "{print $10}")
if %2==pend ("C:Program FilesZabbixextrasmartsmartctl.exe" --attributes %1 | grep Pend | awk "{print $10}")
if %2==temp ("C:Program FilesZabbixextrasmartsmartctl.exe" --attributes %1 | grep Temperature_Celsius | awk "{print $10}")

Alert скрипт

Собственно сам скрипт, который будет отправлять письма и выполнять наши функции:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#/var/lib/zabbixsrv/alertscripts/mail.py
import string
import re
import subprocess
import sys
import time
import os

# функция для отправки писем, ищем готовую, переделываем для себя
def send_mail(recipient, subject, body):
    import smtplib
    from email.MIMEText import MIMEText
    from email.Header import Header
    from email.Utils import formatdate
    encoding='utf-8'
    SMTP_SERVER = 'smtp'
    SENDER_NAME = u'Zabbix Alert'
    session = None
    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    msg['From'] = Header(SENDER_NAME, encoding)
    msg['To'] = recipient
    msg['Date'] = formatdate()
    try:
        session = smtplib.SMTP(SMTP_SERVER)
        session.sendmail(SENDER_NAME, recipient, msg.as_string())
    except Exception as e:
        raise e
    finally:
                # close session
        if session:
            session.quit()

# Zabbix не должен ждать выполнения скрипта, поэтому делаем так, чтобы скрипт работал в фоне.(ищем  готовый пример, переделываем для себя) 			
def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
     try:
         pid = os.fork()
         if pid > 0:
             sys.exit(0)
     except OSError, e:
         sys.stderr.write("fork #1 failed: (%d) %sn" % (e.errno, e.strerror))
         sys.exit(1)
     os.chdir("/")
     os.umask(0)
     os.setsid()
     try:
         pid = os.fork()
         if pid > 0:
            sys.exit(0)
     except OSError, e:
        sys.stderr.write("fork #2 failed: (%d) %sn" % (e.errno, e.strerror))
        sys.exit(1)
     for f in sys.stdout, sys.stderr: f.flush()
     si = file(stdin, 'r')
     so = file(stdout, 'a+')
     se = file(stderr, 'a+', 0)
     os.dup2(si.fileno(), sys.stdin.fileno())
     os.dup2(so.fileno(), sys.stdout.fileno())
     os.dup2(se.fileno(), sys.stderr.fileno())
#Добавляем в оповещение модель харда 
def hddsmart():
    m=re.search('MYparsBLOCK:S+:s+HIP:(?P<hostip>S+):KKEY:(?P<kkey>S+)',a3)
    hostip,kkey= m.group('hostip'),m.group('kkey')
    p = subprocess.Popen('zabbix_get -s '+hostip+' -k '+kkey, shell=True,stdout=subprocess.PIPE)
    bb = a3[0:string.find(a3,'MYparsBLOCK')] + 'HDD: ' + p.stdout.read()
    send_mail(sys.argv[1],a2,bb)
#Подготовка списка машин на удаление. Удалять будем через api в отдельном скрипте
def remove_offline():
    if 'PROBLEM:' in a2:
        m=re.search('MYparsBLOCK:S+:s+HID:(?P<hostid>S+)',a3)
        hostid = m.group('hostid') + 'n'
        hidf=open('/var/log/zabbixsrv/2del_ids', 'a')
        hidf.write(hostid)
        hidf.close
        send_mail(sys.argv[1],a2,a3[0:string.find(a3,'MYparsBLOCK')])

# Костыль, который возвращает к жизни драйвер для упса. Перезапускаем девайс с помощью  утилиты microsoft devcon.
def nutpt():
    if 'PROBLEM:' in a2:
        m=re.search('MYparsBLOCK:S+:s+HIP:(?P<hostip>S+)',a3)
        hostip = m.group('hostip')
        log = ''
        i = 0
        while i < 5:
            p = subprocess.Popen("""zabbix_get -s %s -k 'system.run[net stop "Network UPS Tools"]'"""%(hostip), shell=True,stdout=subprocess.PIPE)
            log +=p.stdout.read()
            time.sleep(10)
            p = subprocess.Popen("""zabbix_get -s %s -k system.run['cd "C:Program FilesZabbixcmd"&devcon.exe restart USBVID_051D*']"""%(hostip), shell=True,stdout=subprocess.PIPE)
            log +=p.stdout.read()
            time.sleep(30)
            p = subprocess.Popen("""zabbix_get -s %s -k 'system.run[net start "Network UPS Tools"]'"""%(hostip), shell=True,stdout=subprocess.PIPE)
            log +=p.stdout.read()
            i += 1
            p = subprocess.Popen("""zabbix_get -s %s -k 'ups.status'"""%(hostip), shell=True,stdout=subprocess.PIPE)
            if 'Error' not in p.stdout.read():
                 i = 8
        if i <> 8:
            send_mail(sys.argv[1],a2,log)
#набор действий при авторегистрации клиента. Пока это только включение smart с помощью smartctl.exe --scan-open
def firstrun():
    m=re.search('MYparsBLOCK:S+:s+HIP:(?P<hostip>S+)',a3)
    hostip = m.group('hostip')
    p = subprocess.Popen("""zabbix_get -s %s -k system.run['cd "C:Program FilesZabbixextrasmart"&smartctl.exe --scan-open']"""%(hostip), shell=True,stdout=subprocess.PIPE)
    log = p.stdout.read()
    send_mail(sys.argv[1],a2,log)

daemonize(stdout='/var/log/zabbixsrv/script_out.log', stderr='/var/log/zabbixsrv/script_err.log')
try:
    a1,a2,a3 = sys.argv[1],sys.argv[2],sys.argv[3]
	#debug(строчку ниже при необходимости можно раскомментировать ) 
    #os.system('echo "' + a1+'  '+a2+'  '+a3 +'" >> /var/log/zabbixsrv/script_dbg.log')
    if 'MYparsBLOCK' in a3:
        eval(re.search('MYparsBLOCK:(?P<myfunc>S+):',a3).group('myfunc'))() # запуск функции полученной из триггера
	else:
        send_mail(sys.argv[1],a2,a3)

except:
    #print sys.exc_info()
    send_mail('admin@domain.local', 'Error in script', str(sys.exc_info()))

Имейте в виду, что если получателей несколько, то и функция выполнится несколько раз. Для чего-то это актуально(например модель HDD), а для чего то может быть даже вредным, поэтому это обязательно надо учесть при настройке Actions.

Скрипт для удаления неактивных хостов

#!/usr/bin/python
#
import os
from pyzabbix import ZabbixAPI, ZabbixAPIException
try:
    os.rename ('/var/log/zabbixsrv/2del_ids','/var/log/zabbixsrv/klist_pr')
except:
    pass
user='apirobot'
pwd='*******'
url = 'https://127.0.0.1/zabbix/'
zh = ZabbixAPI(url)
zh.session.verify = False
zh.login(user=user, password=pwd)

f = open('/var/log/zabbixsrv/klist_pr')


for hnm in f:
    try:
        hid = zh.host.get(filter={"host":hnm.replace('n','')},output=['hostid'])[0]['hostid']
        #zh.host.delete(hostid = hid)  - API change
        zh.host.delete(int(hid))
    except:
        pass
f.close()
os.remove('/var/log/zabbixsrv/klist_pr')

Для работы скрипта нужен аккаунт с правами на удаление машин. Я запускаю скрипт по крону под другим аккаунтом, т. к. здесь хранится пароль в открытом виде.

Подключаем агенты по dns-имени

По дефолту агенты регистрируются так, что zabbix подключается к ним по ip. Меня это не устраивает, поэтому пишем скрипт, который это поправит, а заодно и сообщит о проблемах lookup. За основу взял какой-то скрипт из примеров pyzabbix и немного переделал.

use_fqdn.py

#!/usr/bin/python
#
# -*- coding: utf-8 -*-

import socket
from getpass import getpass
from pyzabbix import ZabbixAPI, ZabbixAPIException


zapi = ZabbixAPI(server='https://127.0.0.1/zabbix/')
zapi.session.verify = False
zapi.login('apirobot', '*******')

body = ''
err = ''

def send_mail(recipient, subject, body):

    import smtplib
    from email.MIMEText import MIMEText
    from email.Header import Header
    from email.Utils import formatdate
    encoding='utf-8'
    SMTP_SERVER = 'smtp'
    SENDER_NAME = u'zabbix@domain.local'
    MAIL_ACCOUNT = 'zabbix@domain.local'
    session = None
    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    msg['From'] = Header(SENDER_NAME, encoding)
    msg['To'] = recipient
    msg['Date'] = formatdate()
    try:
        session = smtplib.SMTP(SMTP_SERVER)
        session.sendmail(MAIL_ACCOUNT, recipient, msg.as_string())
    except Exception as e:
        raise e
    finally:
                # close session
        if session:
            session.quit()


# Loop through all hosts interfaces, getting only "main" interfaces of type "agent"
for h in zapi.hostinterface.get(output=["dns","ip","useip"],selectHosts=["host"],filter={"main":1,"type":1}):
    #print h
    # Make sure the hosts are named according to their FQDN
    #
    if len(h['dns']) == 0:
        try:
            zapi.hostinterface.update(interfaceid=h['interfaceid'], dns = socket.gethostbyaddr(h['hosts'][0]['host'])[0])
        except:
            body += ('FQDN_UPD_ERR: ' + h['hosts'][0]['host']) + 'n'
    try:
        a = socket.gethostbyaddr(h['hosts'][0]['host'])[2][0]
        b = socket.gethostbyaddr(h['dns'])[2][0]
        if (a != b):
            body += ('Warning: %s has dns "%s"' % (h['hosts'][0]['host'], h['dns'])) + 'n'

    except:
        body += ('DNS_LOOKUP_ERR: ' + h['hosts'][0]['host']) + 'n'

    # Make sure they are using hostnames to connect rather than IPs (could be also filtered in the get request)
    if h['useip'] == '1':
        body += ('%s is using IP instead of hostname. Fixing.' % h['hosts'][0]['host']) + 'n'
        try:
            zapi.hostinterface.update(interfaceid=h['interfaceid'], useip=0)
        except ZabbixAPIException as e:
            #print(e)
            err += str(e)+'n'
            err += 'n'
        continue

body += 'nZabbix Errors:' + err
if  len(body) > 16:
    send_mail('admin@domain.local','check agents',body)

Постскриптум

Все скрипты, шаблон и прочие необходимые файлы выложены на github.

Мониторим клиентские ПК в Microsoft AD с помощью Zabbix. Часть 1 — Автоустановка

Автор: mike_Z

Источник

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


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js