STUDY · NETWORK GUIDE

7-1 NETCONF / RESTCONF — SSH と HTTPS で設定を読み書きする

NETCONF は SSH の 830 番、RESTCONF は HTTPS で設定を読み書きします。csr1000v 17.3 で hello と chunked framing、PATCH と 409、candidate と confirmed commit を実測します。

1. 前節の振り返りと本節の内容

前節 6-8 Performance Routing (PfR) で第 6 章が終わりました。第 6 章は拠点と拠点をどう結ぶかを 8 節で扱い、6-8 は経路の品質を測って出口を選ぶ PfRv3 を csr1000v の MC と BR で確かめました。

6-8 の末尾(§19)は、第 7 章の主題を「装置をプログラムから扱う方法」と予告し、第 7 章の 6 節を並べたうえで、第 6 章の中にあった入口に触れました。6 節の最初に挙がったのが、XML / JSON と SSH / HTTP の組み合わせで装置の設定と状態を読み書きする NETCONF / RESTCONF で、これが本節です。入口として挙がった 3 つ(6-3 で SD-WAN Manager が WAN Edge の設定の投入に NETCONF を使っていたこと、6-6 で Catalyst Center の Provision が Plug and Play を含んでいたこと、6-8 の PfR が hub MC 1 か所に書いた policy を各拠点へ配る形で、設定を 1 か所から配る仕組みを CLI の範囲で持っていたこと)は、本節に限らず第 7 章全体への入口として書かれたものです。§19 はそのうえで、装置の設定と状態を人の手の CLI ではなくプログラムで扱う仕組みを次章で見る、と締めています。本節は、このうち次のものを次の場所で受けます。

6-8 §19 の予告本節での受け方場所
XML / JSON と SSH / HTTP の組み合わせで設定と状態を読み書きするNETCONF は SSH(TCP 830)の上で XML の RPC を往復させ、RESTCONF は HTTPS(TCP 443)の上で URL と HTTP のメソッドを使う。本文は XML か JSON。NETCONF は、設定だけを読む <get-config> と、設定と状態を読む <get> を別の操作にしている§2 / §5〜§9
6-3 の SD-WAN Manager が NETCONF で設定を投入6-3 は Cisco の SD-WAN の資料の記述として紹介した。本節は NETCONF の hello・RPC・応答を機器との間で往復させ、送受したバイト列をそのまま示す§5〜§7
設定を 1 か所から配る仕組み本節のクライアントは WSL (Windows Subsystem for Linux) 上のプログラム、相手は csr1000v 1 台。複数の装置へ同じ設定を配る部分は扱わず、その土台になる「プログラムから 1 台の設定を読み書きし、確定と取り消しを機器の側で行う」ところ(candidate と confirmed commit)までを実測する§10
人の手の CLI ではなくプログラムでNETCONF は WSL の Python から SSH の netconf サブシステムを開いて送り、RESTCONF は curl で送る。最後に ncclient と requests の最小例で、ライブラリが利用者のコードから何を隠すかを見る§5 / §8 / §11

6-8 は 6-6 の Catalyst Center の Provision が Plug and Play を含んでいたことにも触れていましたが、Plug and Play と初期設定の自動投入は第 7 章の最後の節 7-6(Day 0 ZTP)で扱います。

第 7 章は全 6 節で、本節はその最初の節です。NETCONF と RESTCONF が運ぶデータの形は YANG のモデルで決まります。本節はモデルの名前が XML の名前空間・JSON のメンバ名・URL に現れるところまでを扱い、モデルの中身は 7-2、状態を購読する仕組み(notification・subscribe)は 7-3 で扱います。

本節は出典に基づく部分と、本ラボの実測に基づく部分を分けて書きます。

層どこ何を根拠にしているか
出典層§2 と、§4〜§11 の引用の段落RFC 6241(NETCONF)・RFC 6242(NETCONF over SSH)・RFC 8040(RESTCONF)・RFC 7951(YANG の JSON 符号化)と、Cisco の Programmability Configuration Guide, Cisco IOS XE Amsterdam 17.3.x の NETCONF・RESTCONF・service-level ACL の章
実測層§3〜§11 の show 出力・wire・curl と Python の出力、§4 と §10 の時刻の表CML 上の csr1000v 17.03.08a 1 台と、WSL 上のクライアント(paramiko・curl・ncclient・requests)。同じトポロジと投入設定で 2 回撮影したうちの 2 回目
実測層(参考)§10.5・§10.7 の「1 回目で観測」の段落と、§12 の confirmed commit の B の項と管理 IF の項同じトポロジと投入設定で、candidate の手順(§10.5)が違う 1 回目の撮影と、撮影の前の準備段階の試行(probe。管理 IF を Mgmt-vrf の Gi1 と global の Gi2 の 2 つにした構成)
確かめていないこと§12—

本ラボは同じトポロジと投入設定で 2 回撮影しました(図の中では 1 回分の撮影を run と書いています)。2 回の取得スクリプトの違いは、candidate の段階(§3 の Phase E)の中だけです。lock の取り方と外し方(1 回目は running の lock を最初の <commit> の前から持っていた)、§10.5 の confirmed commit の手順、§10.6 の close-session の後の CLI の撮影(2 回目で足した)が違います。本節の出力と時刻は、明記した箇所を除き 2 回目の撮影のものです。図の値はすべて 2 回目の撮影のものです。1 回目の結果に触れるのは、§10.5 の「1 回目で観測」の段落(confirmed commit。1 回目の時刻もここにだけ出てきます)、§10.7 の「1 回目で観測」の文(PATCH の後の Loopback72)、§12 の confirmed commit の B の項で、どれも「1 回目」と明記します。準備段階の試行の結果に触れるのは §12 の管理 IF の項だけで、そこにも明記します。

以下に貼る実機出力は改変していません。長い出力から一部を抜き出した箇所には …(省略) を置きました。プロンプトとコマンドを 1 行にまとめた表記(CSR1# show … と WSL $ curl … の形)は本節の組み立てで、取得の記録ではコマンドが見出しの行として別に残っています。NETCONF は、WSL が送ったバイト列と CSR1 から受けたバイト列(以下 wire)を、改行も含めてそのまま載せます。

時刻は、断りの無いかぎり CSR1 の機器の時計(UTC)です。show clock・syslog の行の時刻・HTTP の応答の Date: ヘッダ(機器の上の nginx が付けた値で、秒単位)・show netconf-yang statistics の時刻で書き、取得スクリプトを動かした WSL の時計とは混ぜません。

WSL の側で観測したこと(TCP の接続試行の結果や、Python のライブラリが出した例外とメッセージ)は機器の表示ではないので、そのつど「ツール側の観測」と断ります。


2. NETCONF と RESTCONF — SSH と HTTPS、XML と JSON

NETCONF (Network Configuration Protocol) は、装置の設定の投入・変更・削除をプログラムから行うためのプロトコルです。Cisco の 17.3 の設定ガイドは、NETCONF を次のように定義しています。

NETCONF provides a mechanism to install, manipulate, and delete the configuration of network devices.

符号化は XML で、設定のデータもプロトコルのメッセージも XML で書きます。

It uses an Extensible Markup Language (XML)-based data encoding for the configuration data as well as the protocol messages.

運び方は SSH で、既定のポートは 830 です。

It uses Secure Shell (SSH) as the transport layer across network devices.

It uses SSH port number 830 as the default port.

RFC 6241 も、SSH での運び方を実装の必須としています。

A NETCONF implementation MUST support the SSH transport protocol mapping [RFC6242].

RESTCONF は、HTTP の上で YANG で定義したデータを読み書きするプロトコルです。RFC 8040 は RESTCONF を次のように位置づけています。

RESTCONF uses HTTP methods to provide CRUD operations on a conceptual datastore containing YANG-defined data, which is compatible with a server that implements NETCONF datastores.

CRUD は、作成・読み出し・更新・削除 (Create, Read, Update, Delete) の 4 つの操作のこと。RESTCONF は HTTP のメソッドをこの 4 つに対応させます(§9)。

RESTCONF は HTTP の上に定義されていますが、平文の HTTP では使いません。RFC 8040 は TLS を必須とし、https の既定のポートを 443 としています。

RESTCONF is defined on top of HTTP, but due to the sensitive nature of the information conveyed, RESTCONF requires that the transport-layer protocol provide both data integrity and confidentiality. A RESTCONF server MUST support the Transport Layer Security (TLS) protocol [RFC5246] and SHOULD adhere to [RFC7525]. The RESTCONF protocol MUST NOT be used over HTTP without using the TLS protocol.

Given the nearly ubiquitous support for HTTP over TLS [RFC7230], RESTCONF implementations MUST support the “https” URI scheme, which has the IANA-assigned default port 443.

本文の形式は XML か JSON です。サーバはどちらか一方に対応していればよく、両方に対応してもかまいません。

Content is encoded in either JSON or XML format. A server MUST support one of either XML or JSON encoding. A server MAY support both XML and JSON encoding. A client will need to support both XML and JSON to interoperate with all RESTCONF servers.

NETCONF がセッションを張ったまま要求を送るのに対し、RESTCONF は要求ごとに完結します。RFC 6241 は NETCONF を connection-oriented と書き、Cisco のガイドは RESTCONF を stateless と書いています。

NETCONF is connection-oriented, requiring a persistent connection between peers.

The HTTPS-based RESTCONF protocol (RFC 8040), is a stateless protocol that uses secure HTTP methods to provide CREATE, READ, UPDATE, and DELETE (CRUD) operations on a conceptual datastore containing YANG-defined data, which is compatible with a server that implements NETCONF datastores.

NETCONF は、設定を置く場所をデータストア(datastore)と呼び、操作ごとに対象のデータストアを指定します。RFC 6241 は、設定のデータストアを、機器を初期の状態から目的の動作の状態にするのに要る設定の全体と定め、状態のデータを含まないとしています。

A configuration datastore is defined as the complete set of configuration data that is required to get a device from its initial default state into a desired operational state. The configuration datastore does not include state data or executive commands.

どの機器にも在るのが running(いま動いている設定の全体)です。§7 までの操作は、§7.4 の 1 つを除き running(<running/>)が対象です。編集してから確定する candidate は §10 で扱います。

running configuration datastore: A configuration datastore holding the complete configuration currently active on the device. The running configuration datastore always exists.

RESTCONF は NETCONF を置き換えるものではなく、NETCONF の機能の一部を HTTP で使えるようにしたものです。RFC 8040 は、データストアを使い分けないことと、明示的な lock を持たないことを例に挙げています(RFC 8040 の時点の記述です)。

RESTCONF does not need to mirror the full functionality of the NETCONF protocol, but it does need to be compatible with NETCONF. RESTCONF achieves this by implementing a subset of the interaction capabilities provided by the NETCONF protocol – for instance, by eliminating datastores and explicit locking.

その後の RFC 8527 は RFC 8040 を更新し、NMDA(Network Management Datastore Architecture)を実装するサーバ向けに、データストアの資源を足しました。本ラボの 17.03.08a が RFC 8527 に対応しているかは確かめていません。本節の RESTCONF の要求は、どれも RFC 8040 の {+restconf}/data を使っています。

This document updates RFC 8040 by introducing new datastore resources, adding a new query parameter, and requiring the usage of the YANG library (described in RFC 8525) by RESTCONF servers implementing the NMDA.

ここまでを表にまとめると、以下のとおりです(出典層)。

項目NETCONFRESTCONF
規格RFC 6241(SSH での運び方は RFC 6242)RFC 8040
下の層SSH の netconf サブシステムHTTPS(TLS 必須)
既定のポートTCP 830TCP 443
要求の形XML の <rpc>(中に <get-config>・<edit-config> などの操作)HTTP のメソッドと URL
本文の形式XMLXML または JSON(ヘッダで選ぶ)
セッションhello で始まり、close-session で終わる 1 本のセッション要求ごとに完結
lock<lock> / <unlock>無し
結果の返し方<rpc-reply> の中の <ok/>・<data>・<rpc-error>HTTP のステータス行と本文

NETCONF の要求は RPC(remote procedure call)の形で、<rpc> で包んだ操作を送り、<rpc-reply> で結果を受け取ります。


3. 本ラボの構成

この図を大きく開く ↗

本ラボの構成です。WSL から CSR1 の管理アドレス 1 つへ、CLI は 22、NETCONF は 830、RESTCONF は 443 で届きます。candidate 無効時の反映も示します。

本ラボは、CML 上の csr1000v 1 台(CSR1)と、管理用のスイッチ(mgmt-sw)・外部接続(ext)の 3 ノードです。クライアントは WSL で、CSR1 から見た接続元は 192.168.1.50 です。

CSR1 の管理アドレスは Gi1 の 172.16.1.241/24 の 1 つで、Gi1 は VRF Mgmt-vrf に入れました。CLI の SSH(TCP 22)・NETCONF(TCP 830)・RESTCONF(TCP 443)は、すべてこのアドレスで受けます。

ノードIFアドレス役割
CSR1Gi1(vrf Mgmt-vrf)172.16.1.241/24管理アドレス。CLI・NETCONF・RESTCONF をここで受ける
CSR1Mgmt-vrf の既定経路0.0.0.0/0 → 172.16.1.1WSL(192.168.1.50)への戻り
CSR1Loopback010.7.1.1/32読み書きの対象。description を NETCONF と RESTCONF で書き換える
CSR1Loopback71—RESTCONF の POST で作り、DELETE で消す(§9)
CSR1Loopback72—candidate にだけ置き、RESTCONF の編集の扱いを見る(§10.7)
WSL—192.168.1.50(CSR1 から見た接続元)クライアント(CLI の SSH・NETCONF・RESTCONF)

CSR1 の版は 17.03.08a(IOS XE Amsterdam 17.3.x)で、§15 の Cisco の設定ガイド(17.3.x)と同じ系列です。

snippet
CSR1# show version
Cisco IOS XE Software, Version 17.03.08a
Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.8a, RELEASE SOFTWARE (fc3)
…(省略)
CSR1 uptime is 0 minutes
Uptime for this control processor is 2 minutes
…(省略)
License Level: ax
License Type: N/A(Smart License Enabled)
Next reload license Level: ax
…(省略)
cisco CSR1000V (VXE) processor (revision VXE) with 1104920K/3075K bytes of memory.
…(省略)

クライアントの道具の版は以下のとおりです(WSL の側の記録)。

snippet
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7
python 3.12.3 / paramiko 4.0.0 / ncclient 0.7.1 / requests 2.34.2

CSR1 の起動時の設定(day0)には、ホスト名・利用者(netops・privilege 15)・管理 IF と VRF・Loopback0・CLI 用の SSH などを入れ、NETCONF と RESTCONF の有効化は入れていません。有効化の前と後を撮るためです(§4)。CLI 用の SSH の設定(ip ssh version 2・vty の transport input ssh・RSA 鍵の生成)は、3-1 IP アドレッシング詳細 のラボと同じ形です。取得スクリプトは CLI にログインした後、enable を送らずに show と configure terminal を送る作りです。利用者が privilege 15 であることが、この手順の前提です(NETCONF の章が求める権限も privilege 15 です。§4.2)。

撮影は段階(Phase)に分けて行い、1 本の取得スクリプトで続けて撮りました。図の中の「Phase B」「PB / PC」「PE」は、下の表の Phase B・Phase C・Phase E を指します。

Phase内容本文
0有効化の前の状態§4.1
AAAA・NETCONF・RESTCONF の有効化と準備完了§4.2〜§4.4
BNETCONF(base:1.1)のセッション§5〜§7
B2base:1.0 だけを名乗る NETCONF のセッション§6.2
CRESTCONF§8〜§9
Dncclient と requests§11
Ecandidate の有効化と confirmed commit§10.1〜§10.6
Fcandidate を有効にした状態での RESTCONF§10.7
Z撮影の後の状態§10.8

4. 有効化と準備完了 — Running でも受け付けない時間

4.1 有効化の前

有効化の前の CSR1 は NETCONF が無効(netconf-yang: disabled)で、待ち受けのポートの設定値は 830 でした。

snippet
CSR1# show netconf-yang status
netconf-yang: disabled
netconf-yang ssh port: 830
netconf-yang candidate-datastore: disabled

running-config には、day0 に書いていない ip http secure-server と、trustpoint の SLA-TrustPoint が有効化の前から在りました。aaa new-model は無効(no aaa new-model)で、netconf-yang と restconf の行はありません。

snippet
CSR1# show running-config
Building configuration...

Current configuration : 3932 bytes
!
! Last configuration change at 11:41:02 UTC Tue Sep 29 2026
!
version 17.3
…(省略)
logging buffered 512000 informational
!
no aaa new-model
…(省略)
crypto pki trustpoint SLA-TrustPoint
…(省略)
interface Loopback0
 description initial
 ip address 10.7.1.1 255.255.255.255
!
interface GigabitEthernet1
 description Management (VRF Mgmt-vrf)
 vrf forwarding Mgmt-vrf
 ip address 172.16.1.241 255.255.255.0
 negotiation auto
 no mop enabled
 no mop sysid
!
ip forward-protocol nd
no ip http server
ip http secure-server
!
ip route vrf Mgmt-vrf 0.0.0.0 0.0.0.0 172.16.1.1
ip ssh version 2
…(省略)

NETCONF と RESTCONF を支えるプロセスの状態は、show platform software yang-management process で見ます。8 行のうち Running は pubd だけで、nginx も Not Running でした。

snippet
CSR1# show platform software yang-management process
confd            : Not Running
nesd             : Not Running
syncfd           : Not Running
ncsshd           : Not Running
dmiauthd         : Not Running
nginx            : Not Running
ndbmand          : Not Running
pubd             : Running    

この時点で WSL から CSR1 へ TCP の接続を試した結果は、以下のとおりです(Python の socket.create_connection の結果で、ツール側の観測。接続の打ち切りは 3 秒)。22 は接続でき、830 は拒否、443 は応答が無いまま時間切れでした。

snippet
172.16.1.241:22 open
172.16.1.241:830 refused
172.16.1.241:443 timeout

Cisco のガイドは、nginx の起動の条件を次のように書いています。nginx は ip http secure-server か ip http server の設定で動き、RESTCONF には要るが NETCONF には要らない、という内容です。

The process nginx runs if ip http secure-server or ip http server is configured on the device. This process is not required to be in the running state for NETCONF to function properly. However, the nginx process is required for RESTCONF.

RESTCONF の章は、startup の設定で起動した時点で nginx が動いている、とも書いています。

When a device boots up with the startup configuration, the nginx process will be running. However; DMI proceses are not enabled.

同じ章は、nginx を内部のプロキシの Web サーバとし、HTTPS で届いた RESTCONF の要求をまず nginx が受けて、confd の Web サーバへ渡すと書いています(web serve,r は原文のまま)。§3 の図の 443 の箱はこの記述を示したもので、本ラボは nginx と confd の間を観測していません。DMI の展開は、同じ NETCONF の章の中で 2 通りあります。IPv6 の項は data model interface(DMI)と書き、process の表の dmiauthd の説明は device management interface(DMI)と書いています。本節はどちらかに決めず、DMI と書きます。

NGINX is an internal webserver that acts as a proxy webserver. It provides Transport Layer Security (TLS)-based HTTPS. RESTCONF request sent via HTTPS is first received by the NGINX proxy web serve,r and the request is transferred to the confd web server for further syntax/semantics check.

本ラボの有効化の前(show version の CSR1 uptime is 0 minutes・Uptime for this control processor is 2 minutes の時点。show clock は 11:41:37.781)では、ip http secure-server が running-config に在るのに、nginx は Not Running で、WSL からの 443 の接続試行は時間切れでした。起動から間もない時点の 1 回の観測で、起動からの時間が効いているのか、ほかの条件が効いているのかは確かめていません(§12)。

4.2 有効化 — 6 行を 1 回の configure で

有効化は、Cisco のガイドの手順に沿った 6 行を 1 回の configure で投入しました。AAA の 3 行は NETCONF の章の手順、netconf-yang は同じ章の NETCONF-YANG の設定手順、ip http secure-server と restconf は RESTCONF の章の手順にある行です。

snippet
configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
CSR1(config)#aaa new-model
CSR1(config)#aaa authentication login default local
CSR1(config)#aaa authorization exec default local
CSR1(config)#netconf-yang
CSR1(config)#ip http secure-server
CSR1(config)#restconf
CSR1(config)#end
CSR1#

拒否を示す行(% で始まる行)はありません。ip http secure-server は §4.1 のとおり既定の設定に在った行で、Cisco の手順どおりもう一度投入したものです。手順の該当箇所は以下のとおりです。

Step 4 ip http secure-server Example: Device(config)# ip http secure-server Enables a secure HTTP (HTTPS) server.

AAA の 3 行(aaa new-model・aaa authentication login default local・aaa authorization exec default local)は、4-2 AAA の §3 で扱ったローカル AAA の設定と同じ形です。Cisco のガイドは、AAA の要否を章によって違う書き方で書いています。NETCONF の章は aaa new-model を手順の中で (Optional) とし、入れた場合は AAA の認証と認可が必要になると書きます。

(Optional) Enables authorisation, authentication, and accounting (AAA). If the aaa new-model command is configured, AAA authentication and authorization is required.

RESTCONF の章は、NETCONF と RESTCONF の接続は AAA で認証しなければならないと書きます。

NETCONF and RESTCONF connections must be authenticated using authentication, authorization, and accounting (AAA). As a result, RADIUS or TACACS+ users defined with privilege level 15 access are allowed access into the system.

本ラボは AAA を入れた状態だけを撮りました。AAA を入れない場合の挙動は確かめていません(§12)。利用者の権限について、NETCONF の章は privilege 15 を求めています。

To start working with NETCONF APIs, you must be a user with privilege level 15.

本ラボの利用者 netops の認証の syslog には、External groups: PRIV15 が付きました(§4.3)。

4.3 process が Running になった後も受け付けない時間

configure の後、process の表示を繰り返し撮り、8 行とも Running になった最初の表示は以下のとおりです(直前の show clock と合わせて撮りました)。

snippet
CSR1# show clock
*11:42:31.565 UTC Tue Sep 29 2026
CSR1# show platform software yang-management process
confd            : Running    
nesd             : Running    
syncfd           : Running    
ncsshd           : Running    
dmiauthd         : Running    
nginx            : Running    
ndbmand          : Running    
pubd             : Running    

この後、show netconf-yang datastores を show clock で挟んで繰り返し撮りました。show netconf-yang datastores は NETCONF-YANG のデータストアの情報を表示するコマンドで、本ラボでは要求を受け付ける準備ができたかの関門に使いました。1 回目はエラーでした。

snippet
CSR1# show clock
*11:42:36.177 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
% Error: Currently unable to process request

CSR1# show clock
*11:42:40.592 UTC Tue Sep 29 2026

このエラーの直後に、準備前の要求を 1 回ずつ送りました。RESTCONF の入口(§8.2 の host-meta)への GET は 502 Bad Gateway で、応答のヘッダは Server: openresty でした(curl の指定の意味は §8.1 の表にまとめました)。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - https://172.16.1.241/.well-known/host-meta
HTTP/1.1 502 Bad Gateway
Server: openresty
Date: Tue, 29 Sep 2026 11:42:44 GMT
Content-Type: text/html
Content-Length: 761
Connection: keep-alive
ETag: "6532bd76-2f9"
…(省略)
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
…(省略)

NETCONF は hello を受け取れませんでした(ツール側の観測)。根拠は取得スクリプトの記録(build の判定メモ)だけで、そこには「チャネルが閉じた(受信 0 bytes)」とあります。取得スクリプトがこの文言で読み取りを打ち切るのは、SSH のチャネルが閉じたときと、チャネルが終了の状態(exit status)を受けたときの両方で、どちらだったかは区別していません。hello を受け取れなかった回の出力は保存しない作りなので、受信の中身も残っていません。CSR1 の側の syslog には、この接続の認証が通った行が残っています(下の syslog の 11:42:43.137 の行)。以下、この観測を「NETCONF の hello の受け取り失敗」と書きます。

2 回目の datastores も同じエラーで、3 回目で Datastore Name : running が返りました。

snippet
CSR1# show clock
*11:42:54.131 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
% Error: Currently unable to process request

CSR1# show clock
*11:42:58.509 UTC Tue Sep 29 2026
snippet
CSR1# show clock
*11:43:10.558 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
Datastore Name             : running

CSR1# show clock
*11:43:15.415 UTC Tue Sep 29 2026

同じ時間帯の syslog は以下のとおりです。

snippet
CSR1# show logging
…(省略)
*Sep 29 11:41:56.364: %WSMAN-3-INVALID_TRUSTPOINT: Trustpoint associated with HTTP is either invalid or does not exist
*Sep 29 11:42:05.048: %PKI-6-TRUSTPOINT_CREATE: Trustpoint: TP-self-signed-3478263324 created succesfully
*Sep 29 11:42:05.193: %PSD_MOD-5-DMI_NOTIFY_NETCONF_START: R0/0: psd: PSD/DMI: netconf-yang server has been notified to start
*Sep 29 11:42:06.021: %CRYPTO_ENGINE-5-KEY_ADDITION: A key named TP-self-signed-3478263324 has been generated or imported by crypto-engine
*Sep 29 11:42:06.279: %PKI-4-NOCONFIGAUTOSAVE: Configuration was modified.  Issue "write memory" to save new IOS PKI configuration
*Sep 29 11:42:12.467: %PSD_MOD-5-DMI_NOTIFY_RESTCONF_START: R0/0: psd: PSD/DMI: restconf server has been notified to start
*Sep 29 11:42:14.711: %SYS-5-CONFIG_I: Configured from console by netops on vty1 (192.168.1.50)
*Sep 29 11:42:43.137: %DMI-5-AUTH_PASSED: R0/0: dmiauthd: User 'netops' authenticated successfully from 192.168.1.50:49584 and was authorized for netconf over ssh. External groups: PRIV15
*Sep 29 11:43:10.455: %NDBMAN-5-ACTIVE: R0/0: ndbmand: All data providers active.
*Sep 29 11:43:20.611: %DMI-5-NACM_INIT: R0/0: dmiauthd: NACM configuration has been set to its initial configuration.
*Sep 29 11:43:27.877: %DMI-5-SYNC_COMPLETE: R0/0: dmiauthd: The running configuration has been synchronized to the NETCONF running data store.
*Sep 29 11:43:37.953: %DMI-5-AUTH_PASSED: R0/0: dmiauthd: User 'netops' authenticated successfully from 127.0.0.1:54510 and was authorized for rest over http. External groups: PRIV15

起点を %PSD_MOD-5-DMI_NOTIFY_NETCONF_START(netconf-yang のサーバに起動が通知された行・11:42:05.193)に置いて並べると、以下のとおりです。configure を抜けた時刻(%SYS-5-CONFIG_I)は起点より後で、投入の各行の時刻は撮っていません。

機器の時刻起点からの差何が起きたか
11:41:56.364−8.829 秒%WSMAN-3-INVALID_TRUSTPOINT
11:42:05.048−0.145 秒自己署名の trustpoint の作成(%PKI-6-TRUSTPOINT_CREATE)
11:42:05.1930(起点)%PSD_MOD-5-DMI_NOTIFY_NETCONF_START
11:42:12.467+7.274 秒%PSD_MOD-5-DMI_NOTIFY_RESTCONF_START
11:42:14.711+9.518 秒configure を抜けた(%SYS-5-CONFIG_I)
11:42:31.565+26.372 秒process の表示が 8 行とも Running(直前の show clock)
11:42:36.177〜11:42:40.592+30.984〜+35.399 秒datastores の 1 回目。% Error: Currently unable to process request
11:42:43.137+37.944 秒準備前の NETCONF の認証(%DMI-5-AUTH_PASSED)。WSL の側では hello を受け取れなかった(ツール側の観測)
11:42:44(秒単位)+38.807 秒(秒の頭で計算)準備前の RESTCONF が 502 Bad Gateway(応答の Date:)
11:42:54.131〜11:42:58.509+48.938〜+53.316 秒datastores の 2 回目。同じエラー(最後のエラー)
11:43:10.455+65.262 秒%NDBMAN-5-ACTIVE
11:43:10.558〜11:43:15.415+65.365〜+70.222 秒datastores の 3 回目。Datastore Name : running(最初の応答)
11:43:20.611+75.418 秒%DMI-5-NACM_INIT
11:43:27.877+82.684 秒%DMI-5-SYNC_COMPLETE
11:43:28(秒単位)+82.807 秒show netconf-yang statistics の netconf-start-time
11:43:37.953+92.760 秒syslog に最初に出た RESTCONF の認証(%DMI-5-AUTH_PASSED … rest over http・接続元 127.0.0.1)。datastores の応答の後に回した RESTCONF の poll の要求(下記)

poll の行は、datastores の直前と直後に撮った show clock の範囲です。poll の間隔は機器の時計で 17.954 秒と 16.427 秒で、準備が整った瞬間はこの間隔の中でしか挟めません。

datastores が応答を返すようになった時点を準備完了と呼ぶことにします(NETCONF-YANG のデータストアについての本ラボの関門で、RESTCONF が通るようになった時点ではありません。RESTCONF については下の poll の段落で書きます)。準備完了は「最後にエラーを返した回の直前の show clock(11:42:54.131)」より後で、「最初に応答を返した回の直後の show clock(11:43:15.415)」以前です。起点からの差で書くと 48.938〜70.222 秒の区間になります。

502 の応答(Date: は 11:42:44 で、秒の中のどこかは分かりません)と NETCONF の認証の行(11:42:43.137)は、どちらもこの区間の下限 11:42:54.131 より前で、1 回目の datastores の直後の show clock(11:42:40.592)より後です。どちらも準備完了の前に送った要求で、process の表示が 8 行とも Running になった後(11:42:31.565)の要求でもあります。

Cisco のガイドは、netconf-yang の手順の注記で、モデルに基づくインタフェースのプロセスが完全に動き出すまでに最大 90 秒かかりうると書いています。

After the initial enablement through the CLI, network devices can be managed subsequently through a model based interface. The complete activation of model-based interface processes may require up to 90 seconds.

この 90 秒はプロセスの activation についての記述で、datastores が応答を返すまでの時間とは書いていません。本ラボの区間の上限 70.222 秒と並べることはできますが、両者を結び付けるのは本ラボの読みです。

RESTCONF の章は、AAA と RESTCONF を設定し、nginx と DMI のプロセスが動いていれば要求を受け付けられると書いています。

After AAA and the RESTCONF interface is configured, and nginx process and relevant DMI processes are running; the device is ready to receive RESTCONF requests.

本ラボでは、process の表示が 8 行とも Running になった後でも、datastores はエラーを返し、RESTCONF は 502、NETCONF は hello の受け取り失敗でした。どちらも準備が整う前の結果で、RESTCONF や NETCONF に対応していないことを示すものではありません。同じ種類の要求は、後の撮影で通っています(NETCONF は §5、RESTCONF は §8)。

RESTCONF については、datastores が応答を返した後に、取得スクリプトが RESTCONF の入口(§8.2 の host-meta)へ資格情報付きの GET を送る poll を回しました。応答のステータスコードが 502 か、HTTP の応答が得られなかったときは 10 秒おいて送り直し、それ以外のコードが返った時点で止める作りです。記録に残したのは、最後に返ったコード(200)と、poll にかかった時間(WSL の時計で、秒に丸めて 20 秒)だけです。200 の前に別の回があったか(あれば各回の応答コードと送った時刻)は保存していません。1 回の要求が 20 秒近くかかって 200 を返した場合も、記録は同じ「20 秒」になります。datastores が応答を返した直後に RESTCONF の要求が通ったかどうかは、本ラボの記録からは分かりません。

syslog に RESTCONF の認証として最初に出た行は、上の表の 11:43:37.953 の %DMI-5-AUTH_PASSED(rest over http・利用者 netops・接続元は WSL の 192.168.1.50 ではなく 127.0.0.1)です。起点から +92.760 秒、datastores の区間の上限(11:43:15.415)から 22.538 秒後に当たります。取得スクリプトが 11:42:44 の 502 の後、この syslog を撮るまでに送った HTTP の要求はこの poll だけなので、この行は poll の要求の 1 つです。poll のどの回に当たるかは、各回の時刻を残していないので結び付けられません。

この rest over http の %DMI-5-AUTH_PASSED の行は、RESTCONF の要求ごとには出ていません。撮影の最後に撮った syslog でも、この行は 11:43:37.953 と 11:44:06.233 の 2 行だけで、§8.2 の host-meta から §9.1 の PATCH の後の GET までの 7 本の要求(Date: が 11:43:56)にも、§10.7 の PATCH(Date: が 11:50:21)にも、対応する行はありません。そのため、この行の時刻(+92.760 秒)は、RESTCONF が要求を受け付け始めた時刻の根拠にはなりません。

datastores の応答も、要求の受け付けが始まった瞬間を測ったものではありません。最初の応答(11:43:10.558〜11:43:15.415)の後に、%DMI-5-NACM_INIT・%DMI-5-SYNC_COMPLETE・statistics の netconf-start-time が来ています。%NDBMAN-5-ACTIVE・最初の応答・%DMI-5-NACM_INIT の並びもこの 1 回の観測で、決まった順番として読むものではありません。

有効化の直後の statistics は以下のとおりで、この時点では NETCONF の RPC を 1 件も受けていません。

snippet
CSR1# show netconf-yang statistics
netconf-start-time  : 2026-09-29T11:43:28+00:00
in-rpcs             : 0
in-bad-rpcs         : 0
out-rpc-errors      : 0
out-notifications   : 0
in-sessions         : 0
dropped-sessions    : 0
in-bad-hellos       : 0

4.4 有効化の後の状態と自己署名の trustpoint

有効化の後の status は netconf-yang: enabled で、WSL からの接続試行では 830 と 443 が接続できました(後者はツール側の観測)。

snippet
CSR1# show netconf-yang status
netconf-yang: enabled
netconf-yang ssh port: 830
netconf-yang candidate-datastore: disabled
snippet
172.16.1.241:830 open
172.16.1.241:443 open

syslog の 11:42:05.048 には、自己署名の trustpoint TP-self-signed-3478263324 の作成が記録されています(§4.3 の syslog)。Cisco のガイドは、trustpoint が無ければ NETCONF-YANG を設定したときに自己署名の trustpoint を作ると書いています。

NETCONF-YANG uses the primary trustpoint of a device. If a trustpoint does not exist, when NETCONF-YANG is configured, it creates a self-signed trustpoint.

ただし、有効化の前の running-config(§4.1)には crypto pki trustpoint SLA-TrustPoint が既に在りました。本ラボがこの原典の前提(trustpoint が無い)に当たるかは確かめていません。本ラボは 6 行を 1 回の configure で投入したので、どの行の投入でこの trustpoint が作られたかも分けられません。その 9 秒ほど前の 11:41:56.364 には、HTTP に結び付いた trustpoint が無効か存在しないことを示す %WSMAN-3-INVALID_TRUSTPOINT が出ています。


5. NETCONF のセッション — hello と capability

この図を大きく開く ↗

NETCONF のセッションを hello から close-session まで追った図です。hello の後は、送受とも #長さ … ## の chunked framing で運ばれました。

本ラボの NETCONF は ssh コマンドではなく、WSL の Python の paramiko で CSR1 の TCP 830 に SSH で接続し、netconf サブシステムを開いて送受しました。取得スクリプトは送ったバイト列と受けたバイト列を、そのまま保存しています。スクリプトのコードは本文に載せていませんが、作りは次のとおりです。① SSH の接続と認証の後に netconf サブシステムを開く ② 機器の hello を ]]>]]> まで読む ③ 自分の hello の後に ]]>]]> を付けて送る ④ 双方が base:1.1 のときは、RPC の XML を「改行・#・バイト数・改行」と「改行・##・改行」で挟んで送り、応答を「改行・##・改行」まで読んで chunk の見出しを外す(base:1.0 のときは RPC の後に ]]>]]> を付け、応答を ]]>]]> まで読む)。この手順は SSH の netconf サブシステムを開けるライブラリで組める形です(本ラボで使ったのは paramiko だけです)。

RFC 6242 は、NETCONF を netconf という名前の SSH のサブシステムとして呼び出すと定めています。

Once the SSH session has been established, the NETCONF client will invoke NETCONF as an SSH subsystem called “netconf”.

同じ節の例は、OpenSSH の ssh コマンドで 830 番の netconf サブシステムを呼ぶ形です(原典の例で、本ラボでは実行していません)。

[user@client]$ ssh -s server.example.org -p 830 netconf

Note that the -s option causes the command (“netconf”) to be invoked as an SSH subsystem.

CLI の SSH(TCP 22)は shell を開きます。RFC 6242 は、サブシステムにすることで、スクリプトが shell のプロンプトや起動時のメッセージを読み分けずに済むと書いています。

Running NETCONF as an SSH subsystem avoids the need for the script to recognize shell prompts or skip over extraneous information, such as a system message that is sent at shell start-up.

5.1 機器の hello

NETCONF のセッションは、双方が hello を送るところから始まります。hello には、その側が持つ機能(capability)の一覧が入ります。

When the NETCONF session is opened, each peer (both client and server) MUST send a <hello> element containing a list of that peer’s capabilities.

CSR1 の hello は以下のとおりです。<capability> は 520 本あり、base:1.0・base:1.1・writable-running:1.0 などが並び、最後の <session-id> の後に ]]>]]> が付いて終わります。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
<capability>urn:ietf:params:netconf:capability:writable-running:1.0</capability>
<capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
<capability>urn:ietf:params:netconf:capability:validate:1.0</capability>
…(省略)
<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>
…(省略)
<capability>urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&amp;module-set-id=9a21a4b66ce28c110b54d5a052486d1f</capability>
…(省略)
<capability>
        urn:ietf:params:netconf:capability:notification:1.1
      </capability>
</capabilities>
<session-id>25</session-id></hello>]]>]]>

session-id はサーバの hello にだけ入ります。

A server sending the <hello> element MUST include a <session-id> element containing the session ID for this NETCONF session.

capability の一覧は、機器が名乗った機能の一覧です。一覧に載っていることは、その機能が使える証拠にはなりません。本節で「使えた」と書く機能は、RPC の応答で確かめたものだけです。520 本の中身は 7-2 の範囲で、notification:1.0 の capability も載っていますが、本節では使っていません(7-3 の範囲)。

5.2 WSL の hello

WSL が送った hello は以下のとおりで、base:1.0 と base:1.1 の 2 本だけを名乗り、session-id は付けていません。これも ]]>]]> で終わります。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <capabilities>
    <capability>urn:ietf:params:netconf:base:1.0</capability>
    <capability>urn:ietf:params:netconf:base:1.1</capability>
  </capabilities>
</hello>
]]>]]>

双方が複数の版を共通に名乗った場合は、番号の大きい版を使います。本ラボでは双方が base:1.1 を名乗ったので、このセッションは base:1.1 です。

If more than one protocol version URI in common is present, then the highest numbered (most recent) protocol version MUST be used by both peers.

取得スクリプトは、機器の hello を受け取ってから自分の hello を送っています。RFC 6241 は、相手の capability を受け取るのを待たずに送ると定めているので、本ラボの取得スクリプトの送り方はこの定めと違います。CSR1 はこの順でもセッションを続けました(1 つの実装での観測)。

Each peer sends its <hello> element simultaneously as soon as the connection is open. A peer MUST NOT wait to receive the capability set from the other side before sending its own set.

RFC 6242 の例も、両側が同時に送りうると書いています。

Although the example shows the NETCONF server sending a <hello> message followed by the NETCONF client’s <hello> message, both sides will send the message as soon as the NETCONF subsystem is initialized, perhaps simultaneously.

hello の session-id は、CLI の show netconf-yang sessions の session-id 列と同じ値でした(§7.2)。


6. framing — ]]>]]> と chunked

NETCONF のメッセージの区切り方を framing と呼びます。RFC 6242 は 2 つの区切り方を定めています。hello は必ず ]]>]]> で終わります。

The <hello> message MUST be followed by the character sequence ]]>]]>.

双方が :base:1.1 を名乗ったときは、hello の後のメッセージを chunked framing で運びます。

If the :base:1.1 capability is advertised by both peers, the chunked framing mechanism (see Section 4.2) is used for the remainder of the NETCONF session.

]]>]]> の区切りが hello の後も続くのは、どちらか一方でも base:1.1 を名乗らないときです。次の RFC 6242 の文は、相手(remote peer)の側から見た書き方です。§6.2 は、WSL の側が base:1.0 だけを名乗った例です。

It is only used when the remote peer does not advertise a base protocol version supporting chunked encoding, i.e., a NETCONF implementation only supporting :base:1.0.

chunked framing は、# と長さ(10 進のバイト数)の行の後にその長さのデータを置いた chunk を 1 個以上並べ、最後を ## の行で閉じる形です。1 つのメッセージを複数の chunk に分けて送ってもかまいません。

Chunked-Message = 1*chunk end-of-chunks

The chunk-size field is a string of decimal digits indicating the number of octets in chunk-data. Leading zeros are prohibited, and the maximum allowed chunk-size value is 4294967295.

end-of-chunks = LF HASH HASH LF

6.1 base:1.1 のセッションの wire

§5 のセッション(双方が base:1.1)で最初に送った RPC の wire は以下のとおりです。running の hostname を読む <get-config> です。

snippet
#283
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
<get-config><source><running/></source><filter type="subtree"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname/></native></filter></get-config>
</rpc>

##

先頭の空行は、#283 の前に置かれた改行(LF)です。283 は続くデータのバイト数で、</rpc> の後の改行までを数えています。最後の ## の行が終わりの印です。

CSR1 が返した wire も同じ形でした。

snippet
#235
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname>CSR1</hostname></native></data></rpc-reply>
##

235 は、#235 の行と ## の行に挟まれた XML のバイト数と一致します。このセッションの 9 往復(message-id 101〜109)の応答は、どれも 1 個の chunk と ## の形で返りました。

6.2 base:1.0 だけのセッションの wire

別のセッションで、base:1.0 だけを名乗る hello を取得スクリプトで作って送りました。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <capabilities>
    <capability>urn:ietf:params:netconf:base:1.0</capability>
  </capabilities>
</hello>
]]>]]>

CSR1 の hello は、このセッションでも base:1.1 を名乗っていました(session-id 26)。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
…(省略)
<capability>
        urn:ietf:params:netconf:capability:notification:1.1
      </capability>
</capabilities>
<session-id>26</session-id></hello>]]>]]>

このセッションの <get-config> の wire は、送受とも XML の後に ]]>]]> が付く形で、#<長さ> の行も ## の行もありません。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
<get-config><source><running/></source><filter type="subtree"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname/></native></filter></get-config>
</rpc>
]]>]]>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname>CSR1</hostname></native></data></rpc-reply>]]>]]>

close-session の応答も ]]>]]> で終わりました。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102"><ok/></rpc-reply>]]>]]>

RFC 6242 は、この区切り方を以前の版に沿った実装との互換のために残したものと位置づけています。

This mechanism exists for backwards compatibility with implementations of previous versions of this document.

6.3 HTTP の chunked との違い

RESTCONF の応答のヘッダには Transfer-Encoding: chunked が出ます(§8.3)。これは HTTP の本文の分割転送の指定で、NETCONF の chunked framing とは別の仕組みです。本節で「chunked framing」と書くときは、NETCONF の #<長さ> … ## の形だけを指します。


7. 読む・書く・拒否される — get-config・get・lock・edit-config・rpc-error

§7 の RPC は §6.1 と同じセッションのもので、送った側も応答も wire のまま示します。§10 からは応答の示し方を変えます(§10.3 の冒頭)。

7.1 get-config と get

NETCONF は、設定を読む操作と、設定と状態を読む操作を分けています。

The <get-config> operation retrieves configuration data only, while the <get> operation retrieves configuration and state data.

読む範囲は subtree filter で絞りました。

XML subtree filtering is a mechanism that allows an application to select particular XML subtrees to include in the <rpc-reply> for a <get> or <get-config> operation.

Loopback0 の設定を <get-config> で読んだ要求と応答は以下のとおりです。filter は ietf-interfaces の名前空間の <interface> を <name> で絞っています。

snippet
#324
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102">
<get-config><source><running/></source><filter type="subtree"><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name></interface></interfaces></filter></get-config>
</rpc>

##
snippet
#597
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>initial</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
##

応答の message-id="102" は、要求の message-id="102" と同じです。

The <rpc-reply> element has a mandatory attribute “message-id”, which is equal to the “message-id” attribute of the <rpc> for which this is a response.

同じ Loopback0 を <get> で interfaces-state から読んだ要求と応答は以下のとおりです。

snippet
#295
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103">
<get><filter type="subtree"><interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name></interface></interfaces-state></filter></get>
</rpc>

##
snippet
#1103
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103"><data><interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><admin-status>up</admin-status><oper-status>up</oper-status><last-change>2026-09-29T11:42:35.43+00:00</last-change><if-index>4</if-index><phys-address>00:1e:49:c8:a4:00</phys-address><speed>8000000000</speed><statistics><discontinuity-time>2026-09-29T11:40:43+00:00</discontinuity-time><in-octets>0</in-octets><in-unicast-pkts>0</in-unicast-pkts><in-broadcast-pkts>0</in-broadcast-pkts><in-multicast-pkts>0</in-multicast-pkts><in-discards>0</in-discards><in-errors>0</in-errors><in-unknown-protos>0</in-unknown-protos><out-octets>0</out-octets><out-unicast-pkts>0</out-unicast-pkts><out-broadcast-pkts>0</out-broadcast-pkts><out-multicast-pkts>0</out-multicast-pkts><out-discards>0</out-discards><out-errors>0</out-errors></statistics></interface></interfaces-state></data></rpc-reply>
##

<get-config> の応答には description・type・enabled・IPv4 のアドレスがあり、oper-status や統計はありません。<get> の応答には admin-status・oper-status・統計があり、description はありません。ただし、2 つの要求は操作だけでなくフィルタも違います(<get-config> は interfaces、<get> は interfaces-state)。§7.1 の冒頭の引用のとおり <get> は設定と状態の両方を返す操作で、本ラボの <get> は状態の側の interfaces-state をフィルタで選んだので、返ったのは状態の項目だけでした。同じ interfaces のフィルタで <get> を送る形は撮っていません(§12)。interfaces と interfaces-state がモデルの中でどう分かれているかは 7-2 の範囲です。

7.2 lock

<lock> は、データストアを 1 つのセッションが押さえる操作です。

Description: The <lock> operation allows the client to lock the entire configuration datastore system of a device.

Cisco のガイドは、lock を持たない他の NETCONF のセッションは編集できず、読むことはできると書いています。

The NETCONF lock RPC locks the configuration parser and the running configuration database. All other NETCONF sessions (that do not own the lock) cannot perform edit operations; but can perform read operations.

running を lock した要求と応答は以下のとおりです。

snippet
#158
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="104">
<lock><target><running/></target></lock>
</rpc>

##
snippet
#132
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="104"><ok/></rpc-reply>
##

lock の間に CLI(TCP 22 の別の SSH)で show netconf-yang sessions を見ると、session-id 25(hello と同じ値)・接続元 192.168.1.50 の行の global-lock 列が R でした。凡例の R: は running の lock を表します。一覧に出たのは NETCONF のセッションだけで、CLI の SSH の接続は出ていません。

snippet
CSR1# show netconf-yang sessions
R: Global-lock on running datastore
C: Global-lock on candidate datastore
S: Global-lock on startup datastore

Number of sessions : 1

session-id  transport    username             source-host            global-lock  
--------------------------------------------------------------------------------
25          netconf-ssh  netops               192.168.1.50           R            

本ラボは lock を 1 つのセッションからしか取っていないので、他のセッションの編集が拒まれるところは確かめていません(§12)。

7.3 edit-config

lock を持ったまま、<edit-config> で running の Loopback0 の description を set-by-netconf にしました。送った <config> には operation 属性を書いていません。その場合、RFC 6241 は merge として扱うと定めています。

If the “operation” attribute is not specified, the configuration is merged into the configuration datastore.

snippet
#352
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="105">
<edit-config><target><running/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>set-by-netconf</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
#132
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="105"><ok/></rpc-reply>
##

成功の応答は <ok/> です。

The <ok> element is sent in <rpc-reply> messages if no errors or warnings occurred during the processing of an <rpc> request, and no data was returned from the operation.

直後の CLI の running-config には、同じ description が現れました(編集の前は initial)。

snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 92 bytes
!
interface Loopback0
 description set-by-netconf
 ip address 10.7.1.1 255.255.255.255
end

NETCONF のデータストアの <running/> と、CLI の running-config が同じものかは確かめていません。確かめたのは、NETCONF で running に書いた値が CLI の表示に現れたことです。

unlock の後、show netconf-yang sessions の global-lock 列は None に戻りました。セッションは開いたままで、Number of sessions は 1 のままです。

snippet
#162
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="107">
<unlock><target><running/></target></unlock>
</rpc>

##
snippet
#132
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="107"><ok/></rpc-reply>
##
snippet
CSR1# show netconf-yang sessions
R: Global-lock on running datastore
C: Global-lock on candidate datastore
S: Global-lock on startup datastore

Number of sessions : 1

session-id  transport    username             source-host            global-lock  
--------------------------------------------------------------------------------
25          netconf-ssh  netops               192.168.1.50           None         

7.4 rpc-error

candidate を有効にする前の CSR1 に、candidate を読む <get-config> を送ると、<rpc-error> が返りました。candidate は running とは別に編集してから確定するデータストアで、§10.1 で扱います。

snippet
#285
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="108">
<get-config><source><candidate/></source><filter type="subtree"><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname/></native></filter></get-config>
</rpc>

##
snippet
#403
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="108"><rpc-error>
<error-type>protocol</error-type>
<error-tag>invalid-value</error-tag>
<error-severity>error</error-severity>
<error-message xml:lang="en">Unsupported capability :candidate</error-message><error-info><bad-element>candidate</bad-element>
</error-info>
</rpc-error>
</rpc-reply>
##

<rpc-error> の要素は RFC 6241 §4.3 が定めています。error-type はエラーが起きた層を表します。

error-type: Defines the conceptual layer that the error occurred. Enumeration. One of: * transport (layer: Secure Transport) * rpc (layer: Messages) * protocol (layer: Operations) * application (layer: Content)

error-tag はエラーの条件を表す文字列で、使える値は Appendix A にあります。

error-tag: Contains a string identifying the error condition. See Appendix A for allowed values.

本ラボの error-tag の invalid-value は、Appendix A では次のように定義されています。

error-tag: invalid-value error-type: protocol, application error-severity: error error-info: none Description: The request specifies an unacceptable value for one or more parameters.

error-message の Unsupported capability :candidate の文字列は機器が返したもので、§15 の原典には現れません。§5.1 の hello の 520 本に candidate:1.0 の capability はありませんでした。

7.5 close-session

最後に <close-session> を送り、<ok/> で閉じました。

snippet
#134
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="109">
<close-session/>
</rpc>

##
snippet
#132
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="109"><ok/></rpc-reply>
##

RFC 6241 は、close-session を受けたサーバがセッションに結び付いた lock と資源を解放すると定めています。

When a NETCONF server receives a <close-session> request, it will gracefully close the session. The server will release any locks and resources associated with the session and gracefully close any associated connections. Any NETCONF requests received after a <close-session> request will be ignored.


8. RESTCONF — 入口の発見から JSON と XML まで

8.1 curl の送り方

RESTCONF の要求は、WSL の curl 8.5.0 で送りました。コマンドは実行したとおりに示します。使った指定は以下のとおりです。

指定意味
-sS進捗の表示を消し、エラーだけを出す
-i応答のヘッダも出力する
-kサーバの証明書を検証しない(§11)
--max-time 4040 秒で打ち切る
-X <メソッド>GET・PATCH・POST・DELETE を指定する
-K -curl の設定を標準入力から読む。本ラボは user = "<利用者名>:<パスワード>" の形の設定 1 行を標準入力から渡し、資格情報をコマンドの引数に載せていない
-H 'Accept: …'応答の形式(JSON か XML)を指定する
-H 'Content-Type: …'送る本文の形式を指定する
--data-binary '…'送る本文

資格情報を付けない要求(§9.5)は、コマンドの引数は同じまま、標準入力を空にしました。認証は HTTP の認証で、RFC 8040 は証明書による認証ができない場合に HTTP の認証を使ってよいとしています。

If certificate-based authentication is not feasible (e.g., because one cannot build the required PKI for clients), then HTTP authentication MAY be used.

8.2 入口の発見 — host-meta

RFC 8040 は、クライアントが最初に RESTCONF の API の root(入口の URL)を決めることを求め、その方法として /.well-known/host-meta を読み、restconf の Link を使うことを定めています。

The client discovers this by getting the “/.well-known/host-meta” resource ([RFC6415]) and using the <Link> element containing the “restconf” attribute:

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - https://172.16.1.241/.well-known/host-meta
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 GMT
Content-Type: application/xrd+xml
Content-Length: 107
Connection: keep-alive
Vary: Accept-Encoding

<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
    <Link rel='restconf' href='/restconf'/>
</XRD>

応答は XRD の XML で、<Link rel='restconf' href='/restconf'/> が root は /restconf だと示しています。Cisco の 17.3 のガイドの host-meta の例には Server: の行が無く、Server: nginx が出るのはガイドの後半の設定例(OPTIONS などの応答)です。本ラボの CSR1 は Server: openresty を返しました。

8.3 API の root

/restconf を JSON で GET すると、API の root が返りました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 GMT
Content-Type: application/yang-data+json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Vary: Accept-Encoding
Pragma: no-cache

{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2016-06-21"}}

For example, a request to GET the root resource “/restconf” in JSON format will return a representation of the API resource named “ietf-restconf:restconf”.

root の下には data(設定と状態のデータ)と operations(RPC の操作)があり、yang-library-version は 2016-06-21 でした。

This mandatory leaf identifies the revision date of the “ietf-yang-library” YANG module that is implemented by this server.

応答のヘッダの Transfer-Encoding: chunked は HTTP の分割転送の指定で、§6 の NETCONF の chunked framing とは別物です。

8.4 同じ資源を JSON と XML で読む

hostname を /restconf/data/Cisco-IOS-XE-native:native/hostname で読みました。URL の最初の節点には、YANG のモジュール名とコロンが付きます。

If a node in the path is defined in a module other than its parent node or its parent is the datastore, then the module name followed by a colon character (":") MUST be prepended to the node name in the resource identifier.

Accept を JSON にすると、応答の本文は JSON でした。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/Cisco-IOS-XE-native:native/hostname
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 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-native:hostname": "CSR1"
}

同じ URL で Accept を XML にすると、同じ値が XML で返りました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+xml' https://172.16.1.241/restconf/data/Cisco-IOS-XE-native:native/hostname
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 GMT
Content-Type: application/yang-data+xml
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache


<hostname xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"  xmlns:ios="http://cisco.com/ns/yang/Cisco-IOS-XE-native">CSR1</hostname>

応答の形式は、要求の Accept ヘッダで選びます。

The response output content encoding formats that the client will accept are identified with the “Accept” header field in the request. If it is not specified, the request input encoding format SHOULD be used, or the server MAY choose any supported content encoding format.

JSON では、最上位のメンバ名 Cisco-IOS-XE-native:hostname にモジュール名が付きました。RFC 7951 は、最上位のメンバと、親とモジュールが変わるメンバにだけモジュール名を付けると定めています。

A namespace-qualified member name MUST be used for all members of a top-level JSON object and then also whenever the namespaces of the data node and its parent node are different. In all other cases, the simple form of the member name MUST be used.

XML では、モジュールを名前空間 xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native" で示しています。

8.5 NETCONF で書いた値を RESTCONF で読む

Loopback0 を /restconf/data/ietf-interfaces:interfaces/interface=Loopback0 で読みました。list の要素は、list の名前・=・キーの値で指します。

If there is only one key leaf value, the path segment is constructed by having the list name, followed by an “=” character, followed by the single key leaf value.

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback0
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 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

{
  "ietf-interfaces:interface": {
    "name": "Loopback0",
    "description": "set-by-netconf",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "10.7.1.1",
          "netmask": "255.255.255.255"
        }
      ]
    },
    "ietf-ip:ipv6": {
    }
  }
}

description は、§7.3 で NETCONF の <edit-config> が書いた set-by-netconf でした。JSON のメンバ名にモジュール名が付いたのは、最上位の ietf-interfaces:interface と、別のモジュールに属する ietf-ip:ipv4・ietf-ip:ipv6 だけで、name や description には付いていません。メンバ名へのモジュール名の付け方は、§8.4 の RFC 7951 の定めのとおりです。ただし、list の要素である ietf-interfaces:interface の値は配列ではなく 1 つのオブジェクトで、この点は RFC 7951 の list の表し方と違います(§9.2)。


9. RESTCONF のメソッドとステータス

この図を大きく開く ↗

本ラボの RESTCONF の要求のうち 10 本と、応答の最初のステータス行です。GET・PATCH・POST・DELETE の成功と、409・404・401 の返り方を本ラボの値で並べました。

RFC 8040 §4 は、HTTP のメソッドと NETCONF の操作の対応を表にしています。本節で使ったメソッドの行を日本語で並べると、以下のとおりです(出典層)。

RESTCONF のメソッド対応する NETCONF の操作
GET<get-config>・<get>
POST<edit-config>(operation=“create”)
PATCH<edit-config>(operation は PATCH の中身で決まる)
DELETE<edit-config>(operation=“delete”)

PATCH の本文をそのまま送る形(plain patch)は、merge に当たります。

The “remove” edit operation attribute for the NETCONF <edit-config> RPC operation is not supported by the HTTP DELETE method. The resource must exist or the DELETE method will fail. The PATCH method is equivalent to a “merge” edit operation when using a plain patch (see Section 4.6.1); other media types may provide more granular control.

本ラボの要求と、応答の最初の HTTP/ 行は以下のとおりです(実測層)。

要求最初の HTTP/ 行本文
GET /.well-known/host-metaHTTP/1.1 200 OK§8.2
GET /restconfHTTP/1.1 200 OK§8.3
GET hostname(JSON・XML)HTTP/1.1 200 OK§8.4
GET Loopback0HTTP/1.1 200 OK§8.5
PATCH Loopback0HTTP/1.1 204 No Content§9.1
GET Loopback0(PATCH の後)HTTP/1.1 200 OK§9.1
POST Loopback71HTTP/1.1 201 Created§9.2
GET Loopback71(POST の後)HTTP/1.1 200 OK§9.2
POST Loopback71(2 回目)HTTP/1.1 409 Conflict§9.3
DELETE Loopback71HTTP/1.1 204 No Content§9.4
GET Loopback71(削除後)HTTP/1.1 404 Not Found§9.4
GET Loopback99(作っていない)HTTP/1.1 404 Not Found§9.4
GET Loopback0(資格情報なし)HTTP/1.1 401 Unauthorized§9.5
GET yang-library の module-set-idHTTP/1.1 200 OK§9.7

9.1 PATCH — 204 No Content

Loopback0 の description だけを入れた JSON を PATCH で送りました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X PATCH -K - -H 'Accept: application/yang-data+json' -H 'Content-Type: application/yang-data+json' --data-binary '{"ietf-interfaces:interface": {"name": "Loopback0", "description": "set-by-restconf"}}' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback0
HTTP/1.1 204 No Content
Server: openresty
Date: Tue, 29 Sep 2026 11:43:56 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Last-Modified: Tue, 29 Sep 2026 11:43:56 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: "1790-682236-560726"
Pragma: no-cache

応答は 204 No Content で、本文はありません(Content-Length: 0)。RFC 8040 は、PATCH が成功したとき、本文があれば 200、無ければ 204 を返すと定めています。

If the PATCH request succeeds, a “200 OK” status-line is returned if there is a message-body, and “204 No Content” is returned if no response message-body is sent.

続く GET と CLI の description は set-by-restconf になりました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback0
HTTP/1.1 200 OK
…(省略)
{
  "ietf-interfaces:interface": {
    "name": "Loopback0",
    "description": "set-by-restconf",
…(省略)
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 93 bytes
!
interface Loopback0
 description set-by-restconf
 ip address 10.7.1.1 255.255.255.255
end

9.2 POST — 201 Created と Location

POST は …/ietf-interfaces:interfaces の URL に送り、作る Loopback71 を本文に入れました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X POST -K - -H 'Accept: application/yang-data+json' -H 'Content-Type: application/yang-data+json' --data-binary '{"ietf-interfaces:interface": {"name": "Loopback71", "type": "iana-if-type:softwareLoopback", "description": "created-by-restconf"}}' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces
HTTP/1.1 201 Created
Server: openresty
Date: Tue, 29 Sep 2026 11:43:59 GMT
Content-Type: text/html
Content-Length: 0
Location: https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback71
Connection: keep-alive
Last-Modified: Tue, 29 Sep 2026 11:43:59 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: "1790-682239-441505"
Pragma: no-cache

応答は 201 Created で、Location: ヘッダに作られた資源の URL(…/interface=Loopback71)が入っていました。RFC 8040 は、POST で作ったときの 201 と Location を次のように定めています。

If the POST method succeeds, a “201 Created” status-line is returned and there is no response message-body. A “Location” header field identifying the child resource that was created MUST be present in the response in this case.

作った Loopback71 は、RESTCONF の GET と CLI の両方に現れました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback71
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:43:59 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

{
  "ietf-interfaces:interface": {
    "name": "Loopback71",
    "description": "created-by-restconf",
    "type": "iana-if-type:softwareLoopback",
    "enabled": true,
    "ietf-ip:ipv4": {
    },
    "ietf-ip:ipv6": {
    }
  }
}
snippet
CSR1# show running-config interface Loopback71
Building configuration...

Current configuration : 76 bytes
!
interface Loopback71
 description created-by-restconf
 no ip address
end

PATCH(§9.1)と POST で送った JSON は、ietf-interfaces:interface の値を配列にせず、1 つのオブジェクトで書きました。RFC 7951 は、list の要素を配列で表すと定めています。

A list instance is encoded as a name/array pair, and the array elements are JSON objects.

RFC 8040 の付録の POST の例(Appendix B.2.1)も、list の要素を配列で送っています。CSR1 の 17.03.08a は、オブジェクトの形の PATCH と POST を受け付けました(1 つの実装での観測)。機器が返した GET の応答も同じで、§8.5・§9.1 の Loopback0 と上の Loopback71 のどれも、ietf-interfaces:interface の値は配列ではなく 1 つのオブジェクトでした。配列の形は本ラボでは送っていないので、RFC 7951 の形(配列)を CSR1 が受け付けるかと、他の実装がオブジェクトの形を受け付けるかは、どちらも確かめていません(§12)。

9.3 同じ POST をもう一度 — 409 Conflict と data-exists

同じ POST をもう一度送ると、Loopback71 は既に在るので 409 Conflict が返りました。本文の error-tag は data-exists、error-type は application でした。

snippet
WSL $ curl -sS -i -k --max-time 40 -X POST -K - -H 'Accept: application/yang-data+json' -H 'Content-Type: application/yang-data+json' --data-binary '{"ietf-interfaces:interface": {"name": "Loopback71", "type": "iana-if-type:softwareLoopback", "description": "created-by-restconf"}}' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces
HTTP/1.1 409 Conflict
Server: openresty
Date: Tue, 29 Sep 2026 11:44:02 GMT
Content-Type: application/yang-data+json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Vary: Accept-Encoding
Pragma: no-cache

{
  "errors": {
    "error": [
      {
        "error-message": "object already exists: /if:interfaces/if:interface[if:name='Loopback71']",
        "error-path": "/ietf-interfaces:interfaces",
        "error-tag": "data-exists",
        "error-type": "application"
      }
    ]
  }
}

error-message の中の if: は、§8.4 のモジュール名(ietf-interfaces:)とは違う短い書き方です。何を表す書き方かは 7-2 で扱います。

この場合の error-tag について、RFC 8040 の中で書き方が割れています。§4.4.1 の規定の文は、既に在る資源への POST を 409 で失敗させ、error-tag を resource-denied とすると定めています。

If the data resource already exists, then the POST request MUST fail and a “409 Conflict” status-line MUST be returned. The error-tag value “resource-denied” is used in this case.

一方、RFC 8040 §7.1 の例は、同じ状況(jukebox の資源が既に在るので作れない)で data-exists のエラーを返す例として書かれています。例の応答も 409 Conflict です。

The following example shows an error returned for a “data-exists” error on a data resource. The “jukebox” resource already exists, so it cannot be created.

2 つの error-tag の元の意味は、RFC 6241 の Appendix A にあります。resource-denied は資源の不足で要求を完了できないこと、data-exists は対象のデータが既に在るので完了できないことです。

error-tag: resource-denied error-type: transport, rpc, protocol, application error-severity: error error-info: none Description: Request could not be completed because of insufficient resources.

error-tag: data-exists error-type: application error-severity: error error-info: none Description: Request could not be completed because the relevant data model content already exists. For example, a “create” operation was attempted on data that already exists.

本ラボの 17.03.08a の応答は、409 と data-exists の範囲で RFC 8040 §7.1 の例と一致し、409 は RFC 8040 §4.4.1 とも一致します。error-type は RFC 8040 §7.1 の例の protocol と違って application で、RFC 6241 の Appendix A の data-exists の定義の error-type と一致します。error-message も例の Data already exists; cannot create new resource とは違い、object already exists: … でした。RFC 8040 の中で規定の文として書かれているのは §4.4.1 の resource-denied で、§7.1 は例です。§4.4.1 の resource-denied を data-exists に直す訂正の提案(RFC 8040 の Errata 5761)は、RFC Editor の errata のページで Rejected(却下)になっていて、訂正は認められていません。本機の data-exists は、RFC 8040 §7.1 の例とは一致し、§4.4.1 の規定の文とは違います。ページにある却下の理由は、WG のメーリングリストの議論に基づくという検証者の注と、そのアーカイブの URL だけです。本節が取得した RFC 8040 の本文(rfc8040.txt)では、§4.4.1 が resource-denied、§7.1 の例が data-exists です。

この application・data-exists・object already exists: … の形は、§15 の Cisco の 17.3 のガイドの RESTCONF の章の例にも在ります。既に在る hostname を YANG-Patch の create で作ろうとした PATCH の、JSON の応答の中の error です。

“error-type”: “application”, “error-tag”: “data-exists”, “error-path”: “/Cisco-IOS-XE-native:native/hostname”, “error-message”: “object already exists: /ios:native/ios:hostname”

ただし、この例は YANG-Patch(application/yang-patch+xml の本文を送る PATCH)の要求で、本ラボの POST(§9.2・§9.3)とも、本ラボの PATCH(§9.1。YANG-Patch ではなく、本文をそのまま送る plain patch)とも要求の種類が違います。応答の最上位も ietf-yang-patch:yang-patch-status の中の edit ごとのエラーで、本ラボの errors とは入れ物が違い、例には HTTP のステータスの行もありません。一致しているのは、error の中の 3 つの値の形までです。

9.4 DELETE と、その後の GET

DELETE で Loopback71 を消すと、204 No Content が返りました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X DELETE -K - https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback71
HTTP/1.1 204 No Content
Server: openresty
Date: Tue, 29 Sep 2026 11:44:03 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Last-Modified: Tue, 29 Sep 2026 11:44:02 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: "1790-682242-546345"
Pragma: no-cache

The DELETE method is used to delete the target resource. If the DELETE request succeeds, a “204 No Content” status-line is returned.

消した後に同じ URL を GET すると、404 Not Found でした。CLI の show running-config interface Loopback71 は、設定の代わりに % Invalid input で始まるエラーを返しました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback71
HTTP/1.1 404 Not Found
Server: openresty
Date: Tue, 29 Sep 2026 11:44:03 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache
snippet
CSR1# show running-config interface Loopback71
                                           ^
% Invalid input detected at '^' marker.

一度も作っていない Loopback99 の GET も 404 Not Found でした。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback99
HTTP/1.1 404 Not Found
Server: openresty
Date: Tue, 29 Sep 2026 11:44:06 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache

RFC 8040 は、存在しない資源を読む要求に 404 を返し、error-tag を invalid-value にすると定めています。

If a retrieval request for a data resource represents an instance that does not exist, then an error response containing a “404 Not Found” status-line MUST be returned by the server. The error-tag value “invalid-value” is used in this case.

本ラボの 2 本の 404 は、どちらも本文が 0 バイト(Content-Length: 0)で、error-tag を含む本文はありませんでした。RFC 8040 は 4xx の応答にエラーの情報を含めることを SHOULD としています。

If a status code in the “4xx” range is returned in the status-line, then the error information SHOULD be returned in the response, according to the format defined in Section 7.1.

本文が無かったことは 17.03.08a のこの 2 本の観測で、機器が返さない仕様とも、規定に反するとも一般化しません。

9.5 資格情報なしの GET — 401 Unauthorized

資格情報を付けずに Loopback0 を GET すると、401 Unauthorized が返りました。コマンドの引数は他の GET と同じで、-K - の標準入力を空にしています。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback0
HTTP/1.1 401 Unauthorized
Server: openresty
Date: Tue, 29 Sep 2026 11:44:06 GMT
Content-Type: application/yang-data+json
Transfer-Encoding: chunked
Connection: keep-alive
WWW-Authenticate: Basic realm="restconf"
Vary: Accept-Encoding

{
  "errors": {
    "error": [
      {
        "error-tag": "access-denied",
        "error-type": "protocol"
      }
    ]
  }
}

WWW-Authenticate: Basic realm="restconf" のヘッダは Basic 認証を求めていて、本文の error-tag は access-denied でした。RFC 8040 は、認証されていないクライアントへの応答を次のように定めています。

If the RESTCONF client is not authenticated, the server SHOULD send an HTTP response with a “401 Unauthorized” status-line, as defined in Section 3.1 of [RFC7235]. The error-tag value “access-denied” is used in this case.

9.6 エラー本文の最上位の名前

409(§9.3)と 401(§9.5)のエラーの本文は、どちらも最上位のメンバ名が "errors" で、モジュール名が付いていません。RFC 8040 §7.1 の例のエラーの本文は、最上位が ietf-restconf:errors です。

{ “ietf-restconf:errors” : { “error” : [ { “error-type” : “protocol”, “error-tag” : “lock-denied”, “error-message” : “Lock failed; lock already held” } ] } }

RFC 7951 §4 は最上位の JSON のメンバ名にモジュール名を付けると定めています(§8.4)。本ラボの "errors" は、この 2 つの原典の形とは違いました。原典との差として記録するだけで、規定に反すると断定はしません。

9.7 yang-library の module-set-id

RESTCONF で yang-library の module-set-id を読むと、以下の値が返りました。

snippet
WSL $ curl -sS -i -k --max-time 40 -X GET -K - -H 'Accept: application/yang-data+json' https://172.16.1.241/restconf/data/ietf-yang-library:modules-state/module-set-id
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 29 Sep 2026 11:44: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

{
  "ietf-yang-library:module-set-id": "bcaa2bd1b7330ff7474931011ae702be"
}

同じ時期の NETCONF の hello の yang-library の capability には、module-set-id=9a21a4b66ce28c110b54d5a052486d1f が入っていました(§5.1)。2 つの値は違い、違う理由は確かめていません。yang-library の中身は 7-2 の範囲です。


10. candidate と confirmed commit

この図を大きく開く ↗

candidate と confirmed commit の値の変化です。A は期限の後 3 秒以内に戻り、running も lock した B は期限を過ぎた時点ではまだ戻っておらず、lock を外した後の撮影では戻っていました(1 回の観測)。

10.1 candidate とは

candidate は、動いている設定(running)に影響を与えずに編集でき、後から running へ確定(commit)できるデータストアです。すべての装置が持つものではありません。

candidate configuration datastore: A configuration datastore that can be manipulated without impacting the device’s current configuration and that can be committed to the running configuration datastore. Not all devices support a candidate configuration datastore.

§7 までの running への直接の書き込みと違い、candidate では「書く」と「確定する」が別の操作になります。確定の操作が <commit> です。

The <commit> operation instructs the device to implement the configuration data contained in the candidate configuration.

10.2 candidate の有効化と再起動

CSR1 の candidate は、netconf-yang feature candidate-datastore で有効にします。Cisco のガイドは、切り替えると NETCONF と RESTCONF が再起動することを警告のメッセージで知らせると書いています。

The candidate datastore functionality can be enabled by using the netconf-yang feature candidate-datastore command. When the datastore state changes from running to candidate or back, a warning message is displayed, notifying the user that a restart of NETCONF or RESTCONF will occur in order for the change to take effect.

本ラボの vty の SSH の投入画面には、警告も % の行も出ませんでした。

snippet
configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
CSR1(config)#netconf-yang feature candidate-datastore
CSR1(config)#end
CSR1#

同じ時間帯の syslog には、ConfD(§4.1 の引用の confd)との接続が切れた行、running から candidate への切り替えの行(DMI_NOTIFY_USER)、実行中のセッションを切って再起動する行(DMI_NOTIFY_RESTARTING)が出ていました。console と terminal monitor での表示は撮っていません。

snippet
CSR1# show logging
…(省略)
    Monitor logging: level debugging, 0 messages logged, xml disabled,
…(省略)
*Sep 29 11:44:10.149: %DMI-3-MAAPI_FINISH_TRANS_FAIL: R0/0: dmiauthd: Failed to finish a transaction via DMI MAAPI Lost connection to ConfD (45): Socket to ConfD is closed.
*Sep 29 11:44:10.150: %DMI-3-MAAPI_UNLOCK_FAIL: R0/0: dmiauthd: Failed to unlock the NETCONF running data store via MAAPI Lost connection to ConfD (45): Socket to ConfD is closed.
*Sep 29 11:44:10.166: %NDBMAN-5-RESET: R0/0: ndbmand: A data provider has stopped.
*Sep 29 11:44:11.000: %PSD_MOD-5-DMI_NOTIFY_USER: R0/0: psd: PSD/DMI: netconf-yang and/or restconf is transitioning from running to candidate
*Sep 29 11:44:11.006: %PSD_MOD-5-DMI_NOTIFY_RESTARTING: R0/0: psd: PSD/DMI: netconf-yang and/or restconf will now be restarted, and any sessions in progress will be terminated
*Sep 29 11:44:13.163: %SYS-5-CONFIG_I: Configured from console by netops on vty1 (192.168.1.50)
*Sep 29 11:45:03.464: %NDBMAN-5-ACTIVE: R0/0: ndbmand: All data providers active.
*Sep 29 11:45:21.416: %DMI-5-SYNC_COMPLETE: R0/0: dmiauthd: The running configuration has been synchronized to the NETCONF running data store.

DMI_NOTIFY_USER と DMI_NOTIFY_RESTARTING の 2 行の文言は、Cisco のガイドが載せている警告の文面と同じです。同じ show logging の見出しでは、terminal monitor に出したログ(Monitor logging)は 0 件でした。本ラボの vty の画面には syslog を流していなかったので、投入画面に警告が出なかったことは、機器がこの警告を出さなかったことを意味しません。

再起動の後、§4 と同じ関門を置きました。datastores は 3 回エラーを返した後、running だけを返す回を経て、running と candidate の両方を返しました。3 回目のエラーと、その後の 2 回は以下のとおりです。

snippet
CSR1# show clock
*11:44:58.356 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
% Error: Currently unable to process request

CSR1# show clock
*11:45:02.778 UTC Tue Sep 29 2026
snippet
CSR1# show clock
*11:45:14.885 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
Datastore Name             : running

CSR1# show clock
*11:45:19.496 UTC Tue Sep 29 2026
snippet
CSR1# show clock
*11:45:31.778 UTC Tue Sep 29 2026
CSR1# show netconf-yang datastores
Datastore Name             : running
Datastore Name             : candidate

CSR1# show clock
*11:45:36.213 UTC Tue Sep 29 2026

参考として、DMI_NOTIFY_RESTARTING(11:44:11.006)から数えると、running の応答が返るまでは 47.350〜68.490 秒、candidate が載るまでは 63.879〜85.207 秒の区間です(§4.3 と同じ、poll の前後の show clock で挟む読み方)。running だけの回が撮れたかどうかは、poll の時刻に依存します。

snippet
CSR1# show netconf-yang status
netconf-yang: enabled
netconf-yang ssh port: 830
netconf-yang candidate-datastore: enabled

10.3 hello の変化と running への書き込みの拒否

§10 の RPC は、送った側を wire のまま、応答を chunk の見出しと ## を外した XML で示します(外したのは取得スクリプトで、中身は wire と同じです)。

再起動の後の新しいセッション(session-id 22)の hello では、capability が §5.1 の 520 本から 522 本になりました。§5.1 の hello と <capability> の値を集合で比べると、加わったのは 5 本、消えたのは 3 本です。

変化capability
加わったconfirmed-commit:1.1・confirmed-commit:1.0・candidate:1.0
消えたwritable-running:1.0
値が変わった(消えた 1 本と加わった 1 本)ietf-netconf モジュールの行の features が writable-running,rollback-on-error,validate,xpath から confirmed-commit,candidate,rollback-on-error,validate,xpath に
値が変わった(消えた 1 本と加わった 1 本)yang-library の行の module-set-id が 9a21a4b66ce28c110b54d5a052486d1f から a606b8b587df2a167d75f1129f1977c5 に

§9.7 で RESTCONF の値と比べた hello の module-set-id も、candidate の有効化の後は別の値になっています。session-id は 22 で、再起動の前に最後に開いた NETCONF のセッション(§11 の ncclient の例。§3 の Phase D)の 42 より小さい値になりました。小さくなった理由は確かめていません。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:base:1.1</capability>
<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>
<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.0</capability>
<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
<capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
…(省略)
<capability>
        urn:ietf:params:netconf:capability:notification:1.1
      </capability>
</capabilities>
<session-id>22</session-id></hello>]]>]]>

この状態で running に <edit-config> を送ると、<rpc-error> が返りました。error-message は Unsupported capability :writable-running です。

snippet
#355
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
<edit-config><target><running/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>direct-to-running</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><rpc-error>
<error-type>protocol</error-type>
<error-tag>invalid-value</error-tag>
<error-severity>error</error-severity>
<error-message xml:lang="en">Unsupported capability :writable-running</error-message><error-info><bad-element>running</bad-element>
</error-info>
</rpc-error>
</rpc-reply>

Cisco のガイドは、candidate を有効にすると running は NETCONF から書けず、設定は candidate を通してだけ確定すると書いています。

When the candidate data store is enabled, the running data store is not writable through NETCONF sessions, and all configurations get committed only through the candidate. In other words, the writable-running NETCONF capability is not enabled with the candidate configuration.

10.4 lock・edit-config・commit

candidate は複数のセッションが共有するので、編集の前に lock するのが賢明だと RFC 6241 は書いています。

The candidate configuration can be shared among multiple sessions. Unless a client has specific information that the candidate configuration is not shared, it MUST assume that other sessions are able to modify the candidate configuration at the same time. It is therefore prudent for a client to lock the candidate configuration before modifying it.

candidate を lock すると、global-lock 列は C でした。

snippet
#160
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102">
<lock><target><candidate/></target></lock>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102"><ok/></rpc-reply>
snippet
CSR1# show netconf-yang sessions
R: Global-lock on running datastore
C: Global-lock on candidate datastore
S: Global-lock on startup datastore

Number of sessions : 1

session-id  transport    username             source-host            global-lock  
--------------------------------------------------------------------------------
22          netconf-ssh  netops               192.168.1.50           C            

<edit-config> の target を candidate にして、Loopback0 の description を edited-in-candidate にしました(<ok/>)。

snippet
#359
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103">
<edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103"><ok/></rpc-reply>

commit の前に running と candidate を読むと、running は §9.1 の RESTCONF の set-by-restconf のまま、candidate だけが edited-in-candidate でした。CLI も set-by-restconf のままです。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="104"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>set-by-restconf</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="105"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 93 bytes
!
interface Loopback0
 description set-by-restconf
 ip address 10.7.1.1 255.255.255.255
end

<commit> を送ると、running と CLI も edited-in-candidate になりました。

snippet
#127
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="106">
<commit/>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="106"><ok/></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="107"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 97 bytes
!
interface Loopback0
 description edited-in-candidate
 ip address 10.7.1.1 255.255.255.255
end

10.5 confirmed commit を確認しなかったとき

confirmed commit は、確定に期限を付ける commit です。期限内に確認の commit が来なければ、機器が commit の前の設定へ戻します。RFC 6241 の既定の期限は 600 秒で、<confirm-timeout> で変えられます。

A confirmed <commit> operation MUST be reverted if a confirming commit is not issued within the timeout period (by default 600 seconds = 10 minutes).

確認の commit は、<confirmed> を付けない普通の <commit> です。

The confirming commit is a <commit> operation without the <confirmed> parameter.

Cisco のガイドも、既定の期限を 600 秒と書いています。

The confirmed commit operation will be rolled back if the commit is not issued within the timeout period. The default timeout period is 600 seconds or 10 minutes.

本ラボは <confirm-timeout> を 60 秒にして、確認の commit を送らずに待ちました。lock の条件を変えた 2 通り(A と B)を、同じセッションで続けて撮っています。

条件 A は candidate だけを lock した形です。§10.4 の lock(C)を持ったまま、candidate に confirmed-commit-trial を書き、confirmed commit を送りました。

snippet
#362
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="108">
<edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>confirmed-commit-trial</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
CSR1# show clock
*11:45:53.137 UTC Tue Sep 29 2026
snippet
#184
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="109">
<commit><confirmed/><confirm-timeout>60</confirm-timeout></commit>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="109"><ok/></rpc-reply>
snippet
CSR1# show clock
*11:45:55.755 UTC Tue Sep 29 2026

confirmed commit の直後の CLI は trial の値でした。

snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 100 bytes
!
interface Loopback0
 description confirmed-commit-trial
 ip address 10.7.1.1 255.255.255.255
end

確認の commit を送らずに待った後の CLI は、confirmed commit の前の値に戻っていました。syslog には by system の行が出ています。

snippet
CSR1# show clock
*11:47:45.468 UTC Tue Sep 29 2026
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 97 bytes
!
interface Loopback0
 description edited-in-candidate
 ip address 10.7.1.1 255.255.255.255
end
snippet
CSR1# show logging
…(省略)
*Sep 29 11:45:55.707: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:46:56.028: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:46:56.029: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by system, transaction-id 109

confirmed commit を送った時刻は、前後の show clock(11:45:53.137 と 11:45:55.755)の間です。戻した by system の行は 11:46:56.029 なので、送ってから戻るまでは 60.274〜62.892 秒の区間です。confirm-timeout の 60 秒の後、3 秒以内に戻りました。区間の中の syslog は 11:45:55.707 の %SYS-5-CONFIG_P だけで、ここから数えると 60.322 秒です。

条件 B は running も lock した形です。Cisco のガイドは、candidate を編集する手順として、running と candidate の両方を lock してから編集して commit し、両方を unlock する形を示しています。

Lock the running datastore. Lock the candidate datastore. Make modifications to the candidate configuration through edit-config RPCs with the target candidate. Commit the candidate configuration to the running configuration. Unlock the candidate and running datastores.

B は、running と candidate の両方を lock する点をこの形に合わせ、A の後に同じセッションで running も lock しました。lock の順はこの手順と逆で、§10.4 で candidate を lock した後に running を lock しています。global-lock 列は R, C です。

snippet
#158
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="112">
<lock><target><running/></target></lock>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="112"><ok/></rpc-reply>
snippet
CSR1# show netconf-yang sessions
R: Global-lock on running datastore
C: Global-lock on candidate datastore
S: Global-lock on startup datastore

Number of sessions : 1

session-id  transport    username             source-host            global-lock  
--------------------------------------------------------------------------------
22          netconf-ssh  netops               192.168.1.50           R, C         

candidate に confirmed-commit-trial-locked を書き、A と同じ confirm-timeout 60 の confirmed commit を送りました。

snippet
#369
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="113">
<edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>confirmed-commit-trial-locked</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
CSR1# show clock
*11:47:54.217 UTC Tue Sep 29 2026
snippet
#184
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="114">
<commit><confirmed/><confirm-timeout>60</confirm-timeout></commit>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="114"><ok/></rpc-reply>
snippet
CSR1# show clock
*11:47:56.764 UTC Tue Sep 29 2026
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 107 bytes
!
interface Loopback0
 description confirmed-commit-trial-locked
 ip address 10.7.1.1 255.255.255.255
end

確認の commit を送らずに待った後も、CLI は trial の値のままでした。その後に撮った syslog の最後の行は、行の時刻が B の confirmed commit の前後の show clock の間にある 11:47:56.697 の by netops の行で、その後に by system の行はありません。

snippet
CSR1# show clock
*11:49:46.407 UTC Tue Sep 29 2026
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 107 bytes
!
interface Loopback0
 description confirmed-commit-trial-locked
 ip address 10.7.1.1 255.255.255.255
end
snippet
CSR1# show logging
…(省略)
*Sep 29 11:45:55.707: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:46:56.028: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:46:56.029: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by system, transaction-id 109
*Sep 29 11:47:56.697: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by netops, transaction-id 112

この時点(11:49:46.407)は、confirmed commit を送ってから 109.643〜112.190 秒後で、confirm-timeout の 60 秒を 49.643〜52.190 秒過ぎています。

セッションは開いたまま、running の lock だけを外しました(<ok/>)。

snippet
#162
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="117">
<unlock><target><running/></target></unlock>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="117"><ok/></rpc-reply>

その後の CLI は、confirmed commit の前の値に戻っていました。syslog には 11:49:53.705 の by system の行が出ています。

snippet
CSR1# show clock
*11:49:58.099 UTC Tue Sep 29 2026
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 97 bytes
!
interface Loopback0
 description edited-in-candidate
 ip address 10.7.1.1 255.255.255.255
end
snippet
CSR1# show logging
…(省略)
*Sep 29 11:47:56.697: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by netops, transaction-id 112
*Sep 29 11:49:53.051: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:49:53.703: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:49:53.705: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by system, transaction-id 122

by system の 11:49:53.705 は、待った後の show clock(11:49:46.407)より後で、unlock の後の show clock(11:49:58.099)より前です。送ってから戻るまでは 116.941〜119.488 秒の区間になります。

B について本ラボで確かめられたのは、次のところまでです。

  • 期限を 49.643〜52.190 秒過ぎた時点(show clock 11:49:46.407)の後に撮った CLI は、まだ trial の値だった(その後に撮った syslog にも by system の行は無かった)
  • running の lock を外した後の撮影(show clock 11:49:58.099)では戻っていた

by system の行の時刻 11:49:53.705 は、待った後の show clock(11:49:46.407)より後で、unlock の後の show clock(11:49:58.099)より前です。この 2 つの show clock の間に、running の get-config と CLI(どちらも trial)・syslog の撮影・running の unlock を順に行いましたが、syslog の撮影と、unlock の RPC を機器が処理した時刻は、機器の時計で撮っていません。そのため、by system の行が unlock の前か後かは機器の時計では決まりません。待った後の syslog に by system の行が無かったことも、syslog の行が行の時刻より遅れて現れる可能性を除けないので(§10.7 の syslog には、時刻の早い行が遅い行の後に並んだ箇所があります)、その撮影の時点でまだ戻っていなかった根拠には使いません。running の lock を持っている間に戻った可能性も残り、unlock が戻りの引き金だったかは、この 1 回の観測からは決まりません。close-session はこの後(§10.6)に送ったので、戻りは close-session とは切り離せています。

A と B を並べると、以下のとおりです。

条件lock期限を過ぎた後の CLIby system の時刻confirmed commit からの時間
Acandidate だけ(C)commit の前の値(11:47:45.468)11:46:56.02960.274〜62.892 秒
Brunning も(R, C)trial のまま(11:49:46.407)11:49:53.705(running の unlock との前後は機器の時計では決まらない)116.941〜119.488 秒

A と B は同じセッション・同じ confirm-timeout 60 で撮りましたが、A と B の違いは running の lock の有無だけではありません。B は A の by system の戻りの後に、同じセッションで続けて撮っています(実行の順の違い)。なお、B の lock の順が Cisco の手順と逆だったこと(B の lock の段落)は、A と B の違いではなく、B と Cisco の手順の違いです。confirmed commit の syslog の出方も違い、行の時刻が A の区間にあるのは %SYS-5-CONFIG_P の行だけ、B の区間にあるのは %DMI-5-CONFIG_I ... by netops の行だけでした。どちらも 17.03.08a の 1 回の観測で、B が期限を過ぎても戻っていなかった原因(機器の中の実装)は確かめていません。RFC 6241 §8.4.1 の定め(上の引用と、確認されないときに戻すという次の文)と並べておきます。

If a confirming commit is not issued, the device will revert its configuration to the state prior to the issuance of the confirmed commit.

同じ §8.4.1 は、共有のデータストアでこの機能を使うときは、lock も使うべき(SHOULD)としています。lock を使わないと、他のセッションなどによる設定の変更が、意図せず変えられたり消されたりしうる、という理由です。

For shared configurations, this feature can cause other configuration changes (for example, via other NETCONF sessions) to be inadvertently altered or removed, unless the configuration locking feature is used (in other words, the lock is obtained before the <edit-config> operation is started). Therefore, it is strongly suggested that in order to use this feature with shared configuration datastores, configuration locking SHOULD also be used.

本ラボは A も B も candidate を lock してから <edit-config> を送っていて、B はそれに加えて running も lock していました。lock を勧めるこの文と、期限で戻す MUST の文を並べておきます。B が期限を過ぎても戻っていなかったことと lock との関係は、原因と同じく確かめていません。

lock そのものについては、RFC 6241 §7.5 が、lock を取った後は、このセッションが要求した以外の変更を server が防ぐ(MUST)と定めています。

When the lock is acquired, the server MUST prevent any changes to the locked resource other than those requested by this session. SNMP and CLI requests to modify the resource MUST fail with an appropriate error.

RFC 6241 §8.4.1 の戻す MUST には、lock についての例外が書かれていません。§7.5 が防ぐのは「このセッションが要求した以外の変更」で、期限で戻す変更をそこに含めるとする文も見当たりません。そのうえ §8.4.1 は、上の引用のとおり confirmed commit と lock を併せて使うことを勧めています。RFC 6241 の文面からは、lock を持っている間も期限で戻すことが想定されていると読めます。B の観測(期限を約 50 秒過ぎても trial のまま)はこの読みと食い違って見えますが、機器の中の原因と、unlock との関係は確かめていません。

Cisco の 17.3 のガイドの NETCONF の章にも、関係しうる 4 つの文があります。1 つ目は、確認されなかったときの戻しを、機器が前に commit した設定を取り出して commit し直すこと(rolls back to)と書く文です(time,by は原文のまま)。

If the commit is not confirmed within the specified amount of time,by default, the device automatically retrieves and commits (rolls back to) the previously committed configuration.

2 つ目は、commit の項の、running か candidate が別の NETCONF のセッションに lock されていると <commit> の RPC は失敗する、という文です。

If either the running or the candidate datastore is locked by another NETCONF session, the <commit> RPC will fail with an RPC error reply.

3 つ目は、lock の項の注記で、candidate の lock は Cisco IOS の config lock と running の lock に影響しない(逆も同じ)、という文です。A は candidate だけ、B は running も lock していたので、この文は A と B の違いに関わりえます。ただし、機器の中で何が lock されていたかは撮っていません。

Locking the candidate datastore does not affect the Cisco IOS config lock or the running configuration lock and vice versa.

4 つ目は、§7.2 で引いた、NETCONF の lock の RPC は configuration parser と running の設定のデータベースを lock する、という文です。B の条件(running の lock)そのものについての文ですが、これも B の原因とは書きません。

The NETCONF lock RPC locks the configuration parser and the running configuration database.

B で running と candidate の lock を持っていたのは、confirmed commit を送った同じセッション(session-id 22)です。期限で戻すときの機器の commit に 2 つ目の文(別のセッションの lock)が当てはまるかは、ガイドに書かれておらず、本ラボでも確かめていません。なお、A でも同じセッション 22 が candidate を lock していましたが、A は期限の後に戻りました。期限で戻すときの機器の commit が candidate を経由するかは分からないので、A の結果で 2 つ目の文の当てはまりを否定できたとまでは言えません。この 4 つの文も、B の原因としては書かずに並べるだけにします。

RFC 6241 §8.4.1 は、confirmed commit を送ったセッションが期限の前に終わった場合も、commit の前の状態へ戻すと定めています。

If the session issuing the confirmed commit is terminated for any reason before the confirm timeout expires, the server MUST restore the configuration to its state before the confirmed commit was issued, unless the confirmed commit also included a <persist> element.

1 回目で観測したこととして、本ラボの 1 回目(同じトポロジと投入設定で、running と candidate の両方を lock した confirmed commit)でも、期限を過ぎた時点(confirmed commit から 109.578〜112.128 秒後)の後に撮った CLI は trial のままでした。1 回目はその後、同じセッションで candidate への edit-config と discard-changes を送り、続けて unlock(candidate)・unlock(running)・close-session を送りました。close-session の直後に撮った syslog には by system の行は無く、撮影の最後に撮った syslog に 11:35:18.747 の by system の行(その前に 11:35:18.242 と 11:35:18.745 の %SYS-5-CONFIG_P)が在りました。1 回目は、待った後に送った get-config・edit-config・discard-changes・unlock・close-session を機器が処理した時刻も、close-session の直後の syslog を撮った時刻も、機器の時計で撮っていません。そのため、戻りがどの操作の後だったかも、syslog の行が行の時刻より遅れて現れたかどうかも決められません(syslog に行が無いことを、その撮影の時点でまだ戻っていなかった根拠に使わないのは、B と同じです)。2 回目の B では running の unlock だけを先に送り、candidate の unlock と close-session から切り離して撮りました。なお、1 回目は取得スクリプトの版が 2 回目と違い、手順も B と同じではありません。1 回目の confirmed commit は 1 本だけで、先に撮る A はありません。lock は Cisco の手順どおり running → candidate の順に、最初の <commit> の前から持っていました(2 回目の B は、A の後に candidate → running の順)。

10.6 discard-changes

candidate に to-be-discarded を書き、commit せずに <discard-changes> を送ると、candidate は running の値(edited-in-candidate)に戻りました。

snippet
#355
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="119">
<edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>to-be-discarded</description></interface></interfaces></config></edit-config>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="120"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>to-be-discarded</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
#136
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="121">
<discard-changes/>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="121"><ok/></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="122"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>

This operation discards any uncommitted changes by resetting the candidate configuration with the content of the running configuration.

最後に candidate の unlock と close-session を送りました(どちらも <ok/>)。close の後の CLI は edited-in-candidate でした。

snippet
#164
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="123">
<unlock><target><candidate/></target></unlock>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="123"><ok/></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="124"><ok/></rpc-reply>
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 97 bytes
!
interface Loopback0
 description edited-in-candidate
 ip address 10.7.1.1 255.255.255.255
end

10.7 candidate を有効にした状態での RESTCONF

Cisco のガイドは、RESTCONF は confirmed commit に対応しないと書いています。

RESTCONF does not support confirmed commit.

candidate を有効にしたときに RESTCONF の編集がどこへ入るかは、§15 の Cisco のガイドの NETCONF と RESTCONF の章には見当たりません。RFC 8040 §1.4 は、writable-running を持つ機器では running へ、candidate だけを持つ機器では candidate へ書き、各編集の直後に自動で commit すると定めています。

If the NETCONF server supports :writable-running, all edits to configuration nodes in {+restconf}/data are performed in the running configuration datastore.

Otherwise, if the device supports :candidate, all edits to configuration nodes in {+restconf}/data are performed in the candidate configuration datastore. The candidate MUST be automatically committed to running immediately after each successful edit.

同じ段落は続けて、candidate に在る他からの編集も、この自動の commit で一緒に確定すると書いています。

Any edits from other sources that are in the candidate datastore will also be committed.

本ラボは、原典が定めるこの挙動が CSR1 で起きるか(本ラボの場面に当てはめると、NETCONF で candidate にだけ置いた編集が、RESTCONF の編集の後の自動の commit で running に入るか)を確かめるため、次の手順で撮りました。

  1. 新しい NETCONF のセッション(session-id 25。hello に candidate:1.0 があり writable-running:1.0 が無い)で、running と candidate の Loopback0 が同じ値(edited-in-candidate)であることを確かめる
  2. lock を取らずに、candidate にだけ Loopback72 を作る
  3. running に Loopback72 が無く、candidate に在ることを確かめる
  4. NETCONF のセッションを開いたまま、RESTCONF で Loopback0 の description を PATCH する
  5. running・candidate・CLI を読む

1 の running と candidate の応答は以下のとおりで、Loopback0 の description はどちらも edited-in-candidate でした。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="102"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>edited-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>

2 と 3 の要求と応答は以下のとおりです。running の応答の <data></data> は、Loopback72 が running に無いことを示しています。

snippet
#455
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103">
<edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback72</name><description>pending-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type></interface></interfaces></config></edit-config>
</rpc>

##
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="103"><ok/></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="104"><data></data></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="105"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback72</name><description>pending-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type></interface></interfaces></data></rpc-reply>

4 の PATCH は 204 No Content でした。

snippet
WSL $ curl -sS -i -k --max-time 40 -X PATCH -K - -H 'Accept: application/yang-data+json' -H 'Content-Type: application/yang-data+json' --data-binary '{"ietf-interfaces:interface": {"name": "Loopback0", "description": "restconf-with-candidate"}}' https://172.16.1.241/restconf/data/ietf-interfaces:interfaces/interface=Loopback0
HTTP/1.1 204 No Content
Server: openresty
Date: Tue, 29 Sep 2026 11:50:21 GMT
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Last-Modified: Tue, 29 Sep 2026 11:50:20 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: "1790-682620-946146"
Pragma: no-cache

PATCH の後、candidate にだけ置いた Loopback72 が running に載り、CLI にも現れました。PATCH の値(restconf-with-candidate)は、running・candidate・CLI のどれにも入っています。

snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="106"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback72</name><description>pending-in-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="107"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>restconf-with-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="109"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>Loopback0</name><description>restconf-with-candidate</description><type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type><enabled>true</enabled><ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"><address><ip>10.7.1.1</ip><netmask>255.255.255.255</netmask></address></ipv4><ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"></ipv6></interface></interfaces></data></rpc-reply>
snippet
CSR1# show running-config interface Loopback72
Building configuration...

Current configuration : 77 bytes
!
interface Loopback72
 description pending-in-candidate
 no ip address
end
snippet
CSR1# show running-config interface Loopback0
Building configuration...

Current configuration : 101 bytes
!
interface Loopback0
 description restconf-with-candidate
 ip address 10.7.1.1 255.255.255.255
end

同じ秒の syslog には、Loopback72 の line protocol が up になった行と、NETCONF/RESTCONF による設定の行があります。

snippet
CSR1# show logging
…(省略)
*Sep 29 11:50:21.323: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback72, changed state to up
*Sep 29 11:50:21.328: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:50:21.480: %SYS-5-CONFIG_P: Configured programmatically by process iosp_vty_100001_dmiauthd_fd_182 from console as NETCONF on vty63
*Sep 29 11:50:21.329: %DMI-5-CONFIG_I: R0/0: dmiauthd: Configured from NETCONF/RESTCONF by netops, transaction-id 131

最後の 2 行は、11:50:21.480 の行の後に 11:50:21.329 の行が来ています。syslog のバッファは時刻の順に並ばないことがあるので、本節は時刻を行の順ではなく行の時刻で読んでいます。

candidate にだけ置いた編集が RESTCONF の PATCH の後に running に載ったことは、上の RFC 8040 §1.4 の定め(candidate に書き、各編集の直後に自動で commit し、candidate に在る他からの編集も一緒に確定する)と整合する観察です。1 回目で観測したこととして、1 回目でも PATCH は 204 で、その後に Loopback72 が running と CLI に載りました。

本ラボは、NETCONF のセッションが lock を取っていない状態で撮りました。RFC 8040 §1.4 は、RESTCONF が変えようとするデータストアに NETCONF のクライアントの lock が掛かっているときは、RESTCONF の編集を 409 で失敗させると定めています。この状態は撮っていません(§12)。

If a datastore that would be modified by a RESTCONF operation has an active lock from a NETCONF client, the RESTCONF edit operation MUST fail with a “409 Conflict” status-line. The error-tag value “in-use” is returned in this case.

後始末として <discard-changes> と close-session を送りました(どちらも <ok/>)。running に載った Loopback72 は、撮影の後の running-config にも残っています(§10.8)。

10.8 撮影の後の状態

撮影の後、NETCONF のセッションは残っていませんでした。

snippet
CSR1# show netconf-yang sessions
There are no active sessions

statistics の netconf-start-time は 11:45:21 で、candidate の有効化による再起動の後の %DMI-5-SYNC_COMPLETE(11:45:21.416)と同じ秒です。§4.3 の有効化の直後の値(11:43:28)とは別の値で、数え直されています。

snippet
CSR1# show netconf-yang statistics
netconf-start-time  : 2026-09-29T11:45:21+00:00
in-rpcs             : 34
in-bad-rpcs         : 1
out-rpc-errors      : 1
out-notifications   : 0
in-sessions         : 2
dropped-sessions    : 0
in-bad-hellos       : 0

in-sessions の 2 は、再起動の後に本ラボが開いた NETCONF のセッション(§10.3〜§10.6 の session-id 22 と、§10.7 の session-id 25)の数と同じです。数え方の定義は確かめていないので、並べるだけにします。

最後の running-config は以下のとおりです。最後の変更の時刻は §10.7 の PATCH と同じ秒ですが、表示は by NETCONF でした。

snippet
CSR1# show running-config
Building configuration...

Current configuration : 6400 bytes
!
! Last configuration change at 11:50:21 UTC Tue Sep 29 2026 by NETCONF
!
version 17.3
…(省略)
aaa new-model
!
!
aaa authentication login default local
aaa authorization exec default local 
…(省略)
interface Loopback0
 description restconf-with-candidate
 ip address 10.7.1.1 255.255.255.255
!
interface Loopback72
 description pending-in-candidate
 no ip address
!
…(省略)
netconf-yang
netconf-yang feature candidate-datastore
restconf
end

11. ライブラリで書く — ncclient と requests

§5〜§10 は、wire と curl の出力で要求と応答をそのまま見てきました。本ラボでは、同じ hostname をライブラリで読む最小の例も動かしました。NETCONF は ncclient 0.7.1、RESTCONF は HTTP のライブラリの requests 2.34.2 です。

この 2 つの例は、§9 の RESTCONF の撮影の後、§10 の candidate の有効化の前(§3 の Phase D)に動かしました。下の ncclient の出力の capability が 520 本なのは、そのためです(candidate を有効にした後の hello は、§10.3 のとおり 522 本でした)。

ncclient の例は以下のとおりです。接続先と資格情報は環境変数から読みます。

python
#!/usr/bin/env python3
"""NETCONF の最小例(ncclient): running の hostname を get-config で読む。

接続先と資格情報は環境変数から読む(NC_HOST / NC_USER / NC_PASS)。値をコードに書かない。
"""
import os

from ncclient import manager

FILTER = """
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
  <hostname/>
</native>
"""

with manager.connect(host=os.environ["NC_HOST"], port=830,
                     username=os.environ["NC_USER"], password=os.environ["NC_PASS"],
                     hostkey_verify=False, allow_agent=False, look_for_keys=False) as m:
    print("session-id:", m.session_id)
    print("capabilities:", len(list(m.server_capabilities)))
    reply = m.get_config(source="running", filter=("subtree", FILTER))
    print(reply.xml)
snippet
WSL $ python client_ncclient.py
session-id: 42
capabilities: 520
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:658bd884-660d-47b0-904e-d9d5d06df8b1" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><data><native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"><hostname>CSR1</hostname></native></data></rpc-reply>

利用者のコードには、hello・]]>]]> や #<長さ> … ## の framing・message-id を組み立てる行がありません。返った <rpc-reply> の message-id は urn:uuid: で始まる値でした(§6 の取得スクリプトは 101 からの連番)。ncclient の中でどう処理しているかは観測していません。RFC 6242 は、Messages の層が送るメッセージの符号化と、受けたメッセージの復号を、概念の上では SSH の Transport の層が行うと書いています。

Conceptually, the SSH Transport layer encodes messages sent by the Messages layer, and decodes messages received on the SSH channel before passing them to the Messages layer.

requests の例は以下のとおりです。コードの中のコメントにある「自己署名証明書」は、スクリプトを書いた側の前提です。RESTCONF の HTTPS がどの証明書を使ったかは、本ラボでは確かめていません(§12)。

python
#!/usr/bin/env python3
"""RESTCONF の最小例(requests): running の hostname を JSON で読む。

接続先と資格情報は環境変数から読む(NC_HOST / NC_USER / NC_PASS)。値をコードに書かない。
機器の HTTPS は自己署名証明書なので、ラボでは verify=False で検証を外している(本番では CA 証明書を指定する)。
"""
import os

import requests

url = f"https://{os.environ['NC_HOST']}/restconf/data/Cisco-IOS-XE-native:native/hostname"
r = requests.get(url, auth=(os.environ["NC_USER"], os.environ["NC_PASS"]),
                 headers={"Accept": "application/yang-data+json"}, verify=False, timeout=30)
print(r.status_code, r.reason)
print("Content-Type:", r.headers.get("Content-Type"))
print(r.text)
snippet
WSL $ python client_requests.py
200 OK
Content-Type: application/yang-data+json
{
  "Cisco-IOS-XE-native:hostname": "CSR1"
}

実行したときの標準エラーには、urllib3 の警告が出ました(ツール側のメッセージ)。

snippet
/home/chillarin/projects/study-contents/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py:1110: InsecureRequestWarning: Unverified HTTPS request is being made to host '172.16.1.241'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(

利用者のコードには、Basic 認証のヘッダを組み立てる行がありません(auth= に利用者名とパスワードを渡しているだけ)。Accept ヘッダは利用者のコードが書いています。

2 つの例・curl・取得スクリプトには、ラボのために検証を外している箇所があります。外しているのは 2 系統です。

系統外している箇所原典
TLS のサーバ証明書(RESTCONF)curl の -k・requests の verify=FalseRFC 8040 §2.3
SSH のホスト鍵(NETCONF)ncclient の hostkey_verify=False・取得スクリプトの paramikoRFC 6242 §6

RFC 8040 §2.3 は、クライアントがサーバの TLS の証明書を検証することを求めています。

The RESTCONF client MUST either (1) use X.509 certificate path validation [RFC5280] to verify the integrity of the RESTCONF server’s TLS certificate or (2) match the server’s TLS certificate with a certificate obtained by a trusted mechanism (e.g., a pinned certificate).

RFC 6242 §6 は、パスワードによる認証の情報や、設定と状態のデータを送受する前に、SSH のクライアントがサーバの身元をローカルのポリシーに従って確かめることを求めています。検証した状態での挙動は、本ラボでは撮っていません(§12)。


12. 本ラボで確かめていないこと

本節の実測は 2 回目の撮影を正とし、1 回目の撮影(§10.5・§10.7 と、下の confirmed commit の B の項)と準備段階の試行(管理アドレスを global の IF に置いた場合の項)に触れる箇所は、そのことを明記しました(§1)。以下は観測していません。

  • AAA を入れない場合の挙動。本ラボは AAA を入れた状態だけを撮りました(§4.2)
  • 有効化の前に nginx が Not Running だった理由。起動から間もない時点の 1 回の観測で、起動からの時間との関係は確かめていません(§4.1)
  • 準備完了までの時間が何で決まるか・毎回同じか。本節の区間は 1 回の観測です。datastores の応答は本ラボが置いた関門で、要求の受け付けが始まった瞬間を測ったものではありません(§4.3)
  • 502 と NETCONF の hello の受け取り失敗の内部。どのプロセスが要求を受け、どこで拒んだかは撮っていません。hello の受け取り失敗は、SSH のチャネルが閉じたのか終了の状態を受けたのかも区別していません(§4.3)。機器の側の待ち受けの表示も撮っておらず、830 と 443 の開閉は WSL からの接続試行でしか見ていません
  • 自己署名の trustpoint を作った行と、HTTPS が使った証明書。6 行を 1 回で投入したので分けられず、有効化の前から在った SLA-TrustPoint が原典の前提(trustpoint が無い)とどう関わるかも、RESTCONF の HTTPS がどの証明書を使ったかも確かめていません(§4.4)
  • NACM(NETCONF Access Control Model)。Cisco の NETCONF の章は、認証した利用者を privilege level に応じた NACM の group に入れる、group 単位のアクセス制御と説明しています。本ラボでは syslog に %DMI-5-NACM_INIT と External groups: PRIV15 が出るだけで、NACM の設定と中身は撮っていません
  • service-level ACL。Cisco の 17.3 のガイドには NETCONF と RESTCONF の service-level ACL の章がありますが、本ラボでは設定していません。その章の機能の一覧に Cloud Services Router の名前は見当たりません
  • lock が掛かっているときの他からの編集。lock は 1 つのセッションからしか取っておらず、他の NETCONF のセッションの編集が拒まれるところ(§7.2)も、NETCONF の lock の間の RESTCONF の編集(RFC 8040 §1.4 は 409 と in-use で失敗すると定める。§10.7)も撮っていません。§10.7 は candidate の lock を取らずに撮りました
  • notification・subscribe。hello に notification:1.0 の capability が載っていますが、使っていません(7-3 の範囲)
  • startup・PUT・YANG-Patch・<cancel-commit>・<persist>・kill-session。どれも使っていません
  • confirmed commit の B が期限を過ぎても戻っていなかった原因と、再現性。2 回目の 1 回の観測と、期限を過ぎた後の CLI が trial のままだった点が同じだった 1 回目の観測だけです。2 回目は戻りと running の unlock の前後が、1 回目は戻りが待った後に送ったどの操作(get-config・edit-config・discard-changes・unlock・close-session)の後だったかが、決まっていません(§10.5)
  • list を配列で表した JSON(RFC 7951 の形)での PATCH と POST。本ラボはオブジェクトの形だけを送りました(§9.2)
  • 同じフィルタでの <get-config> と <get> の比較。§7.1 の 2 つの要求はフィルタも違います
  • hello を相手の hello を待たずに送る実装での挙動。本ラボの取得スクリプトは機器の hello を受け取ってから送りました(§5.2。RFC 6241 の定めと違う順)。§11 の ncclient 0.7.1 は、導入したソースの上では相手の hello を待たずに自分の hello を送る作りで、そのセッションは通りました。ただし wire を撮っていないので、実際にどちらの hello が先に送られたかは分かりません
  • NETCONF と RESTCONF それぞれが要求を受け付け始めた時刻。datastores の 1 回目の後に送った準備前の要求は 1 回ずつです。RESTCONF の poll は最後の 200 と WSL の時計での 20 秒だけを残し、200 より前の応答コードと各回の時刻は保存していません(§4.3)
  • TLS の証明書と SSH のホスト鍵を検証した場合の挙動(§11)
  • console と terminal monitor での candidate の警告の表示(§10.2)
  • NETCONF と RESTCONF で module-set-id が違った理由(§9.7)
  • 管理アドレスを global の IF に置いた場合。本ラボは Gi1(Mgmt-vrf)だけです(撮影の前の準備段階の試行では、17.03.08a の 1 回の実測で、Mgmt-vrf の Gi1 と global の Gi2 のどちらでも 830 と 443 に接続でき、NETCONF と RESTCONF の要求の結果に差はありませんでした)
  • 17.3 以外の版。Catalyst 8000V を含め、他の版では撮っていません
  • YANG のモデルの中身(7-2 の範囲)

13. まとめ

本節では、NETCONF と RESTCONF で csr1000v 17.03.08a の設定と状態を WSL のプログラムから読み書きしました。

NETCONF と RESTCONF は、有効化の設定を投入した直後は、しばらく要求を受け付けませんでした(設定を足さずに待つと、後の撮影で通りました)。process の表示が 8 行とも Running になった後でも、datastores はエラーを返し、RESTCONF は 502、NETCONF は hello の受け取り失敗でした。datastores が応答を返すまでは、netconf-yang の起動の通知から 48.938〜70.222 秒の区間でした。RESTCONF は、datastores が応答を返した後に回した poll が、WSL の時計で 20 秒(秒に丸めた値)かかって 200 で止まったところまでしか記録しておらず(途中の回の応答と時刻は残していません)、datastores の応答を RESTCONF の準備完了と同じ時点と読むことはできません(§4.3)。

NETCONF は SSH の netconf サブシステムで hello を交わし、CSR1 は 520 本の capability と session-id を送りました。双方が base:1.1 を名乗ると、hello の後の RPC と応答は #<長さ> … ## の chunked framing で運ばれ、base:1.0 だけを名乗ると ]]>]]> の区切りが続きました(§5・§6)。<get-config> は設定を返し、状態の側(interfaces-state)をフィルタで選んだ <get> は状態の項目を返し、<edit-config> の値は CLI の running-config に現れました。candidate を有効にする前の CSR1 への、candidate を読む <get-config> は <rpc-error> でした(§7)。

RESTCONF は host-meta から root を見つけ、同じ資源を Accept で JSON と XML に分けて返しました。PATCH と DELETE は 204、POST は 201 と Location、重複した POST は 409 と data-exists、存在しない資源は 404、資格情報なしは 401 でした。409 の error-tag は RFC 8040 の中で書き方が割れていて(§4.4.1 の規定の文は resource-denied、§7.1 の例は data-exists)、本機は RFC 8040 §7.1 の例と 409 と data-exists の範囲で一致し、§4.4.1 の規定の文とは違いました(§8・§9)。

candidate を有効にすると、running へ直接は書けなくなり、<commit> で確定する形になりました。confirmed commit は、candidate だけを lock した A では期限の後 3 秒以内に戻りました。running も lock した B は、期限を約 50 秒過ぎた時点ではまだ戻っておらず、running の lock を外した後の撮影では戻っていました。どちらも 1 回の観測で、B で確かめられた範囲と決まっていないことは §10.5 にまとめました。candidate にだけ置いた編集は、RESTCONF の PATCH の後に running に載り、RFC 8040 §1.4 の定めと整合しました(§10.7)。

ncclient と requests の最小例では、framing・message-id・Basic 認証のヘッダを組み立てる行が利用者のコードに無く、ラボでは TLS の証明書と SSH のホスト鍵の検証を外していました(§11)。確かめていないことは §12 にまとめてあります。


14. 次節(7-2 YANG モデル)

本節では、NETCONF の XML の名前空間、RESTCONF の URL と JSON のメンバ名に、ietf-interfaces や Cisco-IOS-XE-native といった YANG のモジュールの名前が現れました。hello には 520 本の capability が並び、yang-library は module-set-id を返しました。本節は、これらが何を定めているかには立ち入っていません。

次節 7-2 は、NETCONF と RESTCONF が運ぶデータの形を決める YANG のデータモデルを扱います。IETF が定めるモデルと OpenConfig のモデルが中心で、本節で名前だけが出てきたモジュールが何を定めているかもそこで扱います。次節では、NETCONF / RESTCONF の中身の形を決める YANG モデルを見ていきましょう。


15. 出典

本節が参照した資料です。10 件のうち RFC 8527 は 2026 年 9 月 30 日、ほかの 9 件は 2026 年 9 月 29 日に取得しました。本文中の > の引用は、その時点のページとテキストの逐語です。

手元の機体は csr1000v 17.03.08a で、Cisco の設定ガイドは同じ IOS XE Amsterdam 17.3.x を対象としています。ガイドの出力例と本ラボの出力の差(Server: ヘッダの値など)は本文に書きました。

資料版・更新日(資料の表記)参照日URL
Programmability Configuration Guide, Cisco IOS XE Amsterdam 17.3.x — Chapter: NETCONF Protocol17.3.x・2020-07-312026-09-29https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/173/b_173_programmability_cg/configuring_yang_datamodel.html
Programmability Configuration Guide, Cisco IOS XE Amsterdam 17.3.x — Chapter: RESTCONF Protocol17.3.x・2020-07-312026-09-29https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/173/b_173_programmability_cg/restconf_protocol.html
Programmability Configuration Guide, Cisco IOS XE Amsterdam 17.3.x — Chapter: NETCONF and RESTCONF Service-Level ACLs17.3.x・2020-07-312026-09-29https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/173/b_173_programmability_cg/netconf_and_restconf_service_level_acls.html
Programmability Configuration Guide, Cisco IOS XE Amsterdam 17.3.x — Book Table of Contents17.3.x・2020-07-312026-09-29https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/prog/configuration/173/b_173_programmability_cg.html
RFC 6241 — Network Configuration Protocol (NETCONF)2011-06(RFC 4741 を廃止)2026-09-29https://www.rfc-editor.org/rfc/rfc6241.txt
RFC 6242 — Using the NETCONF Protocol over Secure Shell (SSH)2011-06(RFC 4742 を廃止)2026-09-29https://www.rfc-editor.org/rfc/rfc6242.txt
RFC 7951 — JSON Encoding of Data Modeled with YANG2016-082026-09-29https://www.rfc-editor.org/rfc/rfc7951.txt
RFC 8040 — RESTCONF Protocol2017-01(RFC 8527 で更新)2026-09-29https://www.rfc-editor.org/rfc/rfc8040.txt
RFC 8527 — RESTCONF Extensions to Support the Network Management Datastore Architecture2019-03(RFC 8040 を更新)2026-09-30https://www.rfc-editor.org/rfc/rfc8527.txt
RFC Errata Report 5761(RFC 8040 §4.4.1・Status: Rejected)報告 2019-06-24・却下 2019-10-172026-09-29https://www.rfc-editor.org/errata/eid5761