Ansibleを使ってStorage(ONTAP)を管理する7【Role編】

## はじめに

本記事では過去のAnsible記事の続きとして、ONTAP管理の為にRole機能を利用する方法について説明します。

Ansibleの**Role機能は、作成したPlaybookを再利用したり構成管理を簡素化するために役立つ機能**です。関連するタスク、変数、テンプレート、ファイルなどの要素をグループ化し、再利用可能な単位として定義します。

例えばONTAP管理では、SVMの作成からVolume作成、Export設定までの処理を1つのPlaybookに記述するのではなく、個別に作成して必要な処理を呼び出して利用します。これにより、**SVMを作成するたびにPlaybookをコピーして編集する必要がなくなります。**
<img width="700" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/2e3e94b4-0c94-ba1c-16b9-f9aadae40efe.png>

## 何をしたい？できる？
- Role機能を使ってAnsibleのPlaybookを実行する
- ONTAP操作(NFS用SVMの一連の作業)をRole機能を使って実施してみる

## AnsibleのRoleについて
**ONTAPで各ユーザに対してNFSファイル共有環境の作成を行う場合に、以下のような操作が必要となります**が、Ansibleで実行する場合には、Playbookに操作したい内容をTaskとして記載し実行する形になるかと思います。

**・BroadcastDomain設定+VLAN設定**
**・SVM作成**
**・Volume作成**
**・LIF作成**
**・NFS作成+Export設定**

ただ、1つのPlaybookへ上記のTaskを全て記載した場合には一連の処理が実施される事となり、Volume作成だけを追加で実施したい場合などには、同じPlaybookの利用ができないといった再利用できないものとなってしまいます。

Ansibleでは、**Playbook を分割し再利用性を高めるための「Roles」という仕組みがあり、Roleに従ったディレクトリ構成にすることで、自動で変数をファイルから読み込んでくれたり、転送するファイルの相対パス指定が楽になったりといった恩恵を受けることができます。**

Roleを利用するにあたっての各種設定ファイルについては、個々に作成することも可能ですが
ansible-galaxyコマンドを使うことでひな型を作成することができます。

```bash:
>  mkdir roles
>  cd roles

>  ansible-galaxy init svm_create
- Role svm_create was created successfully

>  tree svm_create/
svm_create/
|-- README.md
|-- defaults
    |-- main.yml
|-- files
|-- handlers
    |--  main.yml
|-- meta
    |-- main.yml
|-- tasks
    |-- main.yml
|-- templates
|-- tests
    |-- inventory
    |-- test.yml
|-- vars
    |-- main.yml

>  ansible-galaxy init vlan_create
- Role vlan_create was created successfully

>  ls
svm_create  vlan_create
...
```



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

環境のイメージとしては以下の通りです。
<font color="Red"> **本記事では、Roleのひな型作成後は、下図の赤文字ファイルの編集を実施します。**</font>

<img width="700" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/0e1b9dfa-17c1-6964-8170-03b3eb5a4052.png>

ファイル間の連携イメージとしては以下のような形です。
<img width="700" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/cab04cfb-a418-0e2b-a466-df5d048c7c73.png>


## 設定手順
**各Roleの設定を実施し、以下のNFS用SVM作成に必要となる項目の流れについて記載します。**
- Broadcast Domainの作成
- VLAN Interfaceを作成しBroadcast Domainに追加
- SVM作成
- Volume作成
- LIF作成
- NFS作成
- Export設定

**必要な作業としては以下の通りです。**
1. inventory.ymlの作成
2. roles以下に上記の項番のRoleをansible-galaxy init <role名>で作成
3. 作成したRoleにあるtaskディレクトリ上のmain.ymlへ処理のテンプレート作成
   (taskディレクトリ上のmain.ymlにはloopを利用できる形で記載)
4. 変数用のvar.ymlを作成
5. Playbookを作成し、利用するrole名と変数ファイルの記載



### 1. インベントリファイルの作成

以下のような形でインベントリファイルを記載します。
- 全ホストはallというグループに所属
  (ONTAP管理しかしないので、操作しているAnsibleのIPのみ記載)

```bash:
> cat inventory.yml
---
all:
  hosts:
    Linux:
      ansible_host: 192.168.1.100
...
```

