Рубрики
Без рубрики

Нотификации Munin. Хочу все знать.

В этом посте я бы хотел рассказать о том как же  узнать о происходящих событиях мониторинга в Munin. Можно конечно держать открытым веб-интерфейс и постоянно наслаждаться красивыми графиками, а заодно и оперативно видеть возникающие события. Но есть способ лучше: получить от Munin нотификацию.

События в Munin.

Для начала, давайте посмотрим как в munin возникают события:
— Сервер собирает со своих нод сведения о всех сенсорах, имеющихся на них;
— Полученные для каждой метрики мгновенные значения сравниваются с заданными порогами;
— Если это значение выходит за допустимые пределы, то для соответствующей метрики возникает событие.

Как мы с Вами уже знаем в Munin есть два интересных нам события — превышение «предупреждающего» и «критического» порогов. На самом деле нам будет интересно и еще одно состояние метрики — «unknown», которое как бы намекает нам о том что реальное состояние неизвестно (а значит может быть и критическим). Таким образом, по критичности можно разделить события на три типа: «critical», «warning» и «unknown».

Пороговые значения.

Пороги определяют границы, при выходе за которые генерируется то или иное событие и бывают двух типов, соответствующих критичности возникающего события — «critical» и «warning». Пороги могут определять как верхнюю, так и нижнюю границы, то есть, всего существует 4 границы, по две для каждого состояния. При этом порог «critical» имеет более высокий приоритет, то есть если значение выходит за границы для обоих состояний, то используется именно он.

Пороги могут устанавливаться как в самом плагине, так и в конфигурационном файле и задаются в следующем формате: <нижний порог>:<верхний порог> . В этом примере, для ноды my_pc, задаются пороги для двух метрик (channel1 и channel2) плагина myplagin:

[my_pc]
    myplugin.channel1.warning 20:80
    myplugin.channel1.critical 10:90
    myplugin.channel2.warning 5:5
    myplugin.channel2.critical 4:6

Разберем что же это значит:
— channel1 переходит в «warning», если его значение МЕНЬШЕ 20 или БОЛЬШЕ 80;
— channel1 переходит в «critical», если его значение МЕНЬШЕ 10 или БОЛЬШЕ 90;
— channel2 переходит в «warning», если его значение НЕ РАВНО 5;
— channel2 переходит в «critical», если его значение ОТЛИЧАЕТСЯ ОТ 5 БОЛЬШЕ ЧЕМ НА 1 (т. е. МЕНЬШЕ 4 или БОЛЬШЕ 6);

Обработка событий.

Критическое событие в MuninУ нас нас произошло событие для какой-то метрики и что же дальше? А дальше происходит следующее: график этой метрики теперь отображается в соответствующей группе  раздела «problems» и его граница окрашена в желтый или красный цвет. То есть мы сразу видим существующие проблемы.

Но самое главное — выполняется действие определенное в конфигурационном файле Munin. Официальная документация рассказывает нам следующее:

Munin при наступлении события может уведомить нас двумя способами:
— самостоятельно запустить определенную команду;
— передать информацию о событии в Nagios.

Вот пример как в конфигурации можно определить какую команду запускать:

# формат очень простой: contact.<название>.command <команда> [параметры]
contact.someuser.command mail -s "Munin notification" somejuser@fnord.com

В принципе этого уже достаточно для отправки нотификации, но мы же наверняка захотим большего. Поэтому почитав документацию еще чуть-чуть, мы узнаем что текст этой нотификации передается команде через стандартный поток ввода. А еще, текст можно изменить задав его в директиве contact.<название>.text  и при этом, можно не только использовать переменные Munin, но и использовать модуль perl — Text::Balanced!!! Конечно мы с Вами не сможем удержаться и не  использовать такие богатые возможности :).

Передача события в формате JSON.

Да да, именно JSON, ведь всегда очень хорошо если решение задачи будет максимально простым и универсальным. Так как этот пост, к сожалению, не о perl, то я сразу представлю решение, без введения в этот замечательный язык программирования. Тем более, что используя три абзаца документации и наше решение Вы сможете легко разобраться и сами. Итак:

contact.json.text { \
  "group": "${var:group}", \
  "host": "${var:host}", \
  "graph_category": "${var:graph_category}", \
  "graph_title": "${var:graph_title}", \
  "critical": [ ${loop<,>:cfields {"label": "${var:label}", "value": "${var:value}", "wlimits": "${var:wrange}", "climits": "${var:crange}", "extra": "${var:extinfo}"} }], \
  "warning": [ ${loop<,>:wfields {"label": "${var:label}", "value": "${var:value}", "wlimits": "${var:wrange}", "climits": "${var:crange}", "extra": "${var:extinfo}"} }], \
  "unknown": [ ${loop<,>:ufields {"label": "${var:label}", "value": "${var:value}", "wlimits": "${var:wrange}", "climits": "${var:crange}", "extra": "${var:extinfo}"} }], \
  "ok": [ ${loop<,>:ofields {"label": "${var:label}", "value": "${var:value}", "wlimits": "${var:wrange}", "climits": "${var:crange}", "extra": "${var:extinfo}"} }] \
}

