Ansibleでリストの比較

# 背景
- ansibleでとあるクラウド環境内の各ホストの状態を定期チェックさせたい
- できれば、前回の状態と比較して状態が変わったものだけ出したい
- jinja2の **difference()** フィルターでリストを比較し、差分を表示できるようだ

# 環境
- centos 7.6
- ansible 2.9
- Mattermost 5.33.1(docker の mattermost-preview イメージを利用。通知動作の確認用)

# ファイル構成
| ファイル名 | 説明 |
| :--- | :--- |
| unreach_var.yml | 比較するリスト内容を記載したファイル |
| unreach_check.yml | リストを比較するプレイブック |

> 本当はmongodbにホストのスーテタス情報を格納しているので、mongoのクエリで最新の状態を取得して、前回値と比較する予定。

# 手順
## 比較するデータの準備
まずは、テスト用のリストデータを準備。
unreachable(疎通性がない)状態を検知したホストのリストとして登録。
before に前回のリスト、after に最新のリストが格納されている（ということにする）。

```yaml:unreach_var.yml
---

before:
  - { tenantName: "tenant1", hostName: "tena1_h1" }
  - { tenantName: "tenant1", hostName: "tena1_h2" }
  - { tenantName: "tenant1", hostName: "tena1_h4" }


after:
  - { tenantName: "tenant1", hostName: "tena1_h1" }
  - { tenantName: "tenant1", hostName: "tena1_h2" }
  - { tenantName: "tenant1", hostName: "tena1_h3" }
  - { tenantName: "tenant2", hostName: "tena2_h1" }
```

差分内容は下記の通り↓

- 新たに unreachable になった 
    - { tenantName: "tenant1", hostName: "tena1_h3" }
    - { tenantName: "tenant2", hostName: "tena2_h1" }
- unreachable ではなくなった（回復した）
    - { tenantName: "tenant1", hostName: "tena1_h4" }

これらの情報を抽出し、通知できるとうれしい

## Playbook の作成
```yaml:unreachable_check.yml
---

- name: Testing difference filter
  hosts: localhost
  gather_facts: false
  vars_files:
    - unreach_var.yml
  tasks:
    - name: test ### before の内容確認
      debug:
        var: before

    - name: test ### after の内容確認
      debug:
        var: after

    - name: check1 ### before と after に差分がないかチェック
      assert:
        that:
          - before != after
        fail_msg: "No difference!"
        success_msg: "Difference!"

    - name: increace check ### after に新たに追加されたものをリスト
      debug:
        var: after | difference(before)

    - name: fact increase ### 変数名つけてあげる(incr)
      set_fact:
        incr: "{{ after | difference(before) }}"

    - name: increace num check ### after に新たに追加されたホストの数をカウント
      debug:
        var: incr | length

    - name: decreace check ### before からいなくなった(回復した)ホストをリスト
      debug:
        var: before | difference(after)

    - name: fact decrease ### 変数名つけてあげる(decr)
      set_fact:
        decr: "{{ before | difference(after) }}"

    - name: decreace num check ### before からいなくなった(回復した)ホストの数をカウント
      debug:
        var: decr | length

    - name: notify increace ### after に新たに追加されたホスト情報をmattermostに通知
      mattermost:
        url: http://himitu(適宜修正)
        api_key: himitu(適宜修正)
        text: |
          ### {{ incr | length }} 台のホストが新たに Unreachable になりました

          - ホスト情報：
          | tenantName | hostName |
          | :---: | :---: |
          {% for id in range(0, incr | length) %}
              | {{ incr[id].tenantName }} | {{ incr[id].hostName }} |
          {% endfor %}
      when: "{{ incr | length }} > 0"

    - name: notify decreace ### before からいなくなった(回復した)ホスト情報をmattermostに通知
      mattermost:
        url: http://himitu(適宜修正)
        api_key: himitu(適宜修正)
        text: |
          ### {{ decr | length }} 台のホストが Unreachable から回復しました

          - ホスト情報：
          | tenantName | hostName |
          | :---: | :---: |
          {% for id in range(0, decr | length) %}
              | {{ decr[id].tenantName }} | {{ decr[id].hostName }} |
          {% endfor %}
      when: "{{ decr | length }} > 0"
```

## Playbook 実行
```bash
[himitu@himitu]# ansible-playbook unreach_check.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does
not match 'all'

PLAY [Testing difference filter] ***************************************************************************

TASK [test] ************************************************************************************************
ok: [localhost] => {
    "before": [
        {
            "hostName": "tena1_h1", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena1_h2", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena1_h4", 
            "tenantName": "tenant1"
        }
    ]
}

TASK [test] ************************************************************************************************
ok: [localhost] => {
    "after": [
        {
            "hostName": "tena1_h1", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena1_h2", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena1_h3", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena2_h1", 
            "tenantName": "tenant2"
        }
    ]
}

TASK [check1] **********************************************************************************************
ok: [localhost] => {
    "changed": false, 
    "msg": "Difference!"
}

TASK [increace check] **************************************************************************************
ok: [localhost] => {
    "after | difference(before)": [
        {
            "hostName": "tena1_h3", 
            "tenantName": "tenant1"
        }, 
        {
            "hostName": "tena2_h1", 
            "tenantName": "tenant2"
        }
    ]
}

TASK [fact increase] ***************************************************************************************
ok: [localhost]

TASK [increace num check] **********************************************************************************
ok: [localhost] => {
    "incr | length": "2"
}

TASK [decreace check] **************************************************************************************
ok: [localhost] => {
    "before | difference(after)": [
        {
            "hostName": "tena1_h4", 
            "tenantName": "tenant1"
        }
    ]
}

TASK [fact decrease] ***************************************************************************************
ok: [localhost]

TASK [decreace num check] **********************************************************************************
ok: [localhost] => {
    "decr | length": "1"
}

TASK [notify increace] *************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}.
Found: {{ incr | length }} > 0
ok: [localhost]

TASK [notify decreace] *************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}.
Found: {{ decr | length }} > 0
ok: [localhost]

PLAY RECAP *************************************************************************************************
localhost                  : ok=11   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

```
最後の Mattertmost への通知で Waring 出てますがスルーしていただけると幸いです。

## Mattermost 確認
![スクリーンショット 2021-05-20 22.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1035995/adf3faee-8111-1b2e-df5c-a86dc8add6bd.png)
無事に通知してくれました。

# まとめ
difference() フィルター思ったより簡単に使えました。
そもそものチェックの仕方がこれでいいかという方がむしろ気になっていますので、誰かよい方法教えてください。
>現在は API でホストのステータスを取得(jsonデータ) してmongodb に入れてます。