前書き
ネットワークエンジニアはCLIで機器を扱うことが多く、 運用オペレータに引き継ぐ際もコマンド構文や見方などを説明した資料を準備することが多いですが、 REST API やRESTCONFを使うと以下の点で便利です。
- Python等のプログラミングと相性が良く、自動化に繋げやすい
- Webサーバと相性が良いため、オペレータ用のポータル画面も作りやすい
ポータル画面の作成はこの記事の対象外ですが、 応用すると、ポータル画面を通じて、ユーザにACL等を自由に設定変更させることもできます。
なので、REST APIやRESTCONFで設定取得や設定変更できるようになると、 スキルの幅が広がると思います。
という概念はわかっていても、まず何したら良いかわからなくないですか?
特にドキュメント不足の場合は、動作確認を始めるまで以下の課題があります。
- 特定の設定を取得したいときのURL(APIパス)はどうやって調べる?
- 設定変更のとき、どういうデータ構造でPUTすれば良い?
このあたりでつまづく方が多いはずなので、 今回はCisco IOS-XEを題材に、RESTCONFを利用し始めるまでのノウハウをまとめました。 設定取得方法や投入方法がわかってしまえば、自動化スクリプトを作るのも、ポータル画面を作成するのもプログラミングの世界です。 Webにたくさんの解説ページがありますので、調べながら解決できると思います。
ちなみに、RESTCONFはYANGによる定義に基づいてデータを読み書きするだけなので、
任意のshowコマンドを実行させることはできません。
(SNMPのMIBに近いと思います。)
事前準備
IOS-XEの設定
下記のコマンドでRESTCONFを有効化し、ローカルアカウント(admin)で認証できるようにします。
RESTCONFはVersion 16.6以降で対応しています。
ip http authentication local ip http secure-server restconf username admin privilege 15 password hogehoge
Postmanのインストールと設定
Linuxのターミナルからcurlコマンドを叩く手法もありますが、 GUIで操作できるので便利です。 使ったことない方は、下記からSign Upしてダウンロードしてください。
インストールが終わったら、下記を設定します。
認証情報
Basic Authを選択し、先ほどIOS-XEで作成したアカウントとパスワードを入力します。
ヘッダ情報
下記のように入力します。
XML形式も指定できるようですが、見やすさの点でJSON形式が好まれます。
KEY | VALUE |
---|---|
Accept | application/yang-data+json, application/yang-data.errors+json |
Content-Type | application/yang-data+json |
あとはMETHODとURLを入力してSendするだけです。 (設定変更の時はBODYを入力する必要があります)
設定取得
取得にはGETメソッドを使います。
JSON形式で取得できますので、
実現したい設定をCLIで投入し、
下記の方法で取得してデータ構造を分析するのが良いです。
IPアドレスはご自身の環境に置き換えてください。
METHOD | URL |
---|---|
GET | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native |
取得結果
{ "Cisco-IOS-XE-native:native": { "version": "16.6", "boot-start-marker": [ null ], "boot-end-marker": [ null ], (省略) "interface": { "GigabitEthernet": [ { "name": "1", "ip": { "address": { "primary": { "address": "192.168.1.1", "mask": "255.255.255.0" } } }, "mop": { "enabled": false, "sysid": false }, "Cisco-IOS-XE-ethernet:negotiation": { "auto": true } }, (省略)
JSONのデータ構造とURLの関係性
「: {」(コロン、中カッコ)で区切られた部分を、 URLでは「/」(スラッシュ)で表現することができます。
上記の例で「interface」配下のみにアクセスしたい場合は、下記のようにします。
METHOD | URL |
---|---|
GET | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface |
特定のインタフェースなどを指定したい場合は、 「name」または「id」という箇所を探します。
- 「: [ { "name"」 (コロン、大カッコ、中カッコ、name、コロン)
- 「: [ { "id"」(コロン、大カッコ、中カッコ、id、コロン)
具体的には、以下のような箇所です。
"interface": { "GigabitEthernet": [ { "name": "1",
こういった場合は、以下で取得することができます。
METHOD | URL |
---|---|
GET | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1 |
設定変更
設定変更にはPUT(またはPOST)メソッドを使用します。
設定取得と違ってBODY(投入内容)を準備する必要があります。
なお、削除にはDELETEメソッドを使用し、BODYは必要ありません。
BODYの作り方
設定したい内容をCLIで投入してみて、 前述の「設定取得」でデータ構造を見比べながら、 カッコの数などを修正して仕上げていきます。
まず、下記をCLIで投入します。
interface GigabitEthernet1.100 encapsulation dot1Q 100 ip address 192.168.100.1 255.255.255.0
前述の「設定取得」の方法でJSONを取得します。 結果をテキストエディタに貼り付けて、IPアドレスで検索する等で該当箇所を探します。
"interface": { "GigabitEthernet": [ { (省略) "name": "1.100", "encapsulation": { "dot1Q": { "vlan-id": 100 } }, "ip": { "address": { "primary": { "address": "192.168.100.1", "mask": "255.255.255.0" } } } } ], (省略)
これを元にBODYを作れば良さそうですね。
前述のとおり、インタフェースを取得する際は URLが「/GigabitEthernet=1」のような形式で終わります。
「=」(イコール)の手前部分と同じフィールドから書き始めます。 なので、「interface」の部分は不要です。
下記はかなり正解に近いですが、一番外側の { } (中カッコ)が足りないのと、 末尾に , (カンマ)が付いていますのでエラーになります。
"GigabitEthernet": [ { "name": "1.100", "encapsulation": { "dot1Q": { "vlan-id": 100 } }, "ip": { "address": { "primary": { "address": "192.168.100.1", "mask": "255.255.255.0" } } } } ],
という形で作成していきます。
(正解は下記に記載してあります。)
サブインタフェース
投入したいConfig
interface GigabitEthernet1.100 encapsulation dot1Q 100 ip address 192.168.100.1 255.255.255.0
RESTCONFで投入する場合
METHOD | URL |
---|---|
PUT | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1.100 |
BODY
{ "GigabitEthernet": [ { "name": "1.100", "encapsulation": { "dot1Q": { "vlan-id": 100 } }, "ip": { "address": { "primary": { "address": "192.168.100.1", "mask": "255.255.255.0" } } } } ] }
成功すると下記のような200番代のメッセージが表示されます。
BGPネイバー
投入したいConfig
router bgp 65500 neighbor 10.1.1.10 remote-as 65501
RESTCONFで投入する場合
BGPのURLは少し特殊です。 CLIで投入してから、「設定取得」を試すと以下のようになっています。 「id」がありますね。
(省略) "router": { "Cisco-IOS-XE-bgp:bgp": [ { "id": 65500, "bgp": { "default": { "local-preference": 100 }, "log-neighbor-changes": true }, "neighbor": [ { "id": "10.1.1.10", "remote-as": 65501, (省略)
なので、
/Cisco-IOS-XE-native:native/router/Cisco-IOS-XE-bgp/neighbor ではダメで
/Cisco-IOS-XE-native:native/router/Cisco-IOS-XE-bgp:bgp=65500/neighbor
とする必要があります。(neighborのところも id があるので同様です。)
METHOD | URL |
---|---|
PUT | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/router/Cisco-IOS-XE-bgp:bgp=65500/neighbor=10.1.1.10 |
BODY
{ "Cisco-IOS-XE-bgp:neighbor": { "id": "10.1.1.10", "remote-as": 65501 } }
NAT
投入したいConfig
ip nat inside source static 192.168.1.11 172.16.1.11
RESTCONFで投入する場合
NATも特殊です。
CLIで投入してから、「設定取得」を試すと以下のようになっています。
「nat-static-transport-list」の後に「[ (大カッコ)」があるのに、「id」も「name」もありませんね。
(省略) "Cisco-IOS-XE-nat:nat": { "inside": { "source": { "static": { "nat-static-transport-list": [ { "local-ip": "192.168.1.11", "global-ip": "172.16.1.11" } ] } } (省略)
この場合は「=」に続けて、全項目の値をカンマで区切って表現します。
METHOD | URL |
---|---|
PUT | https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/ip/Cisco-IOS-XE-nat:nat/inside/source/static/nat-static-transport-list=192.168.1.11,172.16.1.11 |
BODY
{ "Cisco-IOS-XE-nat:nat-static-transport-list": [ { "local-ip": "192.168.1.11", "global-ip": "172.16.1.11" } ] }
設定保存
RESTCONFで変更した内容は自動で保存されないので、下記URLをSendして設定を保存します。
METHOD | URL |
---|---|
POST | https://192.168.1.1/restconf/operations/cisco-ia:save-config |
show コマンドの代替
show コマンドを直接実行することはできないのですが、 YANGで定義されていれば、値を取得することはできます。
下記はインタフェース情報を取得した例です。
METHOD | URL |
---|---|
GET | https://192.168.1.1/restconf/data/Cisco-IOS-XE-interfaces-oper:interfaces |
取得結果
{ "Cisco-IOS-XE-interfaces-oper:interfaces": { "interface": [ { "name": "GigabitEthernet1", "interface-type": "iana-iftype-ethernet-csmacd", "admin-status": "if-state-up", "oper-status": "if-oper-state-ready", "last-change": "2020-12-10T07:13:32.000907+00:00", "if-index": 1, "phys-address": "00:XX:XX:XX:XX:XX", "speed": "1024000000", "statistics": { "discontinuity-time": "2020-12-10T07:11:43.000889+00:00", "in-octets": "939713058", "in-unicast-pkts": "11791603", "in-broadcast-pkts": "0", "in-multicast-pkts": "0", "in-discards": 0, "in-errors": 0, "in-unknown-protos": 0, "out-octets": 39650477, "out-unicast-pkts": "575540", "out-broadcast-pkts": "0", "out-multicast-pkts": "0", "out-discards": "0", "out-errors": "0", "rx-pps": "4", "rx-kbps": "2", "tx-pps": "0", "tx-kbps": "0", "num-flaps": "0", "in-crc-errors": "0" }, "vrf": "", "ipv4": "192.168.1.1", "ipv4-subnet-mask": "255.255.255.0", "description": "", "mtu": 1500, "input-security-acl": "", "output-security-acl": "", "v4-protocol-stats": { "in-pkts": "3298764", "in-octets": "297857615", "in-error-pkts": "0", "in-forwarded-pkts": "0", "in-forwarded-octets": "0", "in-discarded-pkts": "0", "out-pkts": "569829", "out-octets": "39307817", "out-error-pkts": "0", "out-forwarded-pkts": "569829", "out-forwarded-octets": "0", "out-discarded-pkts": "0" }, "v6-protocol-stats": { "in-pkts": "0", "in-octets": "0", "in-error-pkts": "0", "in-forwarded-pkts": "0", "in-forwarded-octets": "0", "in-discarded-pkts": "0", "out-pkts": "0", "out-octets": "0", "out-error-pkts": "0", "out-forwarded-pkts": "0", "out-forwarded-octets": "0", "out-discarded-pkts": "0" }, "bia-address": "00:XX:XX:XX:XX:XX", "ipv4-tcp-adjust-mss": 0, "ipv6-tcp-adjust-mss": 0, "ether-state": { "negotiated-duplex-mode": "full-duplex", "negotiated-port-speed": "speed-1gb", "auto-negotiate": true, "enable-flow-control": false }, "ether-stats": { "in-mac-control-frames": "0", "in-mac-pause-frames": "0", "in-oversize-frames": "0", "in-jabber-frames": "0", "in-fragment-frames": "0", "in-8021q-frames": "0", "out-mac-control-frames": "0", "out-mac-pause-frames": "0", "out-8021q-frames": "0" } }, (省略)
例によって、下記のようにすれば受信パケット数が取得できます。
METHOD | URL |
---|---|
GET | https://192.168.1.1/restconf/data/Cisco-IOS-XE-interfaces-oper:interfaces/interface=GigabitEthernet1/statistics/in-octets |
取得結果
{ "Cisco-IOS-XE-interfaces-oper:in-octets": "939713058" }
「YANGで定義されている値をどのように知るのか?」 は、 別の記事で書こうと思います。
Postmanの便利機能
Pythonコード等を表示
「Code」というボタンがあるのでクリックすると
下記のように他の方法で同様な動作をさせるためのコードを表示してくれます。 「Python - Requests」を選んだ例です。
生成済みの認証トークンをそのまま送出する方式になっているので、 そのままでは使いにくいですが、参考としては便利だと思います。
JSONのインデントを修正
BODYを編集中、カッコの位置やインデントが乱れてしまいがちです。
「Beautify」というボタンがあるのでクリックすると
このように修正してくれます。 カッコに不足がある等の構文誤りは修正できないので注意しましょう。