Go言語のSNMPパッケージgosnmpを人にやさしくする方法

#  はじめに

前回、Go言語のSNMPパッケージgosnmpを紹介しました。

https://qiita.com/twsnmp/items/45a02243ce3fbca2a55f

このパッケージの利用を、少し人に、やさしくする方法の説明です。

# gosnmpの不便なところ

前回の記事で紹介したサンプルソースコードと実行結果では、MIBの名前を数値のOIDで指定しています。

```go:main.go
	oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"}
	result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS
```
や

```:実行結果
$go run main.go
0: oid: .1.3.6.1.2.1.1.4.0 string: twsnmp@gmail.com
1: oid: .1.3.6.1.2.1.1.7.0 number: 72
```
です。

数値のOIDは、人に、やさしくありません。いったい何の情報をSNMPで取得しているのか、すぐには、わからないからです。MIBのオブジェクトには、人が理解しやすいように名前がついています。数値のOIDではなく名前を使ったほうがよいのです。NET-SNMPの場合は、名前でアクセスできます。

```shell:NET-SNMPの場合
$snmpget -v 2c -c public 192.168.1.210 sysContact.0 sysServices.0
SNMPv2-MIB::sysContact.0 = STRING: twsnmp@gmail.com
SNMPv2-MIB::sysServices.0 = INTEGER: 72
```


:::note info
SNMPを開発したテネシー大学のケース教授がSNMPセミナーで

> ネットワーク管理者が夜中の３時に電話で起こされて、
「.1.3.6.1.2.1.1.7.0の値がオカシイ」
と言われても、何を意味するのか理解できないでしょう。

というジョークで笑いをとっていたのを思い出します。
:::

# MIBのOIDと名前を変換するGo言語のパッケージ

名前問題を解決するために数値のOIDと名前を変換するGo言語のパッケージを作りました。

https://github.com/twsnmp/go-mibdb

です。

## 使い方

```shell:インストール
$go get github.com/twsnmp/go-mibdb
```

```go:main.go
package main

import (
	"fmt"
	"log"
	mibdb "github.com/twsnmp/go-mibdb"
)

func main() {
	m, err := mibdb.NewMIBDB("./mib.txt")
	if err != nil {
		fmt.Printf("NewMIBDB failed err=%v", err)
		return
	}
	fmt.Printf("sysDescr=%s\n", m.NameToOID("sysDescr"))
	fmt.Printf(".1.3.6.1.2.1.1.1.0=%s\n", m.OIDToName(".1.3.6.1.2.1.1.1.0"))
}
```

これを実行すると

```shell:実行結果
$go run main.go
sysDescr=.1.3.6.1.2.1.1.1
.1.3.6.1.2.1.1.1.0=sysDescr.0

```
のようになります。数値のOIDと名前の変換ができます。

## mib.txtの作り方

MIBのOIDの名前の関係はmib.txtというファイルで定義しています。
中身は

```:mib.txt
.1.3
.iso.org
.1.3.6
.iso.org.dod
.1.3.6.1
.iso.org.dod.internet
.1.3.6.1.1
.iso.org.dod.internet.directory
.1.3.6.1.2
.iso.org.dod.internet.mgmt
.1.3.6.1.2.1
.iso.org.dod.internet.mgmt.mib-2
```
のようになっています。２行一組で、１行目がOID、２行目が名前です。


https://github.com/twsnmp/go-mibdb/blob/master/mib.txt

からダウンロードできます。

拡張MIBも含めた定義ファイルが必要ならば自分で作成できます。NET-SNMPのsnmptranslateコマンドが必要です。

```shell:mib.txtの作成
$snmptranslate  -T os  > mib.txt
```

で作成できます。

# サンプルプログラムを人に、やさしく書き換える

前回のサンプルプログラムを、このパッケージを使って書き換えてみます。

```diff_go:main.go

package main

import (
	"fmt"
	"log"

	g "github.com/gosnmp/gosnmp"
+	mibdb "github.com/twsnmp/go-mibdb"
)

func main() {
+	m, err := mibdb.NewMIBDB("./mib.txt")
+	if err != nil {
+		log.Fatalf("NewMIBDB failed err=%v", err)
+	}
	// Default is a pointer to a GoSNMP struct that contains sensible defaults
	// eg port 161, community public, etc
	g.Default.Target = "192.168.1.210"
	// g.Default.Community = "public"
	// g.Default.Port = 161
	err = g.Default.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer g.Default.Conn.Close()

-	oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"}
+	oids := []string{m.NameToOID("sysContact.0"), m.NameToOID("sysServices.0")}
	result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS
	if err2 != nil {
		log.Fatalf("Get() err: %v", err2)
	}

	for i, variable := range result.Variables {
-		fmt.Printf("%d: oid: %s", i, variable.Name)
+		fmt.Printf("%d: oid: %s name:%s = ", i, variable.Name, m.OIDToName(variable.Name))

		// the Value of each variable returned by Get() implements
		// interface{}. You could do a type switch...
		switch variable.Type {
		case g.OctetString:
			bytes := variable.Value.([]byte)
			fmt.Printf("string: %s\n", string(bytes))
		default:
			// ... or often you're just interested in numeric values.
			// ToBigInt() will return the Value as a BigInt, for plugging
			// into your calculations.
			fmt.Printf("number: %d\n", g.ToBigInt(variable.Value))
		}
	}
}

```

 実行すると

 ```shell:実行結果
$go run main.go
0: oid: .1.3.6.1.2.1.1.4.0 name:sysContact.0 = string: twsnmp@gmail.com
1: oid: .1.3.6.1.2.1.1.7.0 name:sysServices.0 = number: 72
 ```
のようになります。人にやさしくなったでしょう。

# 余談

Go言語で開発しているTWSNMPシリーズ

https://note.com/twsnmp/n/na5c62f59833d

は、最初、このパッケージ使って名前とOIDの変換だけ対応していました。
でも、NET-SNMPは取得したMIB情報の表示をもっと人にやさしい方法でできるのが悔しくて、ASN.1のMIB定義ファイルを読み込んでNET-SNMPと同じように表現できる仕組みを作りました。この話も、いつか書こうと思います。