はじめに
Proxmoxは仮想化プラットフォームでKVMをベースとした仮想化やLXCのコンテナ仮想化を利用できるOSSです。
APIもかなり充実していてVMの構築・起動からコンソール操作など細かいところに対応しています。
Ansibleからの操作も、community.generalコレクションにて対応モジュールが多数用意されています。
しかし、VM自動構築でkickstartコマンドを実行しようと思っていたのですが、2023/9時点ではコンソールへのキー入力を行うモジュールはありませんでした。
そのため、URIモジュールとtemplateで無理やり対応してみました。
あまり賢い方法ではないですが、応用の幅が広そうなのでその際のやり方を残します。
環境
- Ansible: v8.2.0
- Proxmox: v7.4-16
実装
Playbookとしては単純でURIモジュールでsendkeyAPIに1文字ずつコードを送っています。
ただ、Proxmoxでsendkeyを送る際に!
→shift-1
のように記号文字をエスケープする必要があります。
エスケープ処理はAnsible実行前にやっておくこともできますが汎用的にしたかったため、Ansible側でエスケープ処理をするようにしました。
エスケープ処理のパターンは以下KVM用の変換ツールを参考にさせてもらっています。
パラメータ
Ansibleに渡すパラメータの例です。
__send_command
に送信したいコマンドを入力します。
__proxmox_host: 192.168.1.1 #<PVEホスト名>
__vm_node: node1 #<PVEノード名>
__vmid: 100 #<VM ID>
__validate_certs: false #<証明書確認するか>
__proxmox_user: admin #<PVEユーザ>
__proxmox_api_token_id: XXXX #<トークンID>
__proxmox_api_token_secret: XXX #<トークンシークレット>
__send_command: "linux inst.ks=cdrom:/ks.cfg" #<VMコンソールに送信するコマンド>
Playbook
OSインストールのインストール画面を一時中断するためにESCキー、コマンド入力後にEnterキーを押す必要があるのですが1文字で表現できなかったのでPlaybook側でハードコードしています。
時々、送信失敗するのでretries
をつけるのとAnsibleログにAPIシークレットが表示されてしまうのでno_log
設定しています。
- name: Send kickstart command
ansible.builtin.uri:
url: "https://{{ __proxmox_host }}:8006/api2/json/nodes/{{ __vm_node }}/qemu/{{ __vmid }}/sendkey"
validate_certs: "{{ __validate_certs }}"
method: PUT
return_content: false
body_format: json
body: >-
{
"key": "{{ item }}"
}
headers:
Content-Type: application/json
Authorization: >-
PVEAPIToken={{
__proxmox_user
}}!{{
__proxmox_api_token_id
}}={{
__proxmox_api_token_secret
}}
no_log: true
loop: "{{ ['esc'] + lookup('template', 'kickstart_command.j2').split() + ['ret'] }}"
retries: 5
delay: 1
Template
Jinja Templateでコマンドを1文字ずつエスケープしています。
詳細な流れは以下の通り
- コマンド1文字ごとに改行コード(
\n
)を入れる -
regex_replace
でエスケープ処理(改行コードが入っているので実質1文字ずつ評価される) - Playbook側で作成した1文字ごと改行コードが入った文字列を
split()
でリストに変換
{{
__send_command |
join('\n') |
regex_replace(' ', 'spc') |
regex_replace('\!', 'shift-1') |
regex_replace('\"', 'shift-apostrophe') |
regex_replace('\#', 'shift-3') |
regex_replace('\$', 'shift-4') |
regex_replace('\%', 'shift-5') |
regex_replace('\&', 'shift-7') |
regex_replace('\'', 'apostrophe') |
regex_replace('\(', 'shift-9') |
regex_replace('\)', 'shift-0') |
regex_replace('\*', 'shift-8') |
regex_replace('\+', 'shift-equal') |
regex_replace('\,', 'comma') |
regex_replace('\-', 'minus') |
regex_replace('\.', 'dot') |
regex_replace('\/', 'slash') |
regex_replace('\:', 'shift-semicolon') |
regex_replace('\;', 'semicolon') |
regex_replace('\<', 'shift-comma') |
regex_replace('\=', 'equal') |
regex_replace('\>', 'shift-dot') |
regex_replace('\?', 'shift-slash') |
regex_replace('\@', 'shift-2') |
regex_replace('\[', 'bracket_left') |
regex_replace('\]', 'bracket_right') |
regex_replace('\^', 'shift-6') |
regex_replace('\_', 'shift-minus') |
regex_replace('\`', 'grave_accent') |
regex_replace('\{', 'shift-bracket_left') |
regex_replace('\|', 'shift-backslash') |
regex_replace('\}', 'shift-bracket_right') |
regex_replace('\~', 'shift-grave_accent') |
regex_replace('([A-Z])', 'shift-\\1') | lower |
regex_replace('\\\\', 'backslash')
}}
結果
例としてlinux inst.ks=cdrom:/ks.cfg
を入れてみると以下のような結果になります。
きちんと記号部分のみエスケープされているのがわかります。
ok: [localhost] => {
"msg": [
"esc",
"l",
"i",
"n",
"u",
"x",
"spc",
"i",
"n",
"s",
"t",
"dot",
"k",
"s",
"equal",
"c",
"d",
"r",
"o",
"m",
"shift-semicolon",
"slash",
"k",
"s",
"dot",
"c",
"f",
"g",
"ret"
]
}
さいごに
Templateを使うことで簡単な文字列エスケープを実装して、Proxmoxのsendkeyを実行できるようにしてみました。
かなり脳筋実装ではありますが、カスタムモジュールやフィルタを作らなくてもある程度は何とかなることが分かりました。
実装の幅が広がったのはとても良かったです。
vSphereであれば、今回作ったsendkeyを行えるcommunity.vmware.vmware_guest_sendkeyモジュールあるのでProxmoxとしても同様のモジュールはあってもよさそうです。
ひとまず、Issueは送ったので時間が空いているときにでも作ってみたいと思います。
参考リンク