Cisco DNA CenterをAnsibleから操作する(自作モジュール編)

# はじめに

以下の記事に続き、サードパーティモジュールをベースに、モジュールを自作してみました。

[Cisco DNA CenterをAnsibleから操作する(uriモジュール編)](https://qiita.com/tech_kitara/items/a571b0c51fc851728b92)
[Cisco DNA CenterをAnsibleから操作する(サードパーティモジュール編)](https://qiita.com/tech_kitara/items/ac4e58ae8f1b220930b7)

# モジュール概要
AnsibleからDNA CenterへHTTPS GETを行う汎用的なモジュール`dnac_get`を作成します。以下の通り、取得する情報毎にURLの末尾が異なるため、v1より右側のパスをオプション`url_path`で指定できるようにします。

例1）デバイス一覧の取得
https://sandboxdnac2.cisco.com/api/v1/network-device

例2）特定のIPアドレスを持つデバイス情報の取得
https://sandboxdnac2.cisco.com/api/v1/network-device/ip-address/IPアドレス

例3）デバイス台数の取得
https://sandboxdnac2.cisco.com/api/v1/network-device/count

例4）全機器のインターフェースカウントの取得
https://sandboxdnac2.cisco.com/api/v1/interface/count

# 自作モジュール
`dnac_get.py`ファイルを作成し、カスタムモジュール用のlibraryディレクトリに格納しました。

```python:dnac_get.py
#!/usr/bin/env python

from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.dnac.dnac import DnaCenter,dnac_argument_spec

def main():
    _setting_exists = False
    module_args = dnac_argument_spec
    module_args.update(
        url_path=dict(type='str',required=True)
        )

    module = AnsibleModule(
        argument_spec = module_args,
        supports_check_mode = True
        )
    #  Define Static Variables 
    url_path = module.params['url_path']

    # instansiate the dnac class
    dnac = DnaCenter(module)

    # Set API Path 
    dnac.api_path = 'api/v1/' + url_path

    # Send GET request
    output =  dnac.get_obj()

    module.exit_json(**output)

if __name__ == "__main__":
  main()
```

# Inventory/Playbook
[Cisco DNA CenterをAnsibleから操作する(uriモジュール編)](https://qiita.com/tech_kitara/items/a571b0c51fc851728b92)と同様に、デバイス情報一覧を取得してみます。

 ```:inventory_dnac1
[cisco]
DNA_Center ansible_host=sandboxdnac2.cisco.com ansible_port=443

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

Playbookは、認証トークンの明示的な取得や、URLをフルパスで指定する必要がなくなったため、全体的にシンプルになっています。

```yaml:playbook_dnac_get2.yml
---

- hosts: cisco
  gather_facts: no
  connection: local

  vars:
    device_list_extracted: [['hostname', 'managementIpAddress', 'platformId', 'softwareVersion', 'role', 'upTime']]

  tasks:
    - name: Get network device list
      dnac_get:
        host: "{{ ansible_host }}"
        port: "{{ ansible_port | int }}"
        username: "{{ username }}"
        password: "{{ password }}"
        url_path: network-device
        validate_certs: false
        use_proxy: false
      register: device_list

    - name: Extract necessary data from the device list
      set_fact:
        device_list_extracted: "{{ device_list_extracted + [[item.hostname, item.managementIpAddress, 
                                   item.platformId, item.softwareVersion, item.role, item.upTime]] }}"
      loop: "{{ device_list.response | flatten(levels=1) }}"

    - name: Display extracted device list
      debug:
        msg: "{{ device_list_extracted }}"
```

# 出力結果
前回と同様の結果が出力されました。

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

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

TASK [Get network device list] **********************************************************************************************
ok: [DNA_Center]

TASK [Extract necessary data from the device list] **************************************************************************
ok: [DNA_Center] => (item={'location': None, 'type': 'Cisco 3504 Wireless LAN Controller', 'errorCode': 'ERROR-CONNECTION-CLOSED', 
'family': 'Wireless Controller', 'role': 'ACCESS', 'lastUpdateTime': 1566722713689, (省略)

TASK [Display extracted device list] ****************************************************************************************
ok: [DNA_Center] => {
    "msg": [
        [
            "hostname",
            "managementIpAddress",
            "platformId",
            "softwareVersion",
            "role",
            "upTime"
        ],
        [
            "3504_WLC",
            "10.10.20.51",
            "AIR-CT3504-K9",
            "8.5.140.0",
            "ACCESS",
            "159 days, 23:12:12.00"
        ],
        [
            "leaf1.labb.local",
            "10.10.20.81",
            "C9300-48U",
            "16.6.4a",
            "ACCESS",
            "127 days, 15:20:37.43"
        ],
        [
            "leaf2.labb.local",
            "10.10.20.82",
            "C9300-48U",
            "16.6.4a",
            "ACCESS",
            "126 days, 22:17:53.01"
        ],
        [
            "spine1.abc.in.labb.local",
            "10.10.20.80",
            "WS-C3850-24P-L",
            "16.3.5b",
            "DISTRIBUTION",
            "159 days, 22:57:45.83"
        ]
(省略)
    ]
}

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

# 最後に
今回は簡単なモジュールでしたが、DNA Centerの機能や関連APIはどんどん拡充されていますので、DNA Centerを勉強しつつ、より高度なモジュールにもトライして、OSSにコントリビュートしたいと思います。