技術を楽しもう!

IT(ネットワーク)業界を中心に、仕事や趣味等、色々な技術を記録します。

Postmanを操作してCisco IOS-XEでRESTCONFの使い方を学習する

https://www.pakutaso.com/shared/img/thumb/KAZUHIRO171013109_TP_V.jpg

前書き

ネットワークエンジニアは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してダウンロードしてください。

www.postman.com

インストールが終わったら、下記を設定します。

認証情報
Basic Authを選択し、先ほどIOS-XEで作成したアカウントとパスワードを入力します。 f:id:takashi-tobey:20210121171644p:plain

ヘッダ情報
下記のように入力します。
XML形式も指定できるようですが、見やすさの点でJSON形式が好まれます。

KEY VALUE
Accept application/yang-data+json, application/yang-data.errors+json
Content-Type application/yang-data+json

f:id:takashi-tobey:20210121171707p:plain

あとはMETHODとURLを入力してSendするだけです。 (設定変更の時はBODYを入力する必要があります)

f:id:takashi-tobey:20210121171957p:plain

設定取得

取得には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番代のメッセージが表示されます。 f:id:takashi-tobey:20210122173903p:plain

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」というボタンがあるのでクリックすると f:id:takashi-tobey:20210122160003p:plain

下記のように他の方法で同様な動作をさせるためのコードを表示してくれます。 「Python - Requests」を選んだ例です。

生成済みの認証トークンをそのまま送出する方式になっているので、 そのままでは使いにくいですが、参考としては便利だと思います。 f:id:takashi-tobey:20210122160634p:plain

JSONのインデントを修正

BODYを編集中、カッコの位置やインデントが乱れてしまいがちです。
「Beautify」というボタンがあるのでクリックすると f:id:takashi-tobey:20210122161354p:plain

このように修正してくれます。 カッコに不足がある等の構文誤りは修正できないので注意しましょう。 f:id:takashi-tobey:20210122161438p:plain

参考にしたページ

www.cisco.com