oVirt の VM のクローンを Ansible で作成する

背景

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 に含んだエンドポイント に、パラメータとして vmVm 型)と storage_domainStorageDomain 型)を渡せばよさそうで、愚直に 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 行近く あり実装方針を相談しないとツラそうなので、ひとまずはワークアラウンドということで。

@kurokobo

くろいです。ギターアンサンブルやら音響やらがフィールドの IT やさんなアルトギター弾き。たまこう 48 期ぎたさん、SFC '07 おんぞう、新日本ギターアンサンブル、Rubinetto。今は野良気味。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です