Winner Code
Veni, vidi, programmare!
Veni, vidi, programmare!
31 июля 2011
С программированием приходится порой сталкиваться и в системном администрировании. Сегодня я хочу рассказать об одной такой истории.
Захотелось мне однажды увидеть единую и наглядную картину об электропитании серверов на работе, а также получать оперативные уведомления в случае его перебоев. В силу своей симпатии к системам на базе Linux решил двигаться в этом направлении. Дома я уже имел удачный опыт использования Apcupsd — демона для контроля ИБП фирмы APC, но здесь меня ждала несколько иная задача.
Дело в том, что данный демон не предусматривает работу с несколькими ИБП, а на работе у меня их три, и не хотелось каждый мониторить разным сервером. Решил поближе познакомиться с устройством этого демона.
Выяснилось, что, по сути, сам демон как раз таки поддерживает работу с несколькими устройствами. Точнее, есть возможность запуска нескольких экземпляров демона, указав каждому свой путь к конфигурационному файлу:
$ /sbin/apcupsd -f <file>
Но такую организацию не поддерживает init-скрипт демона. Во всяком случае, в Debian 6, на базе которого все и производилось. Код этого скрипта (/etc/init.d/apcupsd):
#!/bin/sh ### BEGIN INIT INFO # Provides: apcupsd # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Should-Start: $local_fs # Should-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Starts apcupsd daemon # Description: apcupsd provides UPS power management for APC products. ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/sbin/apcupsd CONFIG=/etc/default/apcupsd NAME=apcupsd DESC="UPS power management" test -x $DAEMON || exit 0 test -e $CONFIG || exit 0 set -e . $CONFIG if [ "x$ISCONFIGURED" != "xyes" ] ; then echo "Please check your configuration ISCONFIGURED in /etc/default/apcupsd" exit 0 fi case "" in start) echo -n "Starting $DESC: " rm -f /etc/apcupsd/powerfail if [ "`pidof apcupsd`" = "" ] then start-stop-daemon --start --quiet --exec $DAEMON echo "$NAME." else echo "" echo "A copy of the daemon is still running. If you just stopped it," echo "please wait about 5 seconds for it to shut down." exit 0 fi ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --oknodo --pidfile /var/run/apcupsd.pid || echo "Not Running." rm -f /var/run/apcupsd.pid echo "$NAME." ;; restart|force-reload) {CONTENT} stop sleep 10 {CONTENT} start ;; status) #/sbin/apcaccess status $APCACCESS status ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0
Демон, как видно, запускается без параметров и использует конфигурационный файл по умолчанию (/etc/apcupsd/apcupsd.conf).
Каждый экземпляр демона должен иметь свой конфигурационный файл, в котором описаны параметры определенного ИБП. В /etc/apcupsd/ создаем папки ups0, ups1 и так далее. Количество папок должно соответствовать количеству контролируемых устройств. Далее, в каждую папку копируем следующие файлы из /etc/apcupsd/:
apccontrol apcupsd.conf changeme commfailure commok killpower offbattery onbattery ups-monitor
Теперь необходимо переписать init-скрипт /etc/init.d/apcupsd, задача которого — запустить нужное количество демонов, передав каждому свой путь к конфигурационному файлу. Вот что получилось у меня:
#!/bin/bash ### BEGIN INIT INFO # Provides: apcupsd # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Should-Start: $local_fs # Should-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Starts apcupsd daemon # Description: apcupsd provides UPS power management for APC products. ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/sbin/apcupsd CONFIG=/etc/default/apcupsd NAME=apcupsd DESC="UPS power management" test -x $DAEMON || exit 0 test -e $CONFIG || exit 0 set -e . $CONFIG if [ "x$ISCONFIGURED" != "xyes" ] ; then echo "Please check your configuration ISCONFIGURED in /etc/default/apcupsd" exit 0 fi case "" in start) for conf in /etc/apcupsd/ups[0-9]/apcupsd.conf ; do inst=${conf:13:4} rm -f /etc/apcupsd/$inst/powerfail echo -n "Starting $DESC ($inst): " if [ -e /var/run/apcupsd-$inst.pid ] then echo "" echo "A copy of the daemon is still running. If you just stopped it," echo "please wait about 5 seconds for it to shut down." exit 0 else start-stop-daemon --start --quiet --name $inst --exec $DAEMON -- -f $conf -P /var/run/apcupsd-$inst.pid #sleep 1 echo "$NAME." fi done ;; stop) for conf in /etc/apcupsd/ups[0-9]/apcupsd.conf ; do inst=${conf:13:4} echo -n "Stopping $DESC ($inst): " start-stop-daemon --stop --oknodo --pidfile /var/run/apcupsd-$inst.pid || echo "Not Running." rm -f /var/run/apcupsd-$inst.pid echo "$NAME." done ;; restart|force-reload) {CONTENT} stop sleep 10 {CONTENT} start ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0
Осталось отредактировать каждый конфигурационный файл (apcupsd.conf). Нас интересуют следующие строки в нем:
#UPSNAME ... UPSCABLE usb ... UPSTYPE usb DEVICE ... SCRIPTDIR /etc/apcupsd ... PWRFAILDIR /etc/apcupsd ... NOLOGINDIR /etc ... NISPORT 3551 ... EVENTSFILE /var/log/apcupsd.events
В них задается имя устройства, тип подключения, путь к файлу устройства, номер прослушиваемого демоном порта и пути к различным папкам и файлам (папка со скриптами, файл для записи событий и т.д.).
Как я уже говорил, у меня к серверу подключено три ИБП. Два из них через USB, а третий пятиметровым COM-кабелем из другого шкафа. Значимые строки в конфигурационных файлах соответственно следующие:
/etc/apcupsd/ups0/apcupsd.conf:
UPSNAME ups0 ... UPSCABLE usb ... UPSTYPE usb DEVICE /dev/usb/hiddev0 ... SCRIPTDIR /etc/apcupsd/ups0 ... PWRFAILDIR /etc/apcupsd/ups0 ... NOLOGINDIR /etc/apcupsd/ups0 ... NISPORT 3551 ... EVENTSFILE /var/log/apcupsd.ups0.events
/etc/apcupsd/ups1/apcupsd.conf:
UPSNAME ups1 ... UPSCABLE usb ... UPSTYPE usb DEVICE /dev/usb/hiddev1 ... SCRIPTDIR /etc/apcupsd/ups1 ... PWRFAILDIR /etc/apcupsd/ups1 ... NOLOGINDIR /etc/apcupsd/ups1 ... NISPORT 3552 ... EVENTSFILE /var/log/apcupsd.ups1.events
/etc/apcupsd/ups2/apcupsd.conf:
UPSNAME ups2 ... UPSCABLE smart ... UPSTYPE apcsmart DEVICE /dev/ttyS0 ... SCRIPTDIR /etc/apcupsd/ups2 ... PWRFAILDIR /etc/apcupsd/ups2 ... NOLOGINDIR /etc/apcupsd/ups2 ... NISPORT 3553 ... EVENTSFILE /var/log/apcupsd.ups2.events
Пробуем стартовать демон:
$ sudo /etc/init.d/apcaccess start Starting UPS power management (ups0): apcupsd. Starting UPS power management (ups1): apcupsd. Starting UPS power management (ups2): apcupsd.
Отлично, стартовало три экземпляра демона. Каждый слушает свой TCP-порт, по которому можно подключиться и запросить информацию об ИБП:
$ sudo netstat -lnp | grep apcupsd tcp 0 0 127.0.0.1:3551 0.0.0.0:* LISTEN 24386/apcupsd tcp 0 0 127.0.0.1:3552 0.0.0.0:* LISTEN 24389/apcupsd tcp 0 0 127.0.0.1:3553 0.0.0.0:* LISTEN 24392/apcupsd
Получить информацию можно следующей командой:
$ sudo apcaccess status <host>:<port>
Допустим, по третьему ИБП:
$ sudo apcaccess status localhost:3553 APC : 001,050,1208 DATE : 2011-07-31 16:15:07 +1100 HOSTNAME : sh01s02.eao.drsk.ru VERSION : 3.14.8 (16 January 2010) debian UPSNAME : ups2 CABLE : Custom Cable Smart MODEL : Smart-UPS 2200 RM UPSMODE : Stand Alone STARTTIME: 2011-07-31 15:17:14 +1100 STATUS : ONLINE LINEV : 230.4 Volts LOADPCT : 31.8 Percent Load Capacity BCHARGE : 100.0 Percent TIMELEFT : 31.0 Minutes MBATTCHG : 5 Percent MINTIMEL : 3 Minutes MAXTIME : 0 Seconds MAXLINEV : 231.8 Volts MINLINEV : 230.4 Volts OUTPUTV : 230.4 Volts SENSE : High DWAKE : 000 Seconds DSHUTD : 090 Seconds DLOWBATT : 02 Minutes LOTRANS : 208.0 Volts HITRANS : 253.0 Volts RETPCT : 000.0 Percent ITEMP : 27.9 C Internal ALARMDEL : 5 seconds BATTV : 55.1 Volts LINEFREQ : 50.0 Hz LASTXFER : Automatic or explicit self test NUMXFERS : 0 TONBATT : 0 seconds CUMONBATT: 0 seconds XOFFBATT : N/A SELFTEST : NO STESTI : 336 STATFLAG : 0x07000008 Status Flag REG1 : 0x00 Register 1 REG2 : 0x00 Register 2 REG3 : 0x00 Register 3 MANDATE : 05/02/06 SERIALNO : JS0619000289 BATTDATE : 05/02/06 NOMOUTV : 230 Volts NOMBATTV : 48.0 Volts EXTBATTS : 0 FIRMWARE : 665.6.I APCMODEL : FWI END APC : 2011-07-31 16:15:51 +1100
Я все это дело привязал к системе мониторинга Cacti, благо, существуют готовые шаблоны и сделать это не составляет особого труда. Получилась вот такая наглядная картина:
Демон Apcupsd позволяет выполнять пользовательский код при наступлении определенного события (например, отключения питания). В каждой папке с конфигурационными файлами устройств имеется файл apccontrol, в котором прописаны стандартные действия на события. Пользовательские действия на определенное событие помещаются в отдельный файл с именем, соответствующим имени события. Список доступных событий можно найти в официальной документации.
При наступлении события сначала выполняются пользовательские действия (если файл с действиями существует). В конце выполнения нужно вернуть ноль (exit 0), в этом случае apccontrol выполнит стандартные действия после пользовательских (например, выключит компьютер по таймауту при отключении питания). Если необходимо отклонить выполнение стандартных действий, нужно вернуть 99 (exit 99) в конце выполнения пользовательских действий.
Вернемся к уведомлениям. Самым удобным и полезным я считаю вариант СМС-уведомлений, так как телефон всегда под рукой, в отличие от компьютера. Для отправки СМС я использую email2sms шлюз. Инструкция по настройке услуги для моего оператора. Суть услуги: создается электронный ящик, при отправке на него письма на телефон прилетает СМС с текстом письма. Похожая услуга должна быть и у других операторов.
Для отправки писем в bash-скриптах я использую программку ssmtp, она есть в репозиториях ArchLinux, Ubuntu, Debian, на счет остальных дистрибутивов не знаю. Для отправки почты нужно завести какую-нибудь учетную запись на почтовом сервере, затем отредактировать конфигурационный файл /etc/ssmtp/ssmtp.conf:
root=e-mail mailhub=smtp-сервер rewriteDomain=домен e-mail’а FromLineOverride=YES authuser=имя пользователя authpass=пароль
Задача — отправлять СМС-уведомления при отключении и возобновлении питания. События в Apcupsd, соответственно onbattery и offbattery.
Скрипт onbattery:
#!/bin/bash ( echo "To: <*****@sms.megafondv.ru>" echo "From: <*****@joker-jar.ru>" echo "Subject: UPS message `date \"+%d.%m.%Y %H:%M:%S\"`" echo " " echo "Power failure on UPS. Running on batteries." ) | ssmtp *****@sms.megafondv.ru exit 0
Скрипт offbattery:
#!/bin/bash ( echo "To: <*****@sms.megafondv.ru>" echo "From: <*****@joker-jar.ru>" echo "Subject: UPS message `date \"+%d.%m.%Y %H:%M:%S\"`" echo " " echo "Power returned to UPS." ) | ssmtp *****@sms.megafondv.ru exit 0
На этом, пожалуй, все. Удачного администрирования!
31 июля 2011, 18:23
отличная статья подумываю об реализации
24 августа 2011, 0:26
Замечательная статья, я не делал реализацию отображения состояния в cacti, стоит попробовать..
И за команду онлайн-опроса состояния apcaccess status - большое человеческое мерси :)
12 сентября 2011, 7:48
Можно по-подробнее насчет email оповещания?
12 сентября 2011, 10:17
Спрашивайте, где именно непонятно :)
29 сентября 2011, 9:14
Спасибо за статью! Один вопрос: в письмах, отправляемых ssmtp пропадает тело письма, т.е. приходит только subject. Скрипт переписал один в один,что я делаю не так?
6 октября 2011, 12:20
Подскажите, пожалуйста. А возможно ли такое (apcupsd+3 ups) реализовать в Win2003?
7 октября 2011, 0:32
Manshtein, глянуть бы скрипт.
erema15, под Windows ни разу не запускал. Если приложению можно указать путь к конфигурационному файлу, то наверняка можно.
7 октября 2011, 9:59
Да в общем-то один в один с вашим.
Собственно, через echo text | mail -s subject ***@gmail.com отправляет нормально, чем и пользуюсь, просто интереcна механика.
7 октября 2011, 16:36
Ну да, сложно тут что-либо гадать, ибо у меня, как говорится, "все работает". Можно поглядеть исходники письма или попробовать на другой сервер отослать.