nk23x
1/6/2011 - 5:03 PM

VMware VM Backup Perl Scripts

VMware VM Backup Perl Scripts

#!/usr/bin/perl -w
use strict;
use Carp;
use Fcntl qw(:mode);
use POSIX qw(:sys_wait_h);

use File::Spec;
use IO::Dir;
use VMware::VmPerl;
use VMware::VmPerl::ConnectParams;
use VMware::VmPerl::VM;

sub timestamp($) {
    my ($message) = @_;
    printf STDERR "%s %s\n", scalar localtime, $message;
}


sub throw($) {
    my ($vm) = @_;
    my ($err, $errstr) = $vm->get_last_error;
    croak "VMControl error $err: $errstr";
}

sub process($$) {
    my ($vm, $proc) = @_;
    if ($vm->get_execution_state == VM_EXECUTION_STATE_ON) {
        my $name = $vm->get_config_file_name;
        timestamp "Suspending $name";
        $vm->suspend(VM_POWEROP_MODE_TRYSOFT) or throw $vm;
        &$proc;
        timestamp "Resuming $name";
        $vm->start(VM_POWEROP_MODE_TRYSOFT) or throw $vm;
    } else {
        &$proc;
    }
}

sub backup($$) {
    my ($vm_dir, $backup_prefix) = @_;
    timestamp "Backing up $vm_dir";
    system '/bin/tar', 'czSf', "$backup_prefix.tar.gz", $vm_dir;
}

die "usage: backup-vms vms-dir backup-dir\n" unless @ARGV == 2;
my ($vms_dir, $backup_dir) = @ARGV;

timestamp 'Start of backup';
tie my %vms_entries, 'IO::Dir', $vms_dir;
my @vm_dirs = grep !/^\./ && $vms_entries{$_}->mode & S_IFDIR, keys %vms_entries;
untie %vms_entries;

# The VmPerl classes' new subs are really broken: they don't use the first
# argument as the class name, and thus you aren't supposed to use constructor
# syntax with them.
my $cp = VMware::VmPerl::ConnectParams::new;
my $vm = VMware::VmPerl::VM::new;

for my $vm_dir (@vm_dirs) {
    my $full_vm_dir = File::Spec->catdir($vms_dir, $vm_dir);
    eval {
        # .vmx file assumed to be named the same as the directory
        $vm->connect($cp, File::Spec->catfile($full_vm_dir, "$vm_dir.vmx"))
            or throw $vm;
        process($vm, sub () {
            backup($full_vm_dir, File::Spec->catdir($backup_dir, $vm_dir));
        });
        $vm->disconnect;