RESTCONF の URI 確認と設定の作り方

最近 RESTCONF のことを調べる必要が出てきて、どうやってアクセス先のURIを見つけたら良いのかや 送信するデータを作ったら良いのかがわからなかったので、忘れないうちに整理。
わからないことがあったら、まず第一に RFC 8040 を参照するのが必須。

なお、実行例では CSR1000v を使用しており、RESTCONF が動作するよう以下の設定が済んでいるものとします。GigabitEthernet2 の IP アドレスは、ご自身の環境に読み替えてください。

aaa new-model
!
aaa authentication login default local
aaa authorization exec default local
!
restconf
!
username admin privilege 15 secret admin
!
interface GigabitEthernet2
 ip address 172.31.5.1 255.255.255.0
 no shutdown
!
ip http secure-server

また、REST へのアクセスには curl を使います。必須で設定しているオプションは以下のとおりです。

  • -u admin:admin :認証にユーザー名「admin」、パスワード「admin」を使用する
  • -k :HTTPS アクセスする際にSSL証明書を検証しない
  • -H 'Accept: application/yang-data+json :RESTCONF サーバからのレスポンスボディを JSON に指定

URIの探し方

RESTCONF 対応度とエントリポイントの確認

RESTCONF に対応している機器は、自身の対応度とエントリポイントを /.well-known/host-meta で提供しています。この URI に GET リクエストを送信することで、その機器の RESTCONF アクセス先が確認できます。

$ curl -k -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/.well-known/host-meta'
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
    <Link rel='restconf' href='/restconf'/>
</XRD>

この下から2行目の <link> タグの部分が確認したい情報です。rel で RESTCONF のバージョンを表し、href がエントリポイントを意味します。

エントリポイントとは、RESTCONF でアクセスするときの一番の大元(ルート)になる場所のことです。上記の出力から、この機器の RESTCONF のアクセスに https://172.31.5.1/restconf/ を使うことがわかりました。

URI の構造

エントリポイントからは順にツリーをたどって、アクセスしたいデータや実行させたい処理を探すことになります。まず、RESTCONF のエントリポイント直下に何があるのかをみてみます(返ってくるデータが見づらいので jq をつかって JSON データを整形しています)。

$ curl -k -s -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/restconf/' | jq
{
  "ietf-restconf:restconf": {
    "data": {},
    "operations": {},
    "yang-library-version": "2016-06-21"
  }
}

ここから、

  • 直下に dataoperations リソースがある
  • YANG ライブラリのバージョンは 2016-06-21 である

事がわかります。

そして実は、この返ってきている JSON データ自体も YANG データです。ietf-restconf モジュールの restconf コンテナ内の情報が返ってきているのです。

YANG データのモジュールとかコンテナとか

YANG データの形式については、RFC 6020 や RFC 7950 で定義されています。

この内容全部に触れるととても大変なので、RESTCONF で使うところをざっくりいうと

  • モジュール(module):ある YANG データモデルの大元の名前。
  • コンテナ(container):配下に何らかのデータ構造を持つものの名前。コンテナ自体はデータを持たない。
  • リスト(list):複数の値を持つものの名前。リスト自体はデータを持たない。
  • リーフ(leaf):コンテナの中に配置される、何らかのデータを持つものの名前。この配下には何もなく、リーフ内のデータのみが存在する。
  • キー(key):リスト内のデータ(リーフ)を特定するための情報

というものです。これらは、RESTCONF でアクセスする URI を構成するのに必要な情報です。上記の情報をもとにして、アクセスしたい URI は以下のように構成されます。

https://{ホスト名}/{エントリポイント}/{リソース}/{モジュール}:{コンテナ}/{リスト}={キー}/{リーフ}

ietf-interfaces.yang

実際の YANG データモデルをもとにみてみましょう。YANG データモデルは GitHub 上でメンテナンスされています。

ネットワーク機器のインターフェース情報に関する YANG モデルは、どのベンダーの機器でも以下の標準 YANG モデルが使えます。

この記事を書いた時点では、「ietf-interfaces@2018-02-20.yang」へのシンボリックリンクとなっていました。こちらで先程のモジュールやコンテナを確認してみましょう。

