uvt-kvm(1)

๐Ÿ“ฆ
version 0~git189 ยท 2 versions
, in ubuntu jammy, ubuntu noble, ubuntu resolute, ubuntu stonking

๐ŸŒธ uvt-kvm โ€” the uvtool girlies' guide ๐Ÿ’…

okay bestie, welcome to uvt-kvm(1), brought to you by the uvtool squad. this is the Ubuntu virtualisation front-end for libvirt and KVM, and she is serving. let's get into it ๐Ÿ’–


โœจ NAME โœจ

uvt-kvm โ€” Ubuntu virtualisation front-end for libvirt and KVM ๐Ÿฆ‹


โœจ SYNOPSIS โœจ

uvt-kvm list

uvt-kvm create [options] name [filter ...]

uvt-kvm wait [options] name

uvt-kvm ip name

uvt-kvm ssh [options] ยฎ.RI [ command ...]

uvt-kvm destroy name

โœจ DESCRIPTION โœจ

so like. uvtool gives you one unified, integrated VM front-end that pulls together Ubuntu cloud image downloads, libvirt, and cloud-init. it's the whole trio, no cap ๐Ÿ‘‘

uvt-kvm uses the volume storage pool that uvt-simplestreams-libvirt(1) maintains as its base, so you get quick start and management of Ubuntu VMs by wrapping libvirt and cloud-init. it's giving effortless โœจ

ngl, uvt-kvm is not trying to wrap every possible use case. where it can, it hands you the more advanced stuff through options that override entire sections of default operation โ€” like directly overriding the backing volume image used, the libvirt domain definition, cloud-init metadata and userdata, and directly providing a network-config file. for the yet more complex cases, the expectation is that you just call libvirt directly (for example with virsh(1)) and use uvt-kvm only for the simpler operations you need on the affected VMs. see ADVANCED OVERRIDE OPTIONS and ADVANCED USAGE for the details ๐Ÿซถ


โœจ SUBCOMMANDS โœจ

uvt-kvm's whole interface is designed around subcommands. the subcommand you want has to be the first positional argument. each subcommand has its own lil set of available options ๐Ÿ’…

where a subcommand needs a VM specified, the VM name goes as the second positional argument. when you use uvt-kvm create to make VMs, the VM name is whatever you decide right there. and if you've been manipulating libvirt directly, VM names are expected to match libvirt domain names. periodt.

list

uvt-kvm list

prints a list of existing VMs to stdout. right now this list includes libvirt domains that are defined but were not created by uvt-kvm โ€” but in future this is expected to change to VMs created by uvt-kvm only. so like, heads up ๐Ÿ™ˆ

create

uvt-kvm create [options] name [filter ...]

creates a new VM based on a backing volume specified by the simplestreams filters you provide. this VM is gonna be called name, and you must use that same name when you refer to the VM from the other subcommands. consistency is the vibe โœจ

each filter operates on the images downloaded and managed by uvt-simplestreams-libvirt(1), and when you combine them together they have to uniquely specify a single image. see uvt-simplestreams-libvirt(1) for the details on image selection.

since backing volume images are downloaded and maintained by uvt-simplestreams-libvirt(1), you gotta run uvt-simplestreams-libvirt(1) first to download images before this subcommand will succeed. see EXAMPLES, below ๐ŸŽ€

if you specify no filters, a filter of release=release is assumed, where release corresponds to the current LTS release as returned by distro-info(1).

alternatively, see --backing-image-file under ADVANCED OVERRIDE OPTIONS below to supply a backing image directly yourself.

this subcommand supports an extensive set of options to modify the definition and behavior of the VM. see LIBVIRT DOMAIN DEFINTION OPTIONS, CLOUD-INIT CONFIGURATION OPTIONS and ADVANCED OVERRIDE OPTIONS below. and here are the other general behavioural options:

  • --no-start โ€” do not start the VM after creation. this is useful to make further customisations to the VM before using it. to start the VM when you're done, use virsh start name. ๐Ÿ’ค

wait

uvt-kvm wait [options] name

