Zabbix обладает достаточно большими возможностями из коробки, но этого иногда может не хватать, и на этот случай есть возможность использовать сторонний скрипт для обработки событий(Report problems to script). К самому скрипту вернемся чуть позже, пока опишу только основную мысль, чтобы было понятно, что и зачем мы добавляем в description триггеров. Скрипт парсит тело письма и ищет строчку MYparsBLOCK:funcname: если находит, то выполняет funcname(), если не находит, — то просто отправляет оповещение. Разумнее всего добавлять это в описание триггеров, поэтому в Actions-Event source –Triggers-operations необходимо добавить в default message — {TRIGGER.DESCRIPTION}.
Необходимо создать шаблон для мониторинга, который будет прикрепляться к новым хостам, с помощью правила авторегистрации. (Action-> Event Source->Auto registration->Link to templates: Win_monitor) Я взял за основу стандартный шаблон zabbix для windows, а также APC Smart UPS Monitoring откуда-то с просторов, выкинул из них все лишнее и добавил то, что нужно мне.
Я уже писал, что реализация затеи с мониторингом упсов оказалась не такой гладкой, как хотелось бы. Упсы постоянно отваливаются, помогает перезапуск драйвера с помощью утилиты devcon, поэтому добавляем в триггер(в description) «robot_ Не приходят данные с ИБП {HOST.NAME}» наш блок с функцией nutp. Ну и никому не нужны мертвые хосты в мониторинге, потому в триггер «Zabbix agent on {HOST.NAME} is unreachable for 7 days» добавляем функцию remove_offline, которая будет удалять хосты из zabbix:
Что касается смартов, то обычные Items и Triggers нам не подойдут, т. к. на разных машинах может быть разное количество смартов. В zabbix есть возможность сделать item и trigger prototype, которые будут создаваться для списка объектов, полученных с помощью low-level discovery rules, подробнее можно почитать здесь. Чтобы правило работало, нам нужно написать скрипт/приложение, которое будет при запуске выдавать список хардов в специальном JSON формате. Сначала я сделал скрипт на powershell, но на части машин скрипт периодически не успевал выполниться за 30 секунд, из-за того, что сам powershell очень долго инициализируется. Пришлось отказаться от powershell и сделать exe приложение на c#(я его не знаю, но он показался достаточно простым, чтобы переписать скрипт). Приложение с помощью smartctl получает список hdd, убирает дублирующиеся(по серийникам) и выводит в нужном нам формате.
Да, на некоторых машинах смарт может быть отключен, поэтому добавим еще одну функцию firstrun, и разместим ее в сообщение правила авторегистрации Action-> Event Source->Auto registration добавляем пункт Send message to ...via script, в тело размещаем:
В шаблоне создаем discovey rule — smart.discovery.
В description прототипов добавляем функцию hddsmart, она будет добавлять модель HDD в тело сообщения триггера, чтобы было понятно, о каком именно харде идет речь, т. к. smartctl использует в качестве имени sda, sdb и т. п.
Большинство параметров агент не понимает, поэтому в конфигурации клиента обязательно нужно прописать все UserParameter.
Собственно сам скрипт, который будет отправлять письма и выполнять наши функции:
Имейте в виду, что если получателей несколько, то и функция выполнится несколько раз. Для чего-то это актуально(например модель HDD), а для чего то может быть даже вредным, поэтому это обязательно надо учесть при настройке Actions.
Для работы скрипта нужен аккаунт с правами на удаление машин. Я запускаю скрипт по крону под другим аккаунтом, т. к. здесь хранится пароль в открытом виде.
По дефолту агенты регистрируются так, что zabbix подключается к ним по ip. Меня это не устраивает, поэтому пишем скрипт, который это поправит, а заодно и сообщит о проблемах lookup. За основу взял какой-то скрипт из примеров pyzabbix и немного переделал.
» Все скрипты, шаблон и прочие необходимые файлы выложены на github.
» Мониторим клиентские ПК в Microsoft AD с помощью Zabbix. Часть 1 — Автоустановка
Готовим 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[ROOT\cimv2,SELECT Name FROM Win32_Processor]
CPU Utilization
perf_counter[\238(_Total)\6]
APC Smart UPS Monitoring: Driver Caption
wmi.get[ROOT\cimv2,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[ROOT\cimv2,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
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[ROOT\cimv2,SELECT Name FROM Win32_Processor]
CPU Utilization
perf_counter[\238(_Total)\6]
APC Smart UPS Monitoring: Driver Caption
wmi.get[ROOT\cimv2,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[ROOT\cimv2,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[ROOT\cimv2,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
{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[ROOT\cimv2,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, @"^Serial\sNumber\:\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\":[\n\n");
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("\t\t\"{{#HDDNAME}}\":\"{0}\"\n", kvp.Key);
Console.WriteLine("\t}\n");
}
}
Console.WriteLine("\n\t]\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]
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
{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)\NUT\bin\upsc.exe" primary@localhost battery.charge
UserParameter=battery.charge.low,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.charge.low
UserParameter=battery.charge.warning,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.charge.warning
UserParameter=battery.mfr.date,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.mfr.date
UserParameter=battery.runtime,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.runtime
UserParameter=battery.runtime.low,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.runtime.low
UserParameter=battery.temperature,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.temperature
UserParameter=battery.type,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.type
UserParameter=battery.voltage,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.voltage
UserParameter=battery.voltage.nominal,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost battery.voltage.nominal
UserParameter=input.sensitivity,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost input.sensitivity
UserParameter=input.transfer.high,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost input.transfer.high
UserParameter=input.transfer.low,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost input.transfer.low
UserParameter=input.voltage,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost input.voltage
UserParameter=output.current,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost output.current
UserParameter=output.frequency,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost output.frequency
UserParameter=output.voltage,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost output.voltage
UserParameter=output.voltage.nominal,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost output.voltage.nominal
UserParameter=ups.beeper.status,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.beeper.status
UserParameter=ups.delay.shutdown,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.delay.shutdown
UserParameter=ups.delay.start,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.delay.start
UserParameter=ups.firmware,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.firmware
UserParameter=ups.firmware.aux,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.firmware.aux
UserParameter=ups.load,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.load
UserParameter=ups.mfr,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.mfr
UserParameter=ups.mfr.date,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.mfr.date
UserParameter=ups.model,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.model
UserParameter=ups.productid,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.productid
UserParameter=ups.serial,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.serial
UserParameter=ups.status,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.status
UserParameter=ups.test.result,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.test.result
UserParameter=ups.timer.reboot,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.timer.reboot
UserParameter=ups.timer.shutdown,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.timer.shutdown
UserParameter=ups.timer.start,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.timer.start
UserParameter=ups.vendorid,"c:\Program Files (x86)\NUT\bin\upsc.exe" primary@localhost ups.timer.vendorid
UserParameter=smart[*],"C:\Program Files\Zabbix\cmd\smart.cmd" "$1" $2
UserParameter=smart.discovery, "C:\Program Files\Zabbix\cmd\hdd_scan.exe"
smart.cmd
@echo off
rem use smart.cmd <disk> < parameter> smart.cmd sda health
cd "C:\Program Files\Zabbix\cmd"
if %2==health ("C:\Program Files\Zabbix\extra\smart\smartctl.exe" -H %1 | grep result | awk "{print $6}")
if %2==model ("C:\Program Files\Zabbix\extra\smart\smartctl.exe" -i %1 | grep "Device Model" | awk -F"Device Model:" "{print $2}")
if %2==realloc ("C:\Program Files\Zabbix\extra\smart\smartctl.exe" --attributes %1 | grep Reallocated_S | awk "{print $10}")
if %2==crc ("C:\Program Files\Zabbix\extra\smart\smartctl.exe" --attributes %1 | grep CRC | awk "{print $10}")
if %2==pend ("C:\Program Files\Zabbix\extra\smart\smartctl.exe" --attributes %1 | grep Pend | awk "{print $10}")
if %2==temp ("C:\Program Files\Zabbix\extra\smart\smartctl.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) %s\n" % (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) %s\n" % (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 Files\Zabbix\cmd\"&devcon.exe restart USB\VID_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 Files\Zabbix\extra\smart\"&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 — Автоустановка
Поделиться с друзьями
ArsenAbakarov
У меня было что-то похожее, для отправки письма писал доп. функцию для разбиения на части, ибо бывает что письма не влазиют в допустимые ~10MB
site6893
святая мария! на улице 21 век, как же раздражають эти любители ограничения в 10мб на размер письма.
leninxxx
Пару лет назад делал аналог, только с мониторингом температурных датчиков и напряжений БП, пришлось использовать aida.
Вот только так и не придумал ничего внятного по смарту. Возьму на заметку Ваше решение, если Вы не против.
nobodysu
Использую такой вариант для мониторинга SMART.
Для всех основных систем, лишние процессы не спавнит, загружает все айтемы зараз. Под винду правда не очень элегантно работает, но надеюсь это временно.