module ietf-interfaces {
  yang-version 1.1;
  namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
  prefix if;
(略)
  container interfaces {
    description
      "Interface parameters.";

    list interface {
      key "name";
(略)
      leaf name {
        type string;
        description
          "The name of the interface.
(略)
      leaf description {
        type string;
        description
(略)
      leaf enabled {
        type boolean;
        default "true";
        description
(略)

この YANG データモデルで、インターフェースの一覧や名前を含むデータが取得できそうです。

YANG データモデルに従ったデータ取得

実際に上記の YANG データモデルに従って、インターフェースの情報を取ってみましょう。

$ curl -k -s -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/restconf/data/ietf-interfaces:interfaces/interface' | jq
{
  "ietf-interfaces:interface": [
    {
      "name": "GigabitEthernet1",
      "type": "iana-if-type:ethernetCsmacd",
      "enabled": true,
      "ietf-ip:ipv4": {
        "address": [
          {
            "ip": "10.0.0.10",
            "netmask": "255.255.255.0"
          }
        ]
      },
      "ietf-ip:ipv6": {}
    },
    {
      "name": "GigabitEthernet2",
      "type": "iana-if-type:ethernetCsmacd",
      "enabled": true,
      "ietf-ip:ipv4": {
        "address": [
          {
            "ip": "172.31.5.1",
            "netmask": "255.255.255.0"
          }
        ]
      },
      "ietf-ip:ipv6": {}
    },
    {
      "name": "GigabitEthernet3",
      "type": "iana-if-type:ethernetCsmacd",
      "enabled": false,
      "ietf-ip:ipv4": {},
      "ietf-ip:ipv6": {}
    },
    {
      "name": "GigabitEthernet4",
      "type": "iana-if-type:ethernetCsmacd",
      "enabled": false,
      "ietf-ip:ipv4": {},
      "ietf-ip:ipv6": {}
    },
    {
      "name": "Loopback0",
      "type": "iana-if-type:softwareLoopback",
      "enabled": true,
      "ietf-ip:ipv4": {
        "address": [
          {
            "ip": "1.1.1.1",
            "netmask": "255.255.255.255"
          }
        ]
      },
      "ietf-ip:ipv6": {}
    }
  ]
}

JSON データがある程度読める人には「なるほど」とわかりやすい形式かと思います。

  • ietf-interfaces:interface」の配下に配列(リスト)形式のデータがあり
  • その中に5つの要素(インターフェース)があり
  • インターフェースの名前(name)、種類(type)、稼働状態(enabled)、IPv4アドレス(ietf-ip:ipv4)とIPv6アドレス(ietf-ip:ipv6)の設定状況が確認できる

というものです。

ここで、先程の「キー」について思い出してみます。「キー」はリスト内のデータを特定するものでした。この ietf-interfaces.yang では、ietf-interfaces モジュールの interfaces コンテナの interface リストのキーが「name」だと定義されていましたので、nameに「Loopback0」を指定して単独のインターフェースが取得できるかやってみましょう。

$ curl -k -s -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/restconf/data/ietf-interfaces:interfaces/interface=Loopback0' | jq
{
  "ietf-interfaces:interface": {
    "name": "Loopback0",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "1.1.1.1",
          "netmask": "255.255.255.255"
        }
      ]
    },
    "ietf-ip:ipv6": {}
  }
}

キーを指定して、ちゃんと指定したインターフェースの情報だけを取得できました。このデータから、特定のデータ(リーフ)だけ取得してみます。

$ curl -k -s -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/restconf/data/ietf-interfaces:interfaces/interface=Loopback0/enabled' | jq
{
  "ietf-interfaces:enabled": true
}

enabled のデータだけ取得できました。こうやって、YANG データモデルから一覧情報や特定のデータだけを取得できるというわけです。

RESTCONF によるデータ(設定)の変更

データの取得だけではなく、書き換えもしてみましょう。これまではデータを取得するので、HTTP GET メソッドだけを使っていましたが、設定を書き込むには「POST/PUT/PATCH」が使えます。

  • POST:新しく設定を作成する、または指定された処理を実行する
  • PUT:新しく設定を作成するか、既存の設定があれば置き換える
  • PATCH:既存の設定に追加、または置き換える

では実際に Loopback0 の設定を変えてみましょう。ここでは description に説明を追加してみます。データを送信する際は、Content-type ヘッダで送信するデータ形式を指定する必要があります。JSON 形式のデータを送信するには、Content-type: application/yang-data+json を指定します。また、curl の -X オプションで、PATCH メソッドの指定も行います。

$ curl -i -k -s -u admin:admin -H 'Content-type: application/yang-data+json' -H 'Accept: application/yang-data+json' -X PATCH 'https://172.31.5.1/restconf/data/ietf-interfaces:interfaces/interface=Loopback0' -d '{
  "ietf-interfaces:interface": {
    "description": "Set by RESTCONF!"
 }
}'
HTTP/1.1 204 No Content
Server: nginx
Date: Wed, 10 Feb 2021 08:15:50 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Last-Modified: Wed, 10 Feb 2021 08:15:50 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: 1612-944950-93666
Pragma: no-cache

HTTP/1.1 204 No Content が返ってきたので、成功しているみたいですね。結果を確認してみましょう。

$ curl -k -s -u admin:admin -H 'Accept: application/yang-data+json' 'https://172.31.5.1/restconf/data/ietf-interfaces:interfaces/interface=Loopback0' | jq
{
  "ietf-interfaces:interface": {
    "name": "Loopback0",
    "description": "Set by RESTCONF!",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "1.1.1.1",
          "netmask": "255.255.255.255"
        }
      ]
    },
    "ietf-ip:ipv6": {}
  }
}

