I would rather not be beholden to Oracle so one of my goals is to transition away from virtualbox
and towards the messy combination of libvirt
, kvm
, qemu
, and ovmf uefi (eg. edk2-ovmf
).
A first-attempt went fairly well, but in addition to adding four separate packages to replace two, it also seems to introduce platform-dependent paths and extra plugins required per-tool. These are just the expenses of using open sourced code without first-party integration support.
In addition to lacking integration support, it also lacks decent documentation as you'll be scouring dozens of sources and praying what you are reading is up to date.
You'll need all of these packages:
The edk2-ovmf
will install a UEFI disk image to /usr/share/ovmf/x64/OVMF.fd
, and both the package and path may vary depending on which linux distribution you are using.
For vagrant to work you will need to add a plugin:
vagrant plugin install vagrant-libvirt
After this it will be able to communicate with qemu/kvm.
A Vagrantfile
may look like this:
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt'
Vagrant.configure("2") do |config|
config.vm.box_url = 'file://' + File.dirname(__FILE__) + '/dist/arch-desktop.box'
config.vm.box = 'arch-desktop'
config.vm.boot_timeout = 1
config.vm.graceful_halt_timeout = 1
config.vm.synced_folder '.', '/vagrant', disabled: true
config.ssh.insert_key = false
config.vm.provider :libvirt do |v|
v.memory = 2048
v.cpus = 2
v.driver = "kvm"
v.machine_arch = 'x86_64'
v.loader = '/usr/share/ovmf/x64/OVMF.fd'
end
end
Libvirt needs to run as a service:
systemctl enable libvirtd.service
This service requires some control of the network and so you'll have to ensure your network utility does not have a dnsproxy.
For example if using connman
then you need to add /etc/systemd/system/connman.service.d/disable_dns_proxy.conf
with:
[Service]
ExecStart=
ExecStart=/usr/bin/connmand -n --nodnsproxy
Finally, you need to create a polkit policy at /etc/polkit-1/rules.d/50-libvirt.rules
for users to operate it:
polkit.addRule(function(action, subject) {
if (action.id == "org.libvirt.unix.manage" &&
subject.isInGroup("sudo")) {
return polkit.Result.YES;
}
});
This example allows only users with sudo
group access to do so, but you could use looser restrictions.
While packer does work without much effort, it does require you to point to the host path of OVMF:
{
"variables": {
"iso_url": "https://mirrors.kernel.org/archlinux/iso/{{isotime \"2006.01\"}}.01/archlinux-{{isotime \"2006.01\"}}.01-x86_64.iso",
"iso_checksum_url": "https://mirrors.kernel.org/archlinux/iso/{{isotime \"2006.01\"}}.01/sha1sums.txt",
"efi_bios": "/usr/share/ovmf/x64/OVMF.fd",
"root_password": "arch",
"username": "vagrant",
"password": "vagrant"
},
"builders": [
{
"headless": true,
"type": "qemu",
"iso_url": "{{ user `iso_url` }}",
"iso_checksum": "file:{{ user `iso_checksum_url` }}",
"vm_name": "arch-desktop",
"format": "qcow2",
"accelerator": "kvm",
"output_directory": "dist/arch-desktop",
"firmware": "{{ user `efi_bios` }}",
"memory": 1024,
"disk_size": "20G",
"disk_interface": "virtio",
"net_device": "virtio-net",
"ssh_username": "root",
"ssh_password": "{{user `root_password`}}",
"ssh_timeout": "20m",
"shutdown_command": "systemctl poweroff",
"boot_wait": "5s",
"boot_command": [
"<enter><wait90s>",
"printf \"{{user `root_password`}}\\n{{user `root_password`}}\\n\" | passwd<enter>",
"systemctl start sshd.service<enter>"
]
}
],
"provisioners": [
{
"type": "file",
"source": "arch.sh",
"destination": "arch.sh"
},
{
"type": "file",
"source": "install",
"destination": "install"
},
{
"type": "shell",
"skip_clean": true,
"expect_disconnect": true,
"environment_vars": [
"DEBUG=y",
"enable_hibernation=y",
"disk=vda",
"root_password={{user `root_password`}}",
"username={{user `username`}}",
"password={{user `password`}}"
],
"script": "setup/install.sh"
}
],
"post-processors": [
{
"type": "vagrant",
"compression_level": 9,
"output": "dist/arch-desktop.img"
}
]
}
It works, but it's not as simple nor well integrated.
To begin with you need 4-6 packages to get things working. Whether this is better or worse than a single monolithic package is debatable, but more packages means more complexity in terms of knowing what you need to get started.
With regards to packer support, it works relatively well actually. The few problems I ran into were related to disk names based on the types of disk drivers specified.
With vagrant you need to install an extra plugin. Further, I have been unable to get vagrant to launch a GUI regardless of the graphics_type
or video_type
used.
It also requires significantly more work to clear when testing a new build. Apparently vagrant box is not linked to virsh (eg. libvirt cli) storage, so you have to delete the image in root storage using virsh
or virt-manager
, and while it is possible to establish userspace storage that's even more undocumented complexity that I haven't had time to get working.
Overall, it's nice to have a purely open sourced build option, but it's way more complicated with less documentation and fewer integrations.