You will need to build a custom filesystem image that includes scripts and/or some other tooling that will run your commands and power off the virtual machine. There are many ways to approach this problem. Here's one possible solution:
Start with an operating system image. For this example, I'm using the debian 12 cloud image.
We'll create a copy-on-write clone of the image to hold our changes. This makes it easy to start over. Here we're creating a 5 GB image; you could adjust the image size as necessary for your workload:
qemu-img create -f qcow2 -F qcow2 \
-b debian-12-genericcloud-amd64.qcow2 root.qcow2 5g
Now we need to embed some additional information in the image. For this example, we need a netplan file to enable DHCP, and an rc.local
script that will perform our clone/build/install/etc actions.
I have a netplan file called network.yaml
that looks like this:
network:
version: 2
renderer: networkd
ethernets:
ens3:
dhcp4: true
And an rc.local
file that looks like this:
#!/bin/sh
set -e
while ! ping -c1 dns.google; do
echo "waiting for network"
sleep 1
done
apt-get -y update
DEBIAN_FRONTEND=noninteractive apt-get -y install gcc make autoconf automake libtool git autopoint gperf texinfo help2man
mkdir -p /srv/build
cd /srv/build
git clone https://git.savannah.gnu.org/git/hello.git
cd hello
./bootstrap
./configure --prefix=/usr/local
make
make install
/usr/local/bin/hello
poweroff
We can use the virt-customize
command to embed these in the image:
virt-customize -a root.qcow2 \
--copy-in network.yaml:/etc/netplan/ \
--copy-in rc.local:/etc/
Now we can use the qemu-system-x86_64
command to boot our image:
qemu-system-x86_64 -m 1g -enable-kvm \
-nographic \
-serial mon:stdio \
-drive file=root.qcow2,driver=qcow2 \
-nic user,model=virtio-net-pci
This will run the commands in our rc.local
file and power off the vm when complete.
If you need to explicitly report results back to the host, you again have lots of options. You could add a second serial port to your virtual machine (-serial file:status.txt
) and write status data there (echo OK > /dev/ttyS1
), or you could capture the console output to a file (replace -serial mon:stdio
with -serial file:console.txt
) and then grep
the output when the vm complete, etc.
If you are concerned about the script not completing due to an error, you could wrap your qemu-system-x86_64
command with the timeout
command. For example, to ensure the qemu vm exits in under 2 minutes:
timeout 120 qemu-system-x86_64 -m 1g -enable-kvm ...