description が期待通りに設定されました。

RESTCONF による設定の追加(EIGRP)

今度は全く設定の入っていないEIGRPを設定してみます。

CSR#show run | s router eigrp
CSR#

EIGRP の設定を YANG データとして取得

といっても、どういう設定を入れていいのかわかりにくいので、まずは実際に入れたらどういうものが見えるのかを知るのが手っ取り早そうです。まず、YANG データモデルからどういう URI を指定すればいいのかを見つけます。

先程同様、GitHub にある YANG データモデルの中から該当するものを見つけます。 vendor/cisco/xe 配下に、バージョンごとの YANG データモデルが格納されています。その中を見ると「Cisco-IOS-XE-eigrp.yang」というぴったりなものがあるので中身を見てみます。が、とてもそのままでは読み解けないので、Python で書かれたツールである「pyang」を使うことにします。

pyang は YANG データモデルの構造やパスを知るのにとても便利なツールです。このツールで、上記の EIGRP に関する YANG データモデルをツリー形式で表示させてみます。

$ pyang -f tree Cisco-IOS-XE-eigrp.yang
module: Cisco-IOS-XE-eigrp

  augment /ios:native/ios:router:
    +--rw eigrp* [id]
       +--rw id                     union
       +--rw passive-interface
       |  +--rw interface?   string
       |  +--rw Tunnel?      uint32
       |  +--rw Vlan?        uint16
       |  +--rw default?     empty
       +--rw address-family* [type]
       |  +--rw type              enumeration
(略)

最初の行から、モジュールが「Cisco-IOS-XE-eigrp」であることが確認できます。その下の augment は、これだけではモジュールにアクセスできないので付加する必要があるパス情報を示しています。じゃあ「/ios」って何よ?ってなりますが、ちゃんとデータモデルに書いてあります。

$ head -n 20 Cisco-IOS-XE-eigrp.yang
module Cisco-IOS-XE-eigrp {
  namespace "http://cisco.com/ns/yang/Cisco-IOS-XE-eigrp";
  prefix ios-eigrp;

  import ietf-inet-types {
    prefix inet;
  }


  import Cisco-IOS-XE-types {
    prefix ios-types;
  }

  import Cisco-IOS-XE-interface-common {
    prefix ios-ifc;
  }

