I thought it might be useful to someone so I'm posting the perl script I use to do daily backups of my Virtual Machines.
The script handles running machines by suspending them, taking an LVM snapshot, then resuming the VM and doing a backup of the snapshot. This gives me a window of less than 60 seconds of downtime per running virtual machine. You will need to customize this to your own LVM volume names, paths, etc.
Note: The editor seems to modify the text so I have attached the script.
#!/usr/bin/perl -w
#
VM Server Virtual Machine Snapshot Backups
#
use VMware::VmPerl;
use VMware::VmPerl::VM;
use VMware::VmPerl::Server;
use VMware::VmPerl::ConnectParams;
use File::Basename;
use strict;
my $backupdir = "/vm/Backups/daily";
my $log_file = "/var/log/vmware/vmbackup.log";
my ($server, $vm);
my $server_name = undef;
my $user = undef;
my $passwd = undef;
Use the default port of 902. Change this if your port is different.
my $port = 902;
open LOG, ">>$log_file";
log_stuff( "Starting backups" );
#
Connect to VMware Server
#
my $connect_params = VMware::VmPerl::ConnectParams::new($server_name,$port,$user,$passwd);
$server = VMware::VmPerl::Server::new();
if (!$server->connect($connect_params)) {
my ($error_number, $error_string) = $server->get_last_error();
log_stuff( "Connect to server: $error_number: $error_string\n" );
die "Could not connect to server: Error $error_number: $error_string\n";
}
#
Get list of Virtual Machines
#
my @vmlist = $server->registered_vm_names();
if (!defined($vmlist[0])) {
my ($error_number, $error_string) = $server->get_last_error();
log_stuff( "get_vms: $error_number: $error_string" );
die "Could not get list of VMs from server: Error $error_number: $error_string\n";
}
#
Virtual Machine Backup
#
foreach my $conf (@vmlist) {
my ($name,$vmdir,$suffix) = fileparse($conf, '\..*' );
connect to virtual machine
$vm = VMware::VmPerl::VM::new();
if (!$vm->connect($connect_params, $conf)) {
my ($error_number, $error_string) = $vm->get_last_error();
log_stuff( "Connect to $conf: $error_number: $error_string\n" );
die "Could not connect to $conf: Error $error_number: $error_string\n";
}
Get the virtual machine display name
my $displayName = $vm->get_config("displayName");
$displayName =~ s/[\()]//g; # remove special characters
Build the path for the backup filename
my $backup = "$backupdir/$displayName.tar.gz";
Get the current state for the virtual machine
my $state = $vm->get_execution_state();
Log what we are doing
log_stuff( "$displayName: Starting backup" );
handle virtual machines that are running by using lvm snapshot with a suspend
if ( $state eq VM_EXECUTION_STATE_ON )
{
log_stuff( "$displayName: Suspending Virtual Machine" );
if (!$vm->suspend()) {
my ($error_number, $error_string) = $vm->get_last_error();
log_stuff( "suspend $conf: $error_number: $error_string\n" );
}
else
{
log_stuff( "$displayName: Creating LVM Snapshot" );
log_stuff( `lvremove -f /dev/data/vmsnapshot` );
log_stuff( `lvcreate -L9.85G -s -n vmsnapshot /dev/data/vmware` );
log_stuff( "$displayName: Resuming Virtual Machine" );
if (!$vm->start()) {
my ($error_number, $error_string) = $vm->get_last_error();
log_stuff( "start $conf: $error_number: $error_string\n" );
}
log_stuff( "$displayName: Mounting LVM Snapshot" );
log_stuff( `mount -t ext3 -v -r /dev/data/vmsnapshot /mnt` );
fix directory to use the snapshot instead of the actual file
$vmdir =~ s!/vm/Machines!/mnt!g;
log_stuff( "$displayName: Backup $vmdir -> $backup.tmp" );
log_stuff( `tar czpf $backup.tmp -C $vmdir .` );
log_stuff( "$displayName: Removing LVM Snapshot" );
log_stuff( `umount -v /mnt` );
log_stuff( `lvremove -f /dev/data/vmsnapshot` );
}
}
else
{
regular backup of suspended or offline machines
log_stuff( "$displayName: Backup $vmdir -> $backup.tmp" );
log_stuff( `tar czpf $backup.tmp -C $vmdir .` );
}
log_stuff( "$displayName: Moving $backup.tmp to $backup" );
log_stuff( `mv -vf $backup.tmp $backup` );
log_stuff( "$displayName: Backup Completed" );
$vm->disconnect();
}
#
Logging
#
sub log_stuff
{
my $msg = shift();
if ( $msg )
{
chomp($msg);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
printf LOG "%4d-%02d-%02d %02d:%02d:%02d $msg\n", $year1900,$mon1,$mday,$hour,$min,$sec;
}
}
Destroys the server object, thus disconnecting from the server.
$server->disconnect();
undef $server;
log_stuff( "Completed backups" );
close LOG;