VMware Workstation Automatic Suspend/Start and Backup Scripts for Ubuntu

I had previously listed some scripts that I use for VMware Workstation management on the Intrinium blog, but have found a few annoyances with running them. So, I’m re-releasing the scripts in their modified form.

The objective performed by these scripts are as follow:

  • They provide a simple way to suspend all virtual machines on host shutdown and resume only a select few on host startup.
  • They provide a way to check if certain virtual machines are running. If they are not running, the script will send an alert (and attempt to start the machine).
  • Backups will occur on a per-machine basis (that can be set to be different than the running list) and will only suspend one machine at a time for backups, starting them back up once the backup is finished. The backup will be sent to a samba share in my example, but this can be configured to be anything you can cp/scp to.

I’ve also modified the scripts to be a little more flexible in terms of modularity (user set directories) and included some error checks. Other than that, you know the routine – test it before you run it and don’t blame me if you delete everything.

First, the startup script I’ve dropped as /etc/init.d/vmwaresuspend (and of course, run “update-rc.d vmwaresuspend defaults”). This script runs on startup and shutdown of the host, making sure to suspend all running virtual machines and start up only machines on the machine list when the host starts back up:

#!/bin/bash

### BEGIN INIT INFO
# Provides: vmwaresuspend
# Required-Start:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Required-Stop: 0 1 6
### END INIT INFO
USER="media" #the user to run VMware as
MLIST="/media/raid/vmware/machine.list" #the machine list for machines to START on startup (see example below)

case "$1" in

start)
if [ $# -gt 1 ]
then
        su ${USER} -c "vmrun start '$2' nogui"
else
        while read VM;
        do
                echo "Starting $VM"
                su ${USER} -c "vmrun start '$VM' nogui"
        done < $MLIST
fi
exit 0
;;

stop)
if [ $# -gt 1 ]
then
        su ${USER} -c "vmrun suspend '$2'"
else
        vmrun list|grep vmx$|while read VM
        do
                echo "Suspending $VM"
                su ${USER} -c "vmrun suspend '$VM'"
        done
fi
exit 0
;;

restart)
# Nothing to be done for restart
exit 0
;;
esac
exit 0

The machine list is simply the full path to the vmx file for the virtual machine, one per line. Example:

/home/vmware/vm1/vm1.vmx
/home/vmware/vm2/vm2.vmx

Next, the script that checks to see if VMs are running. I set the list to be the same one as the startup script above, but you can change it to a different one if you feel like it. I saved this in /root/cron/checkVMs.sh and updated cron to have it run this script every 5 minutes:

#!/bin/bash
MLIST="/media/raid/vmware/machine.list"
LOCKFILE="/media/raid/vmware/backup.lock"

if [ -f ${LOCKFILE} ]; then #checks for the lock file made by backup script
        exit 0
fi
# vmrun list | tail -n+2 | awk -F/ '{print $NF}' | rev | cut -d\. -f2- | rev ...or we can just ps grep :)
while read VM;
do
        if ! ps ax | grep -v grep | grep "$VM" > /dev/null
        then
                echo "$VM is down!"
                #include mail -s here if you don't receive output for cron jobs.
                #include "vmrun start $VM" if you want to start the VM automatically if down
                # - it might not work due to other factors (rebuilding vmware modules, etc)
        fi
done < $MLIST

Lastly, the backup script. Like with the checking script, you can set a different machine list for it if you are wanting to backup other machines. Do note, this script does try to suspend the machine before backup and then start it after the backup completes - if your machine was stopped and you prefer it to remain stopped after the backup, you will have to change the logic of the script. Otherwise, it will start up. I saved this as /root/cron/backupVMs.sh and set cron to run it every week on Sunday night:

#!/bin/bash
LOCKFILE="/media/raid/vmware/backup.lock"
BACKUP_DIR="/home/media/vmwarebackup"
SMB_DIR="//yourShareServer/vmwarebackup"
MLIST="/media/raid/vmware/machine.list"
SUSPEND="/etc/init.d/vmwaresuspend"
CREDFILE="/root/cron/smbcred"

if [ -f $LOCKFILE ]; then
        echo "A backup is currently in progress"
        exit 1
fi
touch $LOCKFILE

mount -t smbfs -o credentials=$CREDFILE $SMB_DIR $BACKUP_DIR

