2013年01月31日

How to use "smiparser" - Free MIB/SMI Parser for Java

これまでSNMP4Jを使って、Java + 実践SNMPプログラミングというテーマで何回か解説してきましたが、MIBパーサーの話は避けてきました。 フリーのJava MIB ParserといえばMibbleがあるのにです。

理由は、Mibbleは良いAPIですがライセンスがGPLだから。 

GPLはコピー・レフトに例えられるようにコードの自由を強く保証しますが、開発者・利用者としてみると原理主義的な面があり、僕には少し窮屈に感じられるのです。

mib-smiparser

今日はJava開発者が、より自由(?)に利用できるsmiparserをご紹介します。

MIBパーサーとMIBブラウザ


ネットワーク機器の監視に利用されるSNMP MIB(Management Information Base)には、MO(Mangement Object)の構造・型・管理手法などを記したMIBファイルと呼ばれる定義ファイルが存在します。

MIBファイルの基本的な構造・記法はSMI(Structure of Management Information / ANS.1のセブセット)としてRFC1155で定義されており、これをパース(解析)するライブラリをMIBパーサーといいます。

では、具体的にどのようなアプリケーションを開発する際にMIBパーサーが必要になるのでしょうか?

シンプルに、ある特定のMOの値を収集(get, getnext, getbulk)・監視するSNMPポーリング・アプリケーション、例えばMRTGであればMIBパーサーを利用せずに開発できるでしょう。

しかし、ユーザーが自由にMIBファイルをロードし、MOを選択・収集出来るMIBブラウザのような対話的なSNMPアプリケーションではMIBパーサーが必要になります。

次に示すのはiDeskCentric社が配布しているMIB Browser(パーソナル版は無料)を使い一般的にはインターネット標準MIB/MIB-II(Internet standard MIB II)と呼ばれているRFC1213-MIBファイルを読み込んだ画面です。

smiparser01

MIBはツリー構造でOID(Object IDentifier)を使ってMOを一意に特定できるようになっています。

ツリーの末端にあるMOを葉(Leaf)、その中でも値の更新が可能(つまり、アクセス権がread-write)なMOはメモとペンでアイコン表現しています。

ifTable.ifIndexが鍵アイコンになっているのは何故でしょうか? これはifIndexの値はifTable(テーブル構造)の行を一意に特定するキーになることを表しています。
(テーブルの構造はifEntryで定義されており、複合キーを持つことも可能)

このような情報は全てMIBファイルに定義されており、MIB定義の動的な追加を行うアプリケーションにはMIBパーサーが必要になるのです。


smiparser -free MIB/SMI Parser for Java


smiparserはApache License 2.0で配布されている無料のMIBパーサー(Java)ライブラリです。

smiparser - SMI Parser for Java - Google Project Hosting

現時点で最新版はバージョン0.6.15。 このライブラリ(jarファイル)の入手には注意が必要です。

ダウンロード・ページから入手出来るjarファイルのサイズは18.4KBしかなく、次のような警告が表示され、利用できません。

Archive for required library: 'smiparser-0.6.15.jar' in project 'hoge' cannot be read or is not a valid ZIP file.

これを回避する為には、Source > Browse > maven2 > smiparser と進み、

smiparser02

このページに表示されたリンクから最新のjarファイルをダウンロードします。

ブラウザでダウンロードする場合は、画面下にある”View raw file”をクリックしましょう。

smiparser03

この他に参照する外部ライブラリとして、antlr-2.xcommons-collectionslog4jを用意します。

smiparser04

これで準備はおしまいです。


How to use smiparser for java


今日はインターネット標準MIB(MIB-II)を使ってsmiparserの使い方を学びます。

尚、RFC1213-MIBをエディタで開くとわかる通り、このMIBファイルはRFC1155-SMI、RFC1212-MIBを外部参照・IMPORTSしています。 

smiparser05

ですので、この2つのファイルも忘れずに用意しましょう。

smiparser06

まずは、次のコードでMIBファイルのロードを行います。

	private SmiMib mibs;
	
	public void loadMibs(File mibDir) throws MalformedURLException {
		File[] mibFiles =  mibDir.listFiles();
		List<URL> mibUrls = new ArrayList<URL>();
		for (File mib: mibFiles) {
			mibUrls.add(mib.toURI().toURL());
			//System.out.println(mib.getName());
		}
		SmiDefaultParser parser = new SmiDefaultParser();		
		parser.getFileParserPhase().setInputUrls(mibUrls);
		mibs = parser.parse();
	}

読み込んだMIBファイルのSyntaxに問題が無ければ正常に終了します。