### 2. 必要となるRoleのひな型をansible-galaxy init で作成

NFS用のSVM作成に必要となる以下のRoleを作成します。
- Broadcast Domainの作成
- VLAN Interfaceを作成しBroadcast Domainに追加
- SVM作成
- Volume作成
- LIF作成
- NFS作成
- Export設定

```bash:
>  ls
inventory.yml  roles  svm_create_playbook.yml  vars.yml

>  cd roles

>  ansible-galaxy init vlan_create
- Role vlan_create was created successfully
(中略)

>  ls roles
bcdomain_create  expolicy_create  lif_create  nfs_create  route_create  svm_create  vlan_create  volume_create
```

### 3. Roleにあるtaskディレクトリ上のmain.ymlへ処理の記載

各Roleの**tasksディレクトリに生成されたひな形のmain.ymlへONTAP moduleの記載を実施します。**

例えば、Volume作成のna_ontap_volumeに関する内容のみroles/volume_create/tasks/main.ymlへ記載といったように、過去記事の、Playbookに一括で記載していたtasks部分を、各Roleのmain.ymlに個々に記載する形です。

また、**設定項目を複数指定できるようにloop利用の記載も併せて実施し**、REST APIを明示的に使うように指定しています。



#### 3-1. Broadcast Domain作成の記載例
```bash:
>  cat roles/bcdomain_create/tasks/main.yml
---
# tasks file for bcdomain_create

- name: create broadcast domain
  netapp.ontap.na_ontap_broadcast_domain:
    state: present
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    hostname: "{{ netapp_hostname }}"
    name: "{{ item.bc_name }}"
    mtu: "{{ item.mtu }}"
    ipspace: "{{ item.ipspace_name }}"
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ bcdomain }}"
```
#### 3-2. VLAN作成+Broadcast Domain追加の記載例
```bash:
>  cat roles/vlan_create/tasks/main.yml
---
# tasks file for vlan_create

- name: Create and add vlan to broadcast domain - REST
  netapp.ontap.na_ontap_net_vlan:
    state: present
    vlanid: "{{ item.vlan_id }}"
    node: "{{ item.node }}"
    parent_interface: "{{ item.port }}"
    broadcast_domain: "{{ item.bc_name }}"
    ipspace: "{{ item.ipspace_name }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    hostname: "{{ netapp_hostname }}"
    enabled: true
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ ports }}"
```
#### 3-3. SVM作成の記載例
```bash:
>  cat roles/svm_create/tasks/main.yml
---
# tasks file for svm_create
#
- name: Create SVM
  netapp.ontap.na_ontap_svm:
    state: present
    name: "{{ item.svm_name }}"
    aggr_list: "{{ item.aggregate }}"
    hostname: "{{ netapp_hostname }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ svm }}"
```
#### 3-4. LIF作成の記載例
```bash:
>  cat roles/lif_create/tasks/main.yml
---
# tasks file for lif_create
- name: Create data interface - REST - NAS
  netapp.ontap.na_ontap_interface:
    state: present
    vserver: "{{ item.svm }}"
    interface_name: "{{ item.ifname }}"
    address: "{{ item.addr }}"
    netmask: "{{ item.nmask }}"
    home_port: "{{ item.h_port }}"
    home_node: "{{ item.h_node }}"
    service_policy: "{{ item.spolicy }}"
    is_auto_revert: "{{ item.arevert }}"
    hostname: "{{ netapp_hostname }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    admin_status: up
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ lifs }}"
```
#### 3-5. Default Gateway追加の記載例
```bash:
>  cat roles/route_create/tasks/main.yml
---
# tasks file for route_create
#
- name: create route - vserver scope, using REST
  netapp.ontap.na_ontap_net_routes:
    state: present
    vserver: "{{ item.svm }}"
    destination: "{{ item.dest }}"
    gateway: "{{ item.gw }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    hostname: "{{ netapp_hostname }}"
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ routes }}"
```

#### 3-6. Volume作成の記載例
```bash:
> cat roles/volume_create/tasks/main.yml
---
# tasks file for volume_create
#
- name: Create FlexVol
  netapp.ontap.na_ontap_volume:
    state: present
    is_online: true
    vserver: "{{ item.svm }}"
    name: "{{ item.volname }}"
    aggregate_name: "{{ item.aggr }}"
    size: "{{ item.size }}"
    size_unit: "{{ item.unit }}"
    space_guarantee: "{{ item.guarantee }}"
    tiering_policy: "{{ item.tiering }}"
    export_policy: "{{ item.expolicy }}"
    percent_snapshot_space: "{{ item.snapshotspace }}"
    junction_path: "{{ item.path }}"
    hostname: "{{ netapp_hostname }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{volumes }}"

```

#### 3-7. NFS有効化の記載例
```bash:
>　cat roles/nfs_create/tasks/main.yml
---
# tasks file for nfs_create
#
- name: create nfs configuration - REST
  netapp.ontap.na_ontap_nfs:
    state: present
    vserver: "{{ item.svm }}"
    nfsv3: "{{ item.v3 }}"
    nfsv4: "{{ item.v4 }}"
    nfsv41: "{{ item.v41 }}"
    tcp: "{{ item.tcp }}"
    udp: "{{ item.udp }}"
    vstorage_state: "{{ item.vstorage }}"
    nfsv4_id_domain: "{{ item.v4domain }}"
    hostname: "{{ netapp_hostname }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    service_state: started
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{ nfs }}"
```
#### 3-8. DefaultのExport PolicyへRule追加の記載例
```bash:
>　cat roles/expolicy_create/tasks/main.yml
---
# tasks file for expolicy_role
#
- name: Create ExportPolicyRule
  netapp.ontap.na_ontap_export_policy_rule:
    state: present
    vserver: "{{ item.svm }}"
    name: "{{ item.policyname }}"
    rule_index: "{{ item.index }}"
    client_match: "{{ item.clmatch }}"
    ro_rule: "{{ item.rorule }}"
    rw_rule: "{{ item.rwrule }}"
    protocol: "{{ item.proto }}"
    super_user_security: "{{ item.superuser }}"
    anonymous_user_id: "{{ item.anon }}"
    allow_suid: "{{ item.suid }}"
    hostname: "{{ netapp_hostname }}"
    username: "{{ netapp_username }}"
    password: "{{ netapp_password }}"
    use_rest: always
    https: true
    validate_certs: false
  loop: "{{expolices }}"
```

### 4. 設定値を記載したvar.ymlを作成
各Roleのtasksのmain.ymlへ渡す設定値を記載したファイルを作成します。
全てのtaskの記載に必要なONTAPへのアクセスアカウントは、各Role単位で変更する必要が無いので、Common Accountの部分へloopを利用しない形で記載してます。




```bash:
>　cat vars.yml
---
# Common Account
netapp_username: admin
netapp_password: XXXXXXXX
netapp_hostname: 192.168.1.200

#BroadcastDomain + Vlan Create
bcdomain:
  -  { bc_name: data_110, ipspace_name: Default, mtu: 1500 }

ports:
  -  { node: PS-8200cl-01, port: a0a, ipspace_name: Default, bc_name: data_110, vlan_id: 110 }
  -  { node: PS-8200cl-02, port: a0a, ipspace_name: Default, bc_name: data_110, vlan_id: 110 }

#SVM Create
svm:
  -  { svm_name: nfs110, aggregate: aggr1_node01 }

#LIF Create
lifs:
  -  { svm: nfs110, ifname: data01, addr: 192.168.110.100, nmask: 255.255.255.0, h_port: a0a-110, h_node: PS-8200cl-01, spolicy: default-data-files, arevert: false }
  -  { svm: nfs110, ifname: mgm01, addr: 192.168.110.1, nmask: 255.255.255.0, h_port: a0a-110, h_node: PS-8200cl-02, spolicy: default-management, arevert: false }

#Route Create
routes:
  -  { svm: nfs110, dest: 0.0.0.0/0, gw: 192.168.110.254 }

#Volume Create
volumes:
  -  { svm: nfs110, volname: vol110, aggr: aggr1_node01, size: 10, unit: gb, guarantee: none, tiering: none, expolicy: default, snapshotspace: 5, path: /vol110 }


#NFS Create
nfs:
  -  { svm: nfs110, v3: enabled, v4: enabled, v41: disabled, tcp: enabled, udp: disabled, vstorage: disabled, v4domain: example.com }

#Export Policy Rule Create
expolices:
  -  { svm: nfs110, policyname: default, index: 1, clmatch: 0.0.0.0/0, rorule: any, rwrule: any, proto: any, superuser: none, anon: 0, suid: true }

```