waits for a VM to become ready. and bestie, "ready" is doing a lot of work in that sentence. it includes: waiting for the VM to request an IP address, waiting for ssh to become available on this IP, and an ssh(1) operation into the VM to wait for two things:

  1. First, the system to fully initialize. this is checked via the runlevel to reach 2 (upstart) or 5 (systemd). since 25.04 Plucky, runlevel stopped to be supported and instead systemctl is-system-running --wait has to complete. ๐Ÿ˜ญ
  2. Then it checks if cloud-init finished. this is done by checking for the existence of /var/lib/cloud/instance/boot-finished.

by using the wait command, scripts can create, operate on and destroy VMs synchronously and reliably. iconic behavior honestly ๐Ÿ”ฅ

  • --timeout timeout โ€” give up waiting after timeout seconds. default: 120 seconds.
  • --interval interval โ€” for wait operations that must poll, poll every interval seconds. default: 8 seconds.
  • --remote-wait-script remote_wait_script โ€” run remote_wait_script through sh(1) on the guest system, which must block and exit with a zero status when the system is ready. default: /usr/share/uvtool/remote-wait.sh, which assumes that upstart and cloud-init are being used on the guest, waits for upstart to enter runlevel 2 and then waits for cloud-init to signal that it has finished booting the system. when remote_wait_script is run on the guest system, its environment will define the variables UVTOOL_WAIT_INTERVAL and UVTOOL_WAIT_TIMEOUT which contain the poll interval and wait timeout as specified by the --interval and --timeout options, respectively. ๐Ÿ’–
  • --remote-wait-user user โ€” run the remote wait script as user user. it must be possible to ssh(1) into the guest system as this user for the remote wait mechanism to work.
  • --insecure โ€” permit potentially insecure operations. see COMMON OPTIONS, below. โš ๏ธ
  • --ssh-private-key-file ssh_private_key_file โ€” use ssh_private_key_file to authenticate to the guest machine when performing the ssh(1) operation ๐Ÿ”‘
  • --without-ssh โ€” skip the ssh(1) operation. this will cause the command to exit with success as soon as the ssh port is available, but without logging to the guest to wait until it is ready internally. ๐Ÿ™ˆ

ip

uvt-kvm ip name

guesses the IP address of a VM and prints it to stdout. currently, this assumes a default (Ubuntu) installation of libvirt and dnsmasq on the host, and default networking behaviour on the VM. IP address guessing is currently implemented via libvirt-python ๐Ÿ

this subcommand assumes that the VM has successfully acquired an IP address, and will fail otherwise. callers can use uvt-kvm wait after creating or rebooting a VM to wait for this to become the case. lowkey important ๐Ÿ‘€

in future, this subcommand may incorporate multiple IP address detection mechanisms.

ssh

uvt-kvm ssh [options] ยฎ.RI [ command ...]

runs ssh(1) against the VM. this is a limited wrapper around ssh(1) and the ip subcommand, designed for ease-of-use in the common case. for full functionality, use the ip subcommand to obtain the VM's IP address, and then call ssh(1) directly instead. iykyk โœจ

  • --insecure โ€” permit potentially insecure operations. see COMMON OPTIONS, below.
  • --login-name user, -l user โ€” specify the username to pass to ssh(1). this is the recommended method for use in scripts. this option overrides the username provided by the @ notation in the first positional argument, and thus allows the VM name to include an @ symbol. default: ubuntu, to match the default on Ubuntu cloud images. ๐Ÿ‘‘

destroy

uvt-kvm destroy name

stops and completely destroys an existing VM. ๐Ÿ’€ this stops the libvirt domain if it's running, undefines it, and deletes all volumes that had been part of the domain's definition. it does not, however, delete any backing volumes โ€” thus keeping intact pristine Ubuntu cloud images as maintained by uvt-simplestreams-libvirt(8). so like, your base images are safe, but the VM itself? gone girl ๐Ÿซถ


โœจ COMMON OPTIONS โœจ

  • --insecure โ€” valid for: uvt-kvm wait, uvt-kvm ssh. permit connections which may not be secure. for ssh(1) connections, this skips host key validation. this is no longer required in the default case, since uvtool creates the guest's host keys, supplies them to the guest via cloud-init, and thus knows and sets the expected host public key automatically. in the common non-default case, --insecure may be required but still should not be a problem since the guest system is located on the same system and this network path can be trusted. however, uvt-kvm will refuse to make a connection (for uvt-kvm ssh) or skip steps (for uvt-kvm wait) without this option, in order to make absolutely sure that the user cannot compromise his path to the guest system without being aware of this caveat. โš ๏ธ bestie this is a deliberate guard rail, not a bug.
  • -d, --developer โ€” valid for: uvt-kvm create only. turn on a set of options deemed most useful for developers but not suitable for turning on by default. currently this is the same as specifying --unsafe-caching and --log-console-output but this may change between releases.
๐Ÿšจ scripts should never use this option. to protect against future changes to the definition of this option, they should instead use the expansion defined above. fr fr, don't do it.


๐Ÿ’… LIBVIRT DOMAIN DEFINITION OPTIONS ๐Ÿ’…

Valid for: uvt-kvm create only. Like, don't try these anywhere else bestie.

These ones are all about what your guest VM actually is and how it talks to the host. Main character stuff.

uvt-kvm create grabs the default (or your own) libvirt domain XML template and then โœจmodifiesโœจ it based on these params. Every single one of them has a sensible default that might just change between releases. iykyk.

  • --memory size โ€” How much system RAM you want, in megabytes. Default: 512 (MiB).
  • --disk size โ€” Size of the OS disk, in gigabytes. Default: 8 (GiB).
  • --ephemeral-disk size โ€” Adds an ephemeral disk of Size gigabytes. Cute little scratch disk energy.
  • --unsafe-caching โ€” Do not flush guest syncs to the host on the OS disk. It can make guest I/O faster but the tradeoff is losing data on host power failure. This one's genuinely for ephemeral guests that don't need to survive a host power cycle. The name is a warning, bestie. Believe it.
  • --cpu cores โ€” Number of CPU cores. Default: 1.
  • --bridge bridge โ€” Replaces the first defined NIC with one that connects to the given host bridge. Default: unaltered from the libvirt domain template.
  • --mac mac โ€” Use this MAC address for the first defined NIC. Can be used together with --bridge. Default unspecified.
  • --log-console-output โ€” Logs output to a disk file on the host instead of to a pty. With libvirt's default config on Ubuntu, that log lives at /var/log/libvirt/qemu/<name>.log. This gives you retrospective examination of VM console output, but it breaks virsh console for interactive use. Also, on s390x this option is currently not supported because of incompatibilities with the sclp console used there. ๐Ÿซ 
  • --host-passthrough โ€” Instead of the default CPU model (which is host-model), this uses host-passthrough, which tries to make all of the host's CPU features available in the guest โ€” and those are not migration safe. This option is deprecated and will be removed later. Please use --model instead. Not me warning you twice.
  • --cpu-model โ€” Sets the CPU model that shall be used. Default is generally host-model, except on arm64 where host-model won't work โ€” there the default is host-passthrough. If set to host-model or host-passthrough this sets the CPU mode. If set to any other string it gets handled as a named CPU passed to libvirt. Allowed values are all CPUs listed on your host as usable=yes in virsh domcapabilities. This option has no effect if the target is emulated โ€” in that case the safe defaults of libvirt get used. ๐Ÿ’€
  • --machine-type type โ€” Sets the machine type to the specified string before instantiating the guest. Run kvm -M ? for a list of types supported by your current system. If it's not set, this section of the template isn't altered before the guest is defined. If it is set, it modifies the internal temporary libvirt domain template at the element domain->os->type and sets the attribute machine to the given value before defining the guest. Which implies libvirt will add the type-dependent default devices when defining the guest.

And here's the tea: if --machine-type is not specified, the current host is an x86_64 system, and the requested guest is arch=amd64, then the machine type will by default be set to the latest available q35 based type โ€” all to overcome the ancient compatibility-oriented defaults of libvirt on x86. She's doing the work for you. ๐Ÿ’…


๐ŸŒธ CLOUD-INIT CONFIGURATION OPTIONS ๐ŸŒธ

Valid for: uvt-kvm create only.

These ones change what happens inside the guest VM itself. It's giving interior design but for your VM.

Unless --user-data is used to override the behaviour, uvt-kvm generates cloud-init userdata with some sensible defaults whenever a VM is created. You can tweak those defaults with these:

  • --password password โ€” Permits login to the VM for the default user ubuntu with password. Handy for debugging, since it also enables a VT login. But using this command line option leaks the password used to other users on the same system, so it should never be used in production for security reasons. Default: no password login. ๐Ÿ”’
  • --run-script-once script_file โ€” Runs script_file as root on the VM the first time it's booted, but never again. You can use this option multiple times to run multiple scripts. If the script exits with a non-zero status, it gets left on the VM in /tmp for debugging purposes. script_file gets copied to the guest, marked as executable, and executed directly โ€” so it must be an appropriate binary, start with a shebang, or otherwise be directly executable by the guest kernel. Default: no scripts.
  • --ssh-public-key-file ssh_public_key_file โ€” Permits login to the VM for the default user ubuntu and the ssh keys specified in ssh_public_key_file. Default: uses the output of ssh-add -L if available; otherwise ~/.ssh/id_rsa.pub. If no source is found at all, a warning gets printed to stderr, and VM creation continues with no arrangement for access to the guest. ๐Ÿซถ (still cute, still accessible, justโ€ฆ not to you)
  • --packages package_list โ€” Installs the comma-separated packages specified in package_list on first boot. This option can be used multiple times; each additional option adds to the final package list. Default: no packages. ๐Ÿ“

๐Ÿ‘‘ ADVANCED OVERRIDE OPTIONS ๐Ÿ‘‘

Valid for: uvt-kvm create only. Big brain territory.

  • --template template_file โ€” The base libvirt domain definition XML template to use when constructing a new VM's definition. It's dynamically altered before domain creation โ€” see LIBVIRT DOMAIN DEFINITION OPTIONS. Default: /usr/share/uvtool/libvirt/template.xml.
  • --guest-arch architecture โ€” Specifies the architecture of the guest template file that will be selected. If an explicit --template is given then --guest-arch has no effect. If neither --template nor --guest-arch are set, the host's architecture determines the default xml template.

If you set an architecture that requires emulation on the current host, the guest will be set up for that. See EMULATION OF FOREIGN ARCHITECTURES for more on using an architecture different to the current system. Default: Current Host architecture.

  • --user-data user_data_file โ€” Overrides cloud-init userdata, using the file supplied instead. This overrides all options in the section CLOUD-INIT CONFIGURATION OPTIONS. Default: as described in CLOUD-INIT CONFIGURATION OPTIONS.
  • --meta-data meta_data_file โ€” Overrides default cloud-init metadata, using the file supplied instead. This does not override any other options, since cloud-init metadata isn't otherwise tunable. Default: minimal file with automatically generated instance-id.
  • --network-config network_config_file โ€” Provides cloud-init network-config using the file supplied. Default: no network-config file.
  • --backing-image-file image_file โ€” Specifies the name of a local file that will be used to create the VM instead of relying on the volume storage pool. It must point to a qcow2 formatted file. This option overrides any simplestreams filters provided. โœจ

๐Ÿ”ฅ ADVANCED USAGE ๐Ÿ”ฅ

uvt-kvm is carefully constructed so it never impedes your ability to use virsh(1) or other libvirt tooling directly at any time โ€” and it hands you override options for supplying backing image volumes plus cloud-init userdata and metadata wherever possible. VMs created by uvt-kvm are not "special" in libvirt. What uvt-kvm does with VMs is well-defined, so advanced users can manipulate a VM with libvirt directly without necessarily losing the ability for uvt-kvm to keep manipulating that same VM for common use cases. No gatekeeping here. ๐Ÿซถ

๐Ÿฆ‹ TERMINOLOGY AND LIFECYCLE ๐Ÿฆ‹

Keeping it simple: uvt-kvm uses create to mean the definition, allocation and running of a VM, and destroy to mean the stopping and removing of all persistent state associated with a VM โ€” including VM-specific disk image files and the VM definition itself. This matches the commonly expected lifecycle of VMs created with uvt-kvm.

That works great for the common use case, but if VMs created with uvt-kvm need to be manipulated with virsh(1) or libvirt directly, then you gotta understand how this lines up with the more complex libvirt terminology.

