Skip to content

Latest commit

 

History

History
542 lines (420 loc) · 17.1 KB

README.md

File metadata and controls

542 lines (420 loc) · 17.1 KB

machina Go Reference Go Report Card

Machina is a lightweight and opinionated virtual machine manager. It uses QEMU to start and stop kernel virtual machines on linux systems. Each virtual machine is operated as a systemd unit.

This software is experimental. There are no gaurantees about API compatibility, file format compatibility, or fitness for purpose.

The systemd units generated by machina currently require the QEMU process to be run as root. This presents a security concern should a malicious virtual machine process ever escape from its containment. In light of this, machina is not yet recommended for production use.

The machina library and program are written in Go.

Core Design Goals and Principals

1. No external dependencies

The machina program is distributed as a single binary with no dependencies beyond those of QEMU itself.

2. Direct invocation of QEMU via systemd

While machina is involved in the generation of systemd unit files and assists with virtual machine preparation and teardown, it is not responsible for launching the QEMU process. Instead, systemd launches the QEMU process directly.

This approach removes machina as a point of failure during normal operation, and allows machina to be updated without interrupting the virtual machines managed by it.

3. Reusable configuration via tagging

The machina design encourages creation and use of virtual machine tags. Tags define a common set of attributes for virtual machines. This reduces the amount of configuration that must be managed for each virtual machine.

4. Explicitly defined system configuration

Instead of interrogating a running environment, machina expects host system configuration to be defined explicitly. As a result, machina can be used to generate systemd units for remote and offline hosts, as long as the host configuration files are available.

5. Succinct virtual machine definitions

Other virtual machine managers tend to haul around a lot of configuration data for each virtual machine in order to reproduce its environment precisely. The machina system goes in the opposite direction, attempting to reduce the per-machine configuration to its most necessary and succinct components.

Users of machina should think about high level concepts for their virtual machines:

  • What disk volumes does it need?
  • Where are my disk images stored?
  • How many cores and how much RAM does it need?
  • What network connections should it have?

The primary task of machina is to translate these concepts into QEMU command lines that can be invoked by systemd. The opinions of the machina designers are expressed in this process, making decisions about PCI Express bus layouts, what QEMU device implementations to use, and other similar concerns.

6. Portable virtual machine definitions

Host system configuration is separate from virtual machine guest configuration.

Virtual machine definitions can easily be relocated to new hosts without modification.

Non-Goals

Support for other hypervisors

The machina design is focused on supporting QEMU and KVM.

Features

Windows and linux guest support

Virtual machines can easily be configured to enable Hyper-V Enlightenments used by Windows guests.

OVMF firmware support

Virtual machines can be supplied with OVMF firmware, allowing the virtual machines to run in the context of a modern UEFI BIOS.

Mediated device support

Machina supports the use of mediated devices provided by some graphics hardware vendors. This allows virtual machines to share access to hardware resources that provide this feature.

Orderly shutdowns

The systemd units will attempt an orderly shutdown of their virtual machines when systemd asks the unit to stop. This is accomplished by issuing QMP shutdown commands which are delivered to the virtual environment as ACPI power commands.

Consistent identifiers

The only identifiers that must be provided for each virtual machine are its name and its machine-wide unique identifier, the UUID.

While machina allows for explicit configuration of MAC addresses, volume serial numbers and other such identifiers, it is capable of producing stable identifiers as needed. It accomplishes this by deriving identifiers from the machine's UUID and name via hashing.

Planned features

Reduced QEMU privileges

The QEMU process that manages a virtual machine should run as a non-root user with least privileges.

Virtual machine dissolution

While virtual machines can easily be disabled via machina disable, there is no facility for removal of their systemd units.

Improved variable handling, patterns and expressions

The currently supported patterns are very rudimentary and don't allow for evaluation of mathematical expressions when determining things like port numbers.

Graphical Configuration Manager

A graphical user interface for creation and management of machina configuration files would be neat.

Features not supported

QCOW disk formats

The designers of machina prefer to operate their virtual machines as raw disk images stored on zfs datasets. This allows for images to be efficiently snapshotted and for snapshots to be efficiently transported. Support for qcow2 and other disk formats has not been a focus, though it would not be difficult to add.

Live migration

There is no support for live migration at this time.

Virtual machine destruction

There is no facility for total annihilation of virtual machines, which would include permanent deletion of their disk volumes.

This is an inherantly dangerous ability and won't be added without much consideration.

Known Issues

Bash completion when using sudo

Bash completion doesn't work when using sudo, as in sudo machina <command>.

Update considerations

Newer versions of machina might make different choices or use different default values when generating systemd units. This can result in changes to the virtual hardware environment of the machines that it manages.

While this poses some risk, most guest operating systems are tolerant of hardware changes.

Usage

Usage: machina <command>

Manages kernel virtual machines via QEMU.

Flags:
  -h, --help    Show context-sensitive help.

Commands:
  install
    Installs the machina command in the system path.

  cat [<machines> ...]
    Displays the machina configuration for virtual machines.

  list
    Lists all of the virtual machines present.

  status <machines> ...
    Displays the systemd unit status for virtual machines.

  observe <machines> ...
    Reports QMP events for virtual machines.

  generate <machines> ...
    Generates systemd unit configuration files from /etc/machina/machine.conf.d/*.conf.json.

  enable <machines> ...
    Enables the systemd units for virtual machines.

  disable <machines> ...
    Disables the systemd units for virtual machines.

  start <machines> ...
    Starts the systemd units for virtual machines.

  stop <machines> ...
    Stops the systemd units for virtual machines.

  shutdown <machines> ...
    Sends a shutdown command to one or more virtual machines.

  prepare <machines> ...
    Prepares the host environment for a virtual machine to start.

  teardown <machines> ...
    Removes host resources prepared for a virtual machine.

  connect <machines-or-connections> ...
    Connects a whole virtual machine or individual connections to the network.

  disconnect <machines-or-connections> ...
    Disconnects a whole virtual machine or individual connections from the network.

  query pci <machines> ...
    Describes the PCI Bus in running virtual machines.

  query cpu <machines> ...
    Describes the virtual CPUs present in running virtual machines.

  gen-id
    Generate a random machine identifier.

  gen-mac
    Generate a random MAC hardware address.

  args <machines> ...
    Displays the QEMU arguments for virtual machines.

  run [<machine>]
    Run a virtual machine directly via QEMU.

Run "machina <command> --help" for more information on a command.

Configuration

The machina design divides configuration into two portions:

  1. System (host) configuration
  2. Virtual machine (guest) configuration

When machina builds a systemd unit for a virtual machine, it reads two files and uses their content to produce the systemd unit:

  1. The system host configuration file (/etc/machina/machina.conf.json)
  2. The virtual machine guest configuration file (/etc/machina/machine.conf.d/[guest-name].conf.json)

The system configuration file defines the storage location, available networks, and interpretation of tags on a particular system. It describes the environmental features that are unique to that particular host.

The virtual machine configuration file defines the name, ID, tags and attributes of a virtual machine. It describes the features of a guest and what it expects from its host.

If a virtual machine definition is relocated to a new host, it gets combined with a different system configuration file, and thus produces a systemd unit that is suitable for that host.

Example Configuration

Note: The configuration file format is subject to change, and this example configuration could fall out of sync as changes are made.

Machine configuration files are stored in individual conf.json files within /etc/machina/machine.conf.d. Here's an example for a theoretical test-vm, which would be placed in a /etc/machina/machine.conf.d/test-vm.conf.json file:

{
	"id": "8b18191a-234f-45ce-b43c-b46e28cd2f70",
	"description": "Test VM (2023-04-27)",
	"tags": ["vdi-employee", "vdi-cad", "windows", "windows-10-media", "firmware-202302"],
	"vars": {
		"employee-id": "9000"
	}
}

System configuration is stored in the /etc/machina/machina.conf.json file. Here's an example that provides definitions for the tags used by the example test machine:

{
	"storage": {
		"guest-firmware-vars": {
			"path": "/zfs-tank/",
			"pattern": "${machine-name}/os/${machine-name}-${volume}.vars.bin",
			"type": "firmware"
		},
		"guest-data": {
			"path": "/zfs-tank/",
			"pattern": "${machine-name}/os/${machine-name}-${volume}.raw",
			"type": "raw"
		},
		"firmware-code": {
			"path": "/usr/lib/machina/firmware/",
			"pattern": "${volume}.code.bin",
			"type": "firmware",
			"readonly": true
		},
		"iso-ahci": {
			"path": "/usr/lib/machina/iso/",
			"pattern": "${volume}.iso",
			"type": "iso-ahci"
		},
		"iso-scsi": {
			"path": "/usr/lib/machina/iso/",
			"pattern": "${volume}.iso",
			"type": "iso-scsi"
		}
	},
	"network": {
		"local": {
			"device": "kvmbr0"
		}
	},
	"mdev": {
		"nvidia-rtx8000": {
			"address": "0000:c1:00.0",
			"types": {
				"cad-standard": "GRID RTX8000-6Q"
			}
		}
	},
	"tag": {
		"vdi-employee": {
			"attrs": {
				"spice": {
					"enabled": true,
					"port-pattern": "5${employee-id}",
					"displays": 1
				},
				"agent": {
					"qemu": {
						"enabled": true,
						"port-pattern": "4${employee-id}"
					}
				}
			}
		},
		"vdi-cad": {
			"attrs": {
				"cpu": {"sockets": 1, "cores": 4},
				"memory": {"ram": 32768}
			},
			"volumes": [
				{"name": "os", "storage": "guest-data", "bootable": true}
			],
			"connections": [
				{
					"name": "0",
					"network": "local"
				}
			],
			"devices": [
				{
					"name": "vgpu",
					"class": "cad-standard"
				}
			]
		},
		"windows": {
			"attrs": {
				"enlightenments": {"enabled": true},
				"qmp": {
					"enabled": true
				}
			},
			"volumes": [
				{"storage": "iso-ahci", "name": "virtio-win-latest"}
			]
		},
		"windows-10-media": {
			"volumes": [
				{"storage": "iso-scsi", "name": "win10"}
			]
		},
		"firmware-202302": {
			"attrs": {
				"firmware": {
					"code": {"storage": "firmware-code", "name": "ovmf-stable202302"},
					"vars": {"storage": "guest-firmware-vars", "name": "firmware"}
				}
			}
		}
	}
}

Here is the resulting summary offered by machina cat test-vm:

  Name: test-vm
  Description: Test VM (2023-04-27)
  ID: 8b18191a-234f-45ce-b43c-b46e28cd2f70
  Tags: vdi-employee,vdi-cad,windows,windows-10-media,firmware-202302
  Vars:
    employee-id: 9000
  Firmware Code (read-only): ovmf-stable202302: firmware-code
  Firmware Variables (read/write): firmware: guest-firmware-vars
  Sockets: 1
  Cores: 4
  RAM: 32768M
  Hyper-V Enlightenments: Enabled
  QMP: Enabled
  QMP Socket Path: /run/machina/test-vm/systemd.0.qmp.socket
  QMP Socket Path: /run/machina/test-vm/command.0.qmp.socket
  QMP Socket Path: /run/machina/test-vm/command.1.qmp.socket
  QEMU Guest Agent: Enabled
  QEMU Guest Agent Port: 49000
  Spice Display: Enabled
  Spice Port: 59000
  Spice Display Count: 1
  Volumes:
    os: guest-data (wwn: 0x5525400908FE6258, serial: 9IQ4PUV68QOCNS2C75OKUS04I4, bootable)
    virtio-win-latest: iso-ahci (wwn: 0x5525400982DB87CE, serial: JAVJJVPOOPTIV73ONECE40VFD4)
    win10: iso-scsi (wwn: 0x5525400F7C2ED5D0, serial: 0V17D9DH4EVT9SFOCV9CFTR0BK)
  Connections:
    0: local (mac: 52:54:00:26:77:fa)
  Devices:
    vgpu: cad-standard (3e2cee4d-1002-4989-af98-3e03b8ad3197)

Here is the resulting systemd unit file, as shown by machina generate --preview test-vm:

[Unit]
Description=machina KVM test-vm
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=1m0s
StartLimitBurst=2

[Service]
Type=simple
ExecStart=qemu-system-x86_64 \
-uuid 8b18191a-234f-45ce-b43c-b46e28cd2f70 \
-name test-vm \
-enable-kvm \
-nodefaults \
-nographic \
-machine type=q35,vmport=off,pflash0=ovmf-stable202302,pflash1=test-vm-firmware \
-cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time \
-smp sockets=1,cores=4 \
-m size=32768M \
-rtc base=utc,clock=host,driftfix=slew \
-mon chardev=qmp.0,mode=control \
-mon chardev=qmp.1,mode=control \
-mon chardev=qmp.2,mode=control \
-spice port=59000,addr=127.0.0.1,disable-ticketing=on \
-global driver=cfi.pflash01,property=secure,value=on \
-global driver=qxl-vga,property=ram_size,value=67108864 \
-global driver=qxl-vga,property=vram_size,value=67108864 \
-boot menu=on,reboot-timeout=5000 \
-object iothread,id=iothread.0 \
-blockdev driver=file,node-name=ovmf-stable202302,read-only=on,filename=/usr/lib/machina/firmware/ovmf-stable202302.code.bin \
-blockdev driver=file,node-name=test-vm-firmware,filename=/zfs-tank/test-vm/os/test-vm-firmware.vars.bin \
-blockdev driver=file,node-name=test-vm-os-file,filename=/zfs-tank/test-vm/os/test-vm-os.raw \
-blockdev driver=raw,node-name=test-vm-os,file=test-vm-os-file \
-blockdev driver=file,node-name=virtio-win-latest,read-only=on,filename=/usr/lib/machina/iso/virtio-win-latest.iso \
-blockdev driver=file,node-name=win10,read-only=on,filename=/usr/lib/machina/iso/win10.iso \
-chardev socket,id=qmp.0,server=on,wait=off,path=/run/machina/test-vm/systemd.0.qmp.socket \
-chardev socket,id=qmp.1,server=on,wait=off,path=/run/machina/test-vm/command.0.qmp.socket \
-chardev socket,id=qmp.2,server=on,wait=off,path=/run/machina/test-vm/command.1.qmp.socket \
-chardev socket,id=guestagent,host=127.0.0.1,port=49000,server=on,wait=off,nodelay=on \
-chardev spicevmc,id=vdagent,debug=0,name=vdagent \
-chardev spicevmc,id=usbredir.0,debug=0,name=usbredir \
-chardev spicevmc,id=usbredir.1,debug=0,name=usbredir \
-netdev tap,id=net.0,ifname=test-vm.0,script=/usr/bin/machina-ifup,downscript=/usr/bin/machina-ifdown \
-device ioh3420,id=pcie.1.0,chassis=0,bus=pcie.0,addr=1.0,multifunction=on \
-device virtio-serial-pci,id=serial,bus=pcie.1.0 \
-device virtserialport,id=serial.0.0,bus=serial.0,nr=1,chardev=guestagent,name=org.qemu.guest_agent.0 \
-device virtserialport,id=serial.0.1,bus=serial.0,nr=2,chardev=vdagent,name=com.redhat.spice.0 \
-device qxl-vga,id=qxl.0,bus=pcie.0,addr=1.1 \
-device ioh3420,id=pcie.1.2,chassis=2,bus=pcie.0,addr=1.2 \
-device qemu-xhci,id=usb,bus=pcie.1.2,p2=4,p3=4 \
-device usb-tablet,id=usb.0.1,bus=usb.0,port=1 \
-device usb-redir,id=usb.0.2,bus=usb.0,port=2,chardev=usbredir.0 \
-device usb-redir,id=usb.0.3,bus=usb.0,port=3,chardev=usbredir.1 \
-device ioh3420,id=pcie.1.3,chassis=3,bus=pcie.0,addr=1.3 \
-device virtio-scsi-pci,id=scsi,bus=pcie.1.3,iothread=iothread.0,num_queues=4 \
-device scsi-hd,id=scsi.0.0,bus=scsi.0,channel=0,scsi-id=0,lun=0,drive=test-vm-os,wwn=0x5525400908FE6258,serial=9IQ4PUV68QOCNS2C75OKUS04I4,bootindex=1 \
-device scsi-cd,id=scsi.0.1,bus=scsi.0,channel=0,scsi-id=0,lun=1,drive=win10 \
-device ide-cd,id=sata.0,bus=ide.1,drive=virtio-win-latest \
-device ioh3420,id=pcie.1.5,chassis=5,bus=pcie.0,addr=1.5 \
-device virtio-net-pci,bus=pcie.1.5,mac=52:54:00:26:77:fa,netdev=net.0 \
-device ioh3420,id=pcie.1.6,chassis=6,bus=pcie.0,addr=1.6 \
-device vfio-pci,id=vfio.0,bus=pcie.1.6,sysfsdev=/sys/bus/mdev/devices/3e2cee4d-1002-4989-af98-3e03b8ad3197
ExecStartPre=machina prepare test-vm
ExecStop=machina shutdown --system --timeout 1m25s test-vm
ExecStopPost=machina teardown test-vm
RestartSec=10s
TimeoutStopSec=1m30s
Restart=on-failure
RuntimeDirectory=machina/test-vm

[Install]
WantedBy=multi-user.target