Ansibleを使ってStorage(ONTAP)を管理する1【Rocky LinuxへAnsible Install編】

## はじめに
管理対象機器が増加すると設定及び運用管理業務の負担は大きくなっていきますが、
この課題に対する解決策の1つとして、作業を標準化し自動化する事で少ない人手で多くの作業を実施し人的ミスも防止するといった事が考えられます。

しかし、日々のタスクを自動化するにしても新規ツールの導入や学習し直しというハードル(学習コスト)がある為、本記事ではRed Hat Enterprise Linux（RHEL）のクローンである**Rockey Linux上に対して自動化、効率化する構成管理ツールAnsibleを導入し、NetApp StorageのOSであるONTAPを管理**する方法についてInstall部分から記載致します。

なお、NetAppで提供しているONTAPの自動化管理ツールとして、**OnCommand Workflow Automation(WFA)が存在しますが、WFAは2022年1月に提供終了**となっています。
最新の**WFA 5.1.1については2025年1月までフルサポート**となりますが、それ以降の自動化ツールを使用する場合には、個別にAPIを利用して自動化用のコードを書いたり、オープンソースのツールを利用することが必要となります。
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/9b330a3d-6cf6-7aab-bf23-51ad0ee40ef3.png)
|:-:|


## 何をしたい？できる？
- **Rocky LinuxへのAnsibleのInstall**
- **Ansibleを使ってONTAPの管理**
- **Ansibleを使い基本的な疎通確認**

## Ansibleとは
Python製の構成管理ツールで、サーバやNetwork Switch、Storageといった機器の設定変更や構築作業を自動化する事で様々なプロセスを効率化する事ができます。
特徴としては、以下のような点が挙げられます。
- YAML形式で記述する事による設定ファイルの可読性の高さ **(Simple)**
- SSH接続介して接続する事によるエージェントレス構成 **(Agentless)**
- 多くのOSや製品をコントロールできるマルチレイヤ対応 **(Powerful)**

以下の画像にあるAnsible GalaxyとはRoleやコレクション (module, plugin, role)をダウンロード、共有を行うWebサイトで、NetAppストレージを管理するコレクションもここからダウンロードして利用します。

|<img width="500" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/c189982f-078e-d5d0-aef8-977b145ada89.png>
|:-:|



## YAMLファイルの基本的な書き方
Ansibleにおいて管理対象に対して行う操作を記載するPlaybookの記述には、YAML形式という記述方法が採用されますが、YAMLの基本的な書き方のルールは以下の通りです。

- --- で開始して、... で終わらせる(任意)
- **タブは使用できないため、代わりにスペースを使用**
- Pythonと同じ様に括弧を使わない入れ子構造
- キーバリュー型である”キー: 値”でデータを表す
- -を並べることでリスト表現が可能

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/936e26d8-3e48-3c68-2031-c82cb77495bc.png)
|:-:|

## 記事における構築環境
本記事では、以下のVersionを使って構築しています。
- 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/70774eb5-68b9-64ac-f8b0-e232da1009d2.png>
|:-:|


## 設定手順

#### 1. ansible用のユーザ作成

AnsibleのInstall用及び設定用ユーザの作成をします。
また、ユーザーにsudo権限を付与する為にwheelグループへユーザを追加します。

:::note info
必須項目ではありませんが、今後の手順上分かりやすくする為に作成しています
:::

```bash:
> sudo useradd -m ansible_user10
[sudo] ansible_user10 のパスワード:

> sudo usermod -aG wheel ansible_user10
```



#### 2. AnsibleのInstall
上記の作成したユーザでログインし、アドオンパッケージとpipの導入を実施します。
```bash:
> sudo dnf install epel-release -y

> sudo dnf install python3-pip -y
```
:::note warn
OS構築時の指定によっては導入済の場合があります
:::

Ansibleでは基本的に公開鍵認証で接続しますが、パスワード認証も実施したいのでsshpassを導入します。

```bash:
> sudo dnf install sshpass
```

Ansibleの導入を実施します。
```bash:
> sudo pip install ansible
```

NetApp固有のPythonモジュールを導入します。
```bash:
> sudo pip install netapp-lib solidfire-sdk-python six requests
```

AnsibleでNetAppストレージを管理する為のコレクション導入とコレクション用ディレクトリの設定を実施します。
```bash:
> sudo ansible-galaxy collection install netapp.ontap netapp.elementsw -p /usr/share/ansible/collections

> sudo chmod +rx /usr/share/ansible/collections
```

