2016/03/17

使用 SNMPD 的 TRAP(異常情況) 功能來觸發管理機制,以 UPS 不斷電系統的自動關機為範例


使用 SNMPD 的 TRAP(異常情況) 功能來觸發管理機制,以 UPS 不斷電系統的自動關機為範例

這篇文章主要說明,當你的 UPS 如果發現 AC(市電)斷電了,我該如何呼叫機器關機勒?
在各 UPS 的管理工具理,都有提供管理工具讓你設定在斷電後快快關閉電腦,但是如果一次要對大量的機器可能就需要付點費用,或是使用其他的方式達成。此文提供透過 UPS 的 SNMPTRAPD 在符合條件的 MIB 資訊被觸發時,就透過接收到的資訊傳遞給負責處理的程序。


介紹 SNMP 
------------
首先我們需要先了解何謂 SNMP 通訊協定,可以參考底下這篇文章
http://www.netadmin.com.tw/article_content.aspx?sn=1209100018


安裝軟體
------------
# Linux mint or ubuntu 的安裝
apt-get install snmp snmpd libsnmp-base libsnmp-dev snmp-mibs-downloader -y

# Linux CentOS 的安裝
yum install net-snmp net-snmp-agent-libs net-snmp-utils -y
systemctl enable snmptrapd.service 

在 /etc/snmp/snmptrapd.conf  可以看到預設的設定檔
這個設定可以在 'snmptrapd.conf(5)' 有詳細的說明如何設定 snmptrapd


設定 SNMPTRAPD 可以接收觸發的封包
------------------------------------
可以參考官方網站的說明
http://www.net-snmp.org/wiki/index.php/TUT:Configuring_snmptrapd

# -----------------
# 修改設定檔 /etc/snmp/snmptrapd.conf
# -----------------
# 這個是我參考官方網站後改寫的 /etc/snmp/snmptrapd.conf 範例
mint snmp # cat /etc/snmp/snmptrapd.conf
# 以 SNMP v2c 版本 , 通訊接收寫入的密碼是 public (很危險記得要改)
# SNMP v2c with community as public
authCommunity   log,execute,net public

# 底下這兩個 traphandle 是很常見的範例, 以 SNMPv2-MIB::coldStart 這個 MIB
# 來觸發後面的 command 動作 /etc/snmp/traps.sh cold
#traphandle SNMPv2-MIB::coldStart    /etc/snmp/traps.sh cold
#traphandle SNMPv2-MIB::warmStart    /etc/snmp/traps.sh warmStart

# 預設的觸發,可以在 traps.sh 產生的 traps.txt 看到紀錄
# 也可以從 /var/log/syslog(ubunut) or /var/log/message(centos) 看到紀錄
# traphandle default  /etc/snmp/traps.sh default

# 這個是 MEGATEC 測試後的 AC 斷電、復電的 MIB 值
# for UPSagent HDP801 (MEGATEC)
traphandle SNMPv2-SMI::mib-2.33.1.6.3.6     /etc/snmp/traps.sh HDP_AC_lost
traphandle SNMPv2-SMI::mib-2.33.1.6.3.2     /etc/snmp/traps.sh HDP_AC_restore
# 關於 MIB 會因為不同的 UPS 裝置送出的 MIB 值都不一樣,
# 實際請參考你的 UPS MIB 相對應的值
# 底下備註是不同 UPS 在實際斷電時, snmptrapd 接收到的 MIB LOG

# 寫一個 traps.sh 程式, 用來處理 snmptrapd 產生的 log 並且依據 log 產生對應的動作
mint snmp # cat /etc/snmp/traps.sh 
#!/bin/bash
# --------------------------------------------
# ref:http://www.net-snmp.org/wiki/index.php/TUT:Configuring_snmptrapd
# --------------------------------------------
logfile='/etc/snmp/traps.txt'

read host
read ip
vars=

while read oid val
do
  if [ "$vars" = "" ]
  then
    vars="$oid = $val"
  else
    vars="$vars, $oid = $val"
  fi
done

echo "trap: $1 $host $ip $vars" > $logfile

# ---------------------------------------------
# by mtchang 2016.3.17
# ---------------------------------------------
actionfile='/etc/snmp/action.txt'
ACstat=$(tail -n1 $logfile | cut -d: -f2 | cut -d" " -f2)

# log to debug 
echo "$ACstat $(whoami) $(date)"
echo "$ACstat $(whoami) $(date)" > $actionfile

if [[ "${ACstat}" == "HDP_AC_restore" ]]; then
    echo "shutdown -c 'Utility power has been restore'" >> $actionfile
    #echo "shutdown -c 'Utility power has been restore'" | bash
    #echo "ssh mtchang@192.168.1.181 'sudo /home/mtchang/cmd/snmptrapd_cancel_poweroff.sh'" | bash
    exit 0
elif [[ "${ACstat}" == "HDP_AC_lost" ]]; then
    echo "shutdown -h -P 3 'After 3 minutes will be Poweroff. The UPS is not on battery power'" >> $actionfile
    #echo "shutdown -h -P 3 'After 3 minutes will be Poweroff. The UPS is not on battery power'" | bash
    #echo "ssh mtchang@192.168.1.181 'sudo /home/mtchang/cmd/snmptrapd_poweroff.sh' &" | bash
    exit 0
else
    echo "ACstat=${ACstat},Nothing to do." >> $actionfile
    exit 1
fi

# 在 Linux ubuntu 上面的重新啟動
# 刪除 snmptrapd 及重新啟動, 因為沒有 script 所以連下三個指令,並看它是否工作
killall -e snmptrapd
snmptrapd 
netstat -antulp | grep snmp

# 在 Linux CentOS 上面的重新啟動
systemctl restart snmptrapd.service 
netstat -antulp | grep snmp


# 本機測試, 使用 snmptrap 產生模擬 snmptrap 封包, 測試觸發條件是否工作
mint snmp # pwd
/etc/snmp
mint snmp # snmptrap -d -v 2c -c public 127.0.0.1 "" SNMPv2-MIB::coldStart SNMPv2-MIB::coldStart s "test coldStart"

Sending 100 bytes to UDP: [127.0.0.1]:162->[0.0.0.0]:0
0000: 30 62 02 01  01 04 06 70  75 62 6C 69  63 A7 55 02    0b.....public.U.
0016: 04 62 25 BD  9A 02 01 00  02 01 00 30  47 30 0F 06    .b%........0G0..
0032: 08 2B 06 01  02 01 01 03  00 43 03 74  0B 62 30 17    .+.......C.t.b0.
0048: 06 0A 2B 06  01 06 03 01  01 04 01 00  06 09 2B 06    ..+...........+.
0064: 01 06 03 01  01 05 01 30  1B 06 09 2B  06 01 06 03    .......0...+....
0080: 01 01 05 01  04 0E 74 65  73 74 20 63  6F 6C 64 53    ......test coldS
0096: 74 61 72 74                                           tart

# 觸發的 log 檔紀錄
mint snmp # cat traps.txt 
trap: default localhost UDP: [127.0.0.1]:35655->[127.0.0.1]:162 SNMPv2-SMI::mib-2.1.3.0 = 0:21:07:30.90, SNMPv2-SMI::snmpModules.1.1.4.1.0 = SNMPv2-SMI::snmpModules.1.1.5.1, SNMPv2-SMI::snmpModules.1.1.5.1 = "test coldStart"

# 判斷後的 action 動作
mint snmp # cat action.txt
Thu Mar 17 10:48:00 CST 2016
Nothing to do.


# 上面的 log 也可以在系統的 /varlog/syslog (ubuntu) 看到, 在 centos 是 /var/log/message
mint snmp # tail /var/log/syslog -n 1
Mar 17 10:48:00 mint snmptrapd[14654]: localhost [UDP: [127.0.0.1]:35655->[127.0.0.1]:162]: Trap , SNMPv2-SMI::mib-2.1.3.0 = Timeticks: (7605090) 21:07:30.90, SNMPv2-SMI::snmpModules.1.1.4.1.0 = OID: SNMPv2-SMI::snmpModules.1.1.5.1, SNMPv2-SMI::snmpModules.1.1.5.1 = STRING: "test coldStart"


# 接下來從遠端機器送個封包,到這台機器. 測試看看遠端是否可以工作
[root@c7 ~]# snmptrap -v 2c -c public 192.168.1.181 "" SNMPv2-MIB::coldStart SNMPv2-MIB::coldStart s "test coldStart"

