For the past few months, I’ve been using a setup for software development where I pass the mouse, keyboard and graphics card to a virtual machine via Linux VFIO. The setup is described in detail in its own blog post.
In short though, the idea is to provision a separate virtual machine for each project, so that I can install whatever tools I need to solve the problem at hand, with no real consequences if I break my install.
The problem
I have multiple virtual machines which are configured to use the same hardware, so I can only run one at a time. Before making the changes outlined in this blog post, the method I used to switch to a different VM was unwieldy: I needed is to shut down the current VM, switch monitor inputs to the host graphics, start a different VM, then switch monitor inputs back the the VM graphics.
I intend to do all of my work in VM’s, so my basic goal was to make a ‘switch VM’ mechanism available from within a VM, to remove any need to interact with the host operating system directly.
Creating an API
My plan for this was to run an agent on the host, which will accept a request to switch VM’s. It would then shut down the current VM, and start up a different one instead.
This was the first time I tried using the libvirt Python bindings before, and I was able to quickly make a script which toggled between two VM’s, which are nested within the VM I’m using for development, because of course they are.
I then extended this to use an orderly shutdown, and to work with an arbitrary number of VM’s. CirrOS was not completing its boot in this environment, so I switched the test VM’s to Debian, and also passed through a USB device to each of them so that I would get an error if I ever tried to boot both VM’s simultaneously.
Last, I exposed this over HTTP as a simple API.
For the first iteration, I installed this on the host, and placed some scripts in a folder which is shared between all of the VM’s. The scripts use curl
commands to call the API. To trigger a VM switch (or a shutdown of the host), I could go to a folder, and run one of these scripts.
Web UI
With that proof-of-concept out of the way, I made a web interface, so that I can use this instead of some bash scripts in a folder. This is a very simple Angular app.
GNOME extension
For the most part, I’m working on Debian using the GNOME desktop environment, which gives me some options for integrating this further into my desktop.
I first considered making a search provider, but settled instead for making a simple standalone extension to display an indicator instead, based on the example app shown running here.
As if nested virtualization was not mind-bending enough, I started testing this using a GNOME session within a GNOME session.
The extension is written in JavaScript, though the JavaScript API for GNOME extensions was very unfamiliar to me. There some obvious quality issues in the code, but it’s not being published to a repository, and it fires off the correct HTTP requests, so I’ll call it a success.
Deploying a GNOME extension manually is as simple as placing some files my home directory.
Result and next steps
I’ve bookmarked this URL in each of my VM’s.
And I’ve also installed this GNOME extension on the VM that runs on boot.
The idea of this setup is to have one virtual machine per project, so a good next step would be to make it easier to kick of the install process.
I’ve put the code for this project online at mike42/vfio-vm-switcher on GitHub.