Cisco DNA CenterをAnsibleから操作する(サードパーティモジュール編)

# はじめに

[前回の記事](https://qiita.com/tech_kitara/items/a571b0c51fc851728b92)で、Ansibleのuriモジュールを使ったCisco DNA Centerの操作方法をご紹介しました。
今回は、World Wide Technologyから公開されているDNA Center用のサードパーティーモジュールを触ってみた結果をご紹介したいと思います。

# ansible-dnac-modulesについて

[GitHub - jandiorio/ansible-dnac-modules](https://github.com/jandiorio/ansible-dnac-modules)

2019年8月時点で以下のモジュールが公開されています。

- dnac_syslog
- dnac_snmpv2_credential
- dnac_snmp
- dnac_ntp
- dnac_ippool
- dnac_group
- dnac_dns
- dnac_discovery
- dnac_dhcp
- dnac_device_role
- dnac_device_assign_site
- dnac_cli_credential
- dnac_activate_credential
- dnac_banner
- dnac_archive_config
- dnac_del_archived_config
- dnac_netflow
- dnac_timezone

内部的には、AnsibleからREST API経由でDNA Centerへアクセスの上、設定変更を行っているようです。

以下のCiscoブログ記事でも、本モジュールについて紹介されています。
[Ansible: Powered by Cisco DNA Center](https://blogs.cisco.com/developer/ansible-for-dna-center)


# 用意した環境
Ansibleは前回と同じく2.8.3を使用しました。
DNA Centerは前回の[Cisco DNA Center 1.2.10 (Always-On)](https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology)だと設定変更権限がないようなので、一つ前の[Cisco DNA Center 1.2.6 (Always-On)](https://devnetsandbox.cisco.com/RM/Diagram/Index/c3c949dc-30af-498b-9d77-4f1c07d835f9?diagramType=Topology)を使用しています。

GitHubのREADME記載の通り、本モジュールを使うには、追加でいくつかセットアップが必要です。
以下はCentOSのPython3.xの仮想環境を使用した例です。

①Pythonモジュールのインストール
Ansible（2.8以降は+paramiko）をインストールした仮想環境に、geopy、requests、timexonefinderを追加インストールします。

```
(venv)$ pip install geopy
(venv)$ pip install requests
(venv)$ pip install timexonefinder==3.4.2
```
②libraryパスの指定
サードパーティモジュールを格納するためのlibraryディレクトリを作成し、ansible.cfgファイルにパスを追加します。

```ansible.cfg
[defaults]
library = /home/centos/venv/ansible/library
```

ansible.cfgの格納先は、ansible --versionコマンドのconfig fileで確認可能です。

```
(venv)$ ansible --version
ansible 2.8.3
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/centos/venv/ansible/library']
  ansible python module location = /home/centos/venv/lib64/python3.6/site-packages/ansible
  executable location = /home/centos/venv/bin/ansible
  python version = 3.6.8 (default, May  2 2019, 20:40:44) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
```

③GitHubレポジトリの複製
GitHub上のサードパーティーモジュールをローカル環境に複製します。

```
(venv)$ cd ~
(venv)$ git clone https://github.com/jandiorio/ansible-dnac-modules.git
```

④Ansibleライブラリパスへの移動
環境ごとに異なるため、適宜読み替えをお願いします。

```
(venv)$ cd venv/lib/python3.6/site-packages/ansible
```

⑤dnacディレクトリの作成
module_utils/networkディレクトリ配下に、dnacディレクトリを新規作成します。

```
(venv)$ mkdir module_utils/network/dnac
```

⑥dnac.pyのdnacディレクトリへのコピー
GitHubから複製したdnac.pyを、dnacディレクトリにコピーします。

```
(venv)$ cp ~/ansible-dnac-modules/dnac.py module_utils/network/dnac/.
```

⑦*.pyのlibraryディレクトリへのコピー
GitHubから複製したすべての「～.py」ファイルを、libraryディレクトリにコピーします。

```
(venv)$ cp ~/ansible-dnac-modules/*.py /home/centos/venv/ansible/library
```

⑧モジュールインストール確認
以下コマンドを実行し、モジュール説明が表示されることを確認します。

```
(venv)$ ansible-doc dnac_dhcp
```

```:表示結果
> DNAC_DHCP    (/home/centos/venv/ansible/library/dnac_dhcp.py)

        Add or delete DHCP Server(s) in the Cisco DNA Center Design
        Workflow.  The DHCP Severs can be different values \ at
        different levels in the group hierarchy.

  * This module is maintained by The Ansible Community
OPTIONS (= is mandatory):

= dhcp_servers
        IP address of the DHCP Server to manipulate.

        type: list
(省略)
```

READMEには記載されていませんが、dnacディレクトリへの`__ini__.py`ファイルの格納も行いました。

# Inventory
前回同様、DNA Centerのログイン情報を指定します。

```inventory_dnac2
[cisco]
DNA_Center ansible_host=sandboxdnac.cisco.com ansible_port=443

[cisco:vars]
username=[DNA Centerのユーザ名]
password=[DNA Centerのパスワード]
```

# Playbook①: NTPサーバ設定の変更
DNA Centerのデザイン要素の一つである、NTPサーバの設定を変更してみます。
前回は最初に認証トークンの取得を行いましたが、本モジュールはバックグラウンドで自動的に取得しているようです。
NTPサーバ`ntp_servers`は適当に`192.168.200.100`を指定しています。リスト形式で複数指定もできるようです。
DNA Centerはデザインをグループ単位で設定できます。グループ名`group_name`はデフォルトが`Global`で、全グループに設定が適用されます。今回は`London`に限定しています。

```yaml:playbook_dnac_ntp2.yml
---

- hosts: cisco
  gather_facts: no
  connection: local

  tasks:
    - name: set the ntp server
      dnac_ntp:
        host: "{{ ansible_host }}"
        port: "{{ ansible_port | int }}"
        username: "{{ username }}"
        password: "{{ password }}"
        validate_certs: false
        use_proxy: false
        group_name: London
        ntp_servers: 192.168.200.100
      register: result

    - name: Debug
      debug:
        msg: "{{ result }}"
```

# 出力結果①: NTPサーバ設定の変更
元々の設定（previous）`192.168.200.101`に対し、`192.168.200.100`を提示（proposed）している事が分かります。

```yaml:
(venv)$ ansible-playbook -i inventory_dnac2 playbook_dnac_ntp2.yml

PLAY [cisco] ****************************************************************************************************************

TASK [set the ntp server] ***************************************************************************************************
changed: [DNA_Center]

TASK [Debug] ****************************************************************************************************************
ok: [DNA_Center] => {
    "msg": {
        "changed": true,
        "failed": false,
        "message": "",
        "msg": "Created object successfully.",
        "original_message": {
            "endTime": 1566711203409,
            "id": "803f32c0-b3cb-46b3-b632-a0028a3433b1",
            "instanceTenantId": "5bd3634ab2bea0004c3ebb58",
            "isError": false,
            "progress": "Created Common Settings successfully.",
            "rootId": "803f32c0-b3cb-46b3-b632-a0028a3433b1",
            "serviceType": "Common Settings Service",
            "startTime": 1566711203329,
            "version": 1566711203329
        },
        "previous": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "inheritedGroupName": "",
                "inheritedGroupUuid": "",
                "instanceType": "ip",
                "instanceUuid": "b7838dbb-192a-4f77-934f-d6d58435abae",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": [
                    "192.168.200.101"
                ],
                "version": 9
            }
        ],
        "proprosed": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "instanceType": "ip",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": [
                    "192.168.200.100"
                ]
            }
        ]
    }
}

PLAY RECAP ******************************************************************************************************************
DNA_Center                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
```

DNA CenterにWebブラウザからログインし、「Design > Network Settings > Network」を選択すると、各グループのネットワーク設定を確認できます。「NTP Server」の項目を見ると、想定通り`192.168.200.100`が設定されています。
![無題0825_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310082/f043ce72-27c6-8550-c7df-f006316607ba.png)


# Playbook②: NTPサーバ設定の削除
Always On環境では、元々NTPサーバ設定がされていなかったので、削除して元に戻しておきます。
Playbook①との違いは、`ntp_servers`を削除し、代わりに`state`を`absent`にしている点です。

```yaml:playbook_dnac_ntp2_del.yml
---

- hosts: cisco
  gather_facts: no
  connection: local

  tasks:
    - name: set the ntp server
      dnac_ntp:
        host: "{{ ansible_host }}"
        port: "{{ ansible_port | int }}"
        username: "{{ username }}"
        password: "{{ password }}"
        validate_certs: false
        use_proxy: false
        group_name: London
        state: absent
      register: result

    - name: Debug
      debug:
        msg: "{{ result }}"
```

# 出力結果②: NTPサーバ設定の削除
今回は提示（proposed）するNTPサーバ（value）が空になっています。

```yaml
(venv)$ ansible-playbook -i inventory_dnac2 playbook_dnac_ntp2_del.yml

PLAY [cisco] ****************************************************************************************************************

TASK [set the ntp server] ***************************************************************************************************
changed: [DNA_Center]

TASK [Debug] ****************************************************************************************************************
ok: [DNA_Center] => {
    "msg": {
        "changed": true,
        "failed": false,
        "message": "",
        "msg": "Created object successfully.",
        "original_message": {
            "endTime": 1566711890724,
            "id": "625ab79a-fc65-409c-b6e6-8cc4f04d4704",
            "instanceTenantId": "5bd3634ab2bea0004c3ebb58",
            "isError": false,
            "progress": "Created Common Settings successfully.",
            "rootId": "625ab79a-fc65-409c-b6e6-8cc4f04d4704",
            "serviceType": "Common Settings Service",
            "startTime": 1566711890564,
            "version": 1566711890564
        },
        "previous": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "inheritedGroupName": "Global",
                "inheritedGroupUuid": "-1",
                "instanceType": "ip",
                "instanceUuid": "acfb3424-2a5c-4d5d-8c52-daa774bcaa1d",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": [
                    "192.168.200.100"
                ],
                "version": 6
            }
        ],
        "proprosed": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "instanceType": "ip",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": []
            }
        ]
    }
}

PLAY RECAP ******************************************************************************************************************
DNA_Center                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

```

DNA CenterのWeb画面上で、「NTP Server」の項目が消えているのが分かります。
![無題0825_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310082/fc3b0223-7046-e0e4-0900-9c491b2d60fd.png)

# （参考）冪等性の確認
Playbook①を2回実行した結果です。`msg`が「Already in desired state.」で、`changed=0`であることから、冪等性が機能しているようです。

```yaml:
(venv)$ ansible-playbook -i inventory_dnac2 playbook_dnac_ntp2.yml

PLAY [cisco] ****************************************************************************************************************

TASK [set the ntp server] ***************************************************************************************************
ok: [DNA_Center]

TASK [Debug] ****************************************************************************************************************
ok: [DNA_Center] => {
    "msg": {
        "changed": false,
        "failed": false,
        "message": "",
        "msg": "Already in desired state.",
        "original_message": "",
        "previous": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "inheritedGroupName": "Global",
                "inheritedGroupUuid": "-1",
                "instanceType": "ip",
                "instanceUuid": "acfb3424-2a5c-4d5d-8c52-daa774bcaa1d",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": [
                    "192.168.200.100"
                ],
                "version": 6
            }
        ],
        "proprosed": [
            {
                "groupUuid": "8be3e6af-04be-4086-94e4-15ad954b252d",
                "instanceType": "ip",
                "key": "ntp.server",
                "namespace": "global",
                "type": "ip.address",
                "value": [
                    "192.168.200.100"
                ]
            }
        ]
    }
}

PLAY RECAP ******************************************************************************************************************
DNA_Center                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
```

# 最後に
冒頭でご紹介したCiscoブログに書かれている通り、これらはあくまでもスタートで、今後もコミュニティーの中で引き続き開発と改修を続けていくとのことです。順調に開発が進み、公式のモジュールとして取り込まれることを期待しています。