# 確認有收到, 但這太危險了. 所以要加強一下安全性
mint snmp # tail /var/log/syslog -n 1
Mar 17 10:51:20 mint snmptrapd[14654]: ip69168.cm.nsysu.edu.tw [UDP: [192.168.1.168]:36762->[192.168.1.181]:162]: Trap , SNMPv2-SMI::mib-2.1.3.0 = Timeticks: (607117) 1:41:11.17, SNMPv2-SMI::snmpModules.1.1.4.1.0 = OID: SNMPv2-SMI::snmpModules.1.1.5.1, SNMPv2-SMI::snmpModules.1.1.5.1 = STRING: "test coldStart"

# 還好他有支援 tcpwrapper 所以從 hosts.allow 設定一下限制可以收到封包的範圍
mint snmp # whereis snmptrapd
snmptrapd: /usr/sbin/snmptrapd /usr/share/man/man8/snmptrapd.8.gz
mint snmp # ldd /usr/sbin/snmptrapd  | grep libwrap
libwrap.so.0 => /lib/x86_64-linux-gnu/libwrap.so.0 (0x00007f14e6eca000)
mint snmp # tail -n 2 /etc/hosts.allow 
snmptrapd:192.168.1.181,192.168.1.168:allow
snmptrapd:ALL:deny


# 測試模擬 UPS agent HDP801 動作
# AC 斷電
snmptrap -v 2c -c public 192.168.1.181 "" SNMPv2-SMI::mib-2.33.1.6.3.6 SNMPv2-SMI::mib-2.33.1.6.3.6 s "The UPS is not on battery power"

# AC 復電
snmptrap -v 2c -c public 192.168.1.181 "" SNMPv2-SMI::mib-2.33.1.6.3.2 SNMPv2-SMI::mib-2.33.1.6.3.2 s "Utility power has been restore"

# 測試 MIB coldStart
snmptrap -d -v 2c -c public 192.168.1.181 "" SNMPv2-MIB::coldStart SNMPv2-MIB::coldStart s "test coldStart"

測試完成後就可以去 UPS 上面設定 snmptrap了



關於 Linux system 關機這件事
------------------
# 在 AC 斷電時,立即啟動關機程序。但是如果電又馬上來了,就會覺得 ooxx 。所以預設關機指令執行後三分鐘後才關電,但這時候如果電來了,就可以執行取消關機的工作。

# 本機三分鐘後關機
shutdown -h -P 3 'After 3 minutes will be Poweroff. The UPS is not on battery power'

# 本機取消關機
shutdown -c 'Utility power has been restore'

# 以上只是把本地端的電源關閉,無法關閉其他遠端的機器
# -----------------------------------------------------------------------------------------
# 如果要控制遠端的電腦來關機,你必須要有遠端的 root 權限。
# 並且透過 ssh key 認證來執行遠端的 script 來關機。
# 所以我把它寫成了關機的批次檔案,放在遠端的機器

# 本機取消關機
mint cmd # cat snmptrapd_cancel_poweroff.sh 
#!/bin/bash
shutdown -c 'Utility power has been restore'

# 本機三分鐘後關機
mint cmd # cat snmptrapd_poweroff.sh 
#!/bin/bash
shutdown -h -P 3 'After 3 minutes will be Poweroff. The UPS is not on battery power'

# 這裡的 test 帳號使一般使用者帳號,已經設定具有 sudo 權限,且不用密碼的 sudo 。
# 並且主機已經設定 ssh key 認證,可以遠端被登入。
# 測試遠端關機的動作
ssh mtchang@192.168.1.181 'sudo /home/mtchang/cmd/snmptrapd_poweroff.sh' &
# 測試遠端取消關機的動作
ssh mtchang@192.168.1.181 'sudo /home/mtchang/cmd/snmptrapd_cancel_poweroff.sh'


#=========================================
# 主文結束
#=========================================

MIB LOG 備註
--------------
# 型號: Phoenixtec USHA Smart II Card 在線式UPS 的 snmptrapd 觸發 LOG 紀錄
# ------------------------------------------------------------------------------------
# 使用該系統自訂的 MIB
# 行為:電源好像有問題
Mar 18 16:32:13 power snmptrapd[21992]: 2016-03-18 16:32:13 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.4]]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (61738343) 7 days, 3:29:43.43#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "Utility power not available"