Installが出来ている事を確認します。
```bash:
> ansible --version
ansible [core 2.14.1]
  config file = None
  configured module search path = ['/home/ansible_user10/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  ansible collection location = /home/ansible_user10/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.14 (main, Nov  7 2022, 00:00:00) [GCC 11.3.1 20220421 (Red Hat 11.3.1-2)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True


> ansible-galaxy collection list
       (中略)
# /usr/share/ansible/collections/ansible_collections
Collection       Version
---------------- -------
netapp.elementsw 21.7.0
netapp.ontap     22.1.0
```

#### 3. 設定ファイル作成

まずはLinuxへAnsibleコマンドを利用してpingを実施したいと思いますが、実行するあたって以下の2種類のファイルが必要になります。
-  Inventory 　(管理対象について記載)
-  Playbook  　(実行する操作について記載)



##### Inventoryの作成
作業フォルダを作成します。
```bash:
> mkdir anansible_work
> cd anansible_work
```

Inventoryの作成を実施します。
この記事では、コピペ＋編集して接続の確認がとりあえずできる事を目的としているので、ファイルの書式や詳細な書き方については割愛します。

**Inventoryの書式は2つあり、ini形式の場合は以下の通りです。**
この例では接続先の対象として自分と管理対象のLinuxについて記載していますがDNS参照やhostsファイルを用意していないので、疎通に利用するIPアドレスを含めて記載しています。
ファイル名は任意でも問題無いです。
```bash:
> cat inventory.ini
Ansible ansible_host=172.16.10.180
Linux ansible_host=172.16.10.148
```

**YAML形式で記載する場合は、以下の通りです。**
YAMLでは任意となりますが--- で開始して、... で終わらせる形となります。
```bash:
> cat inventory.yml
---
all:
  hosts:
    Ansible:
      ansible_host: 172.16.10.180
    Linux:
      ansible_host: 172.16.10.148
...
```

ansibleコマンドで操作した際に、ホストのフィンガープリントを保持するか聞かれてしまう事を避けるために、**管理対象に一度sshでログインし、接続しておきます。**

```bash:
> ssh user01@172.16.10.148
The authenticity of host 172.16.10.148 (172.16.10.148) can’t be established.
ED25519 key fingerprint is SHA256:XXXXX.
This key is not known by any other names 
\\Are you sure you want to continue connecting (yes/no/[fingerprint])? #<=yesを入力
```
SSHログインをした際に**最初に表示されるフィンガープリントと接続するかどうかの確認を聞かれないようにしたい場合**は、ansible上の操作ユーザの設定ファイルに無効についての記載を実施します。
**セキュリティの観点からは、なりすましを避ける為にホストのフィンガープリントを保持する方が推奨です。**

```bash:
> mkdir ~/.ssh
> sudo vi ~/.ssh/config #以下のcatで表示された内容を記載

> cat ~/.ssh/config
host *
        StrictHostKeyChecking no

> sudo chmod 600 ~/.ssh/config
```

　

**Playbookの作成**
ansibleから管理対象に対して実行したい内容をPlayboolにYAML形式で記載します。この記事では管理対象のLinuxと自分(AnsibleのInstallしたサーバ)に対してpingを実行します。

```bash:
> cat playbook_ping.yml
---
- hosts: Ansible
  tasks:
    - ping:

- hosts: Linux
  tasks:
    - ping:
```

YAML形式で書いたinventory.ymlファイル上でallというグループに括りで2つのホスト名をグループ化しているので、inventory.ymlを指定して纏めて2つのホストに処理を実行させる場合には、以下のようなPlayboolでも動作します。

```bash:
---
- hosts: all
  tasks:
    - ping:
...
```


#### 4. Playbookの実行

ansible-playbookコマンドを使用してPlaybookを実行します。
-iを使うとInventoryファイルの指定を実施できます。
この例では、公開鍵認証ではなくパスワード認証で操作を行う為、-uの後に接続先端末に作成されているユーザ名と--ask-passでパスワード認証の指定を実施しています。
**この環境例では、管理対象のどちらにもuser01というユーザを作成しています。(そして同じパスワード)**

以下の実行例のように各管理対象に対してok=2 が出ていれば正常に疎通確認できている形となります。

