AnsibleでvSphere上の仮想マシンにDiskを複数追加する

#<font color="DarkOrange">目次</font>
<ul>
<li><a href="#1-%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">1. はじめに</a></li>
<li><a href="#2-%E4%BD%BF%E7%94%A8%E7%92%B0%E5%A2%83">2. 使用環境</a></li>
<li><a href="#3-ansible%E6%A7%8B%E6%88%90">3. Ansible構成</a></li>
<li><a href="#4-hosts%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">4. hostsを作成する</a></li>
<li><a href="#5-tasks%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">5. tasksを作成する</a></li>
<li><a href="#6-playbook%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">6. playbookを作成する</a></li>
<li><a href="#7-vars%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">7. varsを作成する</a></li>
<li><a href="#8-ansible%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">8. Ansibleを実行する</a></li>
<li><a href="#9-%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">9. おわりに</a></li>
<li><a href="#10-%E6%8A%80%E8%A1%93%E6%83%85%E5%A0%B1">10. 技術情報</a></li>
<li><a href="#11-%E5%8F%82%E8%80%83url">11. 参考URL</a></li>
</ul>

#<font color="DarkOrange">1. はじめに</font>
Ansibleを使いvSphereに仮想マシンを構築することを行っていた際に、vmware_guest_diskモジュールを使用しハードディスクの追加を行っていました。
1つのディスクの追加であれば簡単ですが2枚以上の追加を行う場合、unit_numberのループ方法を考えなくてはいけなかったので、Playbookにまとめました。

#<font color="DarkOrange">2. 使用環境</font>
・CentOS 8
・Ansible 2.9.10
・vSphere 7.0

#<font color="DarkOrange">3. Ansible構成</font>