In libvirt, a VM is called a domain. A domain is first defined, and then independently started. In libvirt terminology, destroy means a VM stop โ€” and after a destroy, the domain still exists and can be restarted. undefine is what finally removes the domain definition. Resources associated with a VM (like disk image files, which in libvirt are called volumes) must be created and destroyed separately. ๐Ÿ’€ The naming really did us dirty there.

When uvt-kvm creates a VM, libvirt volumes are defined and populated, a libvirt domain is defined, marked as autostarted, and the domain started. When uvt-kvm destroys a VM, the corresponding libvirt domain is stopped, domain-specific volumes deleted, and the libvirt domain itself is undefined.

๐Ÿงš EMULATION OF FOREIGN ARCHITECTURES ๐Ÿงš

Emulation of foreign architectures can be done with uvt-kvm. Currently armhf is the only supported target architecture. Be aware that defining filters to use a different architecture image will not be enough. To let uvtool know you want to run as a specific architecture you need to set --guest-arch. If that is set to armhf (just like your simplestreams filters for the image would be), then uvt-kvm will use qemu-system-arm.

Since bootloaders often struggle in these modes, uvt-kvm will provide external kernel and initrd to boot the guest. To be able to extract those from the guest image it needs root permission when syncing the non-native architecture image. ๐Ÿ‘‘


โญ EXAMPLES โญ

# Update uvtool's libvirt volume storage pool with the
# latest Precise image
uvt-simplestreams-libvirt sync release=precise arch=amd64

# Create an ssh key for the local user (if you don't have
# one already)
ssh-keygen
# (...)

# Create an amd64 VM running Precise
uvt-kvm create myvm release=precise arch=amd64

# Wait for the VM to become ready
uvt-kvm wait --insecure myvm

# Shell into the VM to do some testing there
uvt-kvm ssh --insecure myvm
# (...)

# Destroy the VM
uvt-kvm destroy myvm

๐Ÿ”ง TROUBLESHOOTING ๐Ÿ”ง

Okay bestie, something's gone sideways and you're staring at a wall of red text. Deep breaths. Let's vibe-check what's actually happening ๐Ÿ’–

๐Ÿ’€ Common Errors ๐Ÿ’€

  • Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied

Do you even have permission to talk to libvirt, queen? On Ubuntu you must belong to the libvirt group (heads up, this group was called libvirtd on 16.04 and earlier, so if you're on ancient territory that's the name to look for). Peeps with sudo(8) access get added to this group by default, but here's the tea: you only actually get that group membership on your next login after the libvirt-bin package has been installed. So if you just installed it, log out and back in, babes.

Can't wait that long? Want it now? To temporarily add yourself to this group before your next login, try newgrp libvirt (or newgrp libvirtd if you're on Ubuntu 16.04 or earlier) โœจ

  • no supported architecture for os type 'hvm'

Not the vibe. This means libvirt did not find KVM support on your system ๐Ÿ˜ญ Try sudo kvm-ok for diagnostics, and then service libvirt-bin restart to pick up any changes, and retry after that. You got this ๐Ÿซถ


๐Ÿ–ฅ๏ธ Interactive console access ๐Ÿ–ฅ๏ธ

If you literally cannot get into the VM from the host system, here's the move: try using --password to set a password for the default ubuntu user inside the VM, and then log in to the VM over the console so you can poke around and examine it from the inside. Detective mode activated ๐Ÿ”

To access the console interactively, use virsh console name.

But wait โ€” big caveat ๐Ÿšจ interactive access is disabled if you are using --log-console-output or -d. So if you want that interactive console life, you'll have to drop these options if you're using them. No cap.

And one more thing, and this one's important: if you are using --user-data, then --password will be overridden by it, and you will need to modify your cloud-init userdata manually to achieve the same effect. Sorry bestie, that's just how it is ๐Ÿ™ˆ


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€


And that's the whole page, bestie!! You read all three parts, you absolute legend, that's main character energy fr fr ๐Ÿ‘‘ This one's from uvtool, dated 2014-03-10, and it's the uvt-kvm(1) page. Go forth and spin up some iconic little VMs ๐Ÿ’…โœจ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from