第4回: ネットワーク自動化開発実践 - デバイスのモデル化のご利益を感じよう - Cisco NSO を使う

# はじめに

この記事はシスコの同志による Advent Calendar の一部として投稿しています

- 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 (<<< 今年)

個人的には、ネットワーク自動化開発についての[連載を2017年に宣言](https://qiita.com/radiantmarch/items/9df086002ee3f4f334d2) してから 4回目 :scream: の投稿となりました。近年、ネットワーク自動化のニーズはさらに加速していると感じます:smile:。

今回は **Cisco Network Services Orchestrator (NSO)** を使ったデバイス操作の自動化を取り上げます。NSO については書きたいことがたくさんあるので、今回は第1回目ということで、基本的なデバイス操作にしぼってご紹介します :sunglasses: 。


# Cisco NSO とは ？

シスコが提供している**マルチベンダ対応の商用ネットワーク自動化ソフトウェア**です。サポート付きの有償製品ということもあってオープンソースのツール群 (Ansible, Netmiko, etc) と比べると、試したことがあるユーザの数はまだ少ないかもしれませんが、じわじわとユーザが広がっています。

Cisco NSO は[評価版](https://developer.cisco.com/docs/nso/#!getting-nso/getting-nso) を無償で利用できます :heart_eyes: 。シスコ本家のブログに以下の記事がありますので、ぜひこちらから始めてみてください！

- [評価版 Cisco NSO を使ってみよう 2020年版](https://gblogs.cisco.com/jp/2020/02/lets-evaluate-cisco-nso-2020/)


# よくある悩みと NSO でできること

ネットワーク装置をいろんなオープンソースツールやプログラミング言語で操作し初めて少したつと、以下のような課題を感じ始めることがあると思います。

- :sweat_smile: ツールによっては、結局 CLI コマンドをテキスト (String) で入力しなければならず、Python や JavaScript など**プログラミング言語と親和性がよくない**。
- :sweat_smile: 用途ごと (ACL, OSPF, BGP, etc) のモジュールが用意されているが、**すべての機能はカバーされていない**。またモジュールごとに入力パラメータ等の**使い方を学ばないといけない**。
- :sweat_smile: **サポートされるデバイスの種類が少ない**。対応デバイスを追加するには、誰かが対応したモジュールやプラグインを開発してくれるのを待つか、自分で開発してコントリビュートする必要があるが、ユーザには敷居が高い。

NSO は上のような悩みに応えることができます。

- :satisfied: NSO は デバイスの CLI (\*1) と 1:1 対応したドライバ (\*2) により、**様々なフォーマットとプロトコルによるコンフィグに対応**できます。JSON (RESTCONF), XML (NETCONF, RESTCONF), Python Dict (Python) など
- :satisfied: NSO はデバイスの **CLI 全体をモデル化**しているので、CLI で設定できることは NSO 経由で設定できます(\*2)。また、入力パラメータも CLI のものがほぼそのまま利用されるので、ネットワークエンジニアにとって敷居が低いです。
- :satisfied: NSO は 2019年末時点で **80 以上のネットワークデバイス OS に対応**しています。シスコの有償ソフトウェア製品なので、すべて**シスコが開発・サポート**します。一方でユーザによる独自ドライバ開発することも可能です

(*1) 実際にはデバイスのサポートするネイティブな設定方式によります。REST や NETCONF を利用するドライバも用意されています。
(*2) 実際にはデバイスコンフィグに対応したドライバ (NED, Network Element Driver) は常に追加開発が行われており、100% のコンフィグを管理するわけではありません。

さらに NSO には、

- :heart_eyes: **複数機器にまたがるネットワーク全体のサービスをモデル化**する（サービスモデル）

という、たいへん大事な機能を備えているのですが、それはまた後日、少しずつご紹介したいと思います！

以下では、NSO によるデバイス（単体）のコンフィグの表示や変更（CRUD）オペレーションの例をお見せします。


# NSO を触ってみる

まずは NSO にデバイスを登録してみます。今回は Cisco DevNet の [Always On CSR1000v](https://devnetsandbox.cisco.com/RM/Diagram/Index/7b4d4209-a17c-4bc3-9b38-f15184e53a94?diagramType=Topology) を使ってみました。

<details><summary>:hamster: NSO CLI によるデバイス登録 (クリックして展開) </summary>
<div>

```
devices authgroups group iosxe-sandbox-auth
 default-map remote-name developer
 default-map remote-password C1sco12345

devices device iosxe-sandbox
 address   sandbox-iosxe-latest-1.cisco.com
 port      22
 authgroup iosxe-sandbox-auth
 device-type cli ned-id cisco-ios-cli-6.44
 device-type cli protocol ssh
 state admin-state unlocked
```
</div>
</details>

今回は、Cisco IOS ドライバ (cisco-ios-cli-6.44) を使ってデバイスに接続します。以下のように SSH 鍵を取得したのち、`sync-from` コマンドでデバイスのコンフィグを NSO と同期します。

<details><summary>:hamster: NSO へのデバイスコンフィグの同期 (クリックして展開) </summary>
<div>

```
admin@ncs# devices device iosxe-sandbox ssh fetch-host-keys
result unchanged
fingerprint {
    algorithm ssh-rsa
    value c1:83:a0:1b:4d:7f:e4:63:e0:03:85:1a:10:78:95:aa
}
admin@ncs# devices device iosxe-sandbox connect
result true
info (admin) Connected to iosxe-sandbox - 131.226.217.143:22
admin@ncs# devices device iosxe-sandbox sync-from
result true
```
</div>
</details>

これだけで、NSO からこの CSR1000v デバイスのコンフィグを操作できるようになりました！


# NSO でデバイスコンフィグをいろんなフォーマットに変換して表示してみる

まずはコンフィグをいろんな形式で表示してみましょう。

<details><summary>:hamster: Cisco CLI スタイル (クリックして展開) </summary>
<div>

```
admin@ncs# show running-config devices device iosxe-sandbox config
devices device iosxe-sandbox
 config
  tailfned police cirmode
  version   17.3
  service timestamps debug datetime msec
  service timestamps log datetime msec
  no service password-encryption
  service call-home
  login on-success log
  cdp run
  platform console virtual
  platform punt-keepalive disable-kernel-core
  platform qfp utilization monitor load 80
  hostname  csr1000v-1
(snip)
```
</div>
</details>


<details><summary>:hamster: Juniper ブラケットスタイル (クリックして展開) </summary>
<div>

```
admin@ncs# show running-config devices device iosxe-sandbox config | display curly-braces
tailfned {
    police cirmode;
}
version  17.3;
service {
    timestamps {
        debug {
            datetime {
                msec;
            }
        }
        log {
            datetime {
                msec;
            }
        }
    }
    call-home;
}
login {
    on-success {
        log;
    }
}
cdp {
    run true;
}
(snip)
```
</div>
</details>


<details><summary>:hamster: JSON フォーマット (クリックして展開) </summary>
<div>

```
admin@ncs# show running-config devices device iosxe-sandbox config | display json
{
  "data": {
    "tailf-ncs:devices": {
      "device": [
        {
          "name": "iosxe-sandbox",
          "config": {
            "tailf-ned-cisco-ios:tailfned": {
              "police": "cirmode"
            },
            "tailf-ned-cisco-ios:version": "17.3",
            "tailf-ned-cisco-ios:service": {
              "timestamps": {
                "debug": {
                  "datetime": {
                    "msec": [null]
                  }
                },
                "log": {
                  "datetime": {
                    "msec": [null]
                  }
                }
              },
              "call-home": [null]
            },
(snip)
```
</div>
</details>

<details><summary>:hamster: XML フォーマット (クリックして展開) </summary>
<div>

```
admin@ncs# show running-config devices device iosxe-sandbox config | display xml
<config xmlns="http://tail-f.com/ns/config/1.0">
  <devices xmlns="http://tail-f.com/ns/ncs">
    <device>
      <name>iosxe-sandbox</name>
      <config>
        <tailfned xmlns="urn:ios">
          <police>cirmode</police>
        </tailfned>
        <version xmlns="urn:ios">17.3</version>
        <service xmlns="urn:ios">
          <timestamps>
            <debug>
              <datetime>
                <msec/>
              </datetime>
            </debug>
            <log>
              <datetime>
                <msec/>
              </datetime>
            </log>
          </timestamps>
          <call-home/>
        </service>
        <login xmlns="urn:ios">
          <on-success>
            <log/>
          </on-success>
        </login>
        <cdp xmlns="urn:ios">
          <run>true</run>
        </cdp>
(snip)
```
</div>
</details>


<details><summary>:hamster: NSO WebUI 上のフォームとして表示 (クリックして展開) </summary>
<div>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/850233ae-60a0-f838-0fea-16a5d7e454b9.png)

</div>
</details>


このように、NSO にデバイスコンフィグを取り込む (sync-from) することで、NSO でデバイスコンフィグを自由自在に様々なフォーマットで表示させることができます。また、以下のように API 経由で同じデータを取り出すこともできるようになります。

<details><summary>:hamster: Postman から RESTCONF リクエストでコンフィグを取得 (クリックして展開) </summary>
<div>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/cdda5057-8c7a-ba7b-f00b-4796893ec909.png)

</div>
</details>


# NSO でデバイスコンフィグを変更してみる

次にデバイスのコンフィグを NSO から変更してみましょう。

## CLI で変更。
シスコ IOS デバイスと同じような方法でコンフィグできます。

<details><summary>:elephant: CLI (クリックして展開) </summary>
<div>

```
admin@ncs# config
Entering configuration mode terminal
admin@ncs(config)# devices device iosxe-sandbox config
admin@ncs(config-config)# interface Loopback 100
admin@ncs(config-if)# description "Cisco Advent Calendar CLI Test"
admin@ncs(config-if)# commit
Commit complete.
```
</div>
</details>


## RESTCONF で変更。
JSON フォーマットでコンフィグを指定できます。
ここでは Postman を使っていますが、クライアントは当然ながらなんでも大丈夫です

<details><summary>:elephant: RESTCONF (クリックして展開) </summary>
<div>

- Method: `POST`
- URI: `http://{{nsoip}}:{{nsoport}}/restconf/data/tailf-ncs:devices/device={{device}}/config/tailf-ned-cisco-ios:interface`
- Header:
    - `Accept: application/yang-data+json`
    - `Content-Type: application/yang-data+json`
- Body: 

```
{
  "Loopback": [
    {
      "name": "101",
      "description": "Cisco Advent Calendar RESTCONF Test"
    }
  ]
}
```

:elephant: Postman での実行画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/a9a85f24-6ae5-8777-9e79-325a0baf52d2.png)

</div>
</details>


## Python API を利用して、プログラミングで変更
ここでは iPython をつかった対話的な記述をしていますが、もちろん python スクリプトで記述が可能です。

<details><summary>:elephant: Python API (クリックして展開) </summary>
<div>

```
$ ipython
Python 3.8.2 (default, May 25 2020, 00:11:10)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import ncs
   ...: m = ncs.maapi.Maapi()
   ...: s = ncs.maapi.Session(m,'admin','python')
   ...: trans = m.start_write_trans()
   ...: root = ncs.maagic.get_root(trans)

In [2]: dev = root.devices.device['iosxe-sandbox']

In [3]: dev
Out[3]: ListElement name=device tag=617911018 keys={iosxe-sandbox}

In [4]: dev.name
Out[4]: 'iosxe-sandbox'

In [5]: dev.config
Out[5]: Container name=config tag=2105663071

In [6]: dev.config.ios__interface.Loopback['100']
Out[6]: ListElement name=Loopback tag=1158098145 keys={100}

In [7]: dev.config.ios__interface.Loopback['100'].description
Out[7]: 'Cisco Advent Calendar CLI Test'

In [8]: dev.config.interface.Loopback['101'].description
Out[8]: 'Cisco Advent Calendar RESTCONF Test'

In [10]: dev.config.interface.Loopback.create('102')
Out[10]: ListElement name=Loopback tag=1158098145 keys={102}

In [11]: dev.config.interface.Loopback['102'].description = 'Cisco Advent Calendar Python Test'

In [12]: trans.apply()
```
</div>
</details>


## WebUI からフォームを使って変更
WebUI 画面から

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/32613817-2395-5101-f7ef-ee10b4faba2b.png)