### 5. 実施するRoleを記載したPlaybookを作成

vars_filesへ設定値を記載したファイル名を記載し、利用するRoleをroles:部分へ記載します

```bash:
>　cat svm_create_playbook.yml
---
- name: Create NFS SVM
  hosts: localhost
  connection: local
  gather_facts: false
  vars_files:
    - vars.yml

  roles:
    - name : bcdomain_create
    - name : vlan_create
    - name : svm_create
    - name : lif_create
    - name : route_create
    - name : volume_create
    - name : nfs_create
    - name : expolicy_create
```

### 6. Playbookの実行

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

PLAY [Create NFS SVM] ***************************************************************************************************************

TASK [bcdomain_create : create broadcast domain] ************************************************************************************
changed: [localhost] => (item={'bc_name': 'data_110', 'ipspace_name': 'Default', 'mtu': 1500})

TASK [vlan_create : Create and add vlan to broadcast domain - REST] *****************************************************************
changed: [localhost] => (item={'node': 'PS-8200cl-01', 'port': 'a0a', 'ipspace_name': 'Default', 'bc_name': 'data_110', 'vlan_id': 110})
changed: [localhost] => (item={'node': 'PS-8200cl-02', 'port': 'a0a', 'ipspace_name': 'Default', 'bc_name': 'data_110', 'vlan_id': 110})

TASK [svm_create : Create SVM] ******************************************************************************************************
changed: [localhost] => (item={'svm_name': 'nfs110', 'aggregate': 'aggr1_node01'})

TASK [lif_create : Create data interface - REST - NAS] ******************************************************************************
changed: [localhost] => (item={'svm': 'nfs110', 'ifname': 'data01', 'addr': '192.168.110.100', 'nmask': '255.255.255.0', 'h_port': 'a0a-110', 'h_node': 'PS-8200cl-01', 'spolicy': 'default-data-files', 'arevert': False})
changed: [localhost] => (item={'svm': 'nfs110', 'ifname': 'mgm01', 'addr': '192.168.110.1', 'nmask': '255.255.255.0', 'h_port': 'a0a-110', 'h_node': 'PS-8200cl-02', 'spolicy': 'default-management', 'arevert': False})

TASK [route_create : create route - vserver scope, using REST] **********************************************************************
changed: [localhost] => (item={'svm': 'nfs110', 'dest': '0.0.0.0/0', 'gw': '192.168.110.254'})

TASK [volume_create : Create FlexVol] ***********************************************************************************************
changed: [localhost] => (item={'svm': 'nfs110', 'volname': 'vol110', 'aggr': 'aggr1_node01', 'size': 10, 'unit': 'gb', 'guarantee': 'none', 'tiering': 'none', 'expolicy': 'default', 'snapshotspace': 5, 'path': '/vol110'})
[WARNING]: Cannot perform actions: modify when volume is offline.

TASK [nfs_create : create nfs configuration - REST] *********************************************************************************
changed: [localhost] => (item={'svm': 'nfs110', 'v3': 'enabled', 'v4': 'enabled', 'v41': 'disabled', 'tcp': 'enabled', 'udp': 'disabled', 'vstorage': 'disabled', 'v4domain': 'example.com'})

TASK [expolicy_create : Create ExportPolicyRule] ************************************************************************************
changed: [localhost] => (item={'svm': 'nfs110', 'policyname': 'default', 'index': 1, 'clmatch': '0.0.0.0/0', 'rorule': 'any', 'rwrule': 'any', 'proto': 'any', 'superuser': 'none', 'anon': 0, 'suid': True})

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

SVMが作成されている事をStorage側から確認してみます。