```bash:
> ansible-playbook -i inventory.ini -u user01 playbook_ping.yml --ask-pass
SSH password: <=管理対象のSSH接続パスワードを入力

PLAY [Ansible] **********************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************
ok: [Ansible]

TASK [ping] *************************************************************************************************************************
ok: [Ansible]

PLAY [Linux] ************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************
ok: [Linux]

TASK [ping] *************************************************************************************************************************
ok: [Linux]

PLAY RECAP **************************************************************************************************************************
Ansible                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Linux                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

```


YAML形式でInventoryファイルを作成した例も、-iの後の指定を変えるだけで同じような挙動となります。
```bash:
ansible-playbook -i inventory.yml -u user01 playbook_ping.yml --ask-pass
```


#### 5. ONTAP(SVM)へのAPIアクセス用のユーザ作成

既にユーザがいる場合は、そのユーザを使用しても良いですが、ここでは**ONTAPのクラスタ管理上で対象SVMに対してAPIアクセス専用のユーザを作成します。**
ここではSVMの管理者ロールとしてcifs100というSVMにtestuserというユーザを作成した例となります。
```bash:
> security login create -vserver cifs100 -user-or-group-name testuser -application http -authentication-method password -role vsadmin

Please enter a password for user 'testuser':
Please enter it again:

#以下のhttpのみのアクセスで利用できます。
> security login show -vserver cifs100 -user-or-group-name testuser

Vserver: cifs100
                                                                 Second
User/Group                 Authentication                 Acct   Authentication
Name           Application Method        Role Name        Locked Method
-------------- ----------- ------------- ---------------- ------ --------------
testuser       http        password      vsadmin          no     none
```


#### 6. ONTAP(SVM)への接続確認
ONTAPへの操作を記載するPlaybookには、記載にあたって以下の注意点があります。



- **collections:セクションを追加し利用するAnsibleコレクションを定義し、コレクションの場所をチェックできるようにする**
- **Interpreterの指定の追加**
- **Playbookが実行されるホストとしてlocalhost(もしくはAnsibleのInstallされているホスト名)を定義**

サーバへの操作の場合、この行は操作対象のホスト名の記載となりますが、ONTAP（及びElement OS）は、モジュール通信にsshを使用することができない代わりに、httpまたはhttpsを使用して、ローカルホストから開始という挙動となります。

サンプルとなるPlaybookは以下の通りですが、修正項目は以下の4点です。

- hosts: AnsibleのInstallされているホスト名(localhost等)
- hostname: SVMのHTTPSアクセス用LIFのIP
- username: SVMへのAPIアクセス用のユーザ名
- password: SVMへのAPIアクセス用のユーザのパスワード

```bash:
> cat playbook_svm_info.yml
- name: ONTAP Info
  hosts: Ansible
  collections: netapp.ontap
  gather_facts: no
  become: no
  vars:
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: run ONTAP gather facts for vserver info
      na_ontap_rest_info:
        hostname: 172.16.10.147
        username: testuser
        password: XXXXXX
        https: true
        validate_certs: false
        use_rest: Always
        gather_subset:
        - svm/svms
```


Playbookを実行します。コマンドに-vを付けることで、SVM情報が実行結果として表示されます。
(-vvvとするとさらに詳細が確認できます)

```bash:
> ansible-playbook -i inventory.yml -u user01 playbook_svm_info.yml --ask-pass -v
No config file found; using defaults
SSH password:

PLAY [ONTAP Info] *******************************************************************************************************************

TASK [run ONTAP gather facts for vserver info] **************************************************************************************
ok: [Ansible] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "ontap_info": {"svm/svms": {"_links": {"self": {"href": "/api/svm/svms?max_records=1024&fields="}}, "num_records": 1, "records": [{"_links": {"self": {"href": "/api/svm/svms/XXXXXXX"}}, "name": "cifs100", "uuid": "XXXXXXX"}]}}}

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




## 参考及びリンク

[NetApp社におけるサイトのAnsible情報](https://www.netapp.com/ja/devops-solutions/ansible/)

[Getting Started with NetApp and Ansible: Understanding Playbooks](https://netapp.io/2018/10/10/getting-started-with-netapp-and-ansible-understanding-playbooks/)

[Getting Started with NetApp and Ansible: First Playbook Example](https://netapp.io/2018/10/11/getting-started-with-netapp-and-ansible-first-playbook-example/)

[AnsibleでNetApp ONTAP Storageを設定](https://qiita.com/yasuoohno/items/c5615762d336c03512dc)