Ruby SNMPでプライベートMIBを扱う

RubyのSNMPライブラリでプライベートMIBを取り扱う方法を紹介します。
SNMPのオブジェクトID(OID)からオブジェクト名へ変換や、オブジェクト名からOIDに変換ができます。

SNMPのOIDばかりだと混乱してしまうため、積極的にオブジェクト名を使いましょう。



# 環境

- Ubuntu 14.04
- Ruby 2.2.4
  - snmp 1.2.0
- smitools 0.4.8 - smidumpコマンド、MIBからOIDを抜き出すために必要



# インストール

Ubuntuの場合は、apt-getでsmitoolsをインストールします。
必要なGemも合わせてインストールします。

```bash
$ sudo apt-get install smitools
$ gem install snmp
```


# プライベートMIBのダウンロード

今回は例としてCiscoのプライベートMIBであるCISCO-PING-MIBを取り扱います。必要なプライベートMIBを[Cisco SNMP オブジェクトナビゲータ](http://snmp.cloudapps.cisco.com/Support/SNMP/do/BrowseMIB.do?local=ja&step=2&mibName=CISCO-PING-MIB)からダウンロードします。依存関係があるため、下記の3つのMIBをダウンロードします。

- CISCO-SMI
- CISCO-TC
- CISCO-PING-MIB

カレントディレクトリにmibsというディレクトリを作成し、上記のプライベートMIBを保存します。

```bash
$ ls -l ./mibs/
合計 140
-rw-r--r-- 1 kooshin kooshin  16208  2月  7 18:00 CISCO-PING-MIB
-rw-r--r-- 1 kooshin kooshin  16397  2月  7 18:00 CISCO-SMI
-rw-r--r-- 1 kooshin kooshin 102591  2月  7 18:00 CISCO-TC
```


# プライベートMIBの変換

snmpライブラリで取り扱うため、MIBファイルからOIDファイルに変換します。
./mibs/に保存したMIBを変換し、OIDとオブジェクト名が対となったYAMLファイルを作成します。この作業は一度きりのため、irbで変換します。

SNMP::MIB.import_moduleのクラスメソッドで変換します。内部の処理でsmidumpコマンドを呼び出しているため、引数などはそのまま使えます。
今回はCISCO-SMIファイルとCISCO-TCファイルが依存関係としてあるため、`-p`オプションで依存ファイルを指定し、最後に変換対象のCISCO-PING-MIBファイルを指定しています。

SNMP::MIB.import_moduleの実行時にエラーメッセージがなく、モジュール名が返ってくればOKです。

```irb
$ irb
irb(main):001:0> require 'snmp'
=> true
irb(main):002:0> SNMP::MIB.import_module('-p mibs/CISCO-SMI -p mibs/CISCO-TC mibs/CISCO-PING-MIB', './mibs')
=> "CISCO-PING-MIB"
irb(main):003:0> 
```

下記のように「smidump: module `*' contains errors, expect flawed」のエラーが返って来た場合は、依存関係があるにも関わらず、依存ファイルが指定されていない可能性があります。このエラーメッセージが表示されても、YAMLファイルが作成されますが、OIDが欠落して使えない可能性があります。

```irb
irb(main):002:0> SNMP::MIB.import_module('mibs/CISCO-TC', './mibs')
smidump: module `mibs/CISCO-TC' contains errors, expect flawed output
=> "CISCO-TC"
```


SNMP::MIB.import_moduleの実行が成功するとCISCO-PING-MIB.yamlファイルが./mibsディレクトリに保存されます。

```bash
$ ls -la ./mibs
合計 152
drwxrwxr-x 2 kooshin kooshin   4096  2月  7 18:54 .
drwxrwxr-x 4 kooshin kooshin   4096  2月  7 18:40 ..
-rw-r--r-- 1 kooshin kooshin  16208  2月  7 18:00 CISCO-PING-MIB
-rw-rw-r-- 1 kooshin kooshin   1247  2月  7 18:54 CISCO-PING-MIB.yaml
-rw-r--r-- 1 kooshin kooshin  16397  2月  7 18:00 CISCO-SMI
-rw-r--r-- 1 kooshin kooshin 102591  2月  7 18:00 CISCO-TC
$ cat mibs/CISCO-PING-MIB.yaml 
---
ciscoPingMIB: 1.3.6.1.4.1.9.9.16
ciscoPingMIBObjects: 1.3.6.1.4.1.9.9.16.1
ciscoPingTable: 1.3.6.1.4.1.9.9.16.1.1
ciscoPingEntry: 1.3.6.1.4.1.9.9.16.1.1.1
ciscoPingSerialNumber: 1.3.6.1.4.1.9.9.16.1.1.1.1
ciscoPingProtocol: 1.3.6.1.4.1.9.9.16.1.1.1.2
ciscoPingAddress: 1.3.6.1.4.1.9.9.16.1.1.1.3
ciscoPingPacketCount: 1.3.6.1.4.1.9.9.16.1.1.1.4
ciscoPingPacketSize: 1.3.6.1.4.1.9.9.16.1.1.1.5
ciscoPingPacketTimeout: 1.3.6.1.4.1.9.9.16.1.1.1.6
ciscoPingDelay: 1.3.6.1.4.1.9.9.16.1.1.1.7
ciscoPingTrapOnCompletion: 1.3.6.1.4.1.9.9.16.1.1.1.8
ciscoPingSentPackets: 1.3.6.1.4.1.9.9.16.1.1.1.9
ciscoPingReceivedPackets: 1.3.6.1.4.1.9.9.16.1.1.1.10
ciscoPingMinRtt: 1.3.6.1.4.1.9.9.16.1.1.1.11
ciscoPingAvgRtt: 1.3.6.1.4.1.9.9.16.1.1.1.12
ciscoPingMaxRtt: 1.3.6.1.4.1.9.9.16.1.1.1.13
ciscoPingCompleted: 1.3.6.1.4.1.9.9.16.1.1.1.14
ciscoPingEntryOwner: 1.3.6.1.4.1.9.9.16.1.1.1.15
ciscoPingEntryStatus: 1.3.6.1.4.1.9.9.16.1.1.1.16
ciscoPingVrfName: 1.3.6.1.4.1.9.9.16.1.1.1.17
ciscoPingMIBTrapPrefix: 1.3.6.1.4.1.9.9.16.2
ciscoPingMIBTraps: 1.3.6.1.4.1.9.9.16.2.0
ciscoPingMIBConformance: 1.3.6.1.4.1.9.9.16.3
ciscoPingMIBCompliances: 1.3.6.1.4.1.9.9.16.3.1
ciscoPingMIBGroups: 1.3.6.1.4.1.9.9.16.3.2
ciscoPingCompletion: 1.3.6.1.4.1.9.9.16.2.0.1
```

YAMLファイルの中身は、単純なオブジェクト名とOIDのハッシュです。このため、手作業でオブジェクト名とOIDのペアを作成することも簡単にできそうです。



# Ruby内でプライベートMIBの参照

SNMP::MIB.newでインスタンスを作成し、load_moduleメソッドで対象のMIBをロードします。ここでは、mibs変数に保存してます。

VarBindを作成する場合は、SNMP::VarBind.newの代わりにmibs.varbindメソッドを呼び出すことで、OIDではなく名前で指定できます。

mibs.oidで、名前からOIDに変換。mibs.nameで、OIDから名前に変換。

MIBのモジュール名は省略できるため、今回はモジュール名を省略しています。
正式「CISCO-PING-MIB::ciscoPingEntry」、省略形「ciscoPingEntry」

```rb:dev1.rb
require 'snmp'

# プライベートMIBを保持するmibs変数
mibs = SNMP::MIB.new
mibs.load_module('CISCO-PING-MIB', './mibs')

SNMP::Manager.open(host: '172.16.1.132', community: 'private') do |manager|
  # ciscoPingEntryStatusのVarBindを作成し、snmpsetする
  varbind = mibs.varbind('ciscoPingEntryStatus.111', SNMP::Integer.new(5))
  manager.set(varbind)

  # ciscoPingEntryをsnmpwalkする
  manager.walk(mibs.oid('ciscoPingEntry')) do |row|
    puts "#{mibs.name(row.name)}\t#{row.value}"
  end
end
```



# 実行結果

上記のスクリプトを実行すると、下記の結果になります。

```bash:実行結果
$ ruby dev1.rb 
CISCO-PING-MIB::ciscoPingPacketCount.111        5
CISCO-PING-MIB::ciscoPingPacketSize.111 100
CISCO-PING-MIB::ciscoPingPacketTimeout.111      2000
CISCO-PING-MIB::ciscoPingDelay.111      0
CISCO-PING-MIB::ciscoPingTrapOnCompletion.111   2
CISCO-PING-MIB::ciscoPingCompleted.111  2
CISCO-PING-MIB::ciscoPingEntryStatus.111        3
CISCO-PING-MIB::ciscoPingVrfName.111    default
```