執筆者 : 箕浦 真
前回記事が重かったので、今回は小ネタを集めてみた。
1. Qemuを生で起動
大抵の場合、Qemuを直接起動することはあまりなく、libvirt経由で起動することが多いと思う*1。特に、RHEL系ではQemuが/usr/libexec/qemu-kvm
などというところに置かれていることから分かるように、Qemuを直接起動することは想定されていない。しかしながら、筆者のようにさまざまな特殊な動かし方をしたり、起動時のデバッグをおこなったりといった用途では、直接起動せざるを得ないことがある。
1.1 libvirtからの起動を模擬したい場合
libvirtから起動するのと近い動作をさせたい場合、/var/log/libvirt/qemu/
にあるログにコマンドラインが記録されているので、これを参考にする。ところどころ、libvirtが動的に作成したリソースをQemuに渡すような場合があり、注意が必要である。また、KVMを使う場合、Qemuを実行するユーザーがkvmグループ (ディストリビューションによって異なるかもしれない) に属している必要がある。
マスターキー
-object '{"qom-type":"secret",...
のような指定がある。これは、マスターキーという暗号化された乱数である。通常このオプションはそのまま削っても良い。
Qemuモニター
libvirtがQemuを制御するのに利用しているのがQemu monitorである。libvirtでは、/var/lib/libvirt/qemu/domain-ID-NAME/monitor.sock
にUnixソケットを作成し、そのファイルデスクリプターを渡す形がとられている。
Qemuモニターはデバイスではないが、シリアルポートなどのキャラクターデバイスと同様のバックエンドを接続する形をとっている。id=charmonitor
という-chardev
オプションを探し、置き換える。たとえば、Qemuの標準入出力をQemuモニターに接続する場合、-chardev socket,id=charmonitor,fd=29,server=on,wait=off
のようなオプションを、-chardev stdio,id=charmonitor
などと置き換える。-chardev socket,id=charmonitor,path=/tmp/monitor.sock,server=on
などとして、netcatコマンドなどで接続しても良い。
仮想ネットワーク
libvirtは多様なネットワークが扱えるが、既定のdefaultネットワークに接続していることが多いと思う。この実態は、virbr0というLinuxブリッジであり *2、ここに勝手に接続しても特に問題なく動いてくれるので、筆者の場合以下のようなスクリプトを用意し、
#!/bin/sh /usr/bin/ip link set "$1" master virbr0
-netdev
オプションを置き換えることが多い (-netdev tap,fd=32,id=hostnet0,vhost=on,vhostfd=34
→ -netdev tap,id=hostnet0,script=/tmp/ifup
など)。
ブリッジに組み込むため、管理者特権が必要となる。sudoしたくない場合などは、-netdev user,id=hostnet0
を使う。
1.2 とにかくQemuが使いたい場合
libvirtは特に冗長なコマンドラインオプションを使うが、単にQemuを使いたい場合はもっと簡便な書き方もある。例えば、ubuntuで
$ qemu-system-x86_64 -m 1G -accel kvm -cdrom .../ubuntu-22.04-live-server-amd64.iso
のようにすると、ウィンドウが現れてubuntuのインストーラーが起動してくる*3。このウィンドウは、暗黙に指定された-display gtk
によるもので*4、ViewメニューからQemuモニターやシリアルポートを選択して操作することもできる*5。qemu-img
でディスクイメージを作り、-hda some-disk.img
のようにすると、そこへインストールすることもできるはずである。このとき、仮想マシン (VM) には既定のデバイスが接続されており、たとえばuserバックエンドのIntel i82540EM NIC (いわゆるe1000) が見える。-hda
のディスクとCD-ROMはチップセット内蔵のIDEインターフェイスに接続される。
GUI環境でない場合、
qemu-system-x86_64 -m 1G -accel kvm -nographic -cdrom .../ubuntu-22.04-live-server-amd64.iso
のようにすると、標準入出力がシリアルポートに接続され、シリアルコンソールによる運用ができる。VMにはVGAデバイスが接続されているため、grubの画面でカーネルのコマンドラインにconsole=ttyS0
を追加する必要がある*6。
なお、このとき標準入出力はQemuモニターとも接続されており、Control-A、続いてcを押すことで互いに切り替えることができる*7。
2. Qemuモニター
Qemuモニターは、ユーザーからさまざまな制御コマンドを受け付けたり、逆にユーザーへVMで発生したイベントを送信したりするチャネルである。旧来から人間がコマンドを入力するために利用していたが、後にプログラム (libvirtなど) から制御するのに適したJSONベースのプロトコルが追加されている。前章で、特にオプション指定をしなくても暗黙のうちに起動されたモニターは、人間が操作することが前提となっており、HMP (Human Monitor Interface: Pはどこへ行ったのだろう) と呼ばれる。またlibvirtが起動した時のオプション指定で起動されるモニターは、QMP (Qemu Machine Protocol) と呼ばれ、プログラムが利用する前提となっているほか、VMの起動や終了などのイベントを取得することもできる。
Qemuモニターは、HMP、QMPともQemu起動オプションで幾つでも起動できる。モニターを追加する起動オプションは何種類かあるが、-chardev
でキャラクターデバイスバックエンドを定義し、それを利用する場合には-monitor chardev=CHARDEV_ID
を利用するやり方が自由度が高い。-monitor
にはmode
が指定でき、HMPモニターを追加する場合はmode=readline
、QMPモニターを追加したい場合はmode=control
とする。
2.1 HMP (readline)
こちらは人間が操作するものであるため、helpコマンドが使える (help
、help info
)。筆者が使うものを例示すると、
gdbserver PORT
: VMのカーネルをデバッグするために、gdbを接続する端点を作る。gdbからは、target remote localhost:PORT
などとする。c
(cont
): VMの実行を再開する。起動時に-S
オプションをつけると、一時停止状態で起動するため、この状態でVNCやSPICEのクライアントを接続し、モニターからc
と入力することで、VMを起動させるような使い方をする。逆に一時停止するのはs
(stop
) である。sendkey
: 手元の端末が吸い取ってしまうとか、VMと端末とでキーボード配列が違うなどで、入力できないキーを入力するのに利用できる。例えば、Alt-Tabを入力するには、sendkey alt-tab
とする。info qtree
: VMに接続されているデバイスを列挙する。
2.2 QMP (control)
こちらは機械が操作する前提であるため、info qtree
のような便利なものはない。リファレンスを参照すると、膨大なコマンドが用意されていることがわかる。入出力ともJSON形式で、コマンドの実行は{"execute": "COMMAND", "arguments": ARGUMENTS}
のような形式で行う。
まず、お約束としてqmp_capabilities
コマンドを実行する。
{"execute": "qmp_capabilities"} {"return": {}}
たとえば、ディスクイメージのミラーを作るには、drive-mirror
コマンドを利用する。まず、query-block
コマンドで、ディスクの内部名を調べる。出力は見やすさのためjq
を通した。必要なのは、"device"
または"node-name"
である。
{"execute":"query-block"} { "return": [ { "io-status": "ok", "device": "ide0-hd0", "locked": false, "removable": false, "inserted": { "iops_rd": 0, "detect_zeroes": "off", "image": { "virtual-size": 107374182400, "filename": "test.qcow2", "cluster-size": 65536, "format": "qcow2", [中略] "dirty-flag": false }, "iops_wr": 0, "ro": false, "node-name": "#block161", [中略] "file": "test.qcow2" }, "qdev": "/machine/unattached/device[24]", "type": "unknown" }, { "io-status": "ok", "device": "ide1-cd0", [以下略]
"device"
または"node-name"
を指定してdrive-mirror
を実行する。
{"execute":"drive-mirror", "arguments":{"device":"#block161", "target":"mirror.qcow2", "sync":"full", "format":"qcow2"}} Formatting 'mirror.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=107374182400 lazy_refcounts=off refcount_bits=16 {"timestamp": {"seconds": 1684391400, "microseconds": 195622}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "ide0-hd0"}} {"timestamp": {"seconds": 1684391400, "microseconds": 196228}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "ide0-hd0"}} {"return": {}}
"timestamp"
で始まる行は、Qemuから送られてくるイベント情報である。どこまでミラーが進んだかは、query-block-jobs
で調べられる。
{"execute":"query-block-jobs"} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "ide0-hd0", "auto-dismiss": true, "busy": true, "len": 7685668864, "offset": 5225054208, "status": "running", "paused": false, "speed": 0, "ready": false, "type": "mirror"}]}
上記なら、5225054208/7685668864で、約68%まできていることがわかる。しばらくすると、終わったというイベントが送られてくる。
{"timestamp": {"seconds": 1684391585, "microseconds": 984923}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "ide0-hd0"}} {"timestamp": {"seconds": 1684391585, "microseconds": 985061}, "event": "BLOCK_JOB_READY", "data": {"device": "ide0-hd0", "len": 7685668864, "offset": 7685668864, "speed": 0, "type": "mirror"}}
この後、block-job-complete
を実行すると、ディスクイメージが新しいファイルに挿し変わり、古いファイルは切り離される。代わりに、block-job-cancel
を実行すると、新しいファイルが切り離され、古いファイルのみが使われるようになる。どちらかを実行するまでは、両方のイメージが更新され続ける。
{"execute":"block-job-cancel", "arguments":{"device": "ide0-hd0"}} {"return": {}} {"timestamp": {"seconds": 1684391824, "microseconds": 200237}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "ide0-hd0"}} {"timestamp": {"seconds": 1684391824, "microseconds": 200341}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "ide0-hd0"}} {"timestamp": {"seconds": 1684391824, "microseconds": 201503}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "ide0-hd0", "len": 7695040512, "offset": 7695040512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": 1684391824, "microseconds": 201576}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "ide0-hd0"}} {"timestamp": {"seconds": 1684391824, "microseconds": 201621}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "ide0-hd0"}}
2.3 libvirt配下のQemuに対するモニターコマンド
libvirt配下のQemuは、QMPモニターのUnixソケットを持っており、常にlibvirtdと繋がっている。libvirtにはこのモニターにコマンドを送るAPIがあり、またvirshにも対応するqemu-monitor-command
というコマンドがある。
$ virsh qemu-monitor-command --hmp VM info qtree bus: main-system-bus type System dev: kvm-ioapic, id "" gpio-in "" 24 gsi_base = 0 (0x0) mmio 00000000fec00000/0000000000001000 [以下略] $ virsh qemu-monitor-command VM '{"execute":"query-blockstats"}' | jq . { "return": [ { "device": "", "parent": { "stats": { "unmap_operations": 0, "unmap_merged": 0, "flush_total_time_ns": 0, [以下略]
qmp_capabilities
コマンドはすでにlibvirtが実行しているため、あらためて実行する必要はない。
前節で挙げたようなコマンドは、libvirtの知らないところでQemuの状態を変えてしまうので、望ましくない。多くの操作はlibvirt API (対応するvirshコマンド) があるため、これを使うべきである。たとえば前節のディスクイメージのコピーは、virsh blockcopy
というコマンドでできる。
また、Qemuから送られてくるイベントは、qemu-monitor-command
では受け取れず、別途libvirt APIを使って受け取る。
3. リモートからのvirt-manager
リモートマシンのQemu/KVMをGUIで管理するのには、当該リモートマシンでvirt-managerを実行する方法と、リモートマシンで動作するlibvirtdを手元のマシンで動作するvirt-managerで管理する方法とがある。筆者が手元で使っているのはmacbookなので、前者を利用している*8。
3.1 リモートでvirt-managerを動かす
virt-managerは、X11アプリケーションで (も) あるので、sshのX11 forwardingの機能で容易に手元に画面を持ってくることができる。リモートマシンのユーザーがlibvirt
グループに入っていることを確認した上で、
$ ssh -Xf REMOTE virt-manager
(REMOTEは、リモートマシンのIPアドレス)
これで、(自動的にXQuartzが起動して) macの画面にvirt-managerのウィンドウが表示される。しかし、virt-managerはWaylandアプリケーションでもあるので、注意点がある。
リモートマシンでWaylandセッション (gnome-shellなど) が動いていると、virt-managerはそちらのWaylandへ行ってしまう。これを防ぐには、環境変数
GDK_BACKEND
をx11にするか、--display localhost:10.0
などとしてX11ディスプレイを指定する必要がある*9。virt-managerを複数起動することはできず、2度目以降の起動では、起動済みのvirt-managerがフォアグラウンドへ出るだけである。このため、リモートでWaylandセッションが動いているのを忘れて上記を実行すると、何度実行しても特にエラーを表示することもなく手元には何もこなくてハマることになる。
3.2 リモートvirt-managerを使って、音声を聞く
筆者の仕事ではあまりあることではないが、VMのサウンド機能を使いたいことがあるかもしれない。この場合は、pulseaudioを利用する。macOSでもHomebrewなどでpulseaudioが容易にインストールできるので、これを利用するが、~/.config/pulse/cookie
を共有しておく (リモートと手元で同一内容にする) こと、module-native-protocol-tcp
モジュールをロードしておくこと、という2点を確認しておきたい。後者は、pactl
コマンドを使うか、/usr/local/etc/pulse/default.pa
(Homebrewでインストールした場合) を~/.config/pulse/default.pa
にコピーし、#load-module module-native-protocol-tcp
という行のコメントを外した上で、brew services restart pulseaudio
などとしてpulseaudioを再起動する。サウンドを再生する場所は、環境変数PULSE_SERVER
で指定する。
$ ssh -Xf REMOTE env PULSE_SERVER=LOCAL virt-manager
(LOCALは、macbookのIPアドレス)
または、TCPポート4713をsshで適切に転送する。なお、遅延が大きかったりするので、間違ってもVMで音声付き動画を再生しようなどと考えてはならない (笑)。
4. ディスクイメージ操作
VMの外から仮想ディスク上のファイルを操作したいことはよくある。ここでは、オフラインの仮想ディスクイメージを操作する方法を2つ紹介する。
4.1 guestfish
guestfishは、Qemuの仮想ディスクイメージに対してさまざまな操作するコマンドである*10。裏でQemuを動かしてLinuxを起動しているため、LVM上のものを含めてLinuxで扱えるあらゆるファイルシステムが扱える。guestfishは、スクリプトから起動するために、すべてをコマンドライン引数で指定することもできるが、ここでは対話的に利用する例を紹介する。
$ sudo guestfish -a test.qcow2 Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images. [中略] ><fs> run ><fs> list-filesystems /dev/sda1: unknown /dev/sda2: ext4 /dev/ubuntu-vg/ubuntu-lv: ext4 ><fs>
ubuntuではsudoしないと動作しない。これは、裏で動かすQemuの利用するカーネルとして、ホストのvmlinuzを利用すること、またubuntuではvmlinuzがrootにしか読めないことによる。最初にrun
コマンドを実行することにより、Qemuが起動する。起動したLinuxが見ることのできるファイルシステムは、list-filesystems
で列挙することができる。LVMのボリュームも認識されていることがわかる。
><fs> mount /dev/ubuntu-vg/ubuntu-lv / ><fs> ls /root .bashrc .profile .ssh snap ><fs> cat /root/.bashrc # ~/.bashrc: executed by bash(1) for non-login shells. # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) # for examples [中略] ><fs> vi /root/.bashrc [以下略]
ファイルシステムをmountすると、lsコマンドやcatコマンドを使ってディレクトリやファイルの中身を見たり、viで編集したりすることができる。その他、ファイルをホストへコピーするcopy-out、逆にホストのファイルを仮想ディスクイメージ内にコピーするcopy-in、まとめてtarファイルにしたり、tarファイルを展開するtar-out/tar-inといったコマンドがあり、またパーティションテーブルを操作したりfsckやmkfsしたりすることもできる。
4.2 LIO/tcmu-runner
guestfishはかなり高機能で、たいていの用は済ますことができるが、LIO*11を使うとraw形式やQcow/Qcow2形式の仮想ディスクイメージをホストに接続して、より高度な操作を行なうこともできる。
LinuxをiSCSIやFCなど各種SCSIベースプロトコルのターゲットにすることができるLIOには、ブロックデバイスや通常ファイルをバックエンドのストレージとして利用する機能がある。通常ファイルすなわちQemuのraw仮想ディスクイメージである。この他にTCM Userspaceという機能もあり、ユーザースペースで動くデーモンがデータを供給できる。ユーザースペースデーモンの実装としてtcmu-runnerがあり、これにはCeph RBDやQemuのQcow2イメージを利用する機能がある。tcmu-runnerは、ubuntuではパッケージとして提供されているので、apt install tcmu-runner
で容易に導入できる*12。
ここでは、tcmu-runnerを利用して、Qcow2イメージをホストに接続してみよう。まずtcmu-runnerデーモンが動作していることを確認する。
$ systemctl status tcmu-runner ● tcmu-runner.service - LIO Userspace-passthrough daemon Loaded: loaded (/lib/systemd/system/tcmu-runner.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2023-05-16 17:25:09 JST; 3 days ago [以下略]
バックエンドとしてQcow2イメージを追加する。
$ sudo targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type 'help'. /> ls o- / ..................................................................... [...] o- backstores .......................................................... [...] | o- block .............................................. [Storage Objects: 0] | o- fileio ............................................. [Storage Objects: 0] | o- pscsi .............................................. [Storage Objects: 0] | o- ramdisk ............................................ [Storage Objects: 0] | o- user:fbo ........................................... [Storage Objects: 0] | o- user:qcow .......................................... [Storage Objects: 0] | o- user:rbd ........................................... [Storage Objects: 0] | o- user:zbc ........................................... [Storage Objects: 0] o- iscsi ........................................................ [Targets: 0] o- loopback ..................................................... [Targets: 0] o- vhost ........................................................ [Targets: 0] /> /backstores/user:qcow /backstores/user:qcow> create test 100g .../test.qcow2 Created user-backed storage object test size 107374182400. /backstores/user:qcow> / /> ls o- / ..................................................................... [...] o- backstores .......................................................... [...] | o- block .............................................. [Storage Objects: 0] | o- fileio ............................................. [Storage Objects: 0] | o- pscsi .............................................. [Storage Objects: 0] | o- ramdisk ............................................ [Storage Objects: 0] | o- user:fbo ........................................... [Storage Objects: 0] | o- user:qcow .......................................... [Storage Objects: 1] | | o- test .......................... [.../test.qcow2 (100.0GiB) deactivated] | | o- alua ............................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ................... [ALUA state: Active/optimized] | o- user:rbd ........................................... [Storage Objects: 0] | o- user:zbc ........................................... [Storage Objects: 0] o- iscsi ........................................................ [Targets: 0] o- loopback ..................................................... [Targets: 0] o- vhost ........................................................ [Targets: 0] />
100gはサイズが100GiBであることを示す。正しいサイズを指定しないとエラーになる。
これをiSCSIでエクスポートしてももちろんよいのだが、TCM loopbackという機能を用いれば、その場でsdが生える。
/> /loopback /loopback> create Created target naa.500140593fa2b9eb. /loopback> naa.500140593fa2b9eb/luns /loopback/naa...3fa2b9eb/luns> create /backstores/user:qcow/test 0 Created LUN 0. /loopback/naa...3fa2b9eb/luns> / /> ls o- / ..................................................................... [...] o- backstores .......................................................... [...] | o- block .............................................. [Storage Objects: 0] | o- fileio ............................................. [Storage Objects: 0] | o- pscsi .............................................. [Storage Objects: 0] | o- ramdisk ............................................ [Storage Objects: 0] | o- user:fbo ........................................... [Storage Objects: 0] | o- user:qcow .......................................... [Storage Objects: 1] | | o- test ............................ [.../test.qcow2 (100.0GiB) activated] | | o- alua ............................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ................... [ALUA state: Active/optimized] | o- user:rbd ........................................... [Storage Objects: 0] | o- user:zbc ........................................... [Storage Objects: 0] o- iscsi ........................................................ [Targets: 0] o- loopback ..................................................... [Targets: 1] | o- naa.500140593fa2b9eb ............................. [naa.50014059e135b40b] | o- luns ........................................................ [LUNs: 1] | o- lun0 ................................. [user/test (default_tg_pt_gp)] o- vhost ........................................................ [Targets: 0] /> exit Global pref auto_save_on_exit=true Configuration saved to /etc/rtslib-fb-target/saveconfig.json $
これでsdができたはず。
$ journalctl -b 0 -t kernel | tail [前略] May 19 17:40:33 c3 kernel: scsi 14:0:1:0: Direct-Access LIO-ORG TCMU device 0002 PQ: 0 ANSI: 5 May 19 17:40:33 c3 kernel: sd 14:0:1:0: Attached scsi generic sg6 type 0 May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] 209715200 512-byte logical blocks: (107 GB/100 GiB) May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] Write Protect is off May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] Mode Sense: 2f 00 00 00 May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] Optimal transfer size 65536 bytes May 19 17:40:33 c3 kernel: sdh: sdh1 sdh2 sdh3 May 19 17:40:33 c3 kernel: sd 14:0:1:0: [sdh] Attached SCSI disk $ lsblk /dev/sdh NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sdh 8:112 0 100G 0 disk ├─sdh1 8:113 0 1M 0 part ├─sdh2 8:114 0 2G 0 part └─sdh3 8:115 0 98G 0 part └─ubuntu--vg-ubuntu--lv 253:14 0 98G 0 lvm
LVMボリュームも認識されているので、mountするなりmkfsするなり好きなようにいじくり回せばよい。終わったら、targetcliで解除する。
$ sudo targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type 'help'. /> /loopback /loopback> delete naa.50014059e135b40b Deleted Target naa.50014059e135b40b. /loopback> /backstores/user:qcow /backstores/user:qcow> delete test Deleted storage object test. /backstores/user:qcow> / /> ls o- / ..................................................................... [...] o- backstores .......................................................... [...] | o- block .............................................. [Storage Objects: 0] | o- fileio ............................................. [Storage Objects: 0] | o- pscsi .............................................. [Storage Objects: 0] | o- ramdisk ............................................ [Storage Objects: 0] | o- user:fbo ........................................... [Storage Objects: 0] | o- user:qcow .......................................... [Storage Objects: 0] | o- user:rbd ........................................... [Storage Objects: 0] | o- user:zbc ........................................... [Storage Objects: 0] o- iscsi ........................................................ [Targets: 0] o- loopback ..................................................... [Targets: 0] o- vhost ........................................................ [Targets: 0] />
5. その他小ネタ
5.1 apparmorを解除する
ubuntuホスト上でlibvirt配下のQemuに対して、qemu-monitor-command
などで特殊な操作を行なおうとすると、すぐにPermission denied
と言われてしまう。誰がdenyしているのかというと、AppArmorである。libvirtは動的にAppArmorプロファイルを生成・修正するので、libvirt API経由で操作している限りPermission deniedは出ないはずである。
libvirtの知らないところで怪しい実験をしたりしたい場合は、AppArmorを解除する。この方法を2つご紹介する。
- Qemu起動前の場合 --
virsh edit
などでドメイン定義のXMLファイルを編集し、<domain>
要素直下 (<name>
や<devices>
などの並び) に<seclabel type='none'/>
という要素を入れる。 - Qemu起動後の場合 --
virsh domuuid
などでドメインのUUIDを取得し、aa-complainコマンドでcomplainモードにするか、aa-disableでAppArmorを禁止する。例:aa-complain /etc/apparmor.d/libvirt/libvirt-$(virsh domuuid DOMAIN)
5.2 画面解像度を指定する
libvirt配下のQemuのディスプレイ解像度を指定するには、videoデバイスの<model>
要素の中に<resolution>
要素を追加する。
<domain type='kvm'> [中略] <devices> [中略] <video> <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> </video> [以下略]
これを、
<domain type='kvm'> [中略] <devices> [中略] <video> <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'> <resolution x='1280' y='768'/> </model> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> </video> [以下略]
などとする。x='768' y='1024'
のように、縦長画面にすることもできる。ファームウェア画面などは特定の解像度になってしまうし、videoデバイス (model type) によっては利用できないこともある。
*1:OpenStack Novaなどもlibvirtを利用している
*2:場合により異なるので、virsh net-dumpxml defaultなどとして確認する
*3:-mを指定しないと、RAMが128MBしか接続されないため、ubuntuのインストーラーは起動できない
*4:ubuntu 22.04の場合
*5:ショートカットキーとして、Alt-Control-1やAlt-Control-2などが用意されており、それで切り替えることも可能
*6:豆知識: ubuntuの場合、---以降に書くことで、インストールされたシステムにもこのオプションが引き継がれる
*7:Control-Aがエスケープキーとなっており、他の機能はControl-A hで表示される
*8:macOSで動作するvirt-manager移植、というものもないわけではないようだ
*9:これは、gtkを利用したアプリケーション共通の動作である
*10:libguestfsという、ディスクイメージ操作のライブラリがあり、そのCLIシェルがguestfishである。libvirtに対するvirshと似ている
*11:LIOのコアである、ターゲットをエミュレートする部分をTCMと呼ぶようだが、TCMをLIOの別名のように使うことも多いようだ
*12:RHEL向けにはRed Hat社ストレージ製品などにバンドルされるようだ