YANGモデルの理解のためのNSOカスタムサービスパッケージの追加デモ(2/2)

#初めに
YANGはネットワークデバイスのコンフィグをモデル化するのに適した言語とされてますが、CLIに慣れているので私には取っつきにくいものでした。NSOのサービスパッケージを作成する過程でYANGモデルを自作すれば理解しやすかったので、備忘録的に記事に残そうと思いました。

2部構成で1部(YANGの概要、説明)は[こちら](https://qiita.com/masanobu48154/items/8bdf6bbcecf4be93540f)です。

[YANGモデルの理解のためのNSOカスタムサービスパッケージの追加デモ(1/2)](https://qiita.com/masanobu48154/items/8bdf6bbcecf4be93540f)

DevNetのLearning Moduleの「NSO Basics for Network Automation」を見ればもっと詳しく記載されてます。

[-NSO Basics for Network Automation](https://developer.cisco.com/learning/modules/nso-basics)

#NSOにおけるYANG
NSOの概要については、Qiitaにも大変参考になる記事がたくさんあるので割愛します。

NSOには、デバイスマネージャーとサービスマネージャーという2つの主要コンポーネントがあります。デバイスマネージャは、Interfaceや、OSPF、VLANなど個々のネットワークデバイスの設定を管理し、サービスマネージャは、MP-BGPや、Layer 3 MPLS VPNなど、ネットワーク全体のフィーチャーを抽象化し管理します。
このデバイスマネージャーとサービスマネージャーがYANGデータモデリングに大きく依存しています。
その他にも、NSOサーバー構成、ユーザーが追加するカスタムデータ、さらにはシステムアラームなど、NSOはシステム内のすべてのデータをYANGでモデル化しており、CDBのデータにアクセスし保存する方法が統一されています。


#デモの目的、概要、手順
今回は、YANGモデルの構成を理解することを目的として、NSOにカスタムサービスパッケージを追加していきたいと思います。

<dl>
  <dt>デモ環境</dt>
  <dd>DevNet Sandbox 『Cisco Network Services Orchestrator』</dd>
</dl>
<dl>
 <dt>内容</dt>
 <dd>Sandbox内のCML2で構築したL3VPN(SR-MPLS)に新たなサイトを追加するカスタムサービスパッケージを追加し、サイト追加に必要なコンフィグデータをYANGでモデリング</dd>
</dl>


<details>
  <summary>(参考)CML2 core-rtr01へのL3VPN(SR-MPLS)追加コンフィグ</summary>
  <div>

```shell:core-rtr01.cfg
vrf A
 address-family ipv4 unicast
  import route-target
   65432:111
  !
  export route-target
   65432:111
  !
 !
!
vrf B
 address-family ipv4 unicast
  import route-target
   65432:222
  !
  export route-target
   65432:222
  !
 !
!
interface loopback0
 ipv4 address 172.16.252.101 255.255.255.255
 no shutdown
!
interface Loopback111
 vrf A
 ipv4 address 1.1.1.1 255.255.255.255
 no shutdown
!
interface Loopback222
 vrf B
 ipv4 address 2.2.2.1 255.255.255.255
 no shutdown
!
router bgp 65432
 bgp router-id 172.16.252.101
 bgp log neighbor changes detail
 address-family vpnv4 unicast
 !
 neighbor 172.16.252.102
  remote-as 65432
  update-source Loopback0
  address-family vpnv4 unicast
  !
 !
 vrf A
  rd 65432:111
  address-family ipv4 unicast
   !
   redistribute connected
  !
 !
 vrf B
  rd 65432:222
  address-family ipv4 unicast
   !
   redistribute connected
  !
 !
!
segment-routing
 global-block 16000 18000
!
router ospf 1
 segment-routing mpls
 area 0
  segment-routing mpls
  interface Loopback0
   prefix-sid index 111
```

  </div>
</details>

<details>
  <summary>(参考)CML2 core-rtr02へのL3VPN(SR-MPLS)追加コンフィグ</summary>
  <div>

```shell:core-rtr02.cfg
vrf A
 address-family ipv4 unicast
  import route-target
   65432:111
  !
  export route-target
   65432:111
  !
 !
!
vrf B
 address-family ipv4 unicast
  import route-target
   65432:222
  !
  export route-target
   65432:222
  !
 !
!
interface loopback0
 ipv4 address 172.16.252.102 255.255.255.255
 no shutdown
!
interface Loopback111
 vrf A
 ipv4 address 1.1.1.2 255.255.255.255
 no shutdown
!
interface Loopback222
 vrf B
 ipv4 address 2.2.2.2 255.255.255.255
 no shutdown
!
router bgp 65432
 bgp router-id 172.16.252.102
 bgp log neighbor changes detail
 address-family vpnv4 unicast
 !
 neighbor 172.16.252.101
  remote-as 65432
  update-source Loopback0
  address-family vpnv4 unicast
  !
 !
 vrf A
  rd 65432:111
  address-family ipv4 unicast
   !
   redistribute connected
  !
 !
 vrf B
  rd 65432:222
  address-family ipv4 unicast
   !
   redistribute connected
  !
 !
!
segment-routing
 global-block 16000 18000
!
router ospf 1
 segment-routing mpls
 area 0
  segment-routing mpls
  interface Loopback0
   prefix-sid index 222
```
  </div>
</details>

> 今回はCML2の仮想デバイスに設定を反映するところまではいきませんが、新たなサイトを追加する設定項目の参考までに、CML2でL3VPN(SR-MPLS)を構築するためのコンフィグを掲載しました。


<dl>
 <dt>手順</dt>
 <dd>⓪事前準備<br>
①サービスパッケージの作成<br>
②moduleの定義<br>
③leaf、leaf-list、の定義<br>
④containerの定義<br>
⑤listの定義<br>
 </dd>
</dl>

#事前準備

<dl>
  <dt>1. デザイン</dt>
  <dd>新たなサイトを追加するには、①設定を追加するデバイス、②VRFの追加、③VRFに所属するLoopbackアドレスの追加、④その他、が必要になります。
　</dd>
</dl>

①設定を追加するデバイス

|Config|Node|Node Name|Type|
|:----------:|:-----------:|:------------:|:------------:|
|device|値は一つなので<b>leaf</b>|<b>device</b>|NSOに登録したデバイスから参照する<b>leafref</b>|


②VRFの追加
VRFの追加については同じ性質なので、containerにまとめます。
必要なconfigの設定値を適切なnodeに定義します。

|Config|Node|Node Name|type|
|:----------:|:-----------:|:------------:|:------------:|
|-|<b>container</b>|<b>vrf</b>|-|
|vrf-definition|値は一つなので<b>leaf</b>|<b>vrf-name</b>|<b>string</b>|
|route-distinguisher|値は一つなので<b>leaf</b>|<b>rd</b>|regexでパターンを制限した<b>string</b>|
|route-target export|値は一つなので<b>leaf</b>|<b>route-target-export</b>|regexでパターンを制限した<b>string</b>|
|route-target import|値が複数の可能性もあるので<br><b>leaf-list</b>|<b>route-target-import</b>|regexでパターンを制限した<b>string</b>|




③VRFに所属するLoopbackアドレスの追加
Loopbackの追加については同じ性質なので、containerにまとめます。
必要なconfigの設定値を適切なnodeに定義します。

|Config|Node|Node Name|Type|
|:----------:|:-----------:|:------------:|:------------:|
|-|<b>container</b>|<b>loopabck</b>|-|
|Loopback番号|値は一つなので<b>leaf</b>|<b>loopback-number</b>|<b>uint16</b>|
|Loopbackアドレス|値は一つなので<b>leaf</b>|<b>loopback-ip</b>|ietf-inet-typesからインポートした<b>ipv4-address</b>|


④その他

全てのnodeをまとめるため、__container__を作成します。
deviceごとにpolicyを作って設定を反映させたいので、keyとなるpolicy名のleafと、__list__を定義し、インスタンスができるようにします。

|Config|Node|Node Name|Type|
|:----------:|:-----------:|:------------:|:------------:|
|-|<b>container</b>|<b>vpn</b>|-|
|-|<b>list</b>|<b>vpn-policy</b>|-|
|listのキーとなるPolicy名|値は一つなので<b>leaf</b>|<b>policy-name</b>|<b>string</b>|
|Policyの説明|値は一つなので<b>leaf</b>|<b>description</b>|lengthで長さ、regexでパターンを制限した<b>string</b>|
|保守担当者<br>Maintainer|値が複数の可能性もあるので<b>leaf-list</b>|<b>maintainer-members</b>|他で定義したidentity値を参照するための<b>identityref</b>|

<dl>
  <dt>2. DevNet Sandbox を予約</dt>
  <dd>『Cisco Network Services Orchestrator』を予約します。Sandboxの予約の仕方については、下記の記事が大変参考になります。
　</dd>
</dl>

[- 『DevNet Sandbox を使って pyATS/XPRESSO を CML2 と始めよう』](https://qiita.com/tahigash/items/753cffdbf6380afa823f)

![スクリーンショット 2021-10-14 11.12.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1834983/caa103cc-1d82-9f3e-fb09-6e69d7b3dbd6.png)

Sandboxで『Cisco Network Services Orchestrator』がアクティブになれば環境の準備は完了です。

#サービスパッケージの作成
今回はNSOのインスタンスが起動済みで、CML2内の仮装デバイスを全て登録しコンフィグもsync済みのNSOを利用します。
NSO(10.10.20.49)にSSHで接続します。usernameは`developer`、passwordは`C1sco12345`でログインできます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell:
(py3venv) [developer@devbox ~]$ ssh -l developer 10.10.20.49
Warning: Permanently added '10.10.20.49' (ECDSA) to the list of known hosts.
developer@10.10.20.49's password: 
Last login: Thu Oct 14 22:34:04 2021 from 192.168.254.11
[developer@nso ~]$ 
```

  </div>
</details>

NSOはYANGファイルをそのままの形式では使用できないため、最初にコンパイルする必要があります。コンパイルにはMakefileなどが必要になるので、YANGモデルを作成する前に、テンプレートとなるYANGファイルや、コンパイルに必要なファイルを含んだサービスパッケージを`ncs-make-package`コマンドで作成する必要があります。
サービスパッケージは所定のディレクトリに展開する必要があるので、コマンドは`/var/opt/ncs/packages/`に移動してから入力します。

<details>
  <summary>アウトプット</summary>
  <div>

```shell:
[developer@nso ~]$ cd /var/opt/ncs/packages/
[developer@nso packages]$ ncs-make-package --service-skeleton template add-vpn-service
```

  </div>
</details>

> `ncs-make-package`コマンドによって、`add-vpn-service/`ディレクトリが作成されました。
`add-vpn-service/src/`ディレクトリにコンパイルが必要なすべてのファイルとMakefileが配置されています。 Makefileは、`make`コマンドによってソースファイルをビルドする時に必要となります。`./src/yang/`配下にあるすべてのYANGファイルを検索し、それらをNSO互換（バイナリ）形式にコンパイルされます。


`add-vpn-service/src/yang`ディレクトリにテンプレートとなるYANGファイルが作成されますが、今回は自作するので削除します。
また、`add-vpn-service/templates/`ディレクトリにあるコンフィグテンプレートXMLファイルも削除します。 

<details>
  <summary>アウトプット</summary>
  <div>

```shell:
[developer@nso packages]$ rm add-vpn-service/src/yang/add-vpn-service.yang
[developer@nso packages]$ rm add-vpn-service/templates/add-vpn-service-template.xml
```

  </div>
</details>

これでYANGファイルの作成の準備ができました。

#moduleの定義
YANGモmoduleには色々なルールがありますが、今回は最小限のmoduleを定義します。
まず、`touch`でYANGファイルを作成し、それにvimでmoduleを記述します。

<details>
  <summary>アウトプット</summary>
  <div>

```shell:
[developer@nso packages]$ cd add-vpn-service/src/yang/
[developer@nso yang]$ touch add-vpn-service.yang
[developer@nso yang]$ vim add-vpn-service.yang 
```

  </div>
</details>

```yang:
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;
}
```
> セミコロン(";") はその時点でステートメントを完了しますが、中括弧("{" and "}")を使用すると、このステートメントにサブステートメントを追加できます。 この例では、`namespace`と`prefix`はmoduleのサブステートメントです。 `namespace`と`prefix`はこのmoduleを一意に識別するもので、必須です。`namespace`はURI（Uniform Resource Identifier）であり、XML形式でデータをエンコードするときに使用されますが、`prefix`はYANGからこのmoduleを参照するために使用できます。

`pyang`コマンドを使えばYANGの構文が正しいかどうか確かめることができます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
[developer@nso yang]$ pyang --strict add-vpn-service.yang 
[developer@nso yang]$ 
```
  </div>
</details>

> 何もエラーが出なければ正しいです。

moduleには下記のようなステートメントを追加できます。

```yang
  organization
    "EXAMPLE SYSTEMS";

  contact
    "EXAMPLE SYSTEMS
     Customer Service
     E-mail: example@example.com";

  description
    "L3VPN YANG Model.";

  revision 2021-10-15 {
    description
      "Initial revision";
  }
```

> - `organization`:moduleを作成した組織
- `contact`:module担当の連絡先
- `description`:moduleの説明文
- `revision`:バージョン管理
<br>YANGにおいては、改行はステートメントを終了しません。あくまでも、セミコロン(";")か、中括弧("{" and "}")で終了します。

<details>
  <summary>add-vpn-service.yang - クリック</summary>
  <div>

```yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  organization
    "EXAMPLE SYSTEMS";

  contact
    "EXAMPLE SYSTEMS
     Customer Service
     E-mail: example@example.com";

  description
    "L3VPN YANG Model.";

  revision 2021-10-15 {
    description
      "Initial revision";
  }
}
```
  </div>
</details>

moduleを定義できたので、`make`でコンパイルします。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
[developer@nso yang]$ make -C ../
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' に入ります
mkdir -p ../load-dir
/opt/ncs/current/bin/ncsc  `ls add-vpn-service-ann.yang  > /dev/null 2>&1 && echo "-a add-vpn-service-ann.yang"` \
              -c -o ../load-dir/add-vpn-service.fxs yang/add-vpn-service.yang
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' から出ます
[developer@nso yang]$ 
```

  </div>
</details>

> エラーが出なければコンパイルは成功です。

#leaf、leaf-listの定義
まず、先ほどのYANGファイルの続きにleaf、leaf-listを定義します。

####leaf device

```yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  import tailf-ncs {
    prefix ncs;
  }

・・・

  leaf device {
    type leafref {
      path "/ncs:devices/ncs:device/ncs:name";
    }
    description "Configured Device Name";
  }
```
> __type__は`leafref`でNSOのCDBに登録されたデバイスリストから選択できるようにします。このように他のYANGモジュールで定義された__type__や、__node__をインポートすることができます。`import`ステートメントでインポートするモジュールの名前と、`prefix`を定義します。`import`ステートメントは`module`の最初に定義します。
__device__は任意につけたleafの名前です。
全てのnodeに`description`を記述することが望ましいです。

####leaf vrf-name

```yang
  leaf vrf-name {
    type string;
    description "VRF Name";
  }
```
> __type__は`string`にします。

####leaf rd、leaf route-target-export

```yang
  leaf rd {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
    description "Route Distinguisher and Route target";
  }

  leaf route-target-export {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
    description "Route Target Export";
  }
```
> __type__を`string`にし、regexで`pattern`を指定します。__leaf rd__、__leaf route-target-export__どちらも、nnnnn:nnnnnにしたいので、`pattern "[0-9]{1,5}:[0-9]{1,5}"`とします。また、既にデバイスに設定されている__"65432:111"__と、__"65432:222"__を入力不要で選択できるようにしたいので、__type__の`enumeration`も適用します。このように2つの__type__を適用するには、__type__`union`でグルーピングします。

__leaf rd__、__leaf route-target-export__を作成しましたが、どちらもビルトインの__type__そのままではなく、カスタマイズした同じ__type__を適用しています。後に出てくる__leaf route-target-import__も同じ__type__を適用するので、`revision`の直下に`typedef`としてあらかじめ定義しておき、nodeから参照できるようにします。

```yang
・・・
  revision 2021-10-15 {
    description
      "Initial revision";
  }

  typedef rdrt-type {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
  }

・・・

  leaf rd {
    type rdrt-type;
    description "Route Distinguisher and Route target";
  }

  leaf route-target-export {
    type rdrt-type;
    description "Route Target Export";
  }
```

####leaf-list route-target-import

```yang
  leaf-list route-target-import {
    type rdrt-type;
    description "Route Target Export";
  }
```
> __type__は__leaf rd__、__leaf route-target-export__と同じなので、`typedef`で定義した`rdrt-type`にを参照します。route-target-importは複数の可能性があるので、__leaf-list__で定義します。

####leaf loopback-number

```yang
  leaf loopback-number {
    type uint16;
    description "Loopback Interface Number";
  }
```
> __type__は`uint16`にします。


####leaf loopback-ip

```yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  import ietf-inet-types {
    prefix inet;
  }

・・・

  leaf loopback-ip {
    type inet:ipv4-address;
    description "Loopback IPv4 Address.";
  }
```
> __type__は、ietfで定義されたietf-inet-typesモジュールをインポートして、その中で定義されているipv4-addressタイプを使用します。ietf-inet-typesには、アドレスやその他のインターネット関連のオブジェクトや機能について、YANGデータタイプのグループが事前定義されています。

####leaf maintainer-members

```yang

・・・

  import maintainers-base {
    prefix maintainers;
  }

・・・

  identity IETSUNA {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }
  identity TSUNATOSHI {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }

・・・

  leaf-list maintainer-members {
    type identityref {
      base maintainers:base-maintainers;
    }
    description "Maintainers of Customer";
  }
```

> __maintainer-members__は、このVPNサービスの保守担当者のリストです。保守担当者は他のモジュールでも参照される共通のものなので、独立したモジュールで定義してそれをインポートすることにします。`identityref`ステートメントを使えば、他のモジュールで定義したidentityをインポートすることができます。また、`identity`ステートメントでローカルモジュールにidentityを追加することもできます。今回は、IEYASU、HIDETADA、IEMITSUを__maintainers-base.yang__で定義しておき、IETSUNA、TSUNATOSHIを__ローカル__で追加しています。
__identity__は__enumeration__のように入力できるオプションを制限することができます。

インポートされる__identity__を定義するモジュール「maintainers-base.yang」を「add-vpn-service.yang」と同じディレクトリに作成します。

<details>
  <summary>maintainers-base.yang - クリック</summary>
  <div>

```yang:maintainers-base.yang
module maintainers-base {
  namespace "http://example.com/us/yang/maintainers-base";
  prefix base;
  description "The Base Modele For The maintainers.";

  identity base-maintainers {
    description "Cutom identity for the base protocol";
  }
  identity IEYASU {
    base base-maintainers;
    description "The Member of Maintainers";
  }
  identity HIDETADA {
    base base-maintainers;
    description "The Member of Maintainers";
  }
  identity IEMITSU {
    base base-maintainers;
    description "The Member of Maintainers";
  }
}
```
  </div>
</details>


<details>
  <summary>add-vpn-service.yang - クリック</summary>
  <div>

```yang:add-vpn-service.yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  import ietf-inet-types {
    prefix inet;
  }

  import tailf-ncs {
    prefix ncs;
  }

  import maintainers-base {
    prefix maintainers;
  }

  organization
    "EXAMPLE SYSTEMS";

  contact
    "EXAMPLE SYSTEMS
     Customer Service
     E-mail: example@example.com";

  description
    "L3VPN YANG Model.";

  revision 2021-10-15 {
    description
      "Initial revision";
  }

  typedef rdrt-type {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
  }

  identity IETSUNA {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }
  identity TSUNATOSHI {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }

  leaf device {
    type leafref {
      path "/ncs:devices/ncs:device/ncs:name";
    }
    description "Configured Device Name";
  }

  leaf vrf-name {
    type string;
    description "VRF Name";
  }

  leaf rd {
    type rdrt-type;
    description "Route Distinguisher and Route target";
  }

  leaf route-target-export {
    type rdrt-type;
    description "Route Target Export";
  }


  leaf-list route-target-import {
    type rdrt-type;
    description "Route Target Export";
  }

  leaf loopback-number {
    type uint16;
    description "Loopback Interface Number";
  }

  leaf loopback-ip {
    type inet:ipv4-address;
    description "Loopback IPv4 Address.";
  }

  leaf-list maintainer-members {
    type identityref {
      base maintainers:base-maintainers;
    }
    description "Maintainers of Customer";
  }
}
```

  </div>
</details>


leaf、leaf-listを定義できたので、`make`でコンパイルします。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
[developer@nso yang]$ YANGPATH="./yang" make -C ../
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' に入ります
/opt/ncs/current/bin/ncsc  `ls add-vpn-service-ann.yang  > /dev/null 2>&1 && echo "-a add-vpn-service-ann.yang"` \
             --yangpath ./yang -c -o ../load-dir/add-vpn-service.fxs yang/add-vpn-service.yang
/opt/ncs/current/bin/ncsc  `ls maintainers-base-ann.yang  > /dev/null 2>&1 && echo "-a maintainers-base-ann.yang"` \
             --yangpath ./yang -c -o ../load-dir/maintainers-base.fxs yang/maintainers-base.yang
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' から出ます
[developer@nso yang]$ 
```
  </div>
</details>

> YANGPATH = "./yang"の部分は、必要なモジュールがないかYANGフォルダーも検索するようにビルドシステムに指示します。これがないと、システムがモジュールmaintainers-baseを見つけられないというエラーが発生する場合があります。

NSO cliにdeveloperユーザーで接続します。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
[developer@nso src]$ ncs_cli -C -u developer

User developer last logged in 2021-10-17T20:50:34.086471-00:00, to nso, from 10.10.20.49 using rest-https
developer connected from 192.168.254.11 using ssh on nso
developer@ncs# 
```

  </div>
</details>

一つのパッケージに変更を適用した後でも、オペレーションモードの`packages reload`コマンドですべてのパッケージをリロードする必要があります。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs# packages reload 

>>> System upgrade is starting.
>>> Sessions in configure mode must exit to operational mode.
>>> No configuration changes can be performed until upgrade has completed.
>>> System upgrade has completed successfully.
reload-result {
    package add-vpn-service
    result true
}
reload-result {
    package cisco-asa-cli-6.12
    result true
}
reload-result {
    package cisco-ios-cli-6.67
    result true
}
reload-result {
    package cisco-iosxr-cli-7.32
    result true
}
reload-result {
    package cisco-nx-cli-5.20
    result true
}
reload-result {
    package resource-manager
    result true
}
reload-result {
    package selftest
    result true
}
reload-result {
    package svi_verify_example
    result true
}
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem stopped: ncs-dp-10-cisco-ios-cli-6.67:IOSDp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem stopped: ncs-dp-11-cisco-nx-cli-5.20:NexusDp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem stopped: ncs-dp-12-resource-manager:AddressallocationIPvalidation
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem stopped: ncs-dp-9-cisco-asa-cli-6.12:ASADp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem started: ncs-dp-13-cisco-asa-cli-6.12:ASADp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem started: ncs-dp-14-cisco-ios-cli-6.67:IOSDp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem started: ncs-dp-15-cisco-nx-cli-5.20:NexusDp
developer@ncs# 
System message at 2021-10-18 03:33:40...
    Subsystem started: ncs-dp-16-resource-manager:AddressallocationIPvalidation
developer@ncs# 
```
  </div>
</details>

コンフィギュレーションモードで`?`をタイプします。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs# 
developer@ncs# config t
Entering configuration mode terminal
developer@ncs(config)# ?
Possible completions:
  aaa                           AAA management
  alarms                        Alarm management
  alias                         Create command alias.
  cluster                       Cluster configuration
  compliance                    Compliance reporting
  customers                     Customers using services
  device                        
  devices                       The managed devices and device communication settings
  high-availability             Configuration, status and actions concerning NSO Built-in HA
  java-vm                       Control of the NCS Java VM
  loopback-ip                   
  loopback-number               
  maintainer-members            
  nacm                          Access control
  ncs-state                     NCS status information
  packages                      Installed packages
  policy                        Configuration policies
  python-vm                     Control of the NCS Python VM
  rd                            
  resource-pools                
  route-target-export           
  route-target-import           
  scheduler                     Time-based job scheduler suitable for scheduling periodic background work.
  selftest                      
  service-progress-monitoring   Service Progress Monitoring policies
  services                      The services managed by NCS
  session                       Global default CLI session parameters
  side-effect-queue             
  smart-license                 
  snmp                          Top-level container for SNMP related configuration and status objects.
  snmp-notification-receiver    Configure reception of SNMP notifications
  software                      Software management
  ssh                           Global SSH connection configuration
  svi_verify_example            
  tls                           Global TLS keystore configuration
  user                          User specific command aliases and default CLI session parameters
  vrf-name                      
  webui                         Web UI specific configuration
  zombies                       Container for deleted Nano Services that still perform staged deletes.
  ---                           
  abort                         Abort configuration session
  activate                      Activate a statement
  annotate                      Add a comment to a statement
  clear                         Remove all configuration changes
  commit                        Commit current set of changes
  compare                       Compare configuration
  copy                          Copy a list entry
  deactivate                    Deactivate a statement
  describe                      Display transparent command information
  do                            Run an operational-mode command
  end                           Terminate configuration session
  exit                          Exit from current mode
  help                          Provide help information
  insert                        Insert a parameter
  load                          Load configuration from an ASCII file or from terminal
  move                          Move a parameter
  no                            Negate a command or set its defaults
  pwd                           Display current mode path
  rename                        Rename an identifier
  resolved                      Conflicts have been resolved
  revert                        Copy configuration from running
  rload                         Load configuration from an ASCII file or from terminal, relative to current location
  rollback                      Roll back database to last committed version
  save                          Save configuration to an ASCII file
  save-device-cfg               save device settings
  service                       Modify use of network based services
  show                          Show a parameter
  switch                        Change CLI style
  tag                           Manipulate statement tags
  top                           Exit to top level and optionally run command
  validate                      Validate current configuration
developer@ncs(config)# 
```
  </div>
</details>


> __leaf__や、__leaf-list__など定義した__node__がコマンドで設定できるようになりましたが、整理されておらず、いろんなコマンドと一緒に出てくるので煩雑です。
そこで、__container__を利用します。

#containerの定義
__container__を定義します。まず、全体を`vpn`という__container__でグループ化し、vrf設定関連を`vrf`という__container__、VRFに所属するLoopbackアドレス設定関連を`loopback`という__container__にグループ化します。


```yang:add-vpn-service.yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  import ietf-inet-types {
    prefix inet;
  }

  import tailf-ncs {
    prefix ncs;
  }

  import maintainers-base {
    prefix maintainers;
  }

  organization
    "EXAMPLE SYSTEMS";

  contact
    "EXAMPLE SYSTEMS
     Customer Service
     E-mail: example@example.com";

  description
    "L3VPN YANG Model.";

  revision 2021-10-15 {
    description
      "Initial revision";
  }

  typedef rdrt-type {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
  }

  identity IETSUNA {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }
  identity TSUNATOSHI {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }

  container vpn {
    description "L3vpn vrf";

    leaf device {
      type leafref {
        path "/ncs:devices/ncs:device/ncs:name";
      }
      description "Configured Device Name";
    }

    leaf-list maintainer-members {
      type identityref {
        base maintainers:base-maintainers;
      }
      description "Maintainers of Customer";
    }

    container vrf {
      description "Vrf container";

      leaf vrf-name {
        type string;
        description "VRF Name";
      }

      leaf rd {
        type rdrt-type;
        description "Route Distinguisher and Route target";
      }

      leaf route-target-export {
        type rdrt-type;
        description "Route Target Export";
      }

      leaf-list route-target-import {
        type rdrt-type;
        description "Route Target Export";
      }
    }

    container loopback {
      description "Loopback container";

      leaf loopback-number {
        type uint16;
        description "Loopback Interface Number";
      }

      leaf loopback-ip {
        type inet:ipv4-address;
        description "Loopback IPv4 Address.";
      }
    }
  }
}
```


add-vpn-service.yangを更新して、`make`、`packages reload`します。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
[developer@nso yang]$ YANGPATH="./yang" make -C ../
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' に入ります
/opt/ncs/current/bin/ncsc  `ls add-vpn-service-ann.yang  > /dev/null 2>&1 && echo "-a add-vpn-service-ann.yang"` \
             --yangpath ./yang -c -o ../load-dir/add-vpn-service.fxs yang/add-vpn-service.yang
make: ディレクトリ `/var/opt/ncs/packages/add-vpn-service/src' から出ます
[developer@nso yang]$ 
[developer@nso yang]$ 
[developer@nso yang]$ ncs_cli -C -u developer

User developer last logged in 2021-10-18T03:31:49.65825-07:00, to nso, from 192.168.254.11 using cli-ssh
developer connected from 192.168.254.11 using ssh on nso
developer@ncs# 
developer@ncs# packages reload

>>> System upgrade is starting.
>>> Sessions in configure mode must exit to operational mode.
>>> No configuration changes can be performed until upgrade has completed.
>>> System upgrade has completed successfully.
reload-result {
    package add-vpn-service
    result true
}
reload-result {
    package cisco-asa-cli-6.12
    result true
}
reload-result {
    package cisco-ios-cli-6.67
    result true
}
reload-result {
    package cisco-iosxr-cli-7.32
    result true
}
reload-result {
    package cisco-nx-cli-5.20
    result true
}
reload-result {
    package resource-manager
    result true
}
reload-result {
    package selftest
    result true
}
reload-result {
    package svi_verify_example
    result true
}
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem stopped: ncs-dp-14-cisco-ios-cli-6.67:IOSDp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem stopped: ncs-dp-15-cisco-nx-cli-5.20:NexusDp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem stopped: ncs-dp-16-resource-manager:AddressallocationIPvalidation
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem stopped: ncs-dp-13-cisco-asa-cli-6.12:ASADp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem started: ncs-dp-17-cisco-asa-cli-6.12:ASADp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem started: ncs-dp-18-cisco-ios-cli-6.67:IOSDp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem started: ncs-dp-19-cisco-nx-cli-5.20:NexusDp
developer@ncs# 
System message at 2021-10-18 04:09:06...
    Subsystem started: ncs-dp-20-resource-manager:AddressallocationIPvalidation
developer@ncs# 

```
  </div>
</details>


コンフィギュレーションモードで`?`をタイプします。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs# config t
Entering configuration mode terminal
developer@ncs(config)# ?
Possible completions:
  aaa                           AAA management
  alarms                        Alarm management
  alias                         Create command alias.
  cluster                       Cluster configuration
  compliance                    Compliance reporting
  customers                     Customers using services
  devices                       The managed devices and device communication settings
  high-availability             Configuration, status and actions concerning NSO Built-in HA
  java-vm                       Control of the NCS Java VM
  nacm                          Access control
  ncs-state                     NCS status information
  packages                      Installed packages
  policy                        Configuration policies
  python-vm                     Control of the NCS Python VM
  resource-pools                
  scheduler                     Time-based job scheduler suitable for scheduling periodic background work.
  selftest                      
  service-progress-monitoring   Service Progress Monitoring policies
  services                      The services managed by NCS
  session                       Global default CLI session parameters
  side-effect-queue             
  smart-license                 
  snmp                          Top-level container for SNMP related configuration and status objects.
  snmp-notification-receiver    Configure reception of SNMP notifications
  software                      Software management
  ssh                           Global SSH connection configuration
  svi_verify_example            
  tls                           Global TLS keystore configuration
  user                          User specific command aliases and default CLI session parameters
  vpn                           
  webui                         Web UI specific configuration
  zombies                       Container for deleted Nano Services that still perform staged deletes.
  ---                           
  abort                         Abort configuration session
  activate                      Activate a statement
  annotate                      Add a comment to a statement
  clear                         Remove all configuration changes
  commit                        Commit current set of changes
  compare                       Compare configuration
  copy                          Copy a list entry
  deactivate                    Deactivate a statement
  describe                      Display transparent command information
  do                            Run an operational-mode command
  end                           Terminate configuration session
  exit                          Exit from current mode
  help                          Provide help information
  insert                        Insert a parameter
  load                          Load configuration from an ASCII file or from terminal
  move                          Move a parameter
  no                            Negate a command or set its defaults
  pwd                           Display current mode path
  rename                        Rename an identifier
  resolved                      Conflicts have been resolved
  revert                        Copy configuration from running
  rload                         Load configuration from an ASCII file or from terminal, relative to current location
  rollback                      Roll back database to last committed version
  save                          Save configuration to an ASCII file
  save-device-cfg               save device settings
  service                       Modify use of network based services
  show                          Show a parameter
  switch                        Change CLI style
  tag                           Manipulate statement tags
  top                           Exit to top level and optionally run command
  validate                      Validate current configuration
developer@ncs(config)# 
```
</div>
</details>

> __container vpn__のおかげで`vpn`にまとめられました。

`vpn`で`?`をタイプします。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs(config)# vpn ?
Possible completions:
  device  loopback  maintainer-members  vrf
developer@ncs(config)# vpn 
```
</div>
</details>
> __container vrf__、__container loopback__にまとまってます。

では、実際に設定し、NSOのCDBへ反映してみましょう。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs# config t
Entering configuration mode terminal
developer@ncs(config)# vpn ?
Possible completions:
  device  loopback  maintainer-members  vrf
developer@ncs(config)# vpn device ?
Possible completions:
  core-rtr01  core-rtr02  dist-rtr01  dist-rtr02  dist-sw01  dist-sw02  edge-firewall01  edge-sw01  internet-rtr01
developer@ncs(config)# vpn device core-rtr01
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn maintainer-members ?
Possible completions:
  HIDETADA  IEMITSU  IETSUNA  IEYASU  TSUNATOSHI  [
developer@ncs(config)# vpn maintainer-members HIDETADA 
developer@ncs(config)# vpn maintainer-members IEYASU  
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn vrf ?
Possible completions:
  rd  route-target-export  route-target-import  vrf-name
developer@ncs(config)# vpn vrf vrf-name C
developer@ncs(config)#                   
developer@ncs(config)# 
developer@ncs(config)# vpn vrf rd ?
Possible completions:
  <string>  65432:111  65432:222
developer@ncs(config)# vpn vrf rd 65432:333
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn vrf route-target-export ?
Possible completions:
  <string>  65432:111  65432:222
developer@ncs(config)# vpn vrf route-target-export 65432:333
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn vrf route-target-import ?
Possible completions:
  <string>  65432:111  65432:222  [
developer@ncs(config)# vpn vrf route-target-import 65432:111
developer@ncs(config)# vpn vrf route-target-import 65432:222
developer@ncs(config)# vpn vrf route-target-import 65432:333
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn loopback ?
Possible completions:
  loopback-ip  loopback-number
developer@ncs(config)# vpn loopback loopback-number ?
Possible completions:
  <unsignedShort>
developer@ncs(config)# vpn loopback loopback-number 333
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# vpn loopback loopback-ip ?
Possible completions:
  <IPv4 address>
developer@ncs(config)# vpn loopback loopback-ip 3.3.3.1
developer@ncs(config)# 
developer@ncs(config)# 
developer@ncs(config)# show configuration 
vpn device         core-rtr01
vpn maintainer-members [ HIDETADA IEYASU ]
vpn vrf vrf-name    C
vpn vrf rd          65432:333
vpn vrf route-target-export 65432:333
vpn vrf route-target-import [ 65432:111 65432:222 65432:333 ]
vpn loopback loopback-number 333
vpn loopback loopback-ip 3.3.3.1
```
</div>
</details> 
> __leaf__は単一の値、__lear-list__は複数の値のリストを保持してます。

`commit`し、`show running-config vpn`を見てみます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs(config)# commit
Commit complete.
developer@ncs(config)# end
developer@ncs# show running-config vpn
vpn device         core-rtr01
vpn maintainer-members [ HIDETADA IEYASU ]
vpn vrf vrf-name    C
vpn vrf rd          65432:333
vpn vrf route-target-export 65432:333
vpn vrf route-target-import [ 65432:111 65432:222 65432:333 ]
vpn loopback loopback-number 333
vpn loopback loopback-ip 3.3.3.1
developer@ncs# 

```
</div>
</details>
> 設定が反映されています。

別のデバイスへもコンフィグを追加する場合を想定して、今度は違う設定を投入してみます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell

developer@ncs(config)# vpn device core-rtr02
developer@ncs(config)# vpn maintainer-members HIDETADA
developer@ncs(config)# vpn maintainer-members IETSUNA        
developer@ncs(config)# vpn maintainer-members TSUNATOSHI
developer@ncs(config)# vpn vrf vrf-name D
developer@ncs(config)# vpn vrf rd 65432:444
developer@ncs(config)# vpn vrf route-target-export 65432:444
developer@ncs(config)# vpn vrf route-target-import 65432:444
developer@ncs(config)# vpn loopback loopback-number 444
developer@ncs(config)# vpn loopback loopback-ip 4.4.4.1
developer@ncs(config)# show configuration 
vpn device         core-rtr02
vpn maintainer-members [ IETSUNA TSUNATOSHI HIDETADA IEYASU ]
vpn vrf vrf-name    D
vpn vrf rd          65432:444
vpn vrf route-target-export 65432:444
vpn vrf route-target-import [ 65432:111 65432:222 65432:333 65432:444 ]
vpn loopback loopback-number 444
vpn loopback loopback-ip 4.4.4.1
developer@ncs(config)# commit 
Commit complete.
developer@ncs(config)# end
developer@ncs# show running-config vpn
vpn device         core-rtr02
vpn maintainer-members [ IETSUNA TSUNATOSHI HIDETADA IEYASU ]
vpn vrf vrf-name    D
vpn vrf rd          65432:444
vpn vrf route-target-export 65432:444
vpn vrf route-target-import [ 65432:111 65432:222 65432:333 65432:444 ]
vpn loopback loopback-number 444
vpn loopback loopback-ip 4.4.4.1
developer@ncs# 
```
</div>
</details>
> 設定が上書きされてしまいました。これでは複数のデバイスへ個別の設定を保存することができません。

#listの定義
listを使えばいくつでもインスタンスを作れるので、設定が必要なnodeを全て包含するように`vpn-policy`というlistを定義します。

```yang:add-vpn-service.yang
module add-vpn-service {
  namespace "http://example.com/ns/yang/add-vpn-service";
  prefix vpn;

  import ietf-inet-types {
    prefix inet;
  }

  import tailf-ncs {
    prefix ncs;
  }

  import maintainers-base {
    prefix maintainers;
  }

  organization
    "EXAMPLE SYSTEMS";

  contact
    "EXAMPLE SYSTEMS
     Customer Service
     E-mail: example@example.com";

  description
    "L3VPN YANG Model.";

  revision 2021-10-15 {
    description
      "Initial revision";
  }

  typedef rdrt-type {
    type union {
      type string {
        pattern "[0-9]{1,5}:[0-9]{1,5}";
      }
      type enumeration {
        enum "65432:111";
        enum "65432:222";
      }
    }
  }

  identity IETSUNA {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }
  identity TSUNATOSHI {
    base maintainers:base-maintainers;
    description "The Member of Maintainers";
  }

  container vpn {
    description "L3vpn vrf";

    list vpn-policy {
      key "policy-name";
      description "VRF Configured Policy";

      leaf policy-name {
        type string;
      }

      leaf device {
        type leafref {
          path "/ncs:devices/ncs:device/ncs:name";
        }
        description "Configured Device Name";
      }

      leaf-list maintainer-members {
        type identityref {
          base maintainers:base-maintainers;
        }
        description "Maintainers of Customer";
      }

      container vrf {
        description "Vrf container";

        leaf vrf-name {
          type string;
          description "VRF Name";
        }

        leaf rd {
          type rdrt-type;
          description "Route Distinguisher and Route target";
        }

        leaf route-target-export {
          type rdrt-type;
          description "Route Target Export";
        }

        leaf-list route-target-import {
          type rdrt-type;
          description "Route Target Export";
        }
      }

      container loopback {
        description "Loopback container";

        leaf loopback-number {
          type uint16;
          description "Loopback Interface Number";
        }

        leaf loopback-ip {
          type inet:ipv4-address;
          description "Loopback IPv4 Address.";
        }
      }
    }
  }
}
```
> __list__を使えばいくつでもインスタンスを作ることができますが、それぞれのインスタンスを区別するためにユニークな値を持たせる必要があります。今回は`leaf policy-name`を定義してそれぞれのインスタンスにポリシー名を設定するようにしました。

add-vpn-service.yangを更新して、`make`、`packages reload`して、再度設定を投入してみます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs(config)# vpn ?          
Possible completions:
  vpn-policy
developer@ncs(config)# vpn vpn-policy CORE-RTR01_VRF_C
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# ?
Possible completions:
  device               
  loopback             
  maintainer-members   
  vrf                  
  ---                  
  commit               Commit current set of changes
  describe             Display transparent command information
  exit                 Exit from current mode
  help                 Provide help information
  no                   Negate a command or set its defaults
  pwd                  Display current mode path
  rload                Load configuration from an ASCII file or from terminal, relative to current location
  top                  Exit to top level and optionally run command
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# device core-rtr01
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# maintainer-members HIDETADA
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# maintainer-members IEYASU  
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# vrf vrf-name C
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# vrf rd 65432:333
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# vrf route-target-export 65432:333
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# vrf route-target-import 65432:333
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# loopback loopback-number 333
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# loopback loopback-ip 3.3.3.1
developer@ncs(config-vpn-policy-CORE-RTR01_VRF_C)# top
developer@ncs(config)# show configuration 
vpn vpn-policy CORE-RTR01_VRF_C
 device             core-rtr01
 maintainer-members [ HIDETADA IEYASU ]
 vrf vrf-name        C
 vrf rd              65432:333
 vrf route-target-export 65432:333
 vrf route-target-import [ 65432:333 ]
 loopback loopback-number 333
 loopback loopback-ip 3.3.3.1
!
developer@ncs(config)# commit
Commit complete.
developer@ncs(config)# 
developer@ncs(config)# vpn vpn-policy CORE-RTR02_VRF_C  
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# device core-rtr02             
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# maintainer-members HIDETADA      
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# maintainer-members IETSUNA 
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# maintainer-members TSUNATOSHI
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# vrf vrf-name C                   
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# vrf rd 65432:333                 
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# vrf route-target-export 65432:333
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# vrf route-target-import 65432:333
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# loopback loopback-number 333     
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# loopback loopback-ip 3.3.3.2     
developer@ncs(config-vpn-policy-CORE-RTR02_VRF_C)# top                              
developer@ncs(config)# show configuration 
vpn vpn-policy CORE-RTR02_VRF_C
 device             core-rtr02
 maintainer-members [ IETSUNA TSUNATOSHI HIDETADA ]
 vrf vrf-name        C
 vrf rd              65432:333
 vrf route-target-export 65432:333
 vrf route-target-import [ 65432:333 ]
 loopback loopback-number 333
 loopback loopback-ip 3.3.3.2
!
developer@ncs(config)# commit 
Commit complete.
developer@ncs(config)# 
```
  </div>
</details>

`show running-config vpn`を見てみます。

<details>
  <summary>アウトプット</summary>
  <div>

```shell
developer@ncs# show running-config vpn
vpn vpn-policy CORE-RTR01_VRF_C
 device             core-rtr01
 maintainer-members [ HIDETADA IEYASU ]
 vrf vrf-name        C
 vrf rd              65432:333
 vrf route-target-export 65432:333
 vrf route-target-import [ 65432:333 ]
 loopback loopback-number 333
 loopback loopback-ip 3.3.3.1
!
vpn vpn-policy CORE-RTR02_VRF_C
 device             core-rtr02
 maintainer-members [ IETSUNA TSUNATOSHI HIDETADA ]
 vrf vrf-name        C
 vrf rd              65432:333
 vrf route-target-export 65432:333
 vrf route-target-import [ 65432:333 ]
 loopback loopback-number 333
 loopback loopback-ip 3.3.3.2
!

```
  </div>
</details>
> __list__でインスタンスを作ることによって、複数のポリシーを設定することができました。

#まとめ
YANGでサービスパッケージのモデルを作成しNSOのCDBに登録することができました。デバイスへ設定を反映させるためにはXMLテンプレートにYANGで定義したnodeをマッピングしますが、その方法については次回まとめたいと思います。YANGは強力なデータモデリング言語であり、ニーズに合わせて単純なモジュールから複雑なモジュールまで作成できるので、マルチベンダーネットワークにとっては非常に有効な言語であると思います。

#参考リンク

[-DevNet Learning Module 「NSO Basics for Network Automation」](https://developer.cisco.com/learning/modules/nso-basics)