Backup Your XenServer VMs Weekly to a CIFS share

One of the reasons I decided to switch from ESXi to XenServer for my home lab is the ability to perform backups using the Xen API. This functionality is completely free in XenServer, while you need at least an Essentials license (which is not exactly cheap) in order to do the same in ESXi.

There are a number of scripts out there that allow you to backup your VMs, but they might not work out of the box for you if your configuration is different and you need to customize something. In my case, I found this script which looked very good for my needs, with three exceptions:

  1. I don’t use NFS for my backup destinations; I use CIFS instead, so this script would not work with a simple copy-paste;
  2. The script does not compress backups by default;
  3. The script did not successfully unmount the backup folder at the end of the backup (fixed by simply adding the -l option to the umount command).

All changes to the script are very easy but I thought I would share the updated code for future reference.

The updated script

#!/usr/bin/python

import commands, time

def get_backup_vms():
   result = []

   cmd = "xe vm-list is-control-domain=false is-a-snapshot=false"
   output = commands.getoutput(cmd)

   for vm in output.split("\n\n\n"):
      lines = vm.splitlines()
      uuid = lines[0].split(":")[1][1:]
      name = lines[1].split(":")[1][1:]
      result += [(uuid, name)]

   return result

def backup_vm(uuid, filename, timestamp):
   cmd = "xe vm-snapshot uuid=" + uuid + " new-name-label=" + timestamp
   snapshot_uuid = commands.getoutput(cmd)

   cmd = "xe template-param-set is-a-template=false ha-always-run=false uuid=" + snapshot_uuid
   commands.getoutput(cmd)

   cmd = "xe vm-export vm=" + snapshot_uuid + " compress=true filename=" + filename
   commands.getoutput(cmd)

   cmd = "xe vm-uninstall uuid=" + snapshot_uuid + " force=true"
   commands.getoutput(cmd)


cmd = "mount -t cifs //ip_address/XenServer\ Backup -o username=username,password=password /mnt/backup"
commands.getoutput(cmd)
for (uuid, name) in get_backup_vms():
   timestamp = time.strftime("%Y%m%d-%H%M", time.gmtime())
   print timestamp, uuid, name
   filename = "\"/mnt/backup/" + timestamp + " " + name + ".xva\""
   backup_vm(uuid, filename, timestamp)
cmd = "umount -l /mnt/backup"
commands.getoutput(cmd)

username and password in the code above are the credentials used to access the shared folder on your backup storage (in my case, with a Synology, I created a new user specifically for XenServer backups, and I have given this user read and write permissions to the backup destination). The script will then user this user and this password to connect to the share.

Including the password in the code allows you to schedule the backup using cron, however it obviously exposes the password. Therefore, I would recommend you only allow the root user to read, write and execute the file (chmod 700).

Schedule the backup to run automatically

Of course, having a backup script in place and not using it is useless, so let’s make use of cron to schedule it. Let’s say you want to run the backup every Sunday night at 3 AM. Simply run crontab -e and add the following line before saving the file:

0 3 * * 7 /usr/bin/python backup_vms.sh

14 Comments

  1. Hi Daniel, I’ve got the metadata one working but with this one I’m getting this error come up:

    sh: -c: line 3: syntax error near unexpected token `(‘
    sh: -c: line 3: `stderr: Traceback (most recent call last):’
    sh: -c: line 3: syntax error near unexpected token `(‘
    sh: -c: line 3: `stderr: Traceback (most recent call last):’
    sh: -c: line 3: syntax error near unexpected token `(‘
    sh: -c: line 3: `stderr: Traceback (most recent call last):’

    Any ideas?
    Thanks Josh

    • Hi Josh, have you copied and pasted the whole file? Otherwise, I think the error message might be misleading as line 3 doesn’t even have a ‘(‘ symbol.

      Where are you trying to mount the share? Do you have enough free space? If you look at the blog post where the original version of this script is taken from, you will find the commend from a user having an issue similar to yours, perhaps the cause is also similar.

      • Thanks Daniel,
        I did see that post. I’m mounting to /mnt/backup (directory created). Xenserver (6.5SP1) is on its own 136GB drive, unless it’s partitioned (I’ll have a look) then should be plenty of space.

        Did copy and paste the whole file.

        Thanks Josh

        • That’s weird, I haven’t seen this issue even after reinstalling XenServer and trying the script both on 6.5 and 6.5 FP1. Personally, I would try removing the CIFS share part for now and see if I could get it to backup locally. Sorry I couldn’t be of any more help!

  2. For any who may have a similar issue, managed to get it working as it appears to be backing up now.
    Initially I copied and pasted the text via putty. (Using nano)
    This time I used notepad++ to save the file and then used winscp to upload and it worked fine.
    Unless it has something to do with the carriage lines I’m not sure…..

  3. Thanks for your script. I am getting it to run manually as expected. However, the cron job does not run at all. I tried: 39 04 * * * /usr/bin/python backup_vms_mine.py and 39 04 * * * root /usr/bin/python backup_vms_mine.py. PS: I’m guessing you meant python … .py and not … .sh. I know the cron job does not run since I don’t see the VM backup progress in XenCenter. Instead, if I run tail -f /var/log/cron, then I get Nov 29 10:39:01 myserver CROND[3154]: (root) CMD (root /usr/bin/python backup_vms_mine.py). I’ve verified that the cron service is running. Any ideas? Did you have to do some pre-preparation step before running the cron job?

    • Sorry, that time should be Nov 29 04:39:01. Typo in the OP.

    • Hey Roger! I just took a look at the latest version of my cron file, this is what I have at the moment:

      0 1 * * * /usr/bin/perl backup_metadata.pl
      0 3 * * 2 /usr/bin/python backup_vms.sh

      The fact that the script is running manually is a good sign. I don’t remember having to do any other configuration to get this to work. Are you able to run any other script with cron or do you have a problem just with the backup script?

      • Hi Daniel, here are the exact contents of a simple Python file test.py that I created in /usr/bin:

        import os
        print ‘Hello world!
        print os.listdir(os.getcwd())

        I set up a corresponding cron job: 27 12 * * * root /usr/bin/python test.py.

        I found that this cron job did not run. I changed it to 29 12 * * * root /usr/bin/python test.sh. However, this did not run either. Really not sure, at this point, how to get cronjobs to fire on XenServer.??? I would have thought it would be straightforward???

        • How are you checking if the cron job ran? Your test script is not supposed to use the console for its output, if you want to see some output you need to redirect your script to a file, i.e.:

          27 12 * * * root /usr/bin/python test.py > /path/to/script_output.txt

          Therefore, if you simply checked if some output was printed to stdout, you would not see anything, but this doesn’t necessarily mean that the cron job did not run :) Xenserver stores its cron logs under /var/log/cron, so to make sure your cron job ran you could use something like this:

          grep -i test.py /var/log/cron

          Also, how did you create that cron job? Did you use “crontab -e” to do it? Does running “crontab -l” return the list of cron jobs?

Leave a Reply

© 2017 Daniel's TechBlog

Theme by Anders NorénUp ↑

%d bloggers like this: