AnsibleでCisco機器のログ取得プレイブック作成

# はじめに
検証でNW機器を相手にしているネットワークエンジニアにとって、対象機器のログ取得は兎にも角にもまず必要になるものです。
さらに検証環境の規模が大きく対象ノードが多かったり、取得するコマンド群を反復的に取得する場合、ツールで取得することが多いかと思います。
業務ではいまだにTeratermマクロを利用して取得しているところもありますが、個人的に別のツールで取得してみたいと考えてます。
ゆくゆくはログ取得だけではなく、ログ取得以外の機能にも対応できるようにIT自動化ツールであるAnsibleを触ってみたいと思います。
本記事では自動化の一歩として、まずはTeratermで行っているログ取得をAnsibleに置き換えてみたいと思います。

# 実現したいこと
- ios, iosxr, nxosとOSが異なる機器に対してshowコマンドを取得し、ログを保存
- 取得するコマンドはOSごとにグループを作り、グループ内でも役割ごと（L3orL2など）でコマンド群を分ける
- 取得したいコマンドはグループごとにテキストのファイルに記載
- コマンド群はshowコマンドとrouteコマンドに分ける
- ログはプレイブック実行タイミングのフォルダを作成し、その中にホスト単位とコマンド単位のファイルに保存
- プレイブックはインベントリに存在しないOSがあっても動作するように汎用的に作る

# 使用するファイル
使用するファイルが多いので、githubにあげました。
特にsandboxは時間制限があるので、ラボの起動後、すぐにgit cloneでファイルが利用できるのが便利です。
下記のリポジトリをフォークして、クローンしてもらえればすぐにファイルが手元のサーバへ同期できます。

とはいえ、公開用のリポジトリは初めての作成で使い方があまりわかってないので、何かあればコメントいただけると幸いです。

https://github.com/R60-JCW/log_get


