Virtual Machine Management with libvirt

Wednesday, August 30, 2023
Reading time 7 minutes

Continuing with some features present in libvirt that I believe could be useful to know, this post is dedicated, once again, to the administration of virtual machines using the libvirt tool and its application virsh, which allows running some of the perhaps most useful commands when working with files of this type of VM. All these commands are typically executed as root.

Domain Administration

If you want to know the list of currently created domains, you can do so with the following command. You can change –all to –autostart, for example, if you want to see all domains configured to start when the host system boots:

# virsh list --all
 Id   Name    State
------------------------
 2    test    running
 6    test2   running
 9    bot2    running
 -    bot     shut off

To start the domain, simply use the start command:

# virsh start test
Domain 'test' started

To stop it, there are two different ways. It can be stopped regularly using the shutdown command, which is similar to shutting down your computer using the functions that the operating system already provides, although there is also the destroy command, which contrary to what one might think, does not actually delete the domain but simply forcibly shuts it down. This is similar to abruptly disconnecting the power cable from the physical machine.

# virsh shutdown test
Domain 'test' is being shutdown
# virsh destroy test

Finally, if the domain does not appear in the list shown with the command list –autostart, it means you will need to start it manually every time the host system starts. To configure the automatic start of the domain, simply use the autostart command:

# virsh autostart test
Domain 'test' marked as autostarted

If you forget the connection information to the VM using the spice protocol, it can be recovered with or without a password using the following command:

# virsh domdisplay test --include-password --all
spice://localhost:5901?password=pwtest

To find information about the available network interfaces to the virtual machine, as well as the assigned IP addresses, use the following command:

# virsh domifaddr test
 Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 vnet5      52:54:00:56:25:fa    ipv4         192.168.122.61/24

More general information about the domain can be obtained with this command:

# virsh dominfo test
Id:             6
Name:           test
UUID:           35bc6234-fc63-4717-9bcb-18d36de32d25
OS Type:        hvm
State:          running
CPU(s):         2
CPU time:       9671.1s
Max memory:     2097152 KiB
Used memory:    2097152 KiB
Persistent:     yes
Autostart:      enable
Managed save:   no
Security model: apparmor
Security DOI:   0
Security label: libvirt-35bc6234-fc63-4717-9bcb-18d36de32d25 (enforcing)

Exploring Node Information

Before performing some tasks that could be considered more advanced, it is necessary to have a deep understanding of the characteristics of the node where the domain is running. For this, many different commands can be used, among which I have found the following particularly interesting:

Check the number of available and online CPUs, which can be useful when assigning virtual CPUs and doing CPU pinning if necessary:

# virsh nodecpumap --pretty
CPUs present:   12
CPUs online:    12
CPU map:        0-11

Edit the definition of the domain file. Domains are saved in XML format, in /etc/libvirt/qemu, and can be edited manually, although it is recommended to edit them with this command to avoid having to restart libvirt after saving the file if minor changes are made:

# virsh edit test

While editing the domain, you can manually add or remove device definitions. This is especially useful since after completing the system installation, we no longer need the ISO images mounted on the CD drives. So, you can find the device under one of the tags and remove its entire definition.

If the changes are significant, such as the number of virtual CPUs assigned to the domain or new devices, it may be necessary to restart the libvirtd service:

# systemctl restart libvirtd

If you want to rename the domain, you can do so easily with this command, although the virtual disk file is not renamed:

# virsh domrename test new-name

Finally, if you want to delete a domain, you can do so by deleting its definition, which is the information located in the XML file. By default, libvirt does not delete the disk images attached to VMs to avoid data loss. The basic command is this. It should be noted that the domain must already be stopped to be able to delete it:

# virsh undefine test

This command has options that allow you to select the storage devices to be deleted or whether to keep the TPM or NVRAM device, but those are considered advanced cases. Anyway, you can always check the virsh help by typing something like this:

# virsh undefine --help

Backing Up Files

Once I managed to install Windows 10 on a virtual machine using VirtIO drivers, I decided to preserve that machine as a base system. Technically, it is a machine that contains NVDA and Google Chrome installed, and nothing else. My idea is simple: if I ever need a clean machine again, I can recreate this virtual machine. For now, I have only copied the files /etc/libvirt/windows-10-nvda.xml and /var/lib/libvirt/images/windows-10-nvda.qcow2, making sure to remove the ISO image devices from the VM’s XML. You also need to use the virt-sparsify command like this:

# virt-sparsify /var/lib/libvirt/windows-10-nvda.qcow2 path/to/backup/windows-10-nvda.qcow2

This command will do something very necessary: it will remove all the “empty” space in the disk image created for the virtual machine. This is very useful to be able to copy it to any other location. This has a curious explanation. When creating a disk file for the virtual machine, a qcow2 file is created. These files are designed to occupy up to a certain amount of space, for example, 50 GB, but those 50 GB will not be used until the system inside the virtual machine requests it. Hence its name, qcow2 (copy on write 2).

The downside is that many tools, such as stat, ls, or even cp, do not take into account the space currently used by the disk image, but rather the maximum possible space in the file. This means that when copying, the entire 50 GB that the file could weigh is copied, instead of the 15 or 20 that may be occupied. If the entire file size is not used, how does the system copy 50 GB? Because it fills them with zeros, basically making the system reserve all that space. That’s why virt-sparsify reduces the space used on the disk to the truly necessary. It is advisable to run this command to copy the disk image of a VM that you want to save, although depending on the disk size and where it is copied to, it may take quite some time.

Once the virtual machine is stored elsewhere, you can use the file of its definition, which is the XML, and its disk image to recreate it either on the same host or on a different one. This can be done in two steps. First, you have to “define” the virtual machine on the host using this command:

# virsh define --file path/to/file.xml

Finally, using the simple cp command, we can place the disk image in the path defined in the XML. In an unmodified installation, the default path is always /var/lib/libvirt/images. If the path has been changed on any of the hosts, the XML must be edited to manually update the path.

Conclusion

I think this has been the most interesting thing I have seen in several years. On some occasions, I have used Proxmox, which I find to be a fantastic tool, but there is nothing like learning the basics of the technologies on which Proxmox and other tools have been built over the years. At this point, I have a system running several virtual machines for various needs, and now the only thing left is to research how to allow incoming connections to one of them.

Linux Administration NAS

libvirt management linux debian virsh