Permalink: 2015-09-01 01:05:00+09:00 by ruy@ainoniwa.net in technical tags: freebsd snmp bsnmpd smart smartctl social:

今日のお話

つい最近、ファイルサーバを新しくした のですけど、

当然HDDの温度をSNMPで取得できるようにしたいと思うわけで、FreeBSDに標準搭載されているbsnmpdを使ってそれを実現しようと思います。

smartmontoolsのインストール

何にしても、まずはS.M.A.R.Tを取得できるようにしないといけないので、smartctl(が入ってるsmartmontools)をインストールします。:

# pkg install smartmontools

S.M.A.R.Tが有効になっていない場合は、有効にしつつ情報が取得できることを確認しておきます。:

# smartctl -s on /dev/ada0
# smartctl -i /dev/ada0
smartctl 6.4 2015-06-04 r4109 [FreeBSD 10.2-RELEASE amd64] (local build)
Copyright (C) 2002-15, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Family:     JMicron based SSDs
Device Model:     ADATA SP600
Serial Number:    7E1920004364
LU WWN Device Id: 5 707c18 00004cff0
Firmware Version: 3.04
User Capacity:    64,023,257,088 bytes [64.0 GB]
Sector Size:      512 bytes logical/physical
Rotation Rate:    Solid State Device
Form Factor:      < 1.8 inches
Device is:        In smartctl database [for details use: -P show]
ATA Version is:   ACS-2 (minor revision not indicated)
SATA Version is:  SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is:    Sun Aug 23 22:14:59 2015 JST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

bsnmpdのインストール

最初から入っているので不要です。

bsnmp-ucdのインストール

net-snmpdで言うところのexecを実行するには、bsnmp-ucdが必要になるので、それを入れます。:

# pkg install bsnmp-ucd

S.M.A.R.T.値の自動取得スクリプトと独自OIDの生成

次に、ユーザ独自に定義したOIDでS.M.A.R.T.情報が取得できるように適当なスクリプトを書きます。

#!/bin/sh

TMP_DIR="/tmp/snmp_smart"
mkdir -p ${TMP_DIR}

BSNMPD_CONFIG="/etc/snmp_smartctl.config"
echo "# Auto generate: smartctl -A"                 >  ${BSNMPD_CONFIG}
echo "# ------------------------------------------" >> ${BSNMPD_CONFIG}
echo "# extTable OIDs:"                             >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.1   : extIndex"      >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.2   : extNames"      >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.3   : extCommand"    >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.100 : extResult"     >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.101 : extOutput"     >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.102 : extErrFix"     >> ${BSNMPD_CONFIG}
echo "#   1.3.6.1.4.1.2021.8.1.103 : extErrFixCmd"  >> ${BSNMPD_CONFIG}
echo "# ------------------------------------------" >> ${BSNMPD_CONFIG}

count=1
for dev in `ls /dev/ | grep -E -e "^ada[0-9]+\>" -e "^da[0-9]+\>"`; do
  DEV_VALUE_FILE="${TMP_DIR}/${dev}_smart.txt"

  echo "# Auto generate: smartctl -A /dev/$dev" > ${DEV_VALUE_FILE}
  data=`/usr/local/sbin/smartctl -A /dev/$dev | awk '/0x00/ {print $1,$2,$10}'`

  IFS_OLD=${IFS}
  IFS=$'\n'
  for line in $data; do
    echo ${line} >> ${DEV_VALUE_FILE}
    OID=`echo $line | cut -d " " -f 1`
    NAME=`echo $line | cut -d " " -f 2`
    VALUE=`echo $line | cut -d " " -f 3`
    echo "extNames.${count}   = \"SMART_${OID}_${NAME}_${dev}\"" >> ${BSNMPD_CONFIG}
    echo "extCommand.${count} = \"grep ${NAME} ${DEV_VALUE_FILE} | cut -d ' ' -f 3\"" >> ${BSNMPD_CONFIG}
    count=$((count+1))
  done
  IFS=${IFS_OLD}

done

bsnmpdに読み込ませて動かす

さっき書いたスクリプトをこんな感じで置いて、とりあえず一度実行しておくと、スーパー力技コマンド群が生成されます。:

