本記事では、PythonのPySNMPライブラリを使用して、SNMPに対応したIP機器などに要求するSNMPコマンドの基本的な使い方について学んでいきます。
SNMPとは
SNMP(Simple Network Management Protocol)とは、IP機器(サーバやルータなど)を管理するために1988年に導入されたプロトコルです。
SNMPを用いることで、サーバなどのCPUの使用率や温度状態をポーリングしたり、またルータなどのインタフェースダウンなど異常が発生した際にトラップ(Trap)と呼ばれるものを管理者(Manager)に送信することで、障害検知を容易にすることができます。
SNMPはこれまでにSNMPv1、SNMPv2(厳密にはSNMPv2c)、SNMPv3の3つのバージョンがありますが、SNMPv2がデファクトスタンダードとなっています。
PySNMPのインストール
PySNMPは以下のSNMP Labsというところがリリースしているライブラリになります。
以下はDocumentationになります。
それでは、pipからPySNMPをインストールします。
なお、Pythonのバージョンは3.6.8になります。
> pip3 install pysnmp > pip3 list | grep pysnmp pysnmp (4.4.12)
上記バージョンのPySNMPがインストールされました。
Ciscoルータの設定
GNS3上で起動したCiscoルータ(c7200)に以下のsnmp設定を行います
ここではpublic
としてRead Only
、private
としてRead Write
を設定します。
Router(config)# snmp-server community public RO Router(config)# snmp-server community private RW
snmpwalkコマンド
snmpwalkコマンドとは、機器が保有するMIBツリーの一部を読み込むためのコマンドで、実際はget-nextの操作を行うものになります。
以下ではインタフェース情報を取得しています。
# snmpwalk.py from pysnmp.hlapi import * import sys if len(sys.argv) != 4: print("Usage: {} modName symName HOSTNAME".format(sys.argv[0])) sys.exit(1) modName = sys.argv[1] symName = sys.argv[2] router_ip = sys.argv[3] g = nextCmd(SnmpEngine(), CommunityData('public'), UdpTransportTarget((router_ip, 161)), ContextData(), ObjectType(ObjectIdentity(modName, symName)), lexicographicMode=False) while True: try: errorIndication, errorStatus, errorIndex, varBinds = next(g) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % (errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex) - 1][0] or '?')) else: for varBind in varBinds: print(' = '.join([x.prettyPrint() for x in varBind])) except StopIteration: break
上記snmpwalkのPythonスクリプトを実行してみます。
> python3 snmpwalk.py IF-MIB interfaces router_ip IF-MIB::ifNumber.0 = 3 IF-MIB::ifIndex.1 = 1 IF-MIB::ifIndex.2 = 2 IF-MIB::ifIndex.4 = 4 IF-MIB::ifDescr.1 = FastEthernet0/0 IF-MIB::ifDescr.2 = FastEthernet0/1 IF-MIB::ifDescr.4 = Null0 IF-MIB::ifType.1 = ethernetCsmacd IF-MIB::ifType.2 = ethernetCsmacd IF-MIB::ifType.4 = other IF-MIB::ifMtu.1 = 1500 IF-MIB::ifMtu.2 = 1500 IF-MIB::ifMtu.4 = 1500 IF-MIB::ifSpeed.1 = 100000000 IF-MIB::ifSpeed.2 = 100000000 IF-MIB::ifSpeed.4 = 4294967295 IF-MIB::ifPhysAddress.1 = ca:01:17:4c:00:08 IF-MIB::ifPhysAddress.2 = ca:01:17:4c:00:06 IF-MIB::ifPhysAddress.4 = IF-MIB::ifAdminStatus.1 = up IF-MIB::ifAdminStatus.2 = down IF-MIB::ifAdminStatus.4 = up IF-MIB::ifOperStatus.1 = up IF-MIB::ifOperStatus.2 = down IF-MIB::ifOperStatus.4 = up . . .
上記でinterfacesに関するMIB(IF-MIB)が取得できました。
snmpgetコマンド
snmpgetコマンドとは、1度に一つのMIBオブジェクトを取得するコマンドになります。
以下ではインタフェース(FastEthernet0/1)の状態を取得しています。
なお、ifOperStatus
は"The current operational state of the interface"の意味になります。
# snmpget.py from pysnmp.hlapi import * import sys if len(sys.argv) != 4: print("Usage: {} modName symName HOSTNAME".format(sys.argv[0])) sys.exit(1) modName = sys.argv[1] if "." in sys.argv[2]: symName, value = sys.argv[2].split('.') else: symName = sys.argv[2] value = 0 router_ip = sys.argv[3] errorIndication, errorStatus, errorIndex, varBinds = next( getCmd(SnmpEngine(), CommunityData('public'), UdpTransportTarget((router_ip, 161)), ContextData(), ObjectType(ObjectIdentity(modName, symName, value))) ) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % (errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex) - 1][0] or '?')) else: for varBind in varBinds: print(' = '.join([x.prettyPrint() for x in varBind]))
上記snmpgetのPythonスクリプトを実行してみます。
> python3 snmpget.py IF-MIB ifOperStatus.2 router_ip IF-MIB::ifOperStatus.2 = down
ポートがshutdown
状態であることがわかりました。
snmpsetコマンド
snmpsetコマンドとは、対象のオブジェクトの状態を変更する時に用い、コミュニティがread-writeまたはwrite-onlyになっている場合可能になります。
以下では上記で確認したFastEthernet0/1のインタフェースをup
にしてみます。
インタフェースの状態を変更するにはifAdminStatus
を指定し、Integer
を意味するi
の引数の後に変更する値を指定します。
# snmpset.py from pysnmp.hlapi import * import sys if len(sys.argv) != 6: print("Usage: {} modName symName TYPE VALUE HOSTNAME".format(sys.argv[0])) sys.exit(1) modName = sys.argv[1] if "." in sys.argv[2]: symName, value = sys.argv[2].split('.') else: symName = sys.argv[2] value = 0 set_value = sys.argv[4] router_ip = sys.argv[5] if sys.argv[3] == 'i': set_value = int(set_value) errorIndication, errorStatus, errorIndex, varBinds = next( setCmd(SnmpEngine(), CommunityData('private'), UdpTransportTarget((router_ip, 161)), ContextData(), ObjectType(ObjectIdentity(modName, symName, value), set_value)) ) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % (errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex) - 1][0] or '?')) else: for varBind in varBinds: print(' = '.join([x.prettyPrint() for x in varBind]))
上記snmpsetのPythonスクリプトを実行してみます。
> python3 snmpset.py IF-MIB ifAdminStatus.2 i 1 router_ip IF-MIB::ifAdminStatus.2 = up
インタフェースがno shutdwon
になったことが確認できます。
最後に
今回はPythonのPySNMPライブラリを用いて簡単なSNMPコマンドの実行方法について学びました。
次回はSNMPエージェントのシミュレータの作成方法について学んでいきたいと思います。