if mountpoint -q $BACKUP_DIR #did it mount? If not, bad things could happen to your tiny 60GB SSD drive
then
        find $BACKUP_DIR -mtime +30 | xargs rm -rf #remove backups over 30 days old
        datetime=$(date '+%d_%m_%y_%H_%M')
        mkdir $BACKUP_DIR/${datetime}
        cd $BACKUP_DIR/${datetime}
        while read VM;
        do
                mkdir $(basename ${VM})
                ${SUSPEND} stop $VM
                cp -R $(dirname ${VM})/* ./$(basename ${VM})/
                ${SUSPEND} start $VM
        done < $MLIST
        umount -l $BACKUP_DIR
else
        echo "Samba share failed to mount: $BACKUP_DIR"
fi
rm $LOCKFILE

Modify to your hearts content.

Setting up LDAPS to Active Directory in Subsonic/Airsonic

…using an internal CA. Ok, so Subsonic is a really awesome music streaming tool that is multi-platform and lets you stream music to all sorts of devices. The really neat thing I found with it is that it supports LDAP authentication, which means I don’t have to recreate user accounts and keep track of different passwords for all the users in my home network – I just leave that task to my domain controller. I’ve ran into a few interesting issues trying to get it setup though, so for the sake of my sanity if I ever need to set it up again, and for those of you out there that may be struggling with the same issues, I’ve decided to write up a little howto guide.

Prerequisites

So before continuing, I am assuming the following about your setup:

  • AD (Active Directory) is currently installed somewhere on your network, or you can use an LDAP compatible server.
  • LDAPS is enabled for AD (if you set up an Enterprise CA, this process becomes much easier) or for your LDAP server.
  • You have created an account whose sole purpose is to read AD information (the number of times I’ve seen someone use a domain admin account for something like this…is the number of times I’ve owned a network in mere seconds 🙂 ). You’ll need some sort of LDAP binder user that has read capability.
  • You have a list of users in a group on AD/LDAP for which you are granting access to subsonic. This can be a group called <insert name here> or simply just any user in the “Users” group.
  • Subsonic is installed on your Ubuntu system and you have a local admin account that you created for it. If using a docker container, you’ll need to additionally overwrite the cacerts file in the docker image. e.g.
    • (airsonic) -v /etc/ssl/certs/java/cacerts:/etc/ssl/certs/java/cacerts:ro
    • (airsonic-advanced) -v /etc/ssl/certs/java/cacerts:/opt/java/openjdk/lib/security/cacerts:ro
  • You installed HTTPS on subsonic. It’s pretty pointless to use LDAPS without also using HTTPS. Or run it through some kind of reverse proxy like nginx.

Configure the java keystore

LDAPS by nature requires the proper use of certificates, for security reasons of course. In order to verify the certificate from the AD server, the subsonic machine and AD machine must have a common CA that they trust. If you are using an internal CA to issue your LDAPS cert for your AD machine, all it takes is installing the same CA onto subsonic machine. Right? Well, partially.

You see, just like an application like Firefox will have its own individual keystore where it keeps the certificates it trusts, Java does the same thing. So just throwing the cert into /etc/ssl/certs/ won’t do you too much good. Fortunately, Ubuntu makes this process a bit easier.

Make sure you install the ca-certificates-java package (comes preinstalled with openjdk usually):

sudo apt-get install ca-certificates-java

Next, throw your CA cert into /usr/local/share/ca-certificates. Run the following command:

sudo update-ca-certificates

You should get output telling you that at least 1 new certificate has been added. This command will also automatically update your java keystore. You can check to see if your certificate has successfully been imported by running:

keytool -list -v -keystore /etc/ssl/certs/java/cacerts | grep "Your CA Name"

The default password for the java keystore is “changeit”.

Setting up Subsonic

Log into subsonic. Go to Settings -> Advanced and enable LDAP authentication. The biggest difference between LDAP and LDAPS here is the protocol (ldap:// vs ldaps://) and the port number changed (from 389 to 636). For the LDAP information, you will have to substitute your own information. For me, I have a group of users called “Subsonic” who are allowed access to the application. By default, the LDAP URL that is autofilled for you includes all users in the “Users” group, which is not what I wanted. I had to modify the LDAP search filter to include only users in the Subsonic group (if you want to include all users, you can just leave this alone):

LDAP URL:            ldaps://myADserver:636/cn=Users,dc=yourdomain,dc=com
LDAP search filter:  (&(sAMAccountName={0})(&(objectCategory=user)(memberof=cn=Subsonic,cn=Users,dc=yourdomain,dc=com)))
LDAP manager DN:     yourdomain\limiteduser

To explain further, the search filter looks for a return field of sAMAccountName which is the username of the user in the specific place you are looking. The Base DN starts looking at the root of the domain in the Users group (cn=Users,dc=yourdomain,dc=com) with the search filter of “objectCategory=user” (so any user account, but not computer or group accounts) AND a search filter of “memberof=cn=Subsonic…” which looks for the Subsonic group to be part of the user object in AD. Do note, there is a group object which has a list of users…but this is easier.

LDAP configuration in subsonic

If you are interested with fooling around a bit more with possible search strings and base DN configs, check out JXplorer.