# 行為:沒電,切換到電池供電模式
Mar 18 16:32:35 power snmptrapd[21992]: 2016-03-18 16:32:35 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.4]]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (61744393) 7 days, 3:30:43.93#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.1.2.4.0 = INTEGER: 80#011SNMPv2-SMI::enterprises.2468.1.2.1.1.2.5.0 = INTEGER: 121#011SNMPv2-SMI::enterprises.2468.1.2.1.1.2.2.0 = INTEGER: 61#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "The UPS has switched to battery backup power"

# 行為:UPS 沒有在電池供電模式了
Mar 18 16:32:55 power snmptrapd[21992]: 2016-03-18 16:32:55 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.4]]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (61744694) 7 days, 3:30:46.94#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "The UPS is not on battery power"

# 行為:原來是電來了
Mar 18 16:33:03 power snmptrapd[21992]: 2016-03-18 16:33:03 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.4]]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (61744748) 7 days, 3:30:47.48#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "Utility power has been restore"



# 型號:鼎堅 MegaTec Model: CX504 在線式
# ------------------------------------------------------------------------------------
# 使用該系統自訂的 MIB
# UPS 電池測試
Mar 11 15:46:22 c7 snmptrapd[6889]: 2016-03-11 15:46:22 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.168]:162]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (991179) 2:45:11.79#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "The testing is going on UPS"
# UPS 電池測試測試結束
Mar 11 15:46:32 c7 snmptrapd[6889]: 2016-03-11 15:46:32 192.168.1.132 [UDP: [192.168.1.132]:161->[192.168.1.168]:162]:#012DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (992101) 2:45:21.01#011SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-SMI::enterprises.2468.1.2.1.2#011SNMPv2-SMI::enterprises.2468.1.2.1.1.6.2.0 = INTEGER: 1#011SNMPv2-SMI::enterprises.2468.1.2.1.1.6.1.0 = INTEGER: 1#011SNMPv2-SMI::enterprises.2468.1.2.1.1.6.3.0 = INTEGER: 1#011SNMPv2-SMI::enterprises.2468.1.2.1.1.6.4.0 = INTEGER: 1457710859#011SNMPv2-SMI::enterprises.2468.1.2.1.1.6.5.0 = INTEGER: 1500#011SNMPv2-SMI::enterprises.2468.1.2.1.2 = STRING: "The testing of UPS is completed"

# AC 停電
#[root@c7 snmp]# cat traps.txt
#trap: HDP_AC_lost 192.168.1.132 UDP: [192.168.1.132]:162->[192.168.1.168]:162 DISMAN-EVENT-MIB::sysUpTimeInstance = 148:3:54:42.20, SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-SMI::enterprises.935.0.5, SNMPv2-SMI::enterprises.935.0.5 = "UPS has switched to battery power."
# 顯示依據 shell script 的 action.txt 動作內容
#[root@c7 snmp]# cat action.txt
#HDP_AC_lost root Fri Mar 18 16:14:11 CST 2016
#shutdown -h -P 3 'After 3 minutes will be Poweroff. The UPS is not on battery power'


# AC 電來了
#[root@c7 snmp]# cat traps.txt
#trap: HDP_AC_restore 192.168.1.132 UDP: [192.168.1.132]:162->[192.168.1.168]:162 DISMAN-EVENT-MIB::sysUpTimeInstance = 148:3:55:28.20, SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-SMI::enterprises.935.0.9, SNMPv2-SMI::enterprises.935.0.9 = "Utility power has been restored."
# 顯示依據 shell script 的 action.txt 動作內容
#[root@c7 snmp]# cat action.txt
#HDP_AC_restore root Fri Mar 18 16:14:58 CST 2016
#shutdown -c 'Utility power has been restore'


延伸閱讀
----------
UPS-MIB http://www.oidview.com/mibs/0/UPS-MIB.html
snmptrap 使用 http://www.net-snmp.org/wiki/index.php/TUT:snmptrap
snmptrapd 設定 http://www.net-snmp.org/wiki/index.php/TUT:Configuring_snmptrapd
man SNMPTRAP(1)
man snmptrapd.conf(5)















沒有留言: