NetconfとYANGモデル (Cisco-IOS-XE-cli-rpc) で行う IOS XE Atomic Config Replace

# 0. はじめに
:::note info
この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2024 （一枚目） の 8日目として投稿しています。
:::

2017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2019年版: https://qiita.com/advent-calendar/2019/cisco
2020年版: https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2021年版: https://qiita.com/advent-calendar/2021/cisco
2021年版(2枚目): https://qiita.com/advent-calendar/2021/cisco2
2022年版(1,2): https://qiita.com/advent-calendar/2022/cisco
2023年版: https://qiita.com/advent-calendar/2023/cisco
2024年版: https://qiita.com/advent-calendar/2024/cisco

今年で8年目！そろそろ10年が見えてきましたね！（ニッコリ

# 1. これはなに？
- IOS XE / Catalyst9000 を対象にした、NetconfプロトコルとYANGモデルによるIOSコンフィグリプレース
- YANGモデルは [Cisco-IOS-XE-cli-rpc](https://github.com/YangModels/yang/blob/main/vendor/cisco/xe/17151/Cisco-IOS-XE-cli-rpc.yang) を使用
- コンフィグの整合性確認（Validation）および、コンフィグにエラーがあればリプレースを行わない機能をアトミックコンフィグレーションリプレース（Atomic Config Replace、通称 ACR）という
- エンタープライズの世界でも、そろそろコンフィグを、Netconfでモダンに送りたい
- [YANG Suite](https://github.com/CiscoDevNet/yangsuite) みんな使ってますよね！？
- Python スクリプトや Ansible Playbookの自動生成もできるよ

## IOSとコンフィグをやりとりするイメージ図
- IOS XE Netconf データストアのイメージ
![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157325/aa62380e-9a68-7dc3-01bd-9f2198e33cd4.png)
*[DEVNET-2385](https://www.ciscolive.com/c/dam/r/ciscolive/apjc/docs/2024/pdf/DEVNET-2385.pdf)から抜粋・加工

# 2. デモ

## デモその1 - YANG Suite
デモの録画（音が出ますのでご注意ください！）- 4min39sec
※直リンクは[こちら](https://www.youtube.com/watch?v=C6Kk9mPj_wo
)です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/C6Kk9mPj_wo?si=p6aapvIrmV4mIePG" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

### 動画のタイムライン
- 0:13 YANG SuiteとCatalyst9300の疎通確認
- 0:41 IOS-XE-cli-rpcによる設定の取得
- 1:35 設定ミスを仕込む（デモ用）
- 1:59 リプレース準備（Full-Replace）
- 2:52 Validationその１
- 3:35 Validationその２
- 3:55 設定修正してValidation & リプレース成功
- 4:33 Pythonスクリプトの出力

## デモその2 - Python
デモの録画（音が出ますのでご注意ください！）- 1min39sec
※直リンクは[こちら](
)です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/_mUJ_F24pWw?si=wp3q7sSsXa6kZrCa" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 3. 何がうれしい？
- IOS XEデバイスのコンフィグ入れ替えの自動化を、力技（~~流し込み~~）ではなく、モダンな方法で実現できる
- コンフィグエラーの早期検出が可能になる（Validation）
- 問題が発生した場合には、以前の設定に戻る（Rollback）
- 文法、意味、および依存関係の検証が自動で行われる（Syntax, Semantic and Dependency Verification）

# 4. YANG Suite 
- YANG Suiteは、YANGモデル（RFC 6020、RFC 7950）やNETCONF（RFC 6241）などの関連技術に関連するツールのセット
- 様々なYANGアプリケーションプラグインを使用することができるモジュラーインフラストラクチャ
- Djangoでできている
- この二つを見れば、すぐ実行できます [1. インストール](https://github.com/CiscoDevNet/yangsuite) [2. チュートリアル](https://developer.cisco.com/learning/labs/intro-yangsuite/introduction-launching-yang-suite/)
- 参考記事1）[Yangsuite の解説](https://qiita.com/masanobu48154/items/1fcc5abfecdd7adc2bb9)
- 参考記事2）Yang tools 前身の [Yang Explorer]() （2017年！）

## YANG Suite > Netconf > Cisco-IOS-XE-cli-rpc
- YANG Suiteの当該部分
![yangsuite-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157325/cea57dc7-494c-1b4a-527f-f9cdef3b4ca4.png)


# 5. Atomic Config Replace (ACR）

:::note info
Atomic（アトミック）= 一貫性と全体性を持つ操作
「atomic」とは、何かを「分割できない最小単位」として扱うことを意味します。特に、コンフィグレーションやトランザクションにおいて、「全体が成功するか、全体が失敗するか」のいずれかであることを保証する概念を指します。途中で部分的に失敗することは許されず、システムの一貫性を保つことが目的です。
:::

- 完全または部分的なコンフィグの置換（フルリプレースと選択的リプレース）
- フルリプレースは宣言的アプローチであり、設定の"差分"を計算し、IOS XEによって適用する
- 選択的リプレースは命令的アプローチであり、更新するCLI/設定だけを送る
- XMLまたはJSONペイロードとしてデバイスに全体の設定を送信
- 従来のCLIをCLI-RPC YANGモジュールを介してサポート
- RFC 6241を実装し、Candidate設定機能もサポート、コミットオプションあり
- エラーと構文チェックにより、多段階のコミットプロセスが可能になる
- パケット処理に影響を与えない非破壊的な適用ができる

:::note warn
正式リリース前なので、期待される仕様も含みます
:::



# 6. デモのポイント
## 6.1 環境 - Catalyst9300
Yangsuite関連の設定は、[チュートリアル](https://developer.cisco.com/learning/labs/intro-yangsuite/introduction-launching-yang-suite/)を参考に、YANGモジュール、リポジトリ、デバイスプロファイルの設定を行います。

```
Cat9300TMT02#show version | i IOSXE
Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.15.1, RELEASE SOFTWARE (fc4)
*    1 41    C9300-24P          17.15.01          CAT9K_IOSXE           INSTALL

Cat9300TMT02#show run | i netconf-yang
netconf-yang
```

:::note info
Catalyst 9200-Lでも動いたよ♪
:::

## 6.2 流れ

### 6.2.1 動作中のコンフィグを取得
RPC Operationが独特ですね

```xml
<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:b3604433-b7e3-43e6-ad13-75d2d3a8ff1b">
  <get-modelled-config-clis xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc"/>
</nc:rpc>
```

### 6.2.2 リプレース用のコンフィグをNetconfで投げ込む
Validation確認用に、いくつか欠損したコンフィグを投入します

```xml
<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:70990aa6-e077-4f94-bcf7-6873e6413ee1"><config-ios-cli-trans xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc">
    <clis>
    version 17.15
    memory free low-watermark processor 135169
    service password-encryption
    service timestamps debug datetime msec
    service timestamps log datetime msec show-timezone year
    service call-home
    ### 省略（ここに丸ごとコンフィグを入れる） ###
    </clis>
    <operation>full-replace</operation>
  </config-ios-cli-trans>
  </nc:rpc
```

### 6.2.3 Syntax errorの検知
Validationに失敗しているため、適用エラーを受信しました！二回

```xml:ホスト名部分のsyntax errorを検知（Error: on line 11）
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:70990aa6-e077-4f94-bcf7-6873e6413ee1"><rpc-error>
<error-type>application</error-type>
<error-tag>invalid-value</error-tag>
<error-severity>error</error-severity>
<error-path xmlns:cli-ios-xe-rpc="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
    /nc:rpc/cli-ios-xe-rpc:config-ios-cli-trans
  </error-path><error-message xml:lang="en">inconsistent value: Failed to load commands via transaction: external error, 
syntax error: unknown command
Error: on line 11: hostna Cat9300TMT02-TEST <=== これ
</error-message><error-info><bad-element>config-ios-cli-trans</bad-element>
</error-info>
</rpc-error>
</rpc-reply>
```

```xml:sxp部分のsyntax errorを検知（Error: on line 1249）
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:3f65f51b-62fb-48c3-9718-15ce5fae7e69"><rpc-error>
<error-type>application</error-type>
<error-tag>invalid-value</error-tag>
<error-severity>error</error-severity>
<error-path xmlns:cli-ios-xe-rpc="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
    /nc:rpc/cli-ios-xe-rpc:config-ios-cli-trans
  </error-path><error-message xml:lang="en">inconsistent value: Failed to load commands via transaction: external error, cts
syntax error: element does not exist
Error: on line 1249: cts s enable <=== これ
</error-message><error-info><bad-element>config-ios-cli-trans</bad-element>
</error-info>
</rpc-error>
</rpc-reply>
```

### 6.2.4 コンフィグ変更が成功したときのRPC-Reply
```xml
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:714ebce3-1cf6-4052-ba77-888d84080104"><result xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc">Configuration applied successfully</result>
</rpc-reply>
```


# 7. YANG Suiteが生成したPythonスクリプト
YANG Suiteで行ったオペレーション（YANG model Cisco-IOS-XE-cli-rpcを使ったコンフィグリプレース）のPythonスクリプトが生成できました。あとは適当にいじって使えると思うので貼っておきます。便利ですね！

```python
#! /usr/bin/env python
import traceback
import lxml.etree as et
from argparse import ArgumentParser
from ncclient import manager
from ncclient.operations import RPCError

payload = [
'''
<config-ios-cli-trans xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-cli-rpc">
    <clis>

    version 17.15
    memory free low-watermark processor 135169
    service password-encryption
    service timestamps debug datetime msec
    service timestamps log datetime msec show-timezone year
    service call-home
    ### 省略（ここに丸ごとコンフィグを入れる） ###
    
    </clis>
    <operation>full-replace</operation>
  </config-ios-cli-trans>
  
''',
]

if __name__ == '__main__':

    parser = ArgumentParser(description='Usage:')

    # script arguments
    parser.add_argument('-a', '--host', type=str, required=True,
                        help="Device IP address or Hostname")
    parser.add_argument('-u', '--username', type=str, required=True,
                        help="Device Username (netconf agent username)")
    parser.add_argument('-p', '--password', type=str, required=True,
                        help="Device Password (netconf agent password)")
    parser.add_argument('--port', type=int, default=830,
                        help="Netconf agent port")
    args = parser.parse_args()

    # connect to netconf agent
    with manager.connect(host=args.host,
                         port=args.port,
                         username=args.username,
                         password=args.password,
                         timeout=90,
                         hostkey_verify=False,
                         device_params={'name': 'csr'}) as m:

        # execute netconf operation
        for rpc in payload:
            try:
                response = m.dispatch(et.fromstring(rpc))
                data = response.xml
            except RPCError as e:
                data = e.xml
                pass
            except Exception as e:
                traceback.print_exc()
                exit(1)

            # beautify output
            if et.iselement(data):
                data = et.tostring(data, pretty_print=True).decode()

            try:
                out = et.tostring(
                    et.fromstring(data.encode('utf-8')),
                    pretty_print=True
                ).decode()
            except Exception as e:
                traceback.print_exc()
                exit(1)

            print(out)

```

# 8. まとめと今後の展開

NetconfとYANGモデルを使ってIOSコンフィグを投入するやり方の一つとして、意外と実用的ではないかと思いました。

以前に、以下のようなスクリプトでデモをしていたことがありますが、コンフィグのパラメータをすべてモデルに落とし込むのは大変だなぁと思っていました。代替案として、Cisco-IOS-XE-cli-rpcを使えば丸ごとぶち込めるし、Validationもうまくうごき、candidateからのcommitまで含めてオペレーションに落とし込めば、エンタープライズネットワークの環境のモダン化にコスパ的に貢献できる可能性を感じました。

共感いただいたみなさま、ぜひ一緒に~~遊び~~楽しみながらエンジニアライフを送りましょう〜！

- ACR部分はまだ開発中であるらしいが CiscoLive 2024 Melborneで[セッションとDemo](https://www.ciscolive.com/c/dam/r/ciscolive/apjc/docs/2024/pdf/DEVNET-2385.pdf)が行われた
- IOS XE 17.15.1でいろいろ遊べる
- Netconfを使ったコンフィグリプレース部分だけでも有益そう
- 今後の正式リリースを楽しみに待ちたいね

~~~python: 以前のデモ - これはこれで有り
from ncclient import manager
import xmltodict
from env_lab import device  # env_lab.pyからdeviceをインポート

# ユーザーに新しいホスト名を入力させる
new_hostname = input("Enter the new hostname: ")

# ホスト名を変更するためのコンフィグデータ
config_data = f"""
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
    <hostname>{new_hostname}</hostname>
  </native>
</config>
"""

try:
    # Netconfセッションを確立し、コンフィグを編集・コミット
    with manager.connect(**device) as m:
        print("Netconf session established successfully")
        
        # candidateデータストアにコンフィグを適用
        response = m.edit_config(target="candidate", config=config_data)
        print("Configuration applied to candidate datastore")
        print(xmltodict.parse(response.xml))
        
        # コミット確認プロンプト
        confirm_commit = input("Do you want to commit the changes? (y/n): ").strip().lower()
        
        if confirm_commit == 'y':
            # コンフィグをコミット
            response = m.commit()
            print("Configuration committed successfully")
            print(xmltodict.parse(response.xml))
        else:
            print("Configuration commit aborted")
            
except Exception as e:
    print(f"Failed to apply configuration: {e}") 
~~~

みなさま、素敵なクリスマスをお過ごしください。最後まで読んでいただき、どうもありがとうございます。

# 9. 参考
- [DEVNET-2385](https://www.ciscolive.com/c/dam/r/ciscolive/apjc/docs/2024/pdf/DEVNET-2385.pdf)
- [Cisco IOS XE Automation at Cisco Live APJC 2024](https://blogs.cisco.com/developer/cisco-ios-xe-automation-at-cisco-live-apjc-2024)
- [YANG Suite](https://github.com/CiscoDevNet/yangsuite)
- [DevNet Learning Labs - Introduction to Model Driven Programmability (ex: NETCONF/YANG)](https://developer.cisco.com/learning/tracks/EN-Networking-v0/)


# 免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。