まずOIDからオブジェクト名を解決してみましょう。

	public void test() {
		int[] oid = {1, 3, 6, 1, 2, 1, 1, 3}; // ?
		System.out.println(getObjectNameByOID(oid));		
	}
	
	public String getObjectNameByOID(int... oid) {
		String name = null;		
		SmiOidNode node = mibs.findByOid(oid);		
		if (node != null) {
			try {
				name = node.getSingleValue().getId();
			} catch (AssertionError ae) {
			}			
		}
		
		return name;
	}

このコードを実行すると次の出力が得られるはずです。

sysUpTime


もちろん、オブジェクト名からOIDを解決することも出来ます。

	public void test() {
		System.out.println(getOidStrByName("sysUpTime"));		
	}
	
	public String getOidStrByName(String name) {
		String oid = null;
		for (SmiModule module: mibs.getModules()) {
			SmiOidValue sov = module.findOidValue(name);
			if (sov != null) {
				oid = sov.getOidStr();
				break;
			}
		}
		
		return oid;
	}

結果は次の通り。

1.3.6.1.2.1.1.3


今度は、最初に見たMIBツリーを構築してみます。

	public void test() {
		printMibTree("RFC1213-MIB");
	}

	public void printMibTree(String mibName) {
		SmiModule module = mibs.findModule(mibName);
		SmiMib mib = module.getMib();
		SmiOidNode node = mib.getRootNode();
		printTree(node, 0);
	}

	public void printTree(SmiOidNode node, int depth) {
		Collection <? extends SmiOidNode> childs = node.getChildren();
		for (SmiOidNode child : childs) {
			List <SmiOidValue> values = child.getValues();
			for (SmiOidValue var : values) {
				String line = var.getId();
				line = line + " (" + var.getOidStr() + ")";
				String attr = "";
				if (var instanceof SmiTable) {
					line = "+ Table +" + line;
				} else if (var instanceof SmiRow) {
					line = "-> Entry" + line;
					attr = "index: ";
					SmiRow row = (SmiRow) var;
					List <SmiIndex> indexes = row.getIndexes();
					StringBuilder sb = new StringBuilder();
					for (SmiIndex index : indexes) {
						sb.append(" & " + index.getColumn().getId());
					}
					attr += sb.toString().replaceFirst(" & ", "");
				} else if (var instanceof SmiVariable) {
					SmiVariable sv = (SmiVariable) var;
					SmiType type = sv.getType();
					attr = "Syntax: " + type.getPrimitiveType() + ", Ac: "
							+ sv.getAccessToken().getId();
					line = "-> " + line;
				} else if (var instanceof SmiOidValue) {
					int num_child = child.getTotalChildCount();
					if (num_child > 0) {
						line = "+ " + line;
					} else {
						line = "- " + line;
					}
				} else {
					attr = var.getClass().getName();
					line = "? " + line;
				}
				printMibObject(line + " " + attr, depth);
			}
			printTree(child, depth + 1);
		}
	}

	public void printMibObject(String str, int depth) {
		int indent = str.length() + depth * 1;
		String format = "%" + indent + "s";
		System.out.printf(format, str + "\n");
	}

このコードは少し複雑に見えるかもしれませんが、MIB名称からSmiModule、SmiMib、SmiOidNodeと辿り、printTree()メソッドで再帰的にchildノードを検索・出力しています。

各要素はSmiOidValueインスタンスとして取得できますが、そのサブクラス(SmiTable、SmiRow、SmiVariable、SmiOidValue)に応じて出力内容を変え、値を持つLeaf(OID)、つまりVarliableであればタイプ(データ型/Syntax)、アクセス権などの属性を出力しています。 

もし、ツリー構造を構築する必要が無く、単純にSmiVariableだけを取得したい場合は次のようなコードになります。

	public void printAllMibVariables(String mibName) {
		SmiModule module = mibs.findModule(mibName);
		System.out.println(module.getId());
		Collection<smivariable> vars = module.getVariables();
		for (SmiVariable var: vars) {
			/* your code */
		}
	}

printMibTree()メソッドを実行した結果は次のようになります。

  + internet (1.3.6.1) 
   - directory (1.3.6.1.1) 
   + mgmt (1.3.6.1.2) 
    + mib-2 (1.3.6.1.2.1) 
     + system (1.3.6.1.2.1.1) 
      -> sysDescr (1.3.6.1.2.1.1.1) Syntax: OCTET_STRING, Ac: read-only
      -> sysObjectID (1.3.6.1.2.1.1.2) Syntax: OBJECT_IDENTIFIER, Ac: read-only
      -> sysUpTime (1.3.6.1.2.1.1.3) Syntax: TIME_TICKS, Ac: read-only
      -> sysContact (1.3.6.1.2.1.1.4) Syntax: OCTET_STRING, Ac: read-write
      -> sysName (1.3.6.1.2.1.1.5) Syntax: OCTET_STRING, Ac: read-write
      -> sysLocation (1.3.6.1.2.1.1.6) Syntax: OCTET_STRING, Ac: read-write
      -> sysServices (1.3.6.1.2.1.1.7) Syntax: INTEGER, Ac: read-only
     + interfaces (1.3.6.1.2.1.2) 
      -> ifNumber (1.3.6.1.2.1.2.1) Syntax: INTEGER, Ac: read-only
      + Table +ifTable (1.3.6.1.2.1.2.2) 
       -> EntryifEntry (1.3.6.1.2.1.2.2.1) index: ifIndex
        -> ifIndex (1.3.6.1.2.1.2.2.1.1) Syntax: INTEGER, Ac: read-only
        -> ifDescr (1.3.6.1.2.1.2.2.1.2) Syntax: OCTET_STRING, Ac: read-only
        -> ifType (1.3.6.1.2.1.2.2.1.3) Syntax: ENUM, Ac: read-only
        -> ifMtu (1.3.6.1.2.1.2.2.1.4) Syntax: INTEGER, Ac: read-only
        -> ifSpeed (1.3.6.1.2.1.2.2.1.5) Syntax: GAUGE_32, Ac: read-only
        -> ifPhysAddress (1.3.6.1.2.1.2.2.1.6) Syntax: OCTET_STRING, Ac: read-only
        -> ifAdminStatus (1.3.6.1.2.1.2.2.1.7) Syntax: ENUM, Ac: read-write
        -> ifOperStatus (1.3.6.1.2.1.2.2.1.8) Syntax: ENUM, Ac: read-only
        -> ifLastChange (1.3.6.1.2.1.2.2.1.9) Syntax: TIME_TICKS, Ac: read-only
        -> ifInOctets (1.3.6.1.2.1.2.2.1.10) Syntax: COUNTER_32, Ac: read-only
        -> ifInUcastPkts (1.3.6.1.2.1.2.2.1.11) Syntax: COUNTER_32, Ac: read-only
        -> ifInNUcastPkts (1.3.6.1.2.1.2.2.1.12) Syntax: COUNTER_32, Ac: read-only
        -> ifInDiscards (1.3.6.1.2.1.2.2.1.13) Syntax: COUNTER_32, Ac: read-only
        -> ifInErrors (1.3.6.1.2.1.2.2.1.14) Syntax: COUNTER_32, Ac: read-only
        -> ifInUnknownProtos (1.3.6.1.2.1.2.2.1.15) Syntax: COUNTER_32, Ac: read-only
        -> ifOutOctets (1.3.6.1.2.1.2.2.1.16) Syntax: COUNTER_32, Ac: read-only
        -> ifOutUcastPkts (1.3.6.1.2.1.2.2.1.17) Syntax: COUNTER_32, Ac: read-only
        -> ifOutNUcastPkts (1.3.6.1.2.1.2.2.1.18) Syntax: COUNTER_32, Ac: read-only
        -> ifOutDiscards (1.3.6.1.2.1.2.2.1.19) Syntax: COUNTER_32, Ac: read-only
        -> ifOutErrors (1.3.6.1.2.1.2.2.1.20) Syntax: COUNTER_32, Ac: read-only
        -> ifOutQLen (1.3.6.1.2.1.2.2.1.21) Syntax: GAUGE_32, Ac: read-only
        -> ifSpecific (1.3.6.1.2.1.2.2.1.22) Syntax: OBJECT_IDENTIFIER, Ac: read-only
     + at (1.3.6.1.2.1.3) 
     --- snip --- 


どうでしたか?

smiparserのドキュメントは極端に少ないですが、ここまで動くのであれば、興味を持つ方もいるのではないでしょうか。

これを機会に利用者、サンプル・コードが増えると良いですね。



Java Network Programming
Java Network Programming
posted with amazlet at 13.01.31
O'Reilly Media (2009-02-09)
売り上げランキング: 11,227

Understanding SNMP MIBs
Understanding SNMP MIBs
posted with amazlet at 13.01.31
David T. McGinnis, Evan Perkins
Prentice Hall
売り上げランキング: 320,785

Posted by netbuffalo at 22:00│Comments(0)TrackBack(0)Java | 実践SNMP+Java


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/4357282

コメントする

名前
URL
 
  絵文字