Ansibleを使ってStorage(ONTAP)を管理する6【loopについて】

## はじめに

本記事では[Ansibleを使ってStorage(ONTAP)を管理する4【変数編2】](https://qiita.com/s_yosh1d/items/6ce192226ad00c51c236)の続きとして、ONTAP管理の為のインベントリファイルやPlaybook作成に関する内容となります。

今回は範囲は狭いですが、**Taskの繰り返しについて記載いたします。** 
Ansibleではwith_listやwith_itemsのような形でTaskを繰り返し処理を実施していましたが、**この記事ではloopに関する内容になります。**

## 何をしたい？できる？
- ONTAPのVolume作成操作の際に、複数Volume作成を1つのPlaybookで実施したい
- Volumeのリストを別ファイルで作成し、Playbookで呼び出して利用したい
- 複数作成したVolumeに複数のQtreeを作成したい(loop利用で作成したい)


## loopについて
Ansiblenにおけるloopは、Taskを繰り返し実行するために使用されます。
loopを使用する事でリストや配列などのデータ構造を簡単に反復処理ができ、以下のようにONTAPのVolumeを名前部分だけ変えて複数作成したいような場合に利用できます。

<img width="700" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/5acc7daf-405a-478d-4d45-1b6c4ddecbc1.png>

**loopへ記載した値を使用する場合は、{{item}}という変数を使います。**

<img width="500" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/444c64c5-e16e-4a61-a981-80af571e7122.png>

また、loop自体の管理としてloop controlが用意されています。
|オプション |説明　|
|:--------|:--------|
|loop_var|loopで指定したリストに変数名をつけ、特に多重繰り返しを実施したい際に利用します。|
|label|loop内の特定の値をラベルとして定義します|
|pause|loopの実行間隔を指定します(秒単位)|
|index_var|loopされる回数をインデックス表示します|
|extended|trueを指定する事で、拡張loop変数を利用できるようになります。|


## 記事における環境情報
本記事では、以下の環境で実施した内容となります。
- Rocky Linux : 9.1
- Python      : 3.9.14
- Ansible (Core): 2.14.1
- netapp-lib    : 2021.6.25
- netapp.ontap  : 22.1.0


環境のイメージとしては以下の通りです。
|<img width="500" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/6384ee71-5ab1-ca65-e444-f51aad7fc43b.png>
|:-:|

## 設定手順
**まずは、Playbook内にloopで利用する複数のVolume名を記載して挙動を確認してみます。**
そして、別ファイルに作成する複数のVolume名を記載し動作するのか確認し、最後に多重繰り返しを実施してVolumenへのQtree作成を実施します。


### 1. インベントリファイルの確認

以下のような形でインベントリファイルを記載しています。
- 全ホストはallというグループに所属

```bash:
> cat inventory.yml
---
all:
  hosts:
    Ansible:
      ansible_host: 172.16.10.180
    Linux:
      ansible_host: 172.16.10.148
...
```

### 2. Playbook内にloopを記載して作成
ここでは以下の名前部分のみ変更されてVolume作成を繰り返すPalybookを作成します。

・**vol101とvol102というVolumeを2つ作成**
・**Volume名とJinctuionPathは同じ名前にする**


```bash:
>  cat playbook_volume_create.yml
---
- name: Create volume on ONTAP SVM
  hosts: localhost
  connection: local
  gather_facts: false

  vars:
    svm_host: 172.16.10.147
    svm_user: testuser
    svm_password: XXXXXXX
    svm_protocol: https
    vserver: cifs100
    size: 5
    size_unit: gb
    aggregate_name: aggr1_node2
    export_policy: default

  tasks:
  - name: Create volume
    na_ontap_volume:
      state: present
      hostname: "{{ svm_host }}"
      username: "{{ svm_user }}"
      password: "{{ svm_password }}"
      https: "{{ svm_protocol == 'https' }}"
      vserver: "{{ vserver }}"
      size: "{{ size }}"
      size_unit: "{{ size_unit }}"
      aggregate_name: "{{ aggregate_name }}"
      name: "{{ item }}"
      junction_path: "/{{ item }}"
      export_policy: "{{ export_policy }}"
      validate_certs: false
      use_rest: Always
    loop:
       - vol101
       - vol102
```

### 3. Playbookの実行
Playbookに記載されているvol101, vol102の作成が実施されます。

```bash:
>  ansible-playbook -i inventory.yml -u user01 playbook_volume_create.yml --ask-pass
SSH password:

PLAY [Create volume on ONTAP SVM] ***************************************************************************************************

TASK [Create volume] ****************************************************************************************************************
changed: [localhost] => (item=vol101)
changed: [localhost] => (item=vol102)

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

Storage側からも作成が確認できます。
```bash:
> volume show -fields volume,junction-path
vserver volume       junction-path
------- ------------ -------------
cifs100 cifs100_root /
cifs100 vol101       /vol101
cifs100 vol102       /vol102
3 entries were displayed.
```



### 4. 値をハッシュリストとして参照してPlaybookの実行
ここでは以下の内容で繰り返すものを作成します。
・vol101とvol102というVolumeを2つ作成
・**Volume名とJinctuionPathは別の名前にする**
 (vol101のPathを/cifs101のような形)

実現する為にloop部分にハッシュリストで値を記載し、Subキーを参照することができます。
(item.nameやitem.pathのような形で値を参照できる)


```bash:
>  cat playbook_volume_create2.yml
---
- name: Create volume on ONTAP SVM
  hosts: localhost
  connection: local
  gather_facts: false

  vars:
    svm_host: 172.16.10.147
    svm_user: testuser
    svm_password: XXXXXX
    svm_protocol: https
    vserver: cifs100
    size: 5
    size_unit: gb
    aggregate_name: aggr1_node2
    export_policy: default

  tasks:
  - name: Create volume
    na_ontap_volume:
      state: present
      hostname: "{{ svm_host }}"
      username: "{{ svm_user }}"
      password: "{{ svm_password }}"
      https: "{{ svm_protocol == 'https' }}"
      vserver: "{{ vserver }}"
      size: "{{ size }}"
      size_unit: "{{ size_unit }}"
      aggregate_name: "{{ aggregate_name }}"
      name: "{{ item.name }}"
      junction_path: "/{{ item.path }}"
      export_policy: "{{ export_policy }}"
      validate_certs: false
      use_rest: Always
    loop:
      - {name: 'vol101', path: 'cifs101'}
      - {name: 'vol102', path: 'cifs102'}
```

Volumeの作成を実行します。
```bash:
>  ansible-playbook -i inventory.yml -u user01 playbook_volume_create2.yml --ask-pass
SSH password:

PLAY [Create volume on ONTAP SVM] ***************************************************************************************************

TASK [Create volume] ****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

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

Storage側からも作成が確認できます。
```bash:
> volume show -fields volume,junction-path
vserver volume       junction-path
------- ------------ -------------
cifs100 cifs100_root /
cifs100 vol101       /cifs101
cifs100 vol102       /cifs102
3 entries were displayed.
```


### 5. 外部ファイルにloopする変数を記載してPlaybookの実行
ここでは以下の内容で繰り返すものを作成します。
・vol101とvol102というVolumeを2つ作成
・**Volume名とJinctuionPathはPlaybookとは別のファイルに記載**
・**Volume名とJinctuionPathは別の名前にする**
 (vol101のPathを/cifs101のような形)

まず、Volume名とJinctuionPathを記載したファイル(loop_data.yml)を作成します。

```bash:
>  cat loop_data.yml
---
    volumes:
      - {name: 'vol101', path: 'cifs101'}
      - {name: 'vol102', path: 'cifs102'}
```

Playbookは以下のように記載します。
- taskにinclude_varsを追記して、作成した変数記載ファイルの指定
- loopの後に "{{ volumes }}"という変数記載ファイル内のディレクティブ(キーワード)を記載

```bash:
>  cat playbook_volume_create3.yml
---
- name: Create volume on ONTAP SVM
  hosts: localhost
  connection: local
  gather_facts: false

  vars:
    svm_host: 172.16.10.147
    svm_user: testuser
    svm_password: XXXXXXX
    svm_protocol: https
    vserver: cifs100
    size: 5
    size_unit: gb
    aggregate_name: aggr1_node2
    export_policy: default

  tasks:

  - name: include_vars
    include_vars:
      file: loop_data.yml

  - name: Create volume
    na_ontap_volume:
      state: present
      hostname: "{{ svm_host }}"
      username: "{{ svm_user }}"
      password: "{{ svm_password }}"
      https: "{{ svm_protocol == 'https' }}"
      vserver: "{{ vserver }}"
      size: "{{ size }}"
      size_unit: "{{ size_unit }}"
      aggregate_name: "{{ aggregate_name }}"
      name: "{{ item.name }}"
      junction_path: "/{{ item.path }}"
      export_policy: "{{ export_policy }}"
      validate_certs: false
      use_rest: Always
    loop: "{{ volumes }}"
```



Volumeの作成を実行します。

```bash:
>  ansible-playbook -i inventory.yml -u user01 playbook_volume_create3.yml --ask-pass
SSH password:

PLAY [Create volume on ONTAP SVM] ***************************************************************************************************

TASK [include_vars] *****************************************************************************************************************
ok: [localhost]

TASK [Create volume] ****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

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

```


### 6. loop_controlによる多重繰り返し(Qtree作成部分)
ここでは以下の内容で繰り返すものを作成します。
・**vol101とvol102というVolumeを2つ作成**
・**各Volumeにtree01, tree02, tree03というQtreeを作成(Security Styleは夫々変える)**
・**loop_varを利用して多重繰り返し**
・Qtree作成タスクは別ファイル(qtree_create.yml)で作成
・Volume名とJinctuionPathやQtree名等はPlaybookとは別のファイルに記載
・Volume名とJinctuionPathは別の名前にする
 (vol101のPathを/cifs101のような形)
<img width="600" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/e3b991ad-97dc-4b3a-6e75-fa3004605b96.png>



まず、Volume名やQtree名を記載したファイル(loop_data.yml)を作成します。

```bash:
>  cat loop_data.yml
---
    volumes:
      - {name: 'vol101', path: 'cifs101'}
      - {name: 'vol102', path: 'cifs102'}

    qtrees:
      - {name: 'tree01', security_style: 'ntfs'}
      - {name: 'tree02', security_style: 'unix'}
      - {name: 'tree03', security_style: 'ntfs'}
```

Playbookは以下のように記載します。
- taskにinclude_varsを追記して、作成した変数記載ファイルの指定
- Volume作成taskのloopの後に "{{ volumes }}"という変数記載ファイル内のディレクティブ(キーワード)を記載
- qtree_create.ymlというQtree作成taskは別ファイルで作成
- qtree_create.ymlを呼び出して実行するtaskのloopの後に "{{ qtrees }}"を指定
- loop_controlでloop_varを使用し、qtreeという変数名にする

<img width="700" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/b255d3b2-1060-8980-d65b-18f557811f7a.png>

Qtree作成用の別ファイルは以下の通りです。
```bash:
>  cat qtree_create.yml
  - name: Create Qtree
    na_ontap_qtree:
      state: present
      hostname: "{{ svm_host }}"
      username: "{{ svm_user }}"
      password: "{{ svm_password }}"
      https: "{{ svm_protocol == 'https' }}"
      vserver: "{{ vserver }}"
      name: "{{ qtree.name }}"
      flexvol_name: "{{ item.name }}"
      security_style: "{{ qtree.security_style }}"
      validate_certs: false
      use_rest: Always
    loop: "{{ volumes }}"
```

Ansibleコマンド実行の際に指定するplaybookは以下の通りです。
```bash:
>  cat playbook_volume_create4.yml
---
- name: Create volume on ONTAP SVM
  hosts: localhost
  connection: local
  gather_facts: false

  vars:
    svm_host: 172.16.10.147
    svm_user: testuser
    svm_password: XXXXXX
    svm_protocol: https
    vserver: cifs100
    size: 5
    size_unit: gb
    aggregate_name: aggr1_node2
    export_policy: default

  tasks:

  - name: include_vars
    include_vars:
      file: loop_data.yml

  - name: Create volume
    na_ontap_volume:
      state: present
      hostname: "{{ svm_host }}"
      username: "{{ svm_user }}"
      password: "{{ svm_password }}"
      https: "{{ svm_protocol == 'https' }}"
      vserver: "{{ vserver }}"
      size: "{{ size }}"
      size_unit: "{{ size_unit }}"
      aggregate_name: "{{ aggregate_name }}"
      name: "{{ item.name }}"
      junction_path: "/{{ item.path }}"
      export_policy: "{{ export_policy }}"
      validate_certs: false
      use_rest: Always
    loop: "{{ volumes }}"

  - name: Include Create Qtrees
    include_tasks: qtree_create.yml
    loop: "{{ qtrees }}"
    loop_control:
        loop_var: qtree
```



Volumeの作成を実行します。

```bash:
>  ansible-playbook -i inventory.yml -u user01 playbook_volume_create4.yml --ask-pass
SSH password:

PLAY [Create volume on ONTAP SVM] ***************************************************************************************************

TASK [include_vars] *****************************************************************************************************************
ok: [localhost]

TASK [Create volume] ****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

TASK [Include Create Qtrees] ********************************************************************************************************
included: /home/ansible_user10/ansible_work5/qtree_create.yml for localhost => (item={'name': 'tree01', 'security_style': 'ntfs'})
included: /home/ansible_user10/ansible_work5/qtree_create.yml for localhost => (item={'name': 'tree02', 'security_style': 'unix'})
included: /home/ansible_user10/ansible_work5/qtree_create.yml for localhost => (item={'name': 'tree03', 'security_style': 'ntfs'})

TASK [Create Qtree] *****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

TASK [Create Qtree] *****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

TASK [Create Qtree] *****************************************************************************************************************
changed: [localhost] => (item={'name': 'vol101', 'path': 'cifs101'})
changed: [localhost] => (item={'name': 'vol102', 'path': 'cifs102'})

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

```

Storage側からも作成が確認できます。
```bash:
>  volume show -fields volume,junction-path
vserver volume       junction-path
------- ------------ -------------
cifs100 cifs100_root /
cifs100 vol101       /cifs101
cifs100 vol102       /cifs102
3 entries were displayed.

>   qtree show
Vserver    Volume        Qtree        Style        Oplocks   Status
---------- ------------- ------------ ------------ --------- --------
cifs100    cifs100_root  ""           ntfs         enable    normal
cifs100    vol101        ""           ntfs         enable    normal
cifs100    vol101        tree01       ntfs         enable    normal
cifs100    vol101        tree02       unix         enable    normal
cifs100    vol101        tree03       ntfs         enable    normal
cifs100    vol102        ""           ntfs         enable    normal
cifs100    vol102        tree01       ntfs         enable    normal
cifs100    vol102        tree02       unix         enable    normal
cifs100    vol102        tree03       ntfs         enable    normal
9 entries were displayed.
```

## 参考及びリンク
[Ansibleを使ってStorage(ONTAP)を管理する1 【Rocky LinuxへAnsible Install編](https://qiita.com/s_yosh1d/items/f437b8ea284a6e90936f)】

[Ansibleを使ってStorage(ONTAP)を管理する2【ChatGPTでVolume作成用Playbookを作成】](https://qiita.com/s_yosh1d/items/6774760fd046c0314618)

[Ansibleを使ってStorage(ONTAP)を管理する3 【変数編1](https://qiita.com/s_yosh1d/items/62b2c89250ccc7698e70)】

[Ansibleを使ってStorage(ONTAP)を管理する4 【変数編2](https://qiita.com/s_yosh1d/items/6ce192226ad00c51c236)】