~~~
.
|-- add_disk.yml
|-- host_vars
|   `-- vcsa7
|       `-- main.yml
|-- hosts
`-- include_add_disk.yml
~~~

#<font color="DarkOrange">4. hostsを作成する</font>
hostsの内容は以下の通り。ホスト名とIPを記載します。

~~~
[target]
vcsa7 ansible_host="vcenter ip"
~~~

#<font color="DarkOrange">5. tasksを作成する</font>
以下のようなタスク(include_add_disk.yml)を作成していきます。

~~~
---
- name: 仮想マシンのディスク情報を取得
  vmware_guest_disk_info:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    name: "{{ vm_name }}"
    datacenter: "{{ datacenter }}"
  register: disk_info
  delegate_to: localhost
  ignore_errors: true

- name: set fact - controllerFacts
  set_fact:
    controllerFacts: "{{ disk_info.guest_disk_info.values()| map(attribute='controller_key') | list  }}"
  delegate_to: localhost
  ignore_errors: true

- name: set facts - controllerKey
  set_fact:
    controllerKey: "{{ controllerFacts | sort | last  }}"
  delegate_to: localhost
  ignore_errors: true

- name: debug
  debug:
    var: "{{ disk_info.guest_disk_info.values() | selectattr('controller_key','equalto',(controllerKey | int)) | map(attribute='unit_number') | list | sort | last |int +1 }}"
  delegate_to: localhost

- name: set fact - unitNumber
  set_fact:
    unitNumber: "{{ disk_info.guest_disk_info.values() | selectattr('controller_key','equalto',(controllerKey | int)) | map(attribute='unit_number') | list | sort | last |int +1 }}"
  delegate_to: localhost
  ignore_errors: true

- name: Skip SCSI controller No 7
  set_fact:
    unitNumber: "{{ unitNumber | int+1 }}"
  delegate_to: localhost
  ignore_errors: true
  when:
    - "unitNumber is defined"
    - "unitNumber == '7'"

- name: ディスクを追加
  vmware_guest_disk:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    name: "{{ vm_name }}"
    datacenter: "{{ datacenter }}"
    disk:
    - size_gb: "{{ loop_disks.disk_size }}"
      type: "{{ disk_type }}"
      state: present
      datastore: "{{ datastore_name }}"
      scsi_type: "{{ scsi_type }}"
      scsi_controller: "{{ controllerKey[3:4] }}"
      unit_number: "{{ unitNumber }}"
  register: add_disk
  delegate_to: localhost
~~~

まずは、実行対象の仮想マシンのディスク情報を取得します。

~~~
---
- name: 仮想マシンのディスク情報を取得
  vmware_guest_disk_info:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    name: "{{ vm_name }}"
    datacenter: "{{ datacenter }}"
  register: disk_info
  delegate_to: localhost
  ignore_errors: true

~~~

debugの結果は以下のようになります。

~~~
TASK [debug] ****************************************************************************************
ok: [vcsa7] => {
    "disk_info": {
        "changed": false,
        "failed": false,
        "guest_disk_info": {
            "0": {
                "backing_datastore": "2_QNAP_datastore",
                "backing_eagerlyscrub": false,
                "backing_filename": "[2_QNAP_datastore] ansible_test/ansible_test.vmdk",
                "backing_thinprovisioned": false,
                "backing_type": "FlatVer2",
                "backing_uuid": "6000C294-982e-e8d1-f5dc-f106b7fca5fc",
                "backing_writethrough": false,
                "capacity_in_bytes": 17179869184,
                "capacity_in_kb": 16777216,
                "controller_bus_number": 0,
                "controller_key": 1000,
                "controller_type": "paravirtual",
                "key": 2000,
                "label": "Hard disk 1",
                "summary": "16,777,216 KB",
                "unit_number": 0
            }
        }
    }
}
~~~

次に現在使用しているコントローラキーを取得します。
コントローラのタイプにより、コントローラキーが決まってます。
(例 SCSIコントローラ0: 1000 , SCSIコントローラ1: 1001 など...)

~~~
- name: set fact - controllerFacts
  set_fact:
    controllerFacts: "{{ disk_info.guest_disk_info.values()| map(attribute='controller_key') | list  }}"
  delegate_to: localhost
  ignore_errors: true

~~~

debugの結果は以下です。

~~~
TASK [debug] ****************************************************************************************
ok: [vcsa7] => {
    "controllerFacts": [
        1000
    ]
}
~~~

取得したcontrollerFacts をソートします。

~~~
- name: set facts - controllerKey
  set_fact:
    controllerKey: "{{ controllerFacts | sort | last  }}"
  delegate_to: localhost
  ignore_errors: true

~~~

debugの結果は以下です。

~~~
TASK [debug] *****************************************************************************************************
ok: [vcsa7] => {
    "controllerKey": "1000"
}
~~~

※ちなみにSCSIコントローラ0(1000)とSCSIコントローラ1(1001)を使用していた場合は以下のように、『1001』がコントローラキーとして格納されます。そのため、複数のコントローラを使用する場合は考慮が必要です。

~~~
TASK [set fact - controllerFacts] *****************************************************************************************************
ok: [vcsa7]

TASK [debug] *****************************************************************************************************
ok: [vcsa7] => {
    "controllerFacts": [
        1000,
        1001
    ]
}

TASK [set facts - controllerKey] *****************************************************************************************************
ok: [vcsa7]

TASK [debug] *****************************************************************************************************
ok: [vcsa7] => {
    "controllerKey": "1001"
}

~~~

ユニットナンバーを取得します。最後の『int +1』により取得したunit_numberに+1している値がセットされます。

~~~
- name: set fact - unitNumber
  set_fact:
　   unitNumber: "{{ disk_info.guest_disk_info.values() | selectattr('controller_key','equalto',(controllerKey | int)) | map(attribute='unit_number') | list | sort | last |int +1 }}"
  delegate_to: localhost
  ignore_errors: true

~~~

debugの結果は以下になりました。

~~~
TASK [debug] *****************************************************************************************************
ok: [vcsa7] => {
    "unitNumber": "1"
}
~~~

ユニットナンバーで7番は割り当てできないのでスキップを行うようにします。

~~~
- name: Skip SCSI controller No 7
  set_fact:
    unitNumber: "{{ unitNumber | int+1 }}"
  delegate_to: localhost
  ignore_errors: true
  when:
    - "unitNumber is defined"
    - "unitNumber == '7'"
~~~

コントロールキーとユニットナンバーの変数化が完了したら『vmware_guest_disk』モジュールを使いディスクの追加を行います。
『controllerKey[3:4]』はコントローラキー(1000)の4桁目(0)を取得しています。

~~~
- name: ディスクを追加する
  vmware_guest_disk:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    name: "{{ vm_name }}"
    datacenter: "{{ datacenter }}"
    disk:
    - size_gb: "{{ loop_disks.disk_size }}"
      type: "{{ disk_type }}"
      state: present
      datastore: "{{ datastore_name }}"
      scsi_type: "{{ scsi_type }}"
      scsi_controller: "{{ controllerKey[3:4] }}"
      unit_number: "{{ unitNumber }}"
  register: add_disk
  delegate_to: localhost
~~~

#<font color="DarkOrange">6. playbookを作成する</font>
先ほど作成したタスクでplaybook(add_disk.yml)を作成します。

~~~
---
- hosts: all
  gather_facts: false
  tasks:
    - name: タスクを読み込む
      include_tasks: include_add_disk.yml
      with_items:
         "{{ disks }}"
      loop_control:
        loop_var: loop_disks
~~~

#<font color="DarkOrange">7. varsを作成する</font>
変数はhost_varsに格納しています。使用環境により値を変えてください。

~~~
vcenter_hostname: "vcenter hostname"
vcenter_username: "vcenter username"
vcenter_password: "vcenter password"
vm_name: test_vm
datacenter: /Datacenter/vm
disks:
 - disk_size: 10
 - disk_size: 14
disk_type: thin
datastore_name: datastore1
scsi_type: paravirtual
~~~

#<font color="DarkOrange">8. Ansibleを実行する</font>
仮想マシンの設定前の状態です。
![WS000031.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611933/c743b6d0-5124-4af3-ce2a-a1a3abe9bca4.jpeg)
Ansibleを実行します。

~~~
[root@localhost ansible]# ansible-playbook add_disk.yml -i hosts

・
・
・
PLAY RECAP **************************************************************************************************
vcsa7                      : ok=22   changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
~~~

playbookが成功したら仮想マシンを確認します。ディスク2、ディスク3が指定されたサイズで追加されていることが確認できます。
![WS000033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611933/c56b82e5-82b1-c977-15fd-005f1170d5f1.png)


#<font color="DarkOrange">9. おわりに</font>
今回はSCSIコントローラを使用したディスクの追加を行いました。
決め打ちの実行であればここまで考慮する必要はないですが、なるべく汎用的にタスクを作成するには工夫が必要になってきます。

#<font color="DarkOrange">10. 技術情報</font>
[Ansible Moleculeを使ったテストの自動化](https://qiita.com/cxc/items/77c74c369e722b8537e8)の記事を執筆しています。roleをテストしてくれるツールになります。実行環境を自動で構築し、環境の削除も行うことができるので、冪等性を検証するには便利なツールになります。

また、[Ansibleの入門者向け学習ガイド(CentOS編)](http://c.itdo.jp/technical-information/ansible/ansible-tutorial/)や[Ansibleの入門者向け学習ガイド(Windows編)](http://c.itdo.jp/technical-information/ansible/ansible%e5%85%a5%e9%96%80%e8%80%85%e5%90%91%e3%81%91%e5%ad%a6%e7%bf%92%e3%82%ac%e3%82%a4%e3%83%89-windows%e7%b7%a8/)など入門者向けの記事も執筆していますので、参考にしていただければと思います。

#<font color="DarkOrange">11. 参考URL</font>
https://vconfig.pl/2019/08/17/ansible-add-disk-to-vm-skipping-scsi-id-x7/
https://techacademy.jp/magazine/28100
https://docs.ansible.com/ansible/latest/modules/vmware_guest_disk_module.html