  import Cisco-IOS-XE-native {
    prefix ios; ←ここ
  }

/ios」は Cisco-IOS-XE-native モジュールを指しており、前述の通りコロン(:)の後ろはコンテナ名なのでそのルール通り「 /Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router 」が必要だと言うことがわかります。

よって、EIGRP の情報を取得するには

https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp

とすれば良いとわかります。早速やってみます。

$ curl -i -k -s -H 'Accept: application/yang-data+json' -u admin:admin -X GET -u admin:admin 'https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp'
HTTP/1.1 204 No Content
Server: nginx
Date: Fri, 12 Feb 2021 00:06:55 GMT
Content-Type: application/yang-data+json
Content-Length: 0
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache

204 No Content が返ってきているので、RESTCONF での処理は成功したが何もデータがないことがわかります。

EIGRP の設定を YANG データとして RESTCONF で投入

では、CLI で以下の設定をしてからもう一度やってみます。

CSR#show run | s router eigrp
router eigrp 1
 network 172.31.5.0 0.0.0.255
$ curl -i -k -s -H 'Accept: application/yang-data+json' -u admin:admin -X GET -u admin:admin 'https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp'
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 12 Feb 2021 00:09:06 GMT
Content-Type: application/yang-data+json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache

{
  "Cisco-IOS-XE-eigrp:eigrp": [
    {
      "id": 1,
      "network": [
        {
          "number": "172.31.5.0",
          "wild-card": "0.0.0.255"
        }
      ]
    }
  ]
}

今度は 200 OK が返ってきているので、RESTCONF での処理は正常に実行され、データが返されていることがわかります。返された JSON データが、EIGRP の設定に必要な情報なので、これを一旦手元にコピーしておきましょう。

では、CLI で EIGRP の設定を消して、今度は PATCH メソッドで同じデータを放り込んでみます。期待通り同じ設定が得られるでしょうか?

CSR#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
CSR(config)#no router eigrp 1
CSR(config)#end
*Feb 12 00:12:29.421: %SYS-5-CONFIG_I: Configured from console by admin on console
CSR#show run | s router eigrp
CSR#
$ curl -i -k -s -H 'Accept: application/yang-data+json' -u admin:admin -X GET -u admin:admin 'https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp'
HTTP/1.1 204 No Content ←設定を削除したのでデータは返されない
Server: nginx
Date: Fri, 12 Feb 2021 00:12:56 GMT
Content-Type: application/yang-data+json
Content-Length: 0
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache

■PATCHメソッドで、先程と同じデータを JSON 形式で送信する
$ curl -i -H 'Content-type: application/yang-data+json' -H 'Accept: application/yang-data+json' -u admin:admin -k -s -X PATCH "https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp" -d ' 
> {
>   "Cisco-IOS-XE-eigrp:eigrp": [
>     {
>       "id": 1,
>       "network": [
>         {
>           "number": "172.31.5.0",
>           "wild-card": "0.0.0.255"
>         }
>       ]
>     }
>   ]
> }'
HTTP/1.1 204 No Content
Server: nginx
Date: Fri, 12 Feb 2021 00:13:07 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Last-Modified: Fri, 12 Feb 2021 00:13:07 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: 1613-88787-695699
Pragma: no-cache

$ curl -i -k -s -H 'Accept: application/yang-data+json' -u admin:admin -X GET -u admin:admin 'https://172.31.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp'
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 12 Feb 2021 00:13:13 GMT
Content-Type: application/yang-data+json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache

{
  "Cisco-IOS-XE-eigrp:eigrp": [
    {
      "id": 1,
      "network": [
        {
          "number": "172.31.5.0",
          "wild-card": "0.0.0.255"
        }
      ]
    }
  ]
}

期待通り、全く設定がないところに EIGRP の設定を投入することができました。

私の使った環境では、既存の running-config に追加する場合も、POST ではなく PATCH を使う必要があるようです。サポートしているメソッドが決まっているようでした。

$ curl -i -H 'Content-type: application/yang-data+json' -H 'Accept: application/yang-data+json' -u admin:admin -k -s -X PUT "https://172.31.1.5.1/restconf/data/Cisco-IOS-XE-native:native/Cisco-IOS-XE-native:router/eigrp" -d ' ←試しに PUT メソッドを指定
{
  "Cisco-IOS-XE-eigrp:eigrp": [
    {
      "id": 1,
      "network": [
        {
          "number": "172.31.5.0",
          "wild-card": "0.0.0.255"
        }
      ]
    }
  ]
}'
HTTP/1.1 405 Method Not Allowed
Server: nginx
Date: Fri, 12 Feb 2021 00:16:16 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Allow: DELETE, GET, HEAD, PATCH ←POST, PUTはサポートされていないようだ。

まとめ

RESTCONF は使えるかなと思いつつ、YANG データモデルの理解がまず先のようで、なかなか重たいということがわかりました。とはいえ CCNP ENCOR の出題範囲でもありますし、ひいては CCIE の学習にもなるので、もう少し掘り下げて学習を進めようと思います。

コメント

タイトルとURLをコピーしました