# 環境
Ciscoの[sandbox Labs](https://devnetsandbox.cisco.com/RM/Topology)を利用します。
CML2が利用でき、トポロジーも最初から組まれているCisco Modeling Labs内の、[「Multi Platform Network」](https://devnetsandbox.cisco.com/RM/Diagram/Index/685f774a-a5d6-4df5-a324-3774217d0e6b?diagramType=Topology) を利用します。
予約する際、デフォルトでは２時間利用できますが、足りない場合は上限の４時間まで使うように設定を変えることができます。

## ラボの全体図
CML2サーバとAnsibleが利用できるDevboxのサーバは別々のノードですが、マネジメントNW経由でCML2内のノードへアクセスできるようになってます。

![スクリーンショット 2021-05-25 13.37.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224980/14552830-d6fa-a0b3-2341-3f4a5e5d44bc.png)

## トポロジー
ターゲットノードのトポロジーと接続情報です。
![スクリーンショット 2021-05-25 13.51.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224980/3f5e9214-7d0c-173c-e628-8e48cc1a4a57.png)

## Ansibleバージョン
初期の状態では２系と古いので、３系にアップグレードします。

```
ansible 2.9.4
  config file = /home/developer/health-check_common/health_check/ansible.cfg
  configured module search path = [u'/home/developer/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
```

# 事前準備
今回使用するplaybookでは[ios_command](https://docs.ansible.com/ansible/2.9/modules/ios_command_module.html#ansible-collections-cisco-ios-ios-command-module)、[iosxr_command](https://docs.ansible.com/ansible/2.9/modules/iosxr_command_module.html#ansible-collections-cisco-iosxr-iosxr-command-module)、[nxos_command](https://docs.ansible.com/ansible/2.9/modules/nxos_command_module.html#ansible-collections-cisco-nxos-nxos-command-module)のモジュールを利用します。
接続方法としてparamikoを利用するため、paramikoのインストールとSSHの追加設定を行います。

## Devnetサーバにて
初期の状態ではAnsibleのバージョンが2系なので、３系にアップグレードします。

### リポジトリのクローン
上記のリポジトリをクローンし、プレイブックがある階層へ移動します。

```
$ git clone http:XXXXXX.git
$ ls
$ cd cd log_get/health_check/
```

### セットアップshellの実行
ここではAnsibleの３系へのアップグレードとgit configの設定を行なってます。
一部この記事には必要のないntc-templateのインストールも行ってますがお気にせず。

```:setup.sh
sudo yum install -y python36 python36-pip python36-devel
sudo pip-3.6 install --upgrade pip
pip uninstall ansible
pip3 install ansible
pip3 install ntc-templates
pip install --upgrade pip
ansible --version
git config --global user.email "hogehoge@example.com"
git config --global user.name "hogehoge"
```

git configを修正したい場合は任意で変更ください。
問題なければシェルを実行します。

```
$ ./setup.sh
```

実行後、Ansibleのバージョンが3系になっていればOKです。
バージョンに関するワーニングが出てますが、ログの取得には影響なしです。

```
[DEPRECATION WARNING]: Ansible will require Python 3.8 or newer on the controller starting with Ansible 2.12. Current
version: 3.6.8 (default, Sep 14 2019, 14:33:46) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]. This feature will be removed
from ansible-core in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in
ansible.cfg.
ansible [core 2.11.2]
  config file = /home/developer/log_get/health_check/ansible.cfg
  configured module search path = ['/home/developer/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/developer/py3venv/lib/python3.6/site-packages/ansible
  ansible collection location = /home/developer/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/developer/py3venv/bin/ansible
  python version = 3.6.8 (default, Sep 14 2019, 14:33:46) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
  jinja version = 3.0.1
  libyaml = True
```


## CML2サーバにて
CML2へアクセスし、Multi Platform Networkのラボ内のedge-sw01へコンソールアクセスします。
![スクリーンショット 2021-05-25 14.21.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224980/d1252fa7-ee37-a925-ec57-a81032c15f67.png)

ssh用の設定を追加します。([参考](https://sig9.hatenablog.com/entry/2015/07/15/234912))
- 対象: edge-sw01

```
conf t
ip domain-name cisco.com
ip ssh  version 2
username cisco privilege 15 password cisco
line vty 0 4 
login local
crypto key generate rsa (768bit以上)
```

- 対象: core-rtr01/02

```
crypto key generate rsa
conf t
ipv4 access-list PERMIT-SSH 10 permit ipv4 10.10.20.0 0.0.0.255 any
ssh server vrf Mgmt-intf ipv4 access-list PERMIT-SSH
```


#　Ansibleの設定
## ansible設定ファイルの作成
初めてSSH接続する場合など、警告によりplaybookが中断してしまうのを防ぐため、設定ファイルに下記を記載しておきます。[(参考)](https://qiita.com/taka379sy/items/331a294d67e02e18d68d)

```:ansible.cfg
[defaults]
host_key_checking = False

[ssh_connection]
ssh_args = -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
```

## inventoryの作成
今回はインベントリーファイルにホストの情報と変数を記載します。
IOS種別でまとめ上げ、さらに役割でまとめます。

| 親グループ | 子グループ |孫グループ|ホスト|
|:-:|:-:|:-:|:-:|
| ios  | l2|-| edge-sw01|
|ios|xe|dist|dist-rtr01/02|
|ios|xe|internet|internet-rtr01|
| iosxr  | -  |-|core-rtr01/02|
| nxos  |  - |-|dist-sw01/02|


### 変数
#### 共通
ログイン情報は全てのホストで共通のため、`[all:vars]`でまとめてしまいます。
また、接続方法はOSごとに使用するモジュールを使い分けるため、`ansible_connection=network_cli`としています。
OSの種類は各グループ変数内で定義します。

#### グループ変数
また、取得するコマンドは各グループごとにコンフィグとステータス確認用のshow系と経路確認用の２つにリストを分けてます。
それぞれ変数にして、プレイブックから呼び出して使用します。
- commands_show: *GROUPNAME*_show.txt
- commands_route: *GROUPNAME*_route.txt


### インベントリファイル
```:devnet.ini
[ios:children]
l2
xe

[xe:children]
dist
internet

[l2]
edge-sw01 ansible_host=10.10.20.172

[dist]
dist-rtr01 ansible_host=10.10.20.175
dist-rtr02 ansible_host=10.10.20.176

[internet]
internet-rtr01 ansible_host=10.10.20.181

[nxos]
dist_sw01 ansible_host=10.10.20.177
dist_sw02 ansible_host=10.10.20.178

[iosxr]
core-rtr01 ansible_host=10.10.20.173
core-rtr02 ansible_host=10.10.20.174

[all:vars]
ansible_connection=network_cli
ansible_user=cisco
ansible_ssh_user=cisco
ansible_ssh_pass=cisco

[ios:vars]
ansible_network_os=ios

[l2:vars]
commands_show=l2_show.txt
commands_route=l2_route.txt

[dist:vars]
commands_show=dist_show.txt
commands_route=dist_route.txt

[internet:vars]
commands_show=internet_show.txt
commands_route=internet_route.txt

[nxos:vars]
ansible_network_os=nxos
commands_show=nxos_show.txt
commands_route=nxos_route.txt

[iosxr:vars]
ansible_network_os=iosxr
commands_show=iosxr_show.txt
commands_route=iosxr_route.txt
```

## プレイブック
inventoryに記載された3種類のOSへ１つのプレイブックでログを取得したいと思います。
ただし、実際にコマンドを取得するタスクはOSごとに異なるモジュールを使用するため、ロールとしてタスクを分けて、
メインのプレイブック(log_get.yml)からロールを呼び出す形にしたいと思います。

### タスクステップ概要
| # | タスク | 記載場所 |
|:-:|:-|:-|
| 1 | 実行時間の変数作成  | log_get.yml  |
| 2 | ログ保存ディレクトリの変数作成  |  log_get.yml   |
| 3 | ログ保存ディレクトリ作成  |  log_get.yml   |
| 4 | ios用コマンド取得  |  get_ios_command/tasks/main.yml |
| 5 | ios用コマンド保存  |  save_cisco_command/tasks/main.yml |
| 6 | iosxr用コマンド取得  |  get_iosxr_command/tasks/main.yml |
| 7 | iosxr用コマンド保存  |  save_cisco_command/tasks/main.yml |
| 8 | nxos用コマンド取得  |  get_ios_command/tasks/main.yml |
| 9 | nxos用コマンド保存  |  save_cisco_command/tasks/main.yml |

### プレイブック内容

```:log_get.yml
---
- hosts: all     # (1)
  gather_facts: false
  tasks:
    - name: create time # ログ保存日時変数作成
      set_fact:
        exe_date: "{{ lookup('pipe', 'date +%Y%m%d_%H%M_%S') }}"
      run_once: true     # (2) 

    - name: create parent directory
      set_fact:
        logdir_date: "{{ inventory_dir }}/log/log_{{ exe_date }}"
      run_once: true

    - name: create directory for log      # ログ保存用実行日時ディレクトリ作成
      file:
        path: "{{ logdir_date }}/"
        state: directory
      register: logdir
      delegate_to: localhost      # (3)
      run_once: true

- name: ios section
  hosts: ios
  gather_facts: false
  roles:
    - { role: get_ios_command, when: "'ios' in groups and inventory_hostname in groups['ios']" }     # (4)
    - { role: save_cisco_command_logdir, when: "'ios' in groups and inventory_hostname in groups['ios']" }

- name: iosxr section
  hosts: iosxr
  gather_facts: false
  roles:
    - { role: get_iosxr_command, when: "'iosxr' in groups and inventory_hostname in groups['iosxr']" }
    - { role: save_cisco_command_logdir, when: "'iosxr' in groups and inventory_hostname in groups['iosxr']" }

- name: nxos section
  hosts: nxos
  gather_facts: false
  roles:
    - { role: get_nxos_command, when: "'nxos' in groups and inventory_hostname in groups['nxos']" }
    - { role: save_cisco_command_logdir, when: "'nxos' in groups and inventory_hostname in groups['nxos']" }
```

### 解説
#### (1)

```
- hosts: all     # (1)
```
前半のロールを呼び出す部分はhostsに全ての対象を指定しています。
これにより作成した変数(ログ保存日時や保存用のディレクトリなど)が全てのホストで利用できます。
仮に特定の対象のみhostsの対象とした場合、定義されていないホストでは変数がundifinedとなり
、変数の利用ができなくなります。

#### (2)

```
      run_once: true     # (2) 
```
`run_once`は１度のみ実行する場合に設定します。
hostsはallになってますが、全てのホストで実行されてしまうと、ホストごとにバラバラの日時情報が変数に定義されてしまうことになるため、
この設定で１度のみ実行した日時情報を全てのホストで利用するようにします。

#### (3)

```
      delegate_to: localhost      # (3)
```

`delegate_to`は指定された対象にだけ実行させる設定です。
実際にログ保存ディレクトリを作成するのはAnsibleサーバであるコントロールノード内のディレクトリのため、
localhostにすることで、Ansibleサーバ自身へディレクトリを作成します。
これがないと、各ターゲットノードにディレクトリを作成しようとしてしまいます。
また、ここでもrun_onceを設定し、localhostのみの実行としています。


#### (4)

```
  roles:
    - { role: get_ios_command, when: "'ios' in groups and inventory_hostname in groups['ios']" }     # (4)
```

各OSごとに取得用のタスクをrolesディレクティブで呼び出します。
今回のプレイブックは汎用的に使用することも考慮して、環境によっては存在しないOSがあっても動作するように作っています。
もしインベントリーファイルに定義されないOSがあっても使用するプレイブックは変更しなくても良いように
`when`を使いグループの定義とそのグループ内のホストが定義されているときのみロールを呼び出すようにしています。


## ロール
プレイブックから呼び出されるタスクを定義した各ロールです。
今回は取得用は各OSごとにタスクが分かれてますが、保存用のタスクはciscoとして共通で１つのロールを使い回してます。
取得用はOSごとに分かれてるとは言っても、異なる部分は使用するモジュールのみで、大きな違いはありません。

### ios用取得タスク
取得用のコマンドリストファイルを読み込み各行をリストに格納し、
ループで読み込んだリスト全てをcommandsのitemに代入しています。

また、取得結果はset_factで変数化して、別のロールでも使用できるようにしています。
これをshowコマンド用と経路コマンド用とで実施します。

```:roles/get_ios_command/tasks/main.yml
---
- name: get ios_command_show
  ios_command:
    commands: "{{ item }}"
  register: show_result
  loop: "{{ lookup('file', 'files/{{ commands_show }}').splitlines() }}"

- name: set show results
  set_fact:
    show_results: "{{ show_result.results }}"

- name: get ios_command_route
  ios_command:
    commands: "{{ item }}"
  register: route_result
  loop: "{{ lookup('file', 'files/{{ commands_route }}').splitlines() }}"

- name: set route results
  set_fact:
    route_results: "{{ route_result.results }}"
```

### iosxr用取得タスク

```:roles/get_iosxr_command/tasks/main.yml
---
- name: get iosxr_command_show
  iosxr_command:
    commands: "{{ item }}"
  register: show_result
  loop: "{{ lookup('file', 'files/{{ commands_show }}').splitlines() }}"

- name: set show results
  set_fact:
    show_results: "{{ show_result.results }}"

- name: get iosxr_command_route
  iosxr_command:
    commands: "{{ item }}"
  register: route_result
  loop: "{{ lookup('file', 'files/{{ commands_route }}').splitlines() }}"

- name: set route results
  set_fact:
    route_results: "{{ route_result.results }}"
```


### nxos用取得タスク

```:roles/get_nxos_command/tasks/main.yml
---
- name: get nxos_command_show
  nxos_command:
    commands: "{{ item }}"
  register: show_result
  loop: "{{ lookup('file', 'files/{{ commands_show }}').splitlines() }}"

- name: set show results
  set_fact:
    show_results: "{{ show_result.results }}"

- name: get iosxr_command_route
  nxos_command:
    commands: "{{ item }}"
  register: route_result
  loop: "{{ lookup('file', 'files/{{ commands_route }}').splitlines() }}"

- name: set route results
  set_fact:
    route_results: "{{ route_result.results }}"
```

### cisco用保存タスク
[Ansible実践ガイド](https://www.amazon.co.jp/Ansible%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E7%AC%AC3%E7%89%88-impress-top-gear/dp/4295007641)を流用しました。
取得結果をコマンドごとに保存するパートと、ホスト単位でshowコマンド全て、経路コマンド全てを保存するタスクとなってます。


```
---
- name: save show per commands
  copy:
    content: "{{ item.stdout[0] }}"
    dest: "{{ logdir.path }}/{{ inventory_hostname }}_{{ item.item | replace(' ', '_') }}.log"
  loop: "{{ show_results }}"
  loop_control:
    label: "{{ item.item }}"

- name: save route per commands
  copy:
    content: "{{ item.stdout[0] }}"
    dest: "{{ logdir.path }}/{{ inventory_hostname }}_{{ item.item | replace(' ', '_') }}.log"
  loop: "{{ route_results }}"
  loop_control:
    label: "{{ item.item }}"

- name: save show per hosts
  blockinfile:
    block: "{{ item.stdout[0] }}"
    path: "{{ logdir.path }}/{{ inventory_hostname }}_show.log"
    marker: "======================  {{ item.item }} ======================="
    create: yes
  loop: "{{ show_results }}"
  loop_control:
    label: "{{ item.item }}"


- name: save route per hosts
  blockinfile:
    block: "{{ item.stdout[0] }}"
    path: "{{ logdir.path }}/{{ inventory_hostname }}_route.log"
    marker: "======================  {{ item.item }} ======================="
    create: yes
  loop: "{{ route_results }}"
  loop_control:
    label: "{{ item.item }}"
```

## 取得用コマンドリスト

```:files/dist_route.txt
show ip route
show ip route 0.0.0.0
show ip route 172.16.101.0
show ip route 172.16.102.0
show ip route 172.16.103.0
show ip route 172.16.104.0
show ip route 172.16.105.0
show ip route 172.31.0.0
```

```:files/dist_show.txt
show run
show version
show boot
show vrf
show ip interface brief
show ip ospf neighbor
```

```:files/internet_route.txt
show ip route
show ip route 0.0.0.0
show ip route 172.16.0.0
show ip route 172.31.0.0
```

```:files/internet_show.txt
show run
show version
show boot
show vrf
show ip interface brief
```

```:files/iosxr_route.txt
show ip route
show ip route 0.0.0.0
show ip route 17216.101.0
show ip route 17216.102.0
show ip route 17216.103.0
show ip route 17216.104.0
show ip route 17216.105.0
show ip route 172.31.0.0
```

```:files/iosxr_show.txt
show run
show version
admin show platform
show vrf all
show ip interface brief
show ospf neighbor
```

```:files/l2_route.txt
show ip route
```

```:files/l2_show.txt
show run
show version
show boot
show vrf
show ip interface brief
show vlan
show spanning-tree
```

```:files/nxos_route.txt
show ip route
show ip route 0.0.0.0
show ip route 172.16.101.0
show ip route 172.16.102.0
show ip route 172.16.103.0
show ip route 172.16.104.0
show ip route 172.16.105.0
show ip route 172.31.0.0
```

```:files/nxos_show.txt
show version
show running-config
show vpc
show ip interface brief
show port-channel summary
show spanning-tree
show hsrp all
show ip ospf neighbor
```

## ディレクトリ構成
実行後の取得ログも含めた構成を以下に記載します。

```
[developer@devbox health_check]$ tree
.
├── ansible.cfg
├── devnet.ini
├── files                                                # 取得コマンドのリストファイル格納ディレクトリ
│   ├── dist_route.txt                                   # distグループの経路取得コマンドリスト
│   ├── dist_show.txt                                    # distグループのshow取得コマンドリスト
│   ├── internet_route.txt                               # internetグループの経路取得コマンドリスト
│   ├── internet_show.txt.                               # internetグループのshow取得コマンドリスト
│   ├── iosxr_route.txt                                  # iosxrグループの経路取得コマンドリスト
│   ├── iosxr_show.txt                                   # iosxrグループのshow取得コマンドリスト
│   ├── l2_route.txt                                     # l2グループの経路取得コマンドリスト
│   ├── l2_show.txt                                      # l2グループのshow取得コマンドリスト
│   ├── nxos_route.txt                                   # nxosグループの経路取得コマンドリスト
│   └── nxos_show.txt                                    # nxosグループのshow取得コマンドリスト
├── log                                                  # 取得ログ保存ディレクトリ
│   └── log_20210525_0230_43                             # playbook実行時作成ディレクトリ
│       ├── core-rtr01_admin_show_platform.log
│       ├── core-rtr01_route.log                         # ホスト毎経路全コマンド取得ログ
│       ├── core-rtr01_show.log                          # ホスト毎show全コマンド取得ログ
│       ├── core-rtr01_show_ip_interface_brief.log
│       ├── core-rtr01_show_ip_route.log
│       ├── core-rtr01_show_ip_route_0.0.0.0.log
│       ├── core-rtr01_show_ip_route_172.16.101.0.log
│       ├── core-rtr01_show_ip_route_172.16.102.0.log
│       ├── core-rtr01_show_ip_route_172.16.103.0.log
│       ├── core-rtr01_show_ip_route_172.16.104.0.log
│       ├── core-rtr01_show_ip_route_172.16.105.0.log
│       ├── core-rtr01_show_ip_route_172.31.0.0.log
│       ├── core-rtr01_show_ospf_neighbor.log
│       ├── core-rtr01_show_run.log
│       ├── core-rtr01_show_version.log
│       ├── core-rtr01_show_vrf_all.log
│       ├── core-rtr02_admin_show_platform.log
│       ├── core-rtr02_route.log
│       ├── core-rtr02_show.log
│       ├── core-rtr02_show_ip_interface_brief.log
│       ├── core-rtr02_show_ip_route.log
│       ├── core-rtr02_show_ip_route_0.0.0.0.log
│       ├── core-rtr02_show_ip_route_172.16.101.0.log
│       ├── core-rtr02_show_ip_route_172.16.102.0.log
│       ├── core-rtr02_show_ip_route_172.16.103.0.log
│       ├── core-rtr02_show_ip_route_172.16.104.0.log
│       ├── core-rtr02_show_ip_route_172.16.105.0.log
│       ├── core-rtr02_show_ip_route_172.31.0.0.log
│       ├── core-rtr02_show_ospf_neighbor.log
│       ├── core-rtr02_show_run.log
│       ├── core-rtr02_show_version.log
│       ├── core-rtr02_show_vrf_all.log
│       ├── dist-rtr01_route.log
│       ├── dist-rtr01_show.log
│       ├── dist-rtr01_show_boot.log
│       ├── dist-rtr01_show_ip_interface_brief.log
│       ├── dist-rtr01_show_ip_ospf_neighbor.log
│       ├── dist-rtr01_show_ip_route.log
│       ├── dist-rtr01_show_ip_route_0.0.0.0.log
│       ├── dist-rtr01_show_ip_route_172.16.101.0.log
│       ├── dist-rtr01_show_ip_route_172.16.102.0.log
│       ├── dist-rtr01_show_ip_route_172.16.103.0.log
│       ├── dist-rtr01_show_ip_route_172.16.104.0.log
│       ├── dist-rtr01_show_ip_route_172.16.105.0.log
│       ├── dist-rtr01_show_ip_route_172.31.0.0.log
│       ├── dist-rtr01_show_run.log
│       ├── dist-rtr01_show_version.log
│       ├── dist-rtr01_show_vrf.log
│       ├── dist-rtr02_route.log
│       ├── dist-rtr02_show.log
│       ├── dist-rtr02_show_boot.log
│       ├── dist-rtr02_show_ip_interface_brief.log
│       ├── dist-rtr02_show_ip_ospf_neighbor.log
│       ├── dist-rtr02_show_ip_route.log
│       ├── dist-rtr02_show_ip_route_0.0.0.0.log
│       ├── dist-rtr02_show_ip_route_172.16.101.0.log
│       ├── dist-rtr02_show_ip_route_172.16.102.0.log
│       ├── dist-rtr02_show_ip_route_172.16.103.0.log
│       ├── dist-rtr02_show_ip_route_172.16.104.0.log
│       ├── dist-rtr02_show_ip_route_172.16.105.0.log
│       ├── dist-rtr02_show_ip_route_172.31.0.0.log
│       ├── dist-rtr02_show_run.log
│       ├── dist-rtr02_show_version.log
│       ├── dist-rtr02_show_vrf.log
│       ├── dist_sw01_route.log
│       ├── dist_sw01_show.log
│       ├── dist_sw01_show_hsrp_all.log
│       ├── dist_sw01_show_ip_interface_brief.log
│       ├── dist_sw01_show_ip_ospf_neighbor.log
│       ├── dist_sw01_show_ip_route.log
│       ├── dist_sw01_show_ip_route_0.0.0.0.log
│       ├── dist_sw01_show_ip_route_172.16.101.0.log
│       ├── dist_sw01_show_ip_route_172.16.102.0.log
│       ├── dist_sw01_show_ip_route_172.16.103.0.log
│       ├── dist_sw01_show_ip_route_172.16.104.0.log
│       ├── dist_sw01_show_ip_route_172.16.105.0.log
│       ├── dist_sw01_show_ip_route_172.31.0.0.log
│       ├── dist_sw01_show_port-channel_summary.log
│       ├── dist_sw01_show_running-config.log
│       ├── dist_sw01_show_spanning-tree.log
│       ├── dist_sw01_show_version.log
│       ├── dist_sw01_show_vpc.log
│       ├── dist_sw02_route.log
│       ├── dist_sw02_show.log
│       ├── dist_sw02_show_hsrp_all.log
│       ├── dist_sw02_show_ip_interface_brief.log
│       ├── dist_sw02_show_ip_ospf_neighbor.log
│       ├── dist_sw02_show_ip_route.log
│       ├── dist_sw02_show_ip_route_0.0.0.0.log
│       ├── dist_sw02_show_ip_route_172.16.101.0.log
│       ├── dist_sw02_show_ip_route_172.16.102.0.log
│       ├── dist_sw02_show_ip_route_172.16.103.0.log
│       ├── dist_sw02_show_ip_route_172.16.104.0.log
│       ├── dist_sw02_show_ip_route_172.16.105.0.log
│       ├── dist_sw02_show_ip_route_172.31.0.0.log
│       ├── dist_sw02_show_port-channel_summary.log
│       ├── dist_sw02_show_running-config.log
│       ├── dist_sw02_show_spanning-tree.log
│       ├── dist_sw02_show_version.log
│       ├── dist_sw02_show_vpc.log
│       ├── edge-sw01_route.log
│       ├── edge-sw01_show.log
│       ├── edge-sw01_show_boot.log
│       ├── edge-sw01_show_ip_interface_brief.log
│       ├── edge-sw01_show_ip_route.log
│       ├── edge-sw01_show_run.log
│       ├── edge-sw01_show_spanning-tree.log
│       ├── edge-sw01_show_version.log
│       ├── edge-sw01_show_vlan.log
│       ├── edge-sw01_show_vrf.log
│       ├── internet-rtr01_route.log
│       ├── internet-rtr01_show.log
│       ├── internet-rtr01_show_boot.log
│       ├── internet-rtr01_show_ip_interface_brief.log
│       ├── internet-rtr01_show_ip_route.log
│       ├── internet-rtr01_show_ip_route_0.0.0.0.log
│       ├── internet-rtr01_show_ip_route_172.16.0.0.log
│       ├── internet-rtr01_show_ip_route_172.31.0.0.log
│       ├── internet-rtr01_show_run.log
│       ├── internet-rtr01_show_version.log
│       └── internet-rtr01_show_vrf.log
├── log_get.yml                                          # メインのプレイブック
└── roles                                                # log_get.ymlから呼び出されるロール格納ディレクトリ
    ├── get_ios_command                                  # ios用コマンド取得用ロール
    │   └── tasks
    │       └── main.yml
    ├── get_iosxr_command                                # iosxr用コマンド取得用ロール
    │   └── tasks
    │       └── main.yml
    ├── get_nxos_command                                 # nxos用コマンド取得用ロール
    │   └── tasks
    │       └── main.yml
    └── save_cisco_command_logdir                        # 取得コマンド保存用ロール
        └── tasks
            └── main.yml
```

# 実行結果
出力が多いので結果は省きます。。
取得と保存は問題なく実行できました。
各機器に初めて接続する際に、タイムアウトで最初のコマンドが取得できない場合がたまにあります。
その場合はもう一度プレイブックを実行すれば取得できますので、エラーが出たらやり直してください。

```
[developer@devbox health_check]$ ansible-playbook -i devnet.ini log_get.yml

~ snip ~

PLAY RECAP *****************************************************************************************************************
core-rtr01                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dist-rtr01                 : ok=10   changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dist-rtr02                 : ok=10   changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dist_sw01                  : ok=8    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
dist_sw02                  : ok=8    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
edge-sw01                  : ok=10   changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
internet-rtr01             : ok=10   changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```


いくつか保存できたファイルを確認します。


<details><summary>dist-rtr01の全show結果ファイル</summary><div>

```
[developer@devbox health_check]$ cat log/log_20210524_2124_20/dist-rtr01_show.log
======================  show run =======================
Building configuration...

Current configuration : 7135 bytes
!
! Last configuration change at 03:51:14 UTC Tue May 25 2021
!
version 16.11
service timestamps debug datetime msec
service timestamps log datetime msec
service call-home
platform qfp utilization monitor load 80
no platform punt-keepalive disable-kernel-core
platform console serial
!
hostname dist-rtr01
!
boot-start-marker
boot-end-marker
!
!
vrf definition Mgmt-intf
 !
 address-family ipv4
 exit-address-family
 !
 address-family ipv6
 exit-address-family
!
no logging console
enable password cisco
!
no aaa new-model
call-home
 ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
 ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
 contact-email-addr sch-smart-licensing@cisco.com
 profile "CiscoTAC-1"
  active
  destination transport-method http
  no destination transport-method email
!
!
!
!
!
!
!
no ip domain lookup
ip domain name virl.info
!
!
!
login on-success log
!
!
!
!
!
!
!
subscriber templating
!
!
!
!
!
!
multilink bundle-name authenticated
!
!
!
!
!
!
!
!
!
!
!
!
!
crypto pki trustpoint SLA-TrustPoint
 enrollment pkcs12
 revocation-check crl
!
crypto pki trustpoint TP-self-signed-2097243685
 enrollment selfsigned
 subject-name cn=IOS-Self-Signed-Certificate-2097243685
 revocation-check none
 rsakeypair TP-self-signed-2097243685
!
!
crypto pki certificate chain SLA-TrustPoint
 certificate ca 01
  30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
  32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
  6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
  3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
  43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
  526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
  82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
  CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
  1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
  4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
  7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
  68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
  C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
  C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
  DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
  06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
  4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
  03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
  604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
  D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
  467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
  7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
  5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
  80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
  418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
  D697DF7F 28
  	quit
crypto pki certificate chain TP-self-signed-2097243685
 certificate self-signed 01
  30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
  31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
  69666963 6174652D 32303937 32343336 3835301E 170D3231 30353235 30333436
  33315A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
  4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 30393732
  34333638 35308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
  0A028201 0100B4C9 690D9ACC E1D1147B 2DEC9FC1 064D56B7 B8173CFF 50A8835F
  43C12766 917222B4 0780A1DB 5E83F8E8 C19FD753 4AE241A0 6687881D 420DE0BA
  2D6AD7AE C637BEB2 2B9A8ADE 5B4D8E53 3C11F050 C801672F D83FB0F3 E7A65405
  9E11BB28 B0F14AF1 C072194F 1EA4D15A D8837106 13406FFD FD509AFD 1619FB8E
  B4041662 2F85057A 1543BF4D 9F41E930 951862F7 64B7D7FA A4B54168 AA171C42
  4110D654 0E71C1CA AAAAC141 BB13C8DA 1AD7A58D FB56A331 245927C1 0B5F8711
  09A25EE7 E30AA990 CBE36F5F F9303602 7AA974B5 001A951E D4FC7E84 D435F4EF
  415E8778 4C01AEDA 7F4C68E4 55ABC387 A263278E 3AB65D93 1A9A70FC FCA1BC98
  241312A9 B2C30203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
  301F0603 551D2304 18301680 147DB1BF 1B5280CC EB13E990 54F0DB0F 3925CDCE
  AF301D06 03551D0E 04160414 7DB1BF1B 5280CCEB 13E99054 F0DB0F39 25CDCEAF
  300D0609 2A864886 F70D0101 05050003 82010100 A29765CC 703885CE EB1C3690
  338E4823 833BD840 E93F3DE2 6C5A6ADE 4C13886D AE10B109 3095D024 99BC247B
  9871452F EBC70AA5 11033A31 EC42F398 0C0EA487 CCB6BA13 D116B9C2 E7FCAE26
  9D4E3D87 4B44276D 6408DF0F 6B14CA51 2DBC36EB 33B7F5AE 901A0F6B E4DA7895
  D5844142 C07D42FF 262EF293 9246FDC0 AD1F064D 256F5B32 4F43B98A BD3C9444
  9CC53355 630BB3DB 2D74739E 283EBB44 C5EBBAC6 CFF3219F 2811600C 9CA5B131
  53D13240 365AEDB1 605C16B1 2D5190E1 A4481C53 6BD03DD7 14218A52 458CBE8C
  35209B4D 8AAC24DE 4D4F8BCF 6717FB0A 750696D1 EE5BA9F4 9939C51B A9C0EAB4
  F99A88C8 8078D217 68526318 C366E03D C0EE8EEA
  	quit
!
license udi pid CSR1000V sn 9IHALE7J5B1
diagnostic bootup level minimal
!
spanning-tree extend system-id
memory free low-watermark processor 80526
!
!
restconf
!
username cisco privilege 15 secret 9 $9$iVecEqVTUJzHUk$EO2BfGoo4I8.wW.QanPw2rSxwy9NJt6kc3xFNEFLYSA
!
redundancy
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
interface Loopback0
 description to
 no ip address
 shutdown
!
interface GigabitEthernet1
 description to port6.sandbox-backend
 vrf forwarding Mgmt-intf
 ip address 10.10.20.175 255.255.255.0
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet2
 description L3 Link to core-rtr01
 ip address 172.16.252.21 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet3
 description L3 Link to core-rtr02
 ip address 172.16.252.25 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet4
 description L3 Link to dist-sw01
 ip address 172.16.252.2 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet5
 description L3 Link to dist-sw02
 ip address 172.16.252.10 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet6
 description L3 Link to dist-rtr02
 ip address 172.16.252.17 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
router ospf 1
 no log-adjacency-changes
 network 172.16.252.0 0.0.3.255 area 0
!
ip forward-protocol nd
no ip http server
ip http secure-server
ip route vrf Mgmt-intf 0.0.0.0 0.0.0.0 10.10.20.254
!
ip ssh server algorithm authentication password
!
!
!
!
!
!
!
control-plane
!
!
!
!
!
!
line con 0
 exec-timeout 0 0
 password cisco
 stopbits 1
line vty 0 4
 exec-timeout 720 0
 password cisco
 login local
 transport input telnet ssh
!
!
!
!
!
!
end
======================  show run =======================
======================  show version =======================
Cisco IOS XE Software, Version 16.11.01b
Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.11.1b, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2019 by Cisco Systems, Inc.
Compiled Tue 28-May-19 12:45 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2019 by cisco Systems, Inc.
All rights reserved.  Certain components of Cisco IOS-XE software are
licensed under the GNU General Public License ("GPL") Version 2.0.  The
software code licensed under GPL Version 2.0 is free software that comes
with ABSOLUTELY NO WARRANTY.  You can redistribute and/or modify such
GPL code under the terms of GPL Version 2.0.  For more details, see the
documentation or "License Notice" file accompanying the IOS-XE software,
or the applicable URL provided on the flyer accompanying the IOS-XE
software.


ROM: IOS-XE ROMMON

dist-rtr01 uptime is 38 minutes
Uptime for this control processor is 39 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: Unknown reason



This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

A summary of U.S. laws governing Cisco cryptographic products may be found at:
http://www.cisco.com/wwl/export/crypto/tool/stqrg.html

If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: N/A(Smart License Enabled)
Next reload license Level: ax


Smart Licensing Status: UNREGISTERED/No Licenses in Use

cisco CSR1000V (VXE) processor (revision VXE) with 1201684K/3075K bytes of memory.
Processor board ID 9IHALE7J5B1
6 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
3018504K bytes of physical memory.
16162815K bytes of virtual hard disk at bootflash:.
0K bytes of WebUI ODM Files at webui:.

Configuration register is 0x2102
======================  show version =======================
======================  show boot =======================
BOOT variable does not exist
CONFIG_FILE variable does not exist
BOOTLDR variable does not exist
Configuration register is 0x2102
======================  show boot =======================
======================  show vrf =======================
Name                             Default RD            Protocols   Interfaces
  Mgmt-intf                        <not set>             ipv4,ipv6   Gi1
======================  show vrf =======================
======================  show ip interface brief =======================
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       10.10.20.175    YES TFTP   up                    up
GigabitEthernet2       172.16.252.21   YES TFTP   up                    up
GigabitEthernet3       172.16.252.25   YES TFTP   up                    up
GigabitEthernet4       172.16.252.2    YES TFTP   up                    up
GigabitEthernet5       172.16.252.10   YES TFTP   up                    up
GigabitEthernet6       172.16.252.17   YES TFTP   up                    up
Loopback0              unassigned      YES unset  administratively down down
======================  show ip interface brief =======================
======================  show ip ospf neighbor =======================
Neighbor ID     Pri   State           Dead Time   Address         Interface
172.16.252.33     1   FULL/DR         00:00:37    172.16.252.18   GigabitEthernet6
172.16.101.3      1   FULL/BDR        00:00:38    172.16.252.9    GigabitEthernet5
172.16.101.2      1   FULL/BDR        00:00:33    172.16.252.1    GigabitEthernet4
172.16.252.38     1   FULL/BDR        00:00:31    172.16.252.26   GigabitEthernet3
172.16.252.37     1   FULL/BDR        00:00:39    172.16.252.22   GigabitEthernet2
======================  show ip ospf neighbor =======================

```
</div></details>

各コマンドのラベルが結果の前後に挟まれる形で保存できてます。


<details><summary>dist-rtr01のshow run結果ファイル</summary><div>



```
[developer@devbox health_check]$ cat log/log_20210525_0102_00/dist-rtr01_show_run.log
Building configuration...

Current configuration : 7127 bytes
!
! Last configuration change at 07:59:32 UTC Tue May 25 2021
!
version 16.11
service timestamps debug datetime msec
service timestamps log datetime msec
service call-home
platform qfp utilization monitor load 80
no platform punt-keepalive disable-kernel-core
platform console serial
!
hostname dist-rtr01
!
boot-start-marker
boot-end-marker
!
!
vrf definition Mgmt-intf
 !
 address-family ipv4
 exit-address-family
 !
 address-family ipv6
 exit-address-family
!
no logging console
enable password cisco
!
no aaa new-model
call-home
 ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
 ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
 contact-email-addr sch-smart-licensing@cisco.com
 profile "CiscoTAC-1"
  active
  destination transport-method http
  no destination transport-method email
!
!
!
!
!
!
!
no ip domain lookup
ip domain name virl.info
!
!
!
login on-success log
!
!
!
!
!
!
!
subscriber templating
!
!
!
!
!
!
multilink bundle-name authenticated
!
!
!
!
!
!
!
!
!
!
!
!
!
crypto pki trustpoint SLA-TrustPoint
 enrollment pkcs12
 revocation-check crl
!
crypto pki trustpoint TP-self-signed-907296531
 enrollment selfsigned
 subject-name cn=IOS-Self-Signed-Certificate-907296531
 revocation-check none
 rsakeypair TP-self-signed-907296531
!
!
crypto pki certificate chain SLA-TrustPoint
 certificate ca 01
  30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
  32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
  6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
  3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
  43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
  526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
  82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
  CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
  1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
  4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
  7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
  68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
  C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
  C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
  DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
  06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
  4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
  03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
  604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
  D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
  467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
  7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
  5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
  80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
  418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
  D697DF7F 28
  	quit
crypto pki certificate chain TP-self-signed-907296531
 certificate self-signed 01
  3082032E 30820216 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
  30312E30 2C060355 04031325 494F532D 53656C66 2D536967 6E65642D 43657274
  69666963 6174652D 39303732 39363533 31301E17 0D323130 35323530 37353434
  395A170D 33303031 30313030 30303030 5A303031 2E302C06 03550403 1325494F
  532D5365 6C662D53 69676E65 642D4365 72746966 69636174 652D3930 37323936
  35333130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030 82010A02
  82010100 F09924E9 58117EDE FCBC149D A27362AA 8269BE18 B7C63FC3 D1160CB6
  6FD851A5 02F4DD47 067910A7 05943B31 8EE23467 2D06BDDC D881AFAA 95085F76
  CDFEE9B3 607210CB 3378C5A1 E44DE2A4 15F5FE10 5B8C85DA 5015E905 DAE54417
  26581E36 4610CCD8 3A64F884 C0146EBE C64A2FA2 CE72531E AD483DA5 4105302E
  2F174554 05131D36 82EB84EC 7352DD93 586DC644 BA463821 E3EFA56E 7217DF5C
  2F5AFD27 B60167D3 E32B9B7C AF9810C0 B91D3421 079988FE 6C2F6904 4D094FF9
  1998027A AFD74F32 FB16B18A 71DB8144 158D6924 974DC755 7C653086 F46A9F5C
  300CABFF F028A646 49DB454B 6C6181F4 F94F26BE BE840390 16387492 34EB377A
  D7F67F9B 02030100 01A35330 51300F06 03551D13 0101FF04 05300301 01FF301F
  0603551D 23041830 16801442 1EB986A1 84EC50F3 F17C99CB 030CFB62 9E839630
  1D060355 1D0E0416 0414421E B986A184 EC50F3F1 7C99CB03 0CFB629E 8396300D
  06092A86 4886F70D 01010505 00038201 0100AD23 6F23D4BF B6D6DBE5 78B73E1E
  96FE7851 205D44A4 8EC29854 AD579F90 6BAEBEA3 5305C69F B7B59F9C A70D592D
  01F929F3 A2BC54BA 1252292F 1D18336F 3737D3B7 DBD79AB4 6BA908BE BF26F33B
  150C178E 820730AC A7A9629D 3CA7C283 AB4B6508 75ED0903 E37B2A32 4EA19DF8
  87FF2982 A6ED0666 A36C80A5 BE3287A6 3EADEF23 5C7DB2BB 86F55596 C01A191C
  BE1E46A6 BA25318E D924229B 423AC2BC FB0A95B1 F8B22A1F 49C29C74 877B1D65
  85D9F714 11105F2B 8E72EABC 96D29ACD DEF45325 8DDABEE5 8E34070A FD909179
  E604CA7B 2017FE2C D3830E68 63A78AD4 93C284C1 6B0473FE 82FADDAE 5A860EEB
  943B678D FC6DF737 80E0DCFD 7301839A E934
  	quit
!
license udi pid CSR1000V sn 9POVJJJOCRU
diagnostic bootup level minimal
!
spanning-tree extend system-id
memory free low-watermark processor 80526
!
!
restconf
!
username cisco privilege 15 secret 9 $9$iVecEqVTUJzHUk$EO2BfGoo4I8.wW.QanPw2rSxwy9NJt6kc3xFNEFLYSA
!
redundancy
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
interface Loopback0
 description to
 no ip address
 shutdown
!
interface GigabitEthernet1
 description to port6.sandbox-backend
 vrf forwarding Mgmt-intf
 ip address 10.10.20.175 255.255.255.0
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet2
 description L3 Link to core-rtr01
 ip address 172.16.252.21 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet3
 description L3 Link to core-rtr02
 ip address 172.16.252.25 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet4
 description L3 Link to dist-sw01
 ip address 172.16.252.2 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet5
 description L3 Link to dist-sw02
 ip address 172.16.252.10 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
interface GigabitEthernet6
 description L3 Link to dist-rtr02
 ip address 172.16.252.17 255.255.255.252
 negotiation auto
 no mop enabled
 no mop sysid
!
router ospf 1
 no log-adjacency-changes
 network 172.16.252.0 0.0.3.255 area 0
!
ip forward-protocol nd
no ip http server
ip http secure-server
ip route vrf Mgmt-intf 0.0.0.0 0.0.0.0 10.10.20.254
!
ip ssh server algorithm authentication password
!
!
!
!
!
!
!
control-plane
!
!
!
!
!
!
line con 0
 exec-timeout 0 0
 password cisco
 stopbits 1
line vty 0 4
 exec-timeout 720 0
 password cisco
 login local
 transport input telnet ssh
!
!
!
!
!
!
```

</div></details>

ファイル名とconfig内のhostnameが一致してますね。


<details><summary>dist-rtr01のshow version結果ファイル</summary><div>

```
[developer@devbox health_check]$ cat log/log_20210525_0102_00/dist-rtr01_show_version.log
Cisco IOS XE Software, Version 16.11.01b
Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.11.1b, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2019 by Cisco Systems, Inc.
Compiled Tue 28-May-19 12:45 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2019 by cisco Systems, Inc.
All rights reserved.  Certain components of Cisco IOS-XE software are
licensed under the GNU General Public License ("GPL") Version 2.0.  The
software code licensed under GPL Version 2.0 is free software that comes
with ABSOLUTELY NO WARRANTY.  You can redistribute and/or modify such
GPL code under the terms of GPL Version 2.0.  For more details, see the
documentation or "License Notice" file accompanying the IOS-XE software,
or the applicable URL provided on the flyer accompanying the IOS-XE
software.


ROM: IOS-XE ROMMON

dist-rtr01 uptime is 7 minutes
Uptime for this control processor is 9 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: Unknown reason



This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

A summary of U.S. laws governing Cisco cryptographic products may be found at:
http://www.cisco.com/wwl/export/crypto/tool/stqrg.html

If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: N/A(Smart License Enabled)
Next reload license Level: ax


Smart Licensing Status: UNREGISTERED/No Licenses in Use

cisco CSR1000V (VXE) processor (revision VXE) with 1201684K/3075K bytes of memory.
Processor board ID 9POVJJJOCRU
6 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
3018504K bytes of physical memory.
16162815K bytes of virtual hard disk at bootflash:.
0K bytes of WebUI ODM Files at webui:.

Configuration register is 0x2102[developer@devbox health_check]$
```

</div></details>

特に問題なさそう


<details><summary>dist-rtr01の全route結果ファイル</summary><div>

```
[developer@devbox health_check]$ cat log/log_20210525_0102_00/dist-rtr01_route.log
======================  show ip route =======================
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2, m - OMP
       n - NAT, Ni - NAT inside, No - NAT outside, Nd - NAT DIA
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       H - NHRP, G - NHRP registered, g - NHRP registration summary
       o - ODR, P - periodic downloaded static route, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      172.16.0.0/16 is variably subnetted, 21 subnets, 4 masks
O        172.16.101.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:04:17, GigabitEthernet4
O        172.16.102.0/24 [110/41] via 172.16.252.9, 00:04:07, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:10, GigabitEthernet4
O        172.16.103.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
O        172.16.104.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
O        172.16.105.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
C        172.16.252.0/30 is directly connected, GigabitEthernet4
L        172.16.252.2/32 is directly connected, GigabitEthernet4
O        172.16.252.4/30 [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
C        172.16.252.8/30 is directly connected, GigabitEthernet5
L        172.16.252.10/32 is directly connected, GigabitEthernet5
O        172.16.252.12/30
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
C        172.16.252.16/30 is directly connected, GigabitEthernet6
L        172.16.252.17/32 is directly connected, GigabitEthernet6
C        172.16.252.20/30 is directly connected, GigabitEthernet2
L        172.16.252.21/32 is directly connected, GigabitEthernet2
C        172.16.252.24/30 is directly connected, GigabitEthernet3
L        172.16.252.25/32 is directly connected, GigabitEthernet3
O        172.16.252.28/30
           [110/2] via 172.16.252.22, 00:05:34, GigabitEthernet2
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
O        172.16.252.32/30
           [110/2] via 172.16.252.26, 00:05:34, GigabitEthernet3
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
O        172.16.252.36/30
           [110/2] via 172.16.252.26, 00:05:41, GigabitEthernet3
           [110/2] via 172.16.252.22, 00:05:02, GigabitEthernet2
O        172.16.253.0/29 [110/2] via 172.16.252.26, 00:05:28, GigabitEthernet3
                         [110/2] via 172.16.252.22, 00:05:34, GigabitEthernet2
O E2  172.31.0.0/16 [110/20] via 172.16.252.26, 00:05:28, GigabitEthernet3
                    [110/20] via 172.16.252.22, 00:05:34, GigabitEthernet2
======================  show ip route =======================
======================  show ip route 0.0.0.0 =======================
% Network not in table
======================  show ip route 0.0.0.0 =======================
======================  show ip route 172.16.101.0 =======================
Routing entry for 172.16.101.0/24
  Known via "ospf 1", distance 110, metric 41, type intra area
  Last update from 172.16.252.9 on GigabitEthernet5, 00:01:11 ago
  Routing Descriptor Blocks:
    172.16.252.9, from 172.16.101.3, 00:01:11 ago, via GigabitEthernet5
      Route metric is 41, traffic share count is 1
  * 172.16.252.1, from 172.16.101.2, 00:04:18 ago, via GigabitEthernet4
      Route metric is 41, traffic share count is 1
======================  show ip route 172.16.101.0 =======================
======================  show ip route 172.16.102.0 =======================
Routing entry for 172.16.102.0/24
  Known via "ospf 1", distance 110, metric 41, type intra area
  Last update from 172.16.252.1 on GigabitEthernet4, 00:01:11 ago
  Routing Descriptor Blocks:
  * 172.16.252.9, from 172.16.101.3, 00:04:08 ago, via GigabitEthernet5
      Route metric is 41, traffic share count is 1
    172.16.252.1, from 172.16.101.2, 00:01:11 ago, via GigabitEthernet4
      Route metric is 41, traffic share count is 1
======================  show ip route 172.16.102.0 =======================
======================  show ip route 172.16.103.0 =======================
Routing entry for 172.16.103.0/24
  Known via "ospf 1", distance 110, metric 41, type intra area
  Last update from 172.16.252.1 on GigabitEthernet4, 00:01:07 ago
  Routing Descriptor Blocks:
  * 172.16.252.9, from 172.16.101.3, 00:01:12 ago, via GigabitEthernet5
      Route metric is 41, traffic share count is 1
    172.16.252.1, from 172.16.101.2, 00:01:07 ago, via GigabitEthernet4
      Route metric is 41, traffic share count is 1
======================  show ip route 172.16.103.0 =======================
======================  show ip route 172.16.104.0 =======================
Routing entry for 172.16.104.0/24
  Known via "ospf 1", distance 110, metric 41, type intra area
  Last update from 172.16.252.1 on GigabitEthernet4, 00:01:07 ago
  Routing Descriptor Blocks:
  * 172.16.252.9, from 172.16.101.3, 00:01:12 ago, via GigabitEthernet5
      Route metric is 41, traffic share count is 1
    172.16.252.1, from 172.16.101.2, 00:01:07 ago, via GigabitEthernet4
      Route metric is 41, traffic share count is 1
======================  show ip route 172.16.104.0 =======================
======================  show ip route 172.16.105.0 =======================
Routing entry for 172.16.105.0/24
  Known via "ospf 1", distance 110, metric 41, type intra area
  Last update from 172.16.252.1 on GigabitEthernet4, 00:01:07 ago
  Routing Descriptor Blocks:
  * 172.16.252.9, from 172.16.101.3, 00:01:12 ago, via GigabitEthernet5
      Route metric is 41, traffic share count is 1
    172.16.252.1, from 172.16.101.2, 00:01:07 ago, via GigabitEthernet4
      Route metric is 41, traffic share count is 1
======================  show ip route 172.16.105.0 =======================
======================  show ip route 172.31.0.0 =======================
Routing entry for 172.31.0.0/16
  Known via "ospf 1", distance 110, metric 20, type extern 2, forward metric 2
  Last update from 172.16.252.26 on GigabitEthernet3, 00:05:31 ago
  Routing Descriptor Blocks:
    172.16.252.26, from 172.31.252.2, 00:05:31 ago, via GigabitEthernet3
      Route metric is 20, traffic share count is 1
  * 172.16.252.22, from 172.31.252.2, 00:05:37 ago, via GigabitEthernet2
      Route metric is 20, traffic share count is 1
======================  show ip route 172.31.0.0 =======================
```

</div></details>
showコマンド同様の結果です。


<details><summary>dist-rtr01のshow ip route結果ファイル</summary><div>

```
[developer@devbox health_check]$ cat log/log_20210525_0102_00/dist-rtr01_show_ip_route.log
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2, m - OMP
       n - NAT, Ni - NAT inside, No - NAT outside, Nd - NAT DIA
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       H - NHRP, G - NHRP registered, g - NHRP registration summary
       o - ODR, P - periodic downloaded static route, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      172.16.0.0/16 is variably subnetted, 21 subnets, 4 masks
O        172.16.101.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:04:17, GigabitEthernet4
O        172.16.102.0/24 [110/41] via 172.16.252.9, 00:04:07, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:10, GigabitEthernet4
O        172.16.103.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
O        172.16.104.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
O        172.16.105.0/24 [110/41] via 172.16.252.9, 00:01:10, GigabitEthernet5
                         [110/41] via 172.16.252.1, 00:01:05, GigabitEthernet4
C        172.16.252.0/30 is directly connected, GigabitEthernet4
L        172.16.252.2/32 is directly connected, GigabitEthernet4
O        172.16.252.4/30 [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
C        172.16.252.8/30 is directly connected, GigabitEthernet5
L        172.16.252.10/32 is directly connected, GigabitEthernet5
O        172.16.252.12/30
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
C        172.16.252.16/30 is directly connected, GigabitEthernet6
L        172.16.252.17/32 is directly connected, GigabitEthernet6
C        172.16.252.20/30 is directly connected, GigabitEthernet2
L        172.16.252.21/32 is directly connected, GigabitEthernet2
C        172.16.252.24/30 is directly connected, GigabitEthernet3
L        172.16.252.25/32 is directly connected, GigabitEthernet3
O        172.16.252.28/30
           [110/2] via 172.16.252.22, 00:05:34, GigabitEthernet2
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
O        172.16.252.32/30
           [110/2] via 172.16.252.26, 00:05:34, GigabitEthernet3
           [110/2] via 172.16.252.18, 00:06:42, GigabitEthernet6
O        172.16.252.36/30
           [110/2] via 172.16.252.26, 00:05:41, GigabitEthernet3
           [110/2] via 172.16.252.22, 00:05:02, GigabitEthernet2
O        172.16.253.0/29 [110/2] via 172.16.252.26, 00:05:28, GigabitEthernet3
                         [110/2] via 172.16.252.22, 00:05:34, GigabitEthernet2
O E2  172.31.0.0/16 [110/20] via 172.16.252.26, 00:05:28, GigabitEthernet3
                    [110/20] via 172.16.252.22, 00:05:34, GigabitEthernet2[developer@devbox health_check]$
```

</div></details>
大丈夫ですね。



<details><summary>dist-rtr01の詳細経路結果ファイル</summary><div>

```
[developer@devbox health_check]$ cat log/log_20210525_0102_00/dist-rtr01_show_ip_route_172.31.0.0.log
Routing entry for 172.31.0.0/16
  Known via "ospf 1", distance 110, metric 20, type extern 2, forward metric 2
  Last update from 172.16.252.26 on GigabitEthernet3, 00:05:31 ago
  Routing Descriptor Blocks:
    172.16.252.26, from 172.31.252.2, 00:05:31 ago, via GigabitEthernet3
      Route metric is 20, traffic share count is 1
  * 172.16.252.22, from 172.31.252.2, 00:05:37 ago, via GigabitEthernet2
      Route metric is 20, traffic share count is 1[developer@devbox health_check]$
```


</div></details>
問題なし。


# さいごに
取得したいコマンドをテキストファイルに記載して、無事にログを取得することができました。
これでプレイブックや変数ファイルをいじることなく、取得したいコマンドのリストファイルだけの更新でコマンドの増減に対応できると思います。
気になることといえば、コマンドを実行したコマンドプロンプトを含む行が結果に記載されないため、
コマンドだけのログを見たときに、本当にファイル名と同じ結果なのか？とか、みている結果はどのノードのだっけ？とかちょっと迷ったりするのかな、と思いました。
(show runとかはhostnameがあるので判断可能ですね。)

作り込みで確認できれば問題ないですが、後からログだけみたときにどうやってこの対象のログだと証明できるのかなーとか思ったり。
ログはansibleの実行結果を含む形で保存した方が安心なのかなーと心配性な私は思っちゃいました。

今後は、ログをパースするためのプレイブックを作ったり、jinja2のtemplateファイルを利用した変数ファイル自動生成にチャレンジしたいと思います。