Gif 動画:elephant: : [nso-config-webui.gif](https://github.com/radiantmarch/commonrepo/raw/main/nso-config-webui.gif)


# NSO でデバイスに管理コマンドを発行してみる

NSO はデバイスとの CLI セッションを確立しているため、show コマンドなどの管理系コマンドをリモート実行して結果を取得することもできます

## :elephant: NSO CLI でコマンド実行

```cisco
dmin@ncs# devices device iosxe-sandbox live-status exec show ip interface brief
result
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       10.10.20.48     YES NVRAM  up                    up
GigabitEthernet2       unassigned      YES NVRAM  administratively down down
GigabitEthernet3       unassigned      YES NVRAM  administratively down down
csr1000v-1#
```

上記のように実行コマンド結果はあくまでテキストで返ってきます。

NED によっては、一部のオペレーショナルデータは、構造化データの取得をサポートしています。

<details><summary>:elephant: オペレーショナルデータの JSON フォーマットでの取得例 (クリックして展開) </summary>
<div>

```json
admin@ncs# show devices device iosxe-sandbox live-status ios-stats:interfaces | display json
{
  "data": {
    "tailf-ncs:devices": {
      "device": [
        {
          "name": "iosxe-sandbox",
          "live-status": {
            "tailf-ned-cisco-ios-stats:interfaces": [
              {
                "type": "GigabitEthernet",
                "name": "1",
                "admin-status": "up",
                "ip-address": "10.10.20.48/24",
                "mac-address": "0050.56bf.78ac"
              },
              {
                "type": "GigabitEthernet",
                "name": "2",
                "admin-status": "down",
                "mac-address": "0050.56bf.4ea3"
              },
              {
                "type": "GigabitEthernet",
                "name": "3",
                "admin-status": "down",
                "mac-address": "0050.56bf.7db4"
              }
            ]
          }
        }
      ]
    }
  }
}
```
</div>
</details>


## Postman RESTCONF クライアントからコマンド実行

RESTCONF 経由でコマンドを実行することももちろん可能です

<details><summary>:elephant: RESTCONF (クリックして展開) </summary>
<div>

- Method: `POST`
- URI: `http://{{nsoip}}:{{nsoport}}/restconf/operations/devices/device={{device}}/live-status/exec/show`
- Header:
    - `Accept: application/yang-data+json`
    - `Content-Type: application/yang-data+json`
- Body: 

```
{
    "show": {
        "args": "ip int br"
    }
}
```

Postman 実行画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/05ba88d9-1b3b-bd63-800f-7319d613cd27.png)


</div>
</details>


## WebUI から実行
WebUI から実行することももちろん可能です


<details><summary>:elephant: WebUI (クリックして展開) </summary>
<div>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175796/ae059ce6-c503-ee1d-c1df-552344c246a7.png)

</div>
</details>



# まとめ

今回は NSO を使ったデバイス操作の例をまとめてみました。さまざまなフォーマット・プロトコルでデバイスの基本的な操作が可能なことが見ていただけたと思います:wink: 。

ぜひ今回のサンプル設定を参考にして、NSO の試用を始めてみていただけたら幸いです :sunglasses:

次回は NSO が本領を発揮するサービスモデルの使い方・作り方について解説してみたいと思います。


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