С таким кодом, для картинки выше, будет следующий вывод (обратите внимание на секцию «critical»):

{
  "group": "virtual_machines",
  "host": "phy1mo1",
  "graph_category": "Process info",
  "graph_title": "Process count",
  "ok": [
    {
      "label": "upsmon",
      "value": "2.00",
      "wlimits": ":",
      "climits": "1:",
      "extra": ""}
    ,{
      "label": "upsd",
      "value": "1.00",
      "wlimits": ":",
      "climits": "1:",
      "extra": ""}

  ],
  "unknown": [

  ],
  "warning": [

  ],
  "critical": [
    {
      "label": "powercom",
      "value": "1.00",
      "wlimits": ":",
      "climits": "2:",
      "extra": ""}

  ]
}

Все что осталось это создать скрипт который будет разбирать этот JSON и, например, писать нам красивое письмо!

Скрипт для отправки письма.

Давайте в этот раз воспользуемся… ruby! И даже не спрашивайте почему — просто так. Вот ловите:

#!/usr/bin/env ruby

# Наверно не у всех есть свой почтовый сервер, поэтому давайте воспользуемся gmail 
user = "MUNINMAIL@gmail.com" # аккаунт от которого будем посылать нотификацию
password = "MUNINPASSWORD" # пароль для него
recipient = "ME@gmail.com" # получатель нотификации

require 'json'
require 'tlsmail'
require 'net/smtp' 

severity = {
  "critical" => 0,
  "warning" => 0,
  "unknown" => 0,
  "ok" => 0
}

# Получаем аргументы (первый аргумент у нас - имя самого скрипта)
group = ARGV[1] # группа в которую входит компьютер на котором произошло событие
host = ARGV[2] # и его имя
category = ARGV[3] # категория монитора
title = ARGV[4] # и его название
sensor = JSON.parse(STDIN.read) # текст нотификации (это наш JSON)

# Определим какие критичности у нас есть, чтоб указать наивысшую в теме письма
severity.each_pair do |sev, val|
  if !sensor[sev].empty?
    severity[sev] = 1
  end
end

# Составим заголовок для HTML письма
message = <<END
From: Munin monitoring <muninmail@gmail.com>
To: Me <me@gmail.com>
MIME-Version: 1.0
Content-type: text/html
END

# Добавим тему
message += "Subject: #{host}:#{title} #{severity.key(1)}\n"

# И вот само тело письма
message += <<END
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style>
body{font-family:Courier New;}
table{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width: 100%;}
th{text-align: left; font-size:10pt; border-width: 1px;padding: 4px;border-style: dotted;border-color: black;background-color:#DCDCDC;}
th.critical{background-color: #ff0000;}
th.warning{background-color: #00ffff;}
th.unknown{background-color: #aaaaaa;}
td{font-size:10pt; border-width: 1px;padding: 4px;border-style: dotted;border-color: black;}
p{font-size:10pt; font-weight:bold; font-family:Courier New;}
</style>
</head>
<body><table>
<tr><th>Severity</th><th>Channel</th><th>Value</th><th>Limit</th><th>Additional information</th></tr>
END

# Пробежимся по JSON преданному munin и сделаем из него красивую HTML табличку
states = (sensor.find_all {|k,v| v.is_a?(Array)}).to_h
severity.each_pair do |sev, exists|
  if !states[sev].empty?
    states[sev].each do |chan|
      message += "<tr>\n <td class=\"#{sev}\"><b>#{sev}</b></td>\n <td>#{chan['label']}</td>\n <td>#{chan['value']}</td>\n <td>#{chan[(sev[0,1]+"limits")]}</td>\n <td>#{chan['extra']}</td>\n</tr>\n"
    end
  end
end

# Конец тела письма
message += "</table></body>\n</html>"

# И наконец отправляем готовое письмо через gmail
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
Net::SMTP.start("smtp.gmail.com", 587, "gmail.com", user, password, :plain) do |smtp|
  smtp.send_message message, user, recipient
end

Не забываем сделать скрипт исполняемым, пропишем в munin.conf что будем запускать его и с каким текстом:

contact.json.command | /home/munin/bin/send_mail.rb /home/munin/bin/send_mail.rb "${var:group}" "${var:host}" "${var:graph_category}" "${var:graph_title}"
contact.json.text { ... # смотри код выше!

И в результате получим такую табличку:

нотификация мониторинга

Добавить комментарий

Ваш адрес email не будет опубликован.