# chmod 700 /usr/local/etc/snmp_smartctl.sh
# /usr/local/etc/snmp_smartctl.sh
# head -40 /etc/snmp_smartctl.config
# Auto generate: smartctl -A
# ------------------------------------------
# extTable OIDs:
#   1.3.6.1.4.1.2021.8.1.1   : extIndex
#   1.3.6.1.4.1.2021.8.1.2   : extNames
#   1.3.6.1.4.1.2021.8.1.3   : extCommand
#   1.3.6.1.4.1.2021.8.1.100 : extResult
#   1.3.6.1.4.1.2021.8.1.101 : extOutput
#   1.3.6.1.4.1.2021.8.1.102 : extErrFix
#   1.3.6.1.4.1.2021.8.1.103 : extErrFixCmd
# ------------------------------------------
extNames.1   = "SMART_1_Raw_Read_Error_Rate_ada0"
extCommand.1 = "grep Raw_Read_Error_Rate /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.2   = "SMART_2_Throughput_Performance_ada0"
extCommand.2 = "grep Throughput_Performance /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.3   = "SMART_3_Unknown_JMF_Attribute_ada0"
extCommand.3 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.4   = "SMART_5_Reallocated_Sector_Ct_ada0"
extCommand.4 = "grep Reallocated_Sector_Ct /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.5   = "SMART_7_Unknown_JMF_Attribute_ada0"
extCommand.5 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.6   = "SMART_8_Unknown_JMF_Attribute_ada0"
extCommand.6 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.7   = "SMART_9_Power_On_Hours_ada0"
extCommand.7 = "grep Power_On_Hours /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.8   = "SMART_10_Unknown_JMF_Attribute_ada0"
extCommand.8 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.9   = "SMART_12_Power_Cycle_Count_ada0"
extCommand.9 = "grep Power_Cycle_Count /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.10   = "SMART_167_Unknown_JMF_Attribute_ada0"
extCommand.10 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.11   = "SMART_168_SATA_Phy_Error_Count_ada0"
extCommand.11 = "grep SATA_Phy_Error_Count /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.12   = "SMART_169_Unknown_JMF_Attribute_ada0"
extCommand.12 = "grep Unknown_JMF_Attribute /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.13   = "SMART_170_Bad_Block_Count_ada0"
extCommand.13 = "grep Bad_Block_Count /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"
extNames.14   = "SMART_173_Erase_Count_ada0"
extCommand.14 = "grep Erase_Count /tmp/snmp_smart/ada0_smart.txt | cut -d ' ' -f 3"

記述の方法は extNames.XXX となるが、子要素は作れないようだ。つまり extNames.X.YYY とは書けない。

しかし、連番である必要はないので、例えば S.M.A.R.T. に関するOIDは1000番台(extNames.1XXX)、ZFSに関するOIDは2000番台(extNames.2XXX)などのように使い分けることは出来る。

デフォルトのconfigに少し手を入れて起動します。:

# echo 'begemotSnmpdModulePath."ucd" = "/usr/local/lib/snmp_ucd.so"' >> /etc/snmpd.config
# echo '%ucd' >> /etc/snmpd.config
# echo '.include "/etc/snmp_smartctl.config"' >> /etc/snmpd.config
# sysrc bsnmpd_enable="YES"
# service bsnmpd start

ちゃんと起動してきたら、ひとまず先へ進もう。

bsnmpdから値を取得できるようになるまで

さて、すぐにSNMP Getして値を確認。したいのは山々だが、少し待つ必要がある。

net-snmpdのexecやextendで設定したコマンドは「SNMPで取得しようとした時に実行される」のだが、bsnmpdはどうやらポーリング実行のようだ。

実行間隔については bsnmp-ucd(8) に記載されている:

updateInterval          Statistics update interval, in ticks.  The default is 500 ticks (5 seconds).

extCheckInterval    External commands check interval, in ticks.  The default is 100 ticks (1 second).

extUpdateInterval   External commands update interval (used e.g. by fix commands executor), in ticks.  The default is 3000 ticks (30 secondd).

extTimeout          External commands start timeout.  The default is 60 seconds.

と言うことらしいので、多少の振れ幅はあるものの30秒前後で更新されると考えればいいだろう。

これはbsnmpdを再起動した場合もそうなので、タイミングには少し気を付けた方が良いかもしれない。

実際に取得してみる

まぁそんなわけで、少し待てば値がずらずら並ぶようになるので一息ついてから確認に入ろう。

皆さんも既にご存知の通り、SNMP Getのクライアントには bsnmpwalk または bsnmpget を使用します。

例えばda2の温度を取得する場合はこんな感じ:

# smartctl -A /dev/da2 | grep ^194
194 Temperature_Celsius     0x0022   108   101   000    Old_age   Always       -       44
# bsnmpwalk 1.3.6.1.4.1.2021.8.1.2.65
1.3.6.1.4.1.2021.8.1.2.65 = SMART_194_Temperature_Celsius_da2
# bsnmpwalk 1.3.6.1.4.1.2021.8.1.101.65
1.3.6.1.4.1.2021.8.1.101.65 = 44

おー、ちゃんと取れているね。

動かし続けよう

とりあえず、さっきのスクリプトをcronに仕込んで動かし続けてみようと思います。まる。

ちゃんとグラフも描いてみないとね!