home
Creating virtual guests with libvirt can be conveniently automated with task and a few other dependencies. Below I describe how to handcraft a Taskfile.yml
that provides 2 tasks (create
& delete
) which can be used to create and delete individual guests using libvirt.
Apart from task
, the other dependencies needed are:
$ sudo apt install wget libvirt-clients apg gopass guestfs-tools virtinst
When creating the libvirt network, I make reference to br0
which is the bridge interface on my host system running libvirtd
. br0
can be setup using a network interface definition like this:
auto br0
iface br0 inet static
address 192.168.1.10
broadcast 192.168.1.255
netmask 255.255.255.0
gateway 192.168.1.1
dns-nameservers 192.168.1.1
bridge_ports eth0
bridge_stp off
bridge_waitport 0
bridge_fd 0
bridge-utils
is installed.First, create a default Taskfile.yml
file:
$ task --init
Within Taskfile.yml
create vars
and env
:
version: '3'
env:
LIBVIRT_DEFAULT_URI: qemu:///system
vars:
IMAGE_URL: https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
IMAGE_FILENAME:
sh: echo {{ base .IMAGE_URL }}
SSH_PUBKEY: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM+eJVmeHO1fclWlTGwV6TZHKEkGg7ZGVrIHro5jGBvb
HOSTNAME: "debian-test-system"
LIBVIRT_POOL_NAME: "default"
DISK: 10240M
MEMORY: 2048
VCPUS: 2
ROOTPASSWD:
sh: apg -m 8 -a0 -n1
env
sets environment variables for all tasks:LIBVIRT_DEFAULT_URI
is the URI which is running libvirtd
that virsh
should be able to connect tovars
sets variables for all tasks:IMAGE_URL
points to the Debian image which will be used as the base imageSSH_PUBKEY
is your SSH public key. Please replace the above value with something else!HOSTNAME
is the hostname of the virtual guest that will be built and is also used to name a few other libvirt resourcesLIBVIRT_POOL_NAME
is the name of the libvirt pool that the volume will be created inDISK
, MEMORY
& VCPUS
will be used to define the virtual guest resourcesROOTPASSWD
is the random root password that will be stored in gopass
Create a bridge.xml
file which defines a libvirt network:
<network>
<name>debian-test-system</name>
<forward mode="bridge"/>
<bridge name="br0"/>
</network>
bridge name
value refers to the bridge network interface on the host.name
value should match the HOSTNAME
var
within the Taskfile.yml
(e.g. debian-test-system
).Next create the create
task
:
tasks:
create:
summary: "create {{.HOSTNAME}}"
cmds:
- wget -c {{.IMAGE_URL}}
- cmd: echo {{.ROOTPASSWD}} | gopass insert --force {{.HOSTNAME}}-root-password
silent: true
- |
virt-customize -a {{.IMAGE_FILENAME}} \
--root-password password:{{.ROOTPASSWD}} \
--edit '/etc/default/grub: s/^GRUB_CMDLINE_LINUX=".*"$/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0 systemd.unified_cgroup_hierarchy=0"/' \
--run-command 'mkdir -p /boot/grub2 && update-grub' \
--ssh-inject root:string:"{{.SSH_PUBKEY}}" \
--hostname {{.HOSTNAME}}
- virsh vol-create-as --pool {{.LIBVIRT_POOL_NAME}} --name {{.HOSTNAME}} --capacity {{.DISK}} --format qcow2
- virsh vol-upload --vol {{.HOSTNAME}} --pool {{.LIBVIRT_POOL_NAME}} {{.IMAGE_FILENAME}}
- virsh net-define bridge.xml
- virsh net-autostart {{.HOSTNAME}}
- virsh net-start {{.HOSTNAME}}
- |
virt-install \
--virt-type kvm \
--name {{.HOSTNAME}} \
--ram {{.MEMORY}} \
--vcpus={{.VCPUS}} \
--import \
--video virtio \
--disk vol={{.LIBVIRT_POOL_NAME}}/{{.HOSTNAME}},format=qcow2,bus=virtio \
--memballoon virtio \
--boot hd \
--network network:{{.HOSTNAME}},model=virtio \
--noautoconsole \
--cpu host \
--qemu-commandline='-smbios type=1,serial=ds=nocloud' \
--osinfo debian11
wget
will download the Debian imagegopass
is used to store the randomly generated passwordvirt-customize
is used to customise the image before it’s uploaded to the libvirt volume. The root password is updated and the SSH public key defined within vars
is injected.virsh
is used to create a new volume, upload the customised image and create a networkvirt-install
is used to provision the guest with suitable parametersAnd then create the delete
task:
delete:
desc: delete {{.HOSTNAME}}
cmds:
- virsh vol-delete --pool {{.LIBVIRT_POOL_NAME}} {{.HOSTNAME}}
- virsh destroy {{.HOSTNAME}}
- virsh undefine {{.HOSTNAME}}
- virsh net-undefine {{.HOSTNAME}}
- virsh net-destroy {{.HOSTNAME}}
virsh
is used to destroy the resources that were created by the create
taskIf everything is wired up correctly you should be able to create
a virtual guest:
$ task create
task: [create] wget -c https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
--2024-05-12 11:28:30-- https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
Resolving cloud.debian.org (cloud.debian.org)... 194.71.11.165, 194.71.11.163, 2001:6b0:19::163, ...
Connecting to cloud.debian.org (cloud.debian.org)|194.71.11.165|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://laotzu.ftp.acc.umu.se/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2 [following]
--2024-05-12 11:28:33-- https://laotzu.ftp.acc.umu.se/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
Resolving laotzu.ftp.acc.umu.se (laotzu.ftp.acc.umu.se)... 194.71.11.166, 2001:6b0:19::166
Connecting to laotzu.ftp.acc.umu.se (laotzu.ftp.acc.umu.se)|194.71.11.166|:443... connected.
HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable
The file is already fully retrieved; nothing to do.
task: [create] echo Twybajni | gopass insert --force debian-test-system-root-password
task: [create] virt-customize -a debian-12-generic-amd64.qcow2 \
--root-password password:Twybajni \
--edit '/etc/default/grub: s/^GRUB_CMDLINE_LINUX=".*"$/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0 systemd.unified_cgroup_hierarchy=0"/' \
--run-command 'mkdir -p /boot/grub2 && update-grub' \
--ssh-inject root:string:"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM+eJVmeHO1fclWlTGwV6TZHKEkGg7ZGVrIHro5jGBvb" \
--hostname debian-test-system
[ 0.0] Examining the guest ...
[ 3.8] Setting a random seed
virt-customize: warning: random seed could not be set for this type of
guest
[ 3.9] Editing: /etc/default/grub
[ 4.0] Running: mkdir -p /boot/grub2 && update-grub
[ 4.9] SSH key inject: root
[ 5.7] Setting the hostname: debian-test-system
[ 6.5] Setting passwords
[ 7.2] Finishing off
task: [create] virsh vol-create-as --pool default --name debian-test-system --capacity 10240M --format qcow2
Vol debian-test-system created
task: [create] virsh vol-upload --vol debian-test-system --pool default debian-12-generic-amd64.qcow2
task: [create] virsh net-define bridge.xml
Network debian-test-system defined from bridge.xml
task: [create] virsh net-autostart debian-test-system
Network debian-test-system marked as autostarted
task: [create] virsh net-start debian-test-system
Network debian-test-system started
task: [create] virt-install \
--virt-type kvm \
--name debian-test-system \
--ram 2048 \
--vcpus=2 \
--import \
--video virtio \
--disk vol=default/debian-test-system,format=qcow2,bus=virtio \
--memballoon virtio \
--boot hd \
--network network:debian-test-system,model=virtio \
--noautoconsole \
--cpu host \
--osinfo debian11
Starting install...
Creating domain... | 0 B 00:00:00
Grab the root password from gopass
:
$ gopass show -o debian-test-system-root-password
And you should be able to connect to the serial console:
$ virsh console debian-test-system
Connected to domain 'debian-test-system'
Escape character is ^] (Ctrl + ])
debian-test-system login: root
Password:
Linux debian-test-system 6.1.0-21-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.90-1 (2024-05-03) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@debian-test-system:~#
To delete the guest and it’s libvirt resources use the delete
task:
$ task delete
task: [delete] virsh vol-delete --pool default debian-test-system
Vol debian-test-system deleted
task: [delete] virsh destroy debian-test-system
Domain 'debian-test-system' destroyed
task: [delete] virsh undefine debian-test-system
Domain 'debian-test-system' has been undefined
task: [delete] virsh net-undefine debian-test-system
Network debian-test-system has been undefined
task: [delete] virsh net-destroy debian-test-system
Network debian-test-system destroyed
The Taskfile.yml
in it’s entirety can be found here.