I've been writing puppet manifests for a few different projects lately, and I've found it useful to test them using virtual machines managed by vagrant. However, as I've been swapping back and forth between the various projects, and because I'm forgetful, I've also been accidentally leaving VMs running. Not so great for the performance of my ageing laptop.

Vagrant manages boxes on a per-VM basis; it doesn't have a host-wide view of what VMs are running. I decided to write a zsh function that told me the status of my VMs, and learnt a few things along the way.

vagrants () { setopt LOCALOPTIONS unsetopt AUTOPUSHD pwd_orig=$PWD # This is mine, yours is likely different base=$HOME/Boxes vagrants=(${base}/**/.vagrant) for vagrant in $vagrants do cd $vagrant:h print $PWD vagrant status | awk '/^$/ {stat = !stat;next}; stat == 1 {print "\t",$0}' done cd $pwd_orig }

There were two things that I found tricky.

First, I like to automatically push onto the directory stack when I change directories, so that I can change back to previous directories using cd -<TAB>, and I didn't want to dirty the directory stack while moving to my vagrant directories. I tried to store the state of the directory stack and then restore it at the end using the dirs command, but I couldn't work out how to stop the directories being added as a single (broken) entry. Eventually I found that using setopt LOCALOPTIONS in a function will cause previous options to be restored on exit, meaning I could simply turn off AUTOPUSHD.

Second, the vagrant status command doesn't have script-friendly output, though there is an open ticket to provide friendlier output.

Current VM states: default saved To resume this VM, simply run `vagrant up`.

Running vagrant status on half a dozen vagrant directories that might have multiple environments produces pretty unattractive output. Luckily, the output is at least in a predictable format; three paragraphs, with the actual states in the middle paragraph. My awk is pretty rusty, but I was happy to come up with a solution that works for me.

vagrant status | awk '/^$/ {stat = !stat;next}; stat == 1 {print "\t",$0}'

When a blank line is encountered, the stat flag is toggled, and when it's set we print the whole lines. This means the blank line between the first two paragraphs turns the flag on and the blank line after the statuses turns it off again. If someone has a more reliable way of grabbing the second paragraph with less predictable input, I'd love to hear it.

A bonus discovery was zsh's ability to substitute portions of the current path when using cd, allowing you to visit the same subdirectory in a variety of projects.

Boxes/metropolis/manifests % cd metropolis cantoflash Boxes/cantoflash/manifests %