```bash:
> broadcast-domain show -broadcast-domain data_110
  (network port broadcast-domain show)
IPspace Broadcast                                         Update
Name    Domain Name    MTU  Port List                     Status Details
------- ----------- ------  ----------------------------- --------------
Default data_110      1500
                            PS-8200cl-01:a0a-110          complete
                            PS-8200cl-02:a0a-110          complete


> vlan show -vlan-id 110
                 Network Network
Node   VLAN Name Port    VLAN ID  MAC Address
------ --------- ------- -------- -----------------
PS-8200cl-01
       a0a-110
                 a0a     110      02:a0:98:af:de:57
PS-8200cl-02
       a0a-110
                 a0a     110      02:a0:98:af:df:33
2 entries were displayed.


> vserver show -vserver nfs110

                                           Vserver: nfs110
                                      Vserver Type: data
                                   Vserver Subtype: default
                                      Vserver UUID: 6b2efef0-213f-11ee-9257-00a098afde57
                                       Root Volume: nfs110_root
                                         Aggregate: aggr1_node02
                                        NIS Domain: -
                        Root Volume Security Style: unix
                                       LDAP Client: -
                      Default Volume Language Code: C.UTF-8
                                   Snapshot Policy: default
                                     Data Services: data-iscsi, data-nfs,
                                                    data-nvme-tcp
                                           Comment:
                                      Quota Policy: default
                       List of Aggregates Assigned: aggr1_node01
        Limit on Maximum Number of Volumes allowed: unlimited
                               Vserver Admin State: running
                         Vserver Operational State: running
          Vserver Operational State Stopped Reason: -
                                 Allowed Protocols: nfs, cifs, fcp, iscsi,
                                                    ndmp, s3
                              Disallowed Protocols: nvme
                   Is Vserver with Infinite Volume: false
                                  QoS Policy Group: -
                               Caching Policy Name: -
                                       Config Lock: false
                                      IPspace Name: Default
                                Foreground Process: -
                           Logical Space Reporting: false
                         Logical Space Enforcement: false
Default Anti_ransomware State of the Vservers Volumes: disabled
                   Enable Analytics on New Volumes: false
           Enable Activity Tracking on New Volumes: false
                         Total Size of the Volumes: -
                                    Available Size: -
                                      Used Percent: -
               Number of Volumes in Recovery Queue: -
           Storage Space in Recovery Queue Volumes: -
              Max Storage Alert Threshold Exceeded: -
                                     Storage Limit: -
                     Storage Limit Threshold Alert: -
                QoS Adaptive Policy Group Template: -
Anti-ransomware Auto-switch from Learning to Enabled: true
Anti-ransomware Auto-switch Minimum Incoming Data (in percentage): 5%
Anti-ransomware Auto-switch Duration Without New File Extension (in Days): 3
Anti-ransomware Auto-switch Minimum Learning Period: 10
    Anti-ransomware Auto-switch Minimum File Count: 200
Anti-ransomware Auto-switch Minimum File Extension: 10
  Storage Reserved for In-flight Volume Operations: -


> net int show -vserver nfs110
  (network interface show)
            Logical    Status     Network            Current       Current Is
Vserver     Interface  Admin/Oper Address/Mask       Node          Port    Home
----------- ---------- ---------- ------------------ ------------- ------- ----
nfs110
            data01       up/up    192.168.110.100/24 PS-8200cl-01  a0a-110 true
            mgm01        up/up    192.168.110.1/24   PS-8200cl-02  a0a-110 true
2 entries were displayed.


> net route show  -vserver nfs110
  (network route show)
Vserver             Destination     Gateway         Metric
------------------- --------------- --------------- ------
nfs110
                    0.0.0.0/0       192.168.110.254 20


> vol  show  -vserver nfs110
Vserver   Volume       Aggregate    State      Type       Size  Available Used%
--------- ------------ ------------ ---------- ---- ---------- ---------- -----
nfs110    nfs110_root  aggr1_node02 online     RW          1GB    972.5MB    0%
nfs110    vol110       aggr1_node01 online     RW         10GB     9.50GB    0%
2 entries were displayed.


> export-policy rule show -vserver nfs110
             Policy          Rule    Access   Client                RO
Vserver      Name            Index   Protocol Match                 Rule
------------ --------------- ------  -------- --------------------- ---------
nfs110       default         1       any      0.0.0.0/0             any
```

## 参考及びリンク
[Simplicity at its finest, Roles for Ansible ONTAP use](https://netapp.io/2019/03/25/simplicity-at-its-finest-roles-for-ansible-ontap-use/)

[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)】

[Ansibleを使ってStorage(ONTAP)を管理する6 【loopについて](https://qiita.com/s_yosh1d/items/6ce192226ad00c51c236)】