背景
oVirt で VM のクローンを作成したいとき、GUI ではメニュから何も気にせず実行できます。
一方で、これを Ansible から実行しようとしても、oVirt の VM を管理するときに利用する ovirt.ovirt.ovirt_vm
モジュール では実現は難しいようです。クローンに類する操作は次の二種のみしか対応していなさげでした。
- テンプレートからの仮想マシンの作成
- 他の VM のスナップショットからの仮想マシンの作成
実行したいのは、既存の VM からの Ansible を使ったダイレクトなクローン作成です。本エントリでは、これを API を直接叩いてがんばってどうにかする実装例を紹介します。
oVirt 4.4 でテスト済みです。RHV でも動きそうな気はしますが未テストです。AWX での利用を想定していますが、ansible-playbook
でも動作します。
実装例
バリデーションやカスタマイズ性は無視した最小限の実装例です。
プレイブック
後述の変数を渡して実行すると、クローンの作成がリクエストされます。クローン先の VM は、クローン元の VM とそっくりそのままな構成でできあがります。
この実装例では、リクエストが完了した段階でプレイブックが完了します(クローンの作成完了を待ちません)。
---
- hosts: localhost
gather_facts: no
tasks:
- name: Gather the ID of the source VM
ovirt.ovirt.ovirt_vm_info:
auth:
insecure: yes
pattern: "name={{ source_vm }}"
register: _source_vm_info
- name: Request cloning of the source VM
ansible.builtin.uri:
url: "{{ lookup('env','OVIRT_URL') }}/vms/{{ _source_vm_info.ovirt_vms[0].id }}/clone"
validate_certs: no
url_username: "{{ lookup('env','OVIRT_USERNAME') }}"
url_password: "{{ lookup('env','OVIRT_PASSWORD') }}"
method: POST
headers:
Version: "4"
Content-Type: application/xml
Accept: application/xml
body: |
<action>
<vm>
<name>{{ target_vm }}</name>
</vm>
<storage_domain>
<name>{{ target_storage_domain }}</name>
</storage_domain>
</action>
body_format: raw
register: _response
- name: Print HTTP response
ansible.builtin.debug:
msg: "{{ _response.status }}: {{ _response.msg }}"
変数と環境変数
前述のサンプルプレイブックでは、次の三つの 変数 が必要です。AWX を使う場合は Survey などで渡すとよさそうです。
source_vm
で、クローン元の VM の名前を指定しますtarget_vm
で、クローン先の VM の名前を指定しますtarget_storage_domain
で、クローン先の VM を保存するストレージドメイン名を指定します
また、次の三つの 環境変数 も必要です。AWX から実行する場合は、Red Hat Virtualization タイプの Credential を作成してジョブテンプレートで指定する ことで、これらは自動で渡せます。
OVIRT_URL
で、oVirt Engine の API のベース URL を指定します。通常、https://<oVirt Engine の FQDN>/ovirt-engine/api
ですOVIRT_USERNAME
で、oVirt Engine に認証するユーザ名を指定しますOVIRT_PASSWORD
で、OVIRT_USERNAME
で指定したユーザのパスワードを指定します
考え方
冒頭で紹介した ovirt.ovirt.ovirt_vm
モジュール の制約は、このモジュールが利用している Vms
サービスの API の仕様 からきているようです。
一方で、Vms
サービスではなく VM ごとの Vm
サービス には、clone
メソッド(/vms/{vm:id}/clone
への POST
)が用意されています。これを使えれば、他の VM からのダイレクトなクローンが実現できそうです。
これを紐解くと、VM クローン元 VM の ID を URL に含んだエンドポイント に、パラメータとして vm
(Vm
型)と storage_domain
(StorageDomain
型)を渡せばよさそうで、愚直に cURL で実装するとこうなります。
url="https://<oVirt Engine の FQDN>/ovirt-engine/api"
user="ユーザ名"
password='パスワード'
source_vm_id="クローン元の VM の ID"
target_vm_name="クローン先の VM の名前"
target_storage_domain_name="クローン先のストレージドメインの名前"
curl \
--insecure \
--user "${user}:${password}" \
--request POST \
--header "Version: 4" \
--header "Content-Type: application/xml" \
--header "Accept: application/xml" \
--data "
<action>
<vm>
<name>${target_vm_name}</name>
</vm>
<storage_domain>
<name>${target_storage_domain_name}</name>
</storage_domain>
</action>
" \
"${url}/vms/${source_vm_id}/clone"
すなわち、これを Ansible 版 cURL 的な ansible.builtin.uri
モジュールで置き換えたのが先のプレイブックです。
認証情報が必要ですが、AWX で Red Hat Virtualization タイプの Credential を作成してジョブテンプレートに渡せば自動で環境変数として渡されるため、これをそのまま使っています。また、クローン元 VM の ID は、VM の名前で ovirt.ovirt.ovirt_vm_info
モジュールで情報を引っ張って抽出しています。
この実装例では、API に渡す vm
パラメータの中身(XML の <vm></vm>
の中身)には名前だけを指定しており、クローン先の VM の細かな構成はクローン元の VM のそれに準拠します。クローン先の VM の構成をカスタマイズしたい場合は、XML の中身をいじくりまわしてもどうも効果がない(詳細は未検証……)ようなので、後続に ovirt.ovirt.ovirt_vm
モジュールを使うタスクを書いて正攻法で作業するとよさそうです。
まとめ
モジュールが対応していない処理でも、API をがんばって直接たたけばどうにかなる例を紹介しました。
根本的には上流のモジュールで対応させたい気持ちもありますが、ovirt.ovirt.ovirt_vm
モジュールのソースコードは 3,000 行近く あり実装方針を相談しないとツラそうなので、ひとまずはワークアラウンドということで。