Bugs fixed

This commit is contained in:
Juan 2018-08-15 17:00:07 +02:00
parent fee9a47f3f
commit 849554c340
2 changed files with 228 additions and 204 deletions

View File

@ -22,40 +22,32 @@ backup_datastore => 'backups',
# The local directory where backups datastore folder is mounted # The local directory where backups datastore folder is mounted
local_backup_dir => '/mnt/vm-backups', local_backup_dir => '/mnt/vm-backups',
# Backup schedules # Backup jobs
schedules => { backup_jobs => {
schedule1 => { schedule1 => {
machines => ['machine1', 'machine2'], machines => ['test'],
rotation => 'weekly'
},
schedule2 => {
machines => ['machine3', 'machine4'],
rotation => 'daily' rotation => 'daily'
} }
}, },
# Backup rotation configuration # Backup rotation configuration
rotations => { rotations => {
weekly => {
days => 21,
count => 4
},
daily => { daily => {
days => 31, days => 31,
count => 31 count => 31
} }
}, },
# Cloning configuration # Clone jobs
clone => { clone_jobs => {
machine => { machine => {
vm => 'src-machine', vm => 'test',
dst_name => 'dst-machine', dst_name => 'test-clone',
dst_host => '127.0.0.1', dst_host => '127.0.0.1',
dst_datastore => 'datastore1', dst_datastore => 'datastore1',
memory => 4084, memory => 4084,
num_cpus => 2, num_cpus => 2,
mac => '00:00:00:00:00:01', mac => '00:50:56:BF:01:01',
poweron => 1, poweron => 1,
overwrite => 1 overwrite => 1
} }

View File

@ -21,104 +21,104 @@ use constant false => 0;
use constant true => 1; use constant true => 1;
my %opts = ( my %opts = (
operation => { 'operation' => {
type => "=s", type => "=s",
help => "Operation to perform: backuping, cloning, rotate, backup, clone, snapshot, migrate", help => "Operation to perform: backup-job, clone-job, backup, rotate, clone, snapshot, migrate",
required => true required => true
}, },
job => { 'job' => {
type => "=s", type => "=s",
help => "The job name" help => "The job name"
}, },
vmname => { 'vm-name' => {
type => "=s", type => "=s",
variable => "vmname", variable => "vm_name",
help => "Name of the virtual machine" help => "Name of the virtual machine"
}, },
rotation_days => { 'rotation-days' => {
type => "=i", type => "=i",
help => "Rotation days for backups", help => "Rotation days for backups",
default => 0 default => 0
}, },
rotation_count => { 'rotation-count' => {
type => "=i", type => "=i",
help => "Number of backups to keep despite the rotation", help => "Number of backups to keep despite the rotation",
default => 5 default => 5
}, },
dst_name => { 'dst-name' => {
type => "=s", type => "=s",
help => "Name of the new virtual machine" help => "Name of the new virtual machine"
}, },
dst_host => { 'dst-host' => {
type => "=s", type => "=s",
help => "Name of the target host" help => "Name of the target host"
}, },
dst_datastore => { 'dst-datastore' => {
type => "=s", type => "=s",
help => "Destination datastore" help => "Destination datastore"
}, },
memory => { 'memory' => {
type => "=s", type => "=s",
help => "Memory amount in MB" help => "Memory amount in MB"
}, },
num_cpus => { 'num-cpus' => {
type => "=s", type => "=s",
help => "Number of cores", help => "Number of cores",
default => 1 default => 1
}, },
mac => { 'mac' => {
type => "=s", type => "=s",
help => "MAC address" help => "MAC address"
}, },
vnic => { 'vnic' => {
type => "=s", type => "=s",
help => "NIC index", help => "NIC index",
default => 1 default => 1
}, },
priority => { 'priority' => {
type => "=s", type => "=s",
help => "Operation priority: highpriority, slowpriority, defaultpriority", help => "Operation priority: highpriority, slowpriority, defaultpriority",
default => 'defaultpriority' default => 'defaultpriority'
}, },
cpu_reservation => { 'cpu-reservation' => {
type => "=i", type => "=i",
help => "CPU Reservation in Mhz", help => "CPU Reservation in Mhz",
default => 0 default => 0
}, },
mem_reservation => { 'mem-reservation' => {
type => "=i", type => "=i",
help => "Memory reservation", help => "Memory reservation",
default => 0 default => 0
}, },
cbt_size => { 'cbt-size' => {
type => "=i", type => "=i",
help => "CBT size" help => "CBT size"
}, },
cbt => { 'cbt' => {
type => "", type => "",
help => "Whether to enable CBT" help => "Whether to enable CBT"
}, },
overwrite => { 'overwrite' => {
type => "", type => "",
help => "Whether to remove the virtual machine if there is one with the same name", help => "Whether to remove the virtual machine if there is one with the same name",
default => 0 default => 0
}, },
poweron => { 'poweron' => {
type => "", type => "",
help => "Whether to power on machine after operation", help => "Whether to power on machine after operation",
default => 1 default => 1
}, },
show_logs => { 'show-logs' => {
type => "", type => "",
help => "Whether to redirect logs to stdout", help => "Whether to redirect logs to stdout",
default => 0 default => 0
}, },
snapshot_name => { 'snapshot-name' => {
type => "=s", type => "=s",
help => "Name of the snapshot", help => "Name of the snapshot",
default => "snapshot" default => "snapshot"
}, },
snapshot_desc => { 'snapshot-desc' => {
type => "=s", type => "=s",
help => "Snapshot description", help => "Snapshot description",
default => "Snapshot" default => "Snapshot"
@ -131,26 +131,26 @@ Opts::validate();
my $operation = Opts::get_option('operation'); my $operation = Opts::get_option('operation');
my $job = Opts::get_option('job'); my $job = Opts::get_option('job');
my $vmname = Opts::get_option('vmname'); my $vm_name = Opts::get_option('vm-name');
my $rotation_days = Opts::get_option('rotation_days'); my $rotation_days = Opts::get_option('rotation-days');
my $rotation_count = Opts::get_option('rotation_count'); my $rotation_count = Opts::get_option('rotation-count');
my $dst_name = Opts::get_option('dst_name'); my $dst_name = Opts::get_option('dst-name');
my $dst_host = Opts::get_option('dst_host'); my $dst_host = Opts::get_option('dst-host');
my $dst_datastore = Opts::get_option('dst_datastore'); my $dst_datastore = Opts::get_option('dst-datastore');
my $memory = Opts::get_option('memory'); my $memory = Opts::get_option('memory');
my $num_cpus = Opts::get_option('num_cpus'); my $num_cpus = Opts::get_option('num-cpus');
my $mac = Opts::get_option('mac'); my $mac = Opts::get_option('mac');
my $vnic = Opts::get_option('vnic'); my $vnic = Opts::get_option('vnic');
my $priority = Opts::get_option('priority'); my $priority = Opts::get_option('priority');
my $mem_reservation = Opts::get_option('mem_reservation'); my $mem_reservation = Opts::get_option('mem-reservation');
my $cpu_reservation = Opts::get_option('cpu_reservation'); my $cpu_reservation = Opts::get_option('cpu-reservation');
my $cbt_size = Opts::get_option('cbt_size'); my $cbt_size = Opts::get_option('cbt-size');
my $cbt = Opts::option_is_set('cbt'); my $cbt = Opts::option_is_set('cbt');
my $overwrite = Opts::option_is_set('overwrite'); my $overwrite = Opts::option_is_set('overwrite');
my $poweron = Opts::option_is_set('poweron'); my $poweron = Opts::option_is_set('poweron');
my $show_logs = Opts::option_is_set('show_logs'); my $show_logs = Opts::option_is_set('show-logs');
my $snapshot_name = Opts::get_option('snapshot_name'); my $snapshot_name = Opts::get_option('snapshot-name');
my $snapshot_desc = Opts::get_option('snapshot_desc'); my $snapshot_desc = Opts::get_option('snapshot-desc');
my %config; my %config;
my @config_files = ( my @config_files = (
@ -167,7 +167,7 @@ foreach my $config_file (@config_files) {
} }
} }
unless (%config) { unless (%config) {
die "Configuration file not found"; die "Configuration file not found.";
} }
my $vm; my $vm;
@ -180,34 +180,43 @@ my $local_backup_dir = $config{local_backup_dir};
sub log_to_file { sub log_to_file {
my ($message) = @_; my ($message) = @_;
my $time = Time::Piece->new; my $time = Time::Piece->new;
my $timeMark = $time->strftime('%Y-%m-%d %H:%M:%S'); my $time_mark = $time->strftime('%Y-%m-%d %H:%M:%S');
print $log_fh "$timeMark : $message\n"; print $log_fh "$time_mark > $message";
} }
sub log_message { sub log_message {
my ($message) = @_; my ($message) = @_;
if ($log_fh) { if ($log_fh) {
log_to_file $message; log_to_file "MSG: $message\n";
} else { } else {
print "$message\n" print "$message\n"
} }
} }
sub log_error { sub log_error {
my ($message) = @_; my ($error) = @_;
print STDERR color("red").$message.color("reset");
if ($error->isa('SOAP::Fault')) {
$error = $error->faultcode.": ".$error->faultString;
}
print color('red');
print $error;
print color('reset');
if ($log_fh) { if ($log_fh) {
log_to_file $message; log_to_file "ERR: $error";
} }
} }
unless ($show_logs) { unless ($show_logs) {
open($log_fh, '>', $config{log_file}); open($log_fh, '>>', $config{log_file});
} }
eval { eval {
main(); main();
}; };
if ($@) { if ($@) {
log_error($@); log_error $@;
} }
if ($log_fh) { if ($log_fh) {
@ -223,7 +232,7 @@ sub main {
my $url = "https://$vcenter_host/sdk/vimService"; my $url = "https://$vcenter_host/sdk/vimService";
unless (defined ($password)) { unless (defined ($password)) {
die "Password not found $vcenter_host"; die "Password not found $vcenter_host.";
} }
eval { eval {
@ -234,27 +243,26 @@ sub main {
); );
}; };
if ($@) { if ($@) {
die "Cannot connect to $vcenter_host"; die "Cannot connect to $vcenter_host.";
} }
log_message "Connected to $vcenter_host"; log_message "Connected to $vcenter_host.";
eval { eval {
given ($operation) { given ($operation) {
when ('backuping') { when ('backup-job') {
backup_job(); backup_job();
} }
when ('cloning') { when ('clone-job') {
clone_job(); clone_job();
} }
when ('rotate') {
rotate_backup();
}
when ('backup') { when ('backup') {
open_machine(); open_machine();
backup_machine(); backup_machine();
} }
when ('rotate') {
rotate_backup();
}
when ('clone') { when ('clone') {
open_machine(); open_machine();
clone_machine(); clone_machine();
@ -267,6 +275,9 @@ sub main {
open_machine(); open_machine();
migrate_machine(); migrate_machine();
} }
default {
die "Unknown operation '$operation'.";
}
} }
}; };
my $err = $@; my $err = $@;
@ -281,98 +292,108 @@ sub main {
#--------------------------------- Operations #--------------------------------- Operations
sub backup_job() { sub backup_job() {
unless (exists $config{schedules}{$job}) { unless ($job) {
die "Backup job '$job' doesn't exists"; die "Job not defined.";
}
unless (exists $config{backup_jobs}{$job}) {
die "Backup job '$job' doesn't exists.";
} }
log_message "Backup job '$job' started"; log_message "Backup job '$job' started.";
my $schedule = $config{schedules}{friday}; my $backup_job = $config{backup_jobs}{$job};
my $rotation_cfg = $config{rotations}{$schedule->{rotation}}; my $rotation_cfg = $config{rotations}{$backup_job->{rotation}};
$rotation_count = $rotation_cfg->{count}; $rotation_count = $rotation_cfg->{count};
$rotation_days = $rotation_cfg->{days}; $rotation_days = $rotation_cfg->{days};
my @machines = $schedule->{machines}; my @machines = @{$backup_job->{machines}};
foreach $vmname (@machines) { foreach my $machine (@machines) {
eval { eval {
print "$vmname\n"; $vm_name = $machine;
#open_machine(); &open_machine();
#backup_machine(); backup_machine();
#rotate_backup(); rotate_backup();
}; };
if ($@) { if ($@) {
log_error($@); log_error $@;
} }
} }
log_message "Backup job '$job' finished"; log_message "Backup job '$job' finished.";
} }
sub clone_job() { sub clone_job() {
unless (exists $config{clone}{$job}) { unless ($job) {
die "Cloning job '$job' doesn't exists"; die "Job not defined.";
}
unless (exists $config{clone_jobs}{$job}) {
die "Clone job '$job' doesn't exists.";
} }
log_message "Clone job '$job' started"; log_message "Clone job '$job' started.";
my $cfg = $config{clone}{$job}; my $clone_job = $config{clone_jobs}{$job};
$vmname = $cfg->{vm}; $vm_name = $clone_job->{vm};
$dst_name = $cfg->{dst_name}; $dst_name = $clone_job->{dst_name};
$dst_host = $cfg->{dst_host}; $dst_host = $clone_job->{dst_host};
$dst_datastore = $cfg->{dst_datastore}; $dst_datastore = $clone_job->{dst_datastore};
$memory = $cfg->{memory}; $memory = $clone_job->{memory};
$num_cpus = $cfg->{num_cpus}; $num_cpus = $clone_job->{num_cpus};
$mac = $cfg->{mac}; $mac = $clone_job->{mac};
$poweron = $cfg->{poweron}; $poweron = $clone_job->{poweron};
$overwrite = $cfg->{overwrite}; $overwrite = $clone_job->{overwrite};
open_machine(); open_machine();
clone_machine(); clone_machine();
log_message "Clone job '$job' finished"; log_message "Clone job '$job' finished.";
} }
sub backup_machine() { sub backup_machine() {
log_message "Backup of '$vmname' started"; log_message "Backup of '$vm_name' started.";
my $time = Time::Piece->new; my $time = Time::Piece->new;
my $timeMark = $time->strftime($time_pattern); my $time_mark = $time->strftime($time_pattern);
my $tmpDir = ".$timeMark"; my $tmpDir = ".$time_mark";
my $backup_datastore = $config{backup_datastore}; my $backup_datastore = $config{backup_datastore};
my $dsBackupPath = "[$backup_datastore] $vmname/$tmpDir"; my $ds_tmp_dir = "[$backup_datastore] $vm_name/$tmpDir";
my $local_dir = "$local_backup_dir/$vmname"; my $local_dir = "$local_backup_dir/$vm_name";
my $localTmpDir = "$local_dir/$tmpDir"; my $local_tmp_dir = "$local_dir/$tmpDir";
my $tarFile = "$local_dir/$vmname-$timeMark.tar.gz"; my $tar_file = "$local_dir/${vm_name}_$time_mark.tar.gz";
if (-e $localTmpDir) { if (-e $local_tmp_dir) {
die "Backup directory already exists: $localTmpDir"; die "Temporary backup directory already exists: $local_tmp_dir";
} }
my $serviceContent = Vim::get_service_content(); my $service_content = Vim::get_service_content();
my $fileManager = Vim::get_view(mo_ref => $serviceContent->fileManager); my $file_manager = Vim::get_view(mo_ref => $service_content->fileManager);
my $dc = Vim::find_entity_view( my $dc = Vim::find_entity_view(
view_type => "Datacenter", view_type => "Datacenter",
filter => {'name' => $config{datacenter}} filter => {'name' => $config{datacenter}}
); );
$fileManager->MakeDirectory( log_message "Creating temporary backup directory: $ds_tmp_dir";
name => $dsBackupPath, $file_manager->MakeDirectory(
name => $ds_tmp_dir,
datacenter => $dc, datacenter => $dc,
createParentDirectories => true createParentDirectories => true
); );
eval { eval {
$fileManager->CopyDatastoreFile( my $vm_path_name = $vm->config->files->vmPathName;
sourceName => $vm->config->files->vmPathName, log_message "Copying machine configuration file: $vm_path_name";
$file_manager->CopyDatastoreFile(
sourceName => $vm_path_name,
sourceDatacenter => $dc, sourceDatacenter => $dc,
destinationName => "$dsBackupPath/$vmname.vmx", destinationName => "$ds_tmp_dir/$vm_name.vmx",
destinationDatacenter => $dc destinationDatacenter => $dc
); );
my $vdm = Vim::get_view(mo_ref => $serviceContent->virtualDiskManager); my $vdm = Vim::get_view(mo_ref => $service_content->virtualDiskManager);
my $devices = $vm->config->hardware->device; my $devices = $vm->config->hardware->device;
$vm->RemoveAllSnapshots(); $vm->RemoveAllSnapshots();
@ -385,13 +406,14 @@ sub backup_machine() {
foreach my $device (@$devices) { foreach my $device (@$devices) {
if ($device->isa('VirtualDisk')) { if ($device->isa('VirtualDisk')) {
my $diskPath = $device->backing->fileName; my $disk_path = $device->backing->fileName;
my $diskFile = basename($diskPath); my $disk_file = basename($disk_path);
log_message "Copying virtual disk file: $disk_path";
$vdm->CopyVirtualDisk( $vdm->CopyVirtualDisk(
sourceName => $diskPath, sourceName => $disk_path,
sourceDatacenter => $dc, sourceDatacenter => $dc,
destName => "$dsBackupPath/$diskFile", destName => "$ds_tmp_dir/$disk_file",
destDatacenter => $dc destDatacenter => $dc
); );
} }
@ -399,34 +421,40 @@ sub backup_machine() {
$vm->RemoveAllSnapshots(); $vm->RemoveAllSnapshots();
system("tar -I pigz -cf $tarFile -C $localTmpDir ."); log_message "Compressing to TAR file: $tar_file";
system("tar -I pigz -cf $tar_file -C $local_tmp_dir .");
}; };
my $err = $@; my $err = $@;
rmtree($localTmpDir); if ($err) {
log_error "An error ocurred during '$vm_name' backup, aborting."
}
log_message "Removing temporary directory: $local_tmp_dir";
rmtree($local_tmp_dir);
if ($err) { if ($err) {
die $err; die $err;
} }
log_message "Backup of '$vmname' successfully created"; log_message "Backup of '$vm_name' successfully created.";
} }
sub rotate_backup() { sub rotate_backup() {
log_message "Rotating '$vmname' backups"; log_message "Rotating '$vm_name' backups.";
use List::Util qw[min]; use List::Util qw[min];
my $local_dir = "$local_backup_dir/$vmname"; my $local_dir = "$local_backup_dir/$vm_name";
if ($rotation_days == 0 && $rotation_count == 0) { if ($rotation_days == 0 && $rotation_count == 0) {
return; return;
} }
my $regex = qr/\Q$vmname\E_(\d{4}-\d{2}-\d{2}_\d{2}-\d{2})\.tar\.gz$/; my $regex = qr/\Q$vm_name\E_(\d{4}-\d{2}-\d{2}_\d{2}-\d{2})\.tar\.gz$/;
my @deleteFiles; my @delete_files;
my $fileCount = 0; my $file_count = 0;
my $rotateTime = Time::Piece->new(); my $rotateTime = Time::Piece->new();
$rotateTime -= ONE_DAY * $rotation_days; $rotateTime -= ONE_DAY * $rotation_days;
@ -437,57 +465,57 @@ sub rotate_backup() {
unless (@reResult = $file =~ $regex) { unless (@reResult = $file =~ $regex) {
next; next;
} }
$fileCount++; $file_count++;
my ($fileMatch) = @reResult; my ($fileMatch) = @reResult;
my $fileTime = Time::Piece->strptime($fileMatch, $time_pattern); my $fileTime = Time::Piece->strptime($fileMatch, $time_pattern);
if ($fileTime < $rotateTime) { if ($fileTime < $rotateTime) {
push(@deleteFiles, $file); push(@delete_files, $file);
} }
} }
my $removeCount = scalar(@deleteFiles); my $delete_count = scalar(@delete_files);
my $keptCount = $fileCount - $removeCount; my $kept_count = $file_count - $delete_count;
if ($keptCount < $rotation_count) { if ($kept_count < $rotation_count) {
@deleteFiles = sort @deleteFiles; @delete_files = sort @delete_files;
splice(@deleteFiles, -min($rotation_count - $keptCount, $removeCount)); splice(@delete_files, -min($rotation_count - $kept_count, $delete_count));
} }
$removeCount = scalar(@deleteFiles); $delete_count = scalar(@delete_files);
if ($removeCount == $fileCount) { if ($delete_count == $file_count) {
die "Rotation aborted, because is trying to remove all backups"; die "Rotation aborted, because is trying to remove all backups.";
} }
foreach my $deleteFile (@deleteFiles) { foreach my $deleteFile (@delete_files) {
log_message "Removing $deleteFile (Not done until script is tested for a while)"; log_message "Removing $deleteFile (Not done until script is tested for a while).";
#unlink $deleteFile; #unlink $deleteFile;
} }
if (scalar(@deleteFiles) == 0) { if (scalar(@delete_files) == 0) {
log_message "No backups to clean"; log_message "No backups to clean.";
} else { } else {
log_message "$removeCount backups cleaned"; log_message "$delete_count backups cleaned.";
} }
closedir($dh); closedir($dh);
} }
sub clone_machine { sub clone_machine {
log_message "Cloning '$vmname' to '$dst_name'"; log_message "Cloning '$vm_name' to '$dst_name'.";
check_datastore(); check_datastore();
my $vmOriginal = Vim::find_entity_view( my $original_vm = Vim::find_entity_view(
view_type => 'VirtualMachine', view_type => 'VirtualMachine',
filter => {'name' => $dst_name} filter => {'name' => $dst_name}
); );
if ($vmOriginal) { if ($original_vm) {
if ($overwrite) { if ($overwrite) {
set_power_state($vmOriginal, "poweredOff"); set_power_state($original_vm, "poweredOff");
sleep(20); sleep(20);
$vmOriginal->Destroy(); $original_vm->Destroy();
} else { } else {
die "Machine with same name exists" die "Machine with same name exists"
} }
@ -509,12 +537,12 @@ sub clone_machine {
} }
} }
my $relocateSpec; my $relocate_spec;
my $macType = "Generated"; my $mac_type = "Generated";
my $host_view = $vm->summary->runtime->host; my $host_view = $vm->summary->runtime->host;
if (defined($mac)) { if (defined($mac)) {
$macType = "Manual" $mac_type = "Manual"
} }
if (defined ($dst_host)) { if (defined ($dst_host)) {
$host_view = Vim::find_entity_view( $host_view = Vim::find_entity_view(
@ -531,13 +559,13 @@ sub clone_machine {
filter => {'name' => $dst_datastore}, filter => {'name' => $dst_datastore},
properties => ['name'] properties => ['name']
); );
$relocateSpec = VirtualMachineRelocateSpec->new( $relocate_spec = VirtualMachineRelocateSpec->new(
pool => $comp_res_view->resourcePool, pool => $comp_res_view->resourcePool,
host => $host_view, host => $host_view,
datastore => $ds_new datastore => $ds_new
); );
} else { } else {
$relocateSpec = VirtualMachineRelocateSpec->new( $relocate_spec = VirtualMachineRelocateSpec->new(
pool => $comp_res_view->resourcePool, pool => $comp_res_view->resourcePool,
host => $host_view host => $host_view
); );
@ -553,31 +581,31 @@ sub clone_machine {
} }
} }
my $currMac = $vnic_device->macAddress; my $curr_mac = $vnic_device->macAddress;
my $network = Vim::get_view(mo_ref => $vnic_device->backing->network, properties => ['name']); my $network = Vim::get_view(mo_ref => $vnic_device->backing->network, properties => ['name']);
my $config_spec_operation = VirtualDeviceConfigSpecOperation->new('edit'); my $config_spec_operation = VirtualDeviceConfigSpecOperation->new('edit');
my $backing_info = VirtualEthernetCardNetworkBackingInfo->new(deviceName => $network->{'name'}); my $backing_info = VirtualEthernetCardNetworkBackingInfo->new(deviceName => $network->{'name'});
my $nicType = ref($vnic_device); my $nic_type = ref($vnic_device);
my @nicClasses = ( my @nic_classes = (
'VirtualE1000', 'VirtualE1000',
'VirtualPCNet32', 'VirtualPCNet32',
'VirtualVmxnet2', 'VirtualVmxnet2',
'VirtualVmxnet3' 'VirtualVmxnet3'
); );
if (not($nicType ~~ @nicClasses)) { if (not($nic_type ~~ @nic_classes)) {
Util::disconnect(); Util::disconnect();
die "Unable to retrieve NIC type"; die "Unable to retrieve NIC type.";
} }
my $newNetworkDevice = $nicType->new( my $new_network_device = $nic_type->new(
key => $vnic_device->key, key => $vnic_device->key,
unitNumber => $vnic_device->unitNumber, unitNumber => $vnic_device->unitNumber,
controllerKey => $vnic_device->controllerKey, controllerKey => $vnic_device->controllerKey,
backing => $backing_info, backing => $backing_info,
addressType => $macType, addressType => $mac_type,
macAddress => $mac macAddress => $mac
); );
@ -596,10 +624,10 @@ sub clone_machine {
); );
my $guest_id = ($vm->guest->guestId =~ m/^other/) ? "debian6_64Guest" : $vm->guest->guestId; my $guest_id = ($vm->guest->guestId =~ m/^other/) ? "debian6_64Guest" : $vm->guest->guestId;
my $vm_dev_spec = VirtualDeviceConfigSpec->new(device => $newNetworkDevice, operation => $config_spec_operation); my $vm_dev_spec = VirtualDeviceConfigSpec->new(device => $new_network_device, operation => $config_spec_operation);
my $extra_conf = OptionValue->new(key => "guestinfo.hostname", value => $dst_name); my $extra_conf = OptionValue->new(key => "guestinfo.hostname", value => $dst_name);
my $changeSpec = VirtualMachineConfigSpec->new( my $change_spec = VirtualMachineConfigSpec->new(
deviceChange => [$vm_dev_spec], deviceChange => [$vm_dev_spec],
name => $dst_name, name => $dst_name,
memoryMB => $memory, memoryMB => $memory,
@ -609,37 +637,37 @@ sub clone_machine {
memoryAllocation => $mem_res, memoryAllocation => $mem_res,
extraConfig => [$extra_conf] extraConfig => [$extra_conf]
); );
my $cloneSpec = VirtualMachineCloneSpec->new( my $clone_spec = VirtualMachineCloneSpec->new(
powerOn => $poweron, powerOn => $poweron,
template => 0, template => 0,
location => $relocateSpec, location => $relocate_spec,
config => $changeSpec config => $change_spec
); );
$vm->CloneVM( $vm->CloneVM(
folder => $vm->parent, folder => $vm->parent,
name => $dst_name, name => $dst_name,
spec => $cloneSpec spec => $clone_spec
); );
log_message "Clone '$dst_name' of '$vmname' successfully created"; log_message "Clone '$dst_name' of '$vm_name' successfully created.";
} }
sub snapshot_machine() { sub snapshot_machine() {
log_message "Creating snapshot of '$vmname' with CBT $cbt"; log_message "Creating snapshot of '$vm_name' with CBT $cbt.";
if ($cbt) { if ($cbt) {
if ($vm->capability->changeTrackingSupported) { if ($vm->capability->changeTrackingSupported) {
setCBT(true); setCBT(true);
} else { } else {
die "CBT not supported"; die "CBT not supported.";
} }
my $vmbackup = Vim::find_entity_view( my $vm_backup = Vim::find_entity_view(
view_type => 'VirtualMachine', view_type => 'VirtualMachine',
filter => {'name' => $dst_name} filter => {'name' => $dst_name}
); );
my $devices = $vmbackup->config->hardware->device; my $devices = $vm_backup->config->hardware->device;
my $vm_dst_datastore; my $vm_dst_datastore;
foreach (@$devices) { foreach (@$devices) {
@ -658,27 +686,27 @@ sub snapshot_machine() {
my $vm_ds = Vim::get_views(mo_ref_array => $vm->get_property('datastore')); my $vm_ds = Vim::get_views(mo_ref_array => $vm->get_property('datastore'));
$vm_datastore = join(',', map($_->get_property('name'), @{$vm_ds})); $vm_datastore = join(',', map($_->get_property('name'), @{$vm_ds}));
my $vmFolder = "/vmfs/volumes/$vm_datastore/$vmname"; my $vm_folder = "/vmfs/volumes/$vm_datastore/$vm_name";
my $command = "cd $vmFolder && ".q[ls -lhr *-0*-ctk.vmdk|head -n1 |awk '{print $9}' |cut -c].(length($vmname)+2)."-".(length($vmname)+7); my $command = "cd $vm_folder && ".q[ls -lhr *-0*-ctk.vmdk|head -n1 |awk '{print $9}' |cut -c].(length($vm_name)+2)."-".(length($vm_name)+7);
my $fic_snap = execute_ssh_command($command); my $snap_file = execute_ssh_command($command);
if (length($fic_snap) > 0) { if (length($snap_file) > 0) {
$command = "cd $vmFolder && ".q[ls -lhr *-0*-delta.vmdk|head -n1 |awk '{print $5}' | sed '$s/...$//']; $command = "cd $vm_folder && ".q[ls -lhr *-0*-delta.vmdk|head -n1 |awk '{print $5}' | sed '$s/...$//'];
my $tamano_snp = execute_ssh_command($command); my $snp_size = execute_ssh_command($command);
if (($tamano_snp + 0) >= $cbt_size) { if (($snp_size + 0) >= $cbt_size) {
set_power_state($vmbackup, "poweredOff"); set_power_state($vm_backup, "poweredOff");
$command = "scp -i /etc/ssh/ssh_host_dsa_key $vmFolder/$vmname.vmdk root@"."$dst_host:/vmfs/volumes/$dst_datastore/$vm_dst_datastore"; $command = "scp -i /etc/ssh/ssh_host_dsa_key $vm_folder/$vm_name.vmdk root@"."$dst_host:/vmfs/volumes/$dst_datastore/$vm_dst_datastore";
execute_ssh_command($command); execute_ssh_command($command);
create_snapshot($vm); create_snapshot($vm);
create_snapshot($vmbackup); create_snapshot($vm_backup);
my $fichero = "$vmname-".substr($fic_snap,0,length($fic_snap)-1)."*.vmdk"; my $vmdk_file = "$vm_name-".substr($snap_file,0,length($snap_file)-1)."*.vmdk";
log_message "Copying snapshot $fichero is less or equal than $cbt_size with a size of ".length($fic_snap).""; log_message "Copying snapshot $vmdk_file is less or equal than $cbt_size with a size of ".length($snap_file).".";
$command = "scp -i /etc/ssh/ssh_host_dsa_key $vmFolder/$fichero root@"."$dst_host:/vmfs/volumes/$dst_datastore/$vm_dst_datastore"; $command = "scp -i /etc/ssh/ssh_host_dsa_key $vm_folder/$vmdk_file root@"."$dst_host:/vmfs/volumes/$dst_datastore/$vm_dst_datastore";
execute_ssh_command($command); execute_ssh_command($command);
# Deletes all snapshots # Deletes all snapshots
@ -686,7 +714,7 @@ sub snapshot_machine() {
$vm->RemoveAllSnapshots(); $vm->RemoveAllSnapshots();
create_snapshot($vm); create_snapshot($vm);
} else { } else {
log_message "No snapshot is made since the size of $vmname-$fic_snap-delta.vmdk is less than $cbt_size"; log_message "No snapshot is made since the size of $vm_name-$snap_file-delta.vmdk is less than $cbt_size.";
} }
} else { } else {
log_message "Creating first snapshot"; log_message "Creating first snapshot";
@ -696,11 +724,11 @@ sub snapshot_machine() {
create_snapshot($vm); create_snapshot($vm);
} }
log_message "Snapshot of '$vmname' successfully created"; log_message "Snapshot of '$vm_name' successfully created.";
} }
sub migrate_machine { sub migrate_machine {
log_message "Migrating '$vmname' to $dst_host"; log_message "Migrating '$vm_name' to $dst_host.";
unless ($priority) { unless ($priority) {
$priority = "highPriority"; $priority = "highPriority";
@ -735,19 +763,23 @@ sub migrate_machine {
); );
$vm->PowerOnVM(); $vm->PowerOnVM();
log_message "Migration of '$vmname' successfull"; log_message "Migration of '$vm_name' successfull.";
} }
#--------------------------------- Utils #--------------------------------- Utils
sub open_machine() { sub open_machine() {
unless ($vm_name) {
die "Machine name not set.";
}
$vm = Vim::find_entity_view( $vm = Vim::find_entity_view(
view_type => 'VirtualMachine', view_type => 'VirtualMachine',
filter => {name => $vmname} filter => {name => $vm_name}
); );
unless ($vm) { unless ($vm) {
die "Machine '$vmname' not found"; die "Machine '$vm_name' not found.";
} }
my $vm_remote_host = $vm->runtime->host; my $vm_remote_host = $vm->runtime->host;
@ -759,7 +791,7 @@ sub open_machine() {
$vm_datastore = $_->{name}; $vm_datastore = $_->{name};
} }
log_message "Found machine '$vmname' at host $remote_host at datastore '$vm_datastore'"; log_message "Found machine '$vm_name' at host $remote_host at datastore '$vm_datastore'.";
} }
sub setCBT { sub setCBT {
@ -768,7 +800,7 @@ sub setCBT {
return; return;
} }
eval { eval {
log_message "Switching CBT to $enable"; log_message "Switching CBT to $enable.";
my $spec = Vim::VirtualMachineConfigSpec->new(changeTrackingEnabled => $enable); my $spec = Vim::VirtualMachineConfigSpec->new(changeTrackingEnabled => $enable);
my $task = $vm->ReconfigVM_Task(spec => $spec); my $task = $vm->ReconfigVM_Task(spec => $spec);
}; };
@ -776,12 +808,12 @@ sub setCBT {
} }
sub create_snapshot { sub create_snapshot {
my ($vmsnapshot) = @_; my ($vm_snapshot) = @_;
my $spec = VirtualMachineConfigSpec->new(changeTrackingEnabled => $cbt); my $spec = VirtualMachineConfigSpec->new(changeTrackingEnabled => $cbt);
$vmsnapshot->ReconfigVM_Task(spec => $spec); $vm_snapshot->ReconfigVM_Task(spec => $spec);
$vmsnapshot->CreateSnapshot( $vm_snapshot->CreateSnapshot(
name => $snapshot_name, name => $snapshot_name,
description => $snapshot_desc, description => $snapshot_desc,
memory => 0, memory => 0,
@ -791,8 +823,8 @@ sub create_snapshot {
sub execute_ssh_command { sub execute_ssh_command {
my ($command) = @_; my ($command) = @_;
my $promptEnd = '/\w+[\$\%\#\>]\s{0,1}$/o'; my $prompt_end = '/\w+[\$\%\#\>]\s{0,1}$/o';
my $ssh = Net::OpenSSH->new($remote_host) or die "Cannot connect to $dst_host via SSH"; my $ssh = Net::OpenSSH->new($remote_host) or die "Cannot connect to $dst_host via SSH.";
log_message "SSH: $remote_host: $command"; log_message "SSH: $remote_host: $command";
my $result = $ssh->capture($command); my $result = $ssh->capture($command);
log_message "SSH: Result: $result"; log_message "SSH: Result: $result";
@ -801,18 +833,18 @@ sub execute_ssh_command {
} }
sub set_power_state { sub set_power_state {
my ($vmPower, $state) = @_; my ($vm_power, $state) = @_;
eval { eval {
if ($vmPower->runtime->powerState->val ne $state) { if ($vm_power->runtime->powerState->val ne $state) {
given ($state) { given ($state) {
when ('poweredOff') { when ('poweredOff') {
$vmPower->ShutdownGuest(); $vm_power->ShutdownGuest();
log_message "Turning off ".$vmPower->name; log_message "Turning off ".$vm_power->name;
} }
when ('poweredOn') { when ('poweredOn') {
$vmPower->PowerOnVM(); $vm_power->PowerOnVM();
log_message "Turning on ".$vmPower->name; log_message "Turning on ".$vm_power->name;
} }
} }
sleep(50); sleep(50);
@ -837,5 +869,5 @@ sub check_datastore {
} }
} }
die "Datastore '$dst_datastore' does not exist"; die "Datastore '$dst_datastore' does not exist.";
} }