828 lines
19 KiB
Perl
Executable File
828 lines
19 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
use strict;
|
|
use warnings;
|
|
use experimental qw(switch say);
|
|
|
|
use VMware::VIRuntime;
|
|
use VMware::VICredStore;
|
|
use VMware::VILib;
|
|
use VMware::VIExt;
|
|
use Time::Piece;
|
|
use Time::Seconds;
|
|
use File::Path;
|
|
use Net::OpenSSH;
|
|
use Sys::CPU;
|
|
use Term::ANSIColor;
|
|
use List::Util qw(min max);
|
|
|
|
use constant false => 0;
|
|
use constant true => 1;
|
|
|
|
my %opts = (
|
|
'operation' => {
|
|
type => "=s",
|
|
help => "Operation to perform: backup-job, clone-job, backup, rotate, clone, snapshot, migrate",
|
|
required => true
|
|
},
|
|
'job' => {
|
|
type => "=s",
|
|
help => "The job name"
|
|
},
|
|
'vm-name' => {
|
|
type => "=s",
|
|
variable => "VM_NAME",
|
|
help => "Name of the virtual machine"
|
|
},
|
|
'rotation-days' => {
|
|
type => "=i",
|
|
help => "Rotation days for backups",
|
|
default => 0
|
|
},
|
|
'rotation-count' => {
|
|
type => "=i",
|
|
help => "Number of backups to keep despite the rotation days",
|
|
default => 0
|
|
},
|
|
'dst-name' => {
|
|
type => "=s",
|
|
help => "Name of the new virtual machine"
|
|
},
|
|
'dst-host' => {
|
|
type => "=s",
|
|
help => "Name of the target host"
|
|
},
|
|
'dst-datastore' => {
|
|
type => "=s",
|
|
help => "Destination datastore"
|
|
},
|
|
'memory' => {
|
|
type => "=s",
|
|
help => "Memory amount in MB"
|
|
},
|
|
'num-cpus' => {
|
|
type => "=s",
|
|
help => "Number of cores",
|
|
default => 1
|
|
},
|
|
'mac' => {
|
|
type => "=s",
|
|
help => "MAC address"
|
|
},
|
|
'vnic' => {
|
|
type => "=s",
|
|
help => "NIC index",
|
|
default => 1
|
|
},
|
|
'priority' => {
|
|
type => "=s",
|
|
help => "Operation priority: highpriority, slowpriority, defaultpriority",
|
|
default => 'defaultpriority'
|
|
},
|
|
'cpu-reservation' => {
|
|
type => "=i",
|
|
help => "CPU Reservation in Mhz",
|
|
default => 0
|
|
},
|
|
'mem-reservation' => {
|
|
type => "=i",
|
|
help => "Memory reservation",
|
|
default => 0
|
|
},
|
|
'cbt-size' => {
|
|
type => "=i",
|
|
help => "CBT size"
|
|
},
|
|
'cbt' => {
|
|
type => "",
|
|
help => "Whether to enable CBT"
|
|
},
|
|
'overwrite' => {
|
|
type => "",
|
|
help => "Whether to remove the destination machine if it exists; Be very careful with this option!",
|
|
default => 0
|
|
},
|
|
'poweron' => {
|
|
type => "",
|
|
help => "Whether to power on machine after operation",
|
|
default => 1
|
|
},
|
|
'show-logs' => {
|
|
type => "",
|
|
help => "Whether to redirect logs to stdout",
|
|
default => 0
|
|
},
|
|
'snapshot-name' => {
|
|
type => "=s",
|
|
help => "Name of the snapshot",
|
|
default => "snapshot"
|
|
},
|
|
'snapshot-desc' => {
|
|
type => "=s",
|
|
help => "Snapshot description",
|
|
default => "Snapshot"
|
|
}
|
|
);
|
|
|
|
Opts::add_options(%opts);
|
|
Opts::parse();
|
|
# FIXME: When enabled, asks for user and password
|
|
#Opts::validate();
|
|
|
|
my $operation = Opts::get_option('operation');
|
|
my $job = Opts::get_option('job');
|
|
my $vm_name = Opts::get_option('vm-name');
|
|
my $rotation_days = Opts::get_option('rotation-days');
|
|
my $rotation_count = Opts::get_option('rotation-count');
|
|
my $dst_name = Opts::get_option('dst-name');
|
|
my $dst_host = Opts::get_option('dst-host');
|
|
my $dst_datastore = Opts::get_option('dst-datastore');
|
|
my $memory = Opts::get_option('memory');
|
|
my $num_cpus = Opts::get_option('num-cpus');
|
|
my $mac = Opts::get_option('mac');
|
|
my $vnic = Opts::get_option('vnic');
|
|
my $priority = Opts::get_option('priority');
|
|
my $mem_reservation = Opts::get_option('mem-reservation');
|
|
my $cpu_reservation = Opts::get_option('cpu-reservation');
|
|
my $cbt_size = Opts::get_option('cbt-size');
|
|
my $cbt = Opts::option_is_set('cbt');
|
|
my $overwrite = Opts::option_is_set('overwrite');
|
|
my $poweron = Opts::option_is_set('poweron');
|
|
my $show_logs = Opts::option_is_set('show-logs');
|
|
my $snapshot_name = Opts::get_option('snapshot-name');
|
|
my $snapshot_desc = Opts::get_option('snapshot-desc');
|
|
|
|
my %config;
|
|
my @config_files = (
|
|
'/etc/vn-vmware/config.my.pl',
|
|
'/etc/vn-vmware/config.pl',
|
|
'config.my.pl',
|
|
'config.pl'
|
|
);
|
|
|
|
foreach my $config_file (@config_files) {
|
|
if (-e $config_file) {
|
|
%config = do $config_file;
|
|
last;
|
|
}
|
|
}
|
|
unless (%config) {
|
|
die "Configuration file not found.";
|
|
}
|
|
|
|
my $vm;
|
|
my $remote_host;
|
|
my $vm_datastore;
|
|
my $log_fh;
|
|
my $time_pattern = '%Y-%m-%d_%H-%M';
|
|
my $local_backup_dir = $config{local_backup_dir};
|
|
|
|
sub log_to_file {
|
|
my ($message) = @_;
|
|
my $time = Time::Piece->new;
|
|
my $time_mark = $time->strftime('%Y-%m-%d %H:%M:%S');
|
|
print $log_fh "$time_mark $message";
|
|
}
|
|
sub log_message {
|
|
my ($message) = @_;
|
|
|
|
if ($log_fh) {
|
|
log_to_file "MSG: $message\n";
|
|
} else {
|
|
print "$message\n"
|
|
}
|
|
}
|
|
sub log_error {
|
|
my ($error) = @_;
|
|
|
|
if ($error->isa('SoapFault')) {
|
|
my $detail = ref $error->detail;
|
|
$error = "SoapFault: $detail: $error->{fault_string}";
|
|
}
|
|
unless (substr($error, -1) eq "\n") {
|
|
$error .= "\n";
|
|
}
|
|
|
|
print color('red');
|
|
print $error;
|
|
print color('reset');
|
|
|
|
if ($log_fh) {
|
|
log_to_file "ERR: $error";
|
|
}
|
|
}
|
|
|
|
unless ($show_logs) {
|
|
open($log_fh, '>>', $config{log_file});
|
|
}
|
|
|
|
eval {
|
|
main();
|
|
};
|
|
if ($@) {
|
|
log_error $@;
|
|
}
|
|
|
|
if ($log_fh) {
|
|
close $log_fh;
|
|
}
|
|
|
|
sub main {
|
|
|
|
my $vcenter_host = $config{hostname};
|
|
my $username = $config{user};
|
|
|
|
VMware::VICredStore::init(filename => $config{credentials_file});
|
|
my $password = VMware::VICredStore::get_password(server => $vcenter_host, username => $username);
|
|
my $url = "https://$vcenter_host/sdk/vimService";
|
|
|
|
unless (defined ($password)) {
|
|
die "Password not found $vcenter_host.";
|
|
}
|
|
|
|
eval {
|
|
Vim::login(
|
|
service_url => $url,
|
|
user_name => $username,
|
|
password => $password
|
|
);
|
|
};
|
|
if ($@) {
|
|
die "Cannot connect to $vcenter_host.";
|
|
}
|
|
|
|
log_message "Connected to $vcenter_host.";
|
|
|
|
my $about = Vim::get_service_content()->about;
|
|
log_message "Version: $about->{apiType} $about->{version}";
|
|
|
|
eval {
|
|
unless ($operation) {
|
|
die "Operation not defined.";
|
|
}
|
|
given ($operation) {
|
|
when ('backup-job') {
|
|
backup_job();
|
|
}
|
|
when ('clone-job') {
|
|
clone_job();
|
|
}
|
|
when ('backup') {
|
|
open_machine();
|
|
backup_machine();
|
|
}
|
|
when ('rotate') {
|
|
rotate_backup();
|
|
}
|
|
when ('clone') {
|
|
open_machine();
|
|
clone_machine();
|
|
}
|
|
when ('snapshot') {
|
|
open_machine();
|
|
snapshot_machine();
|
|
}
|
|
when ('migrate') {
|
|
open_machine();
|
|
migrate_machine();
|
|
}
|
|
default {
|
|
die "Unknown operation '$operation'.";
|
|
}
|
|
}
|
|
};
|
|
my $err = $@;
|
|
|
|
Vim::logout();
|
|
|
|
if ($err) {
|
|
die $err;
|
|
}
|
|
}
|
|
|
|
#--------------------------------- Operations
|
|
|
|
sub backup_job() {
|
|
unless ($job) {
|
|
die "Job not defined.";
|
|
}
|
|
unless (exists $config{backup_jobs}{$job}) {
|
|
die "Backup job '$job' doesn't exists.";
|
|
}
|
|
|
|
log_message "Backup job '$job' started.";
|
|
|
|
my $backup_job = $config{backup_jobs}{$job};
|
|
|
|
if (exists $backup_job->{rotation}) {
|
|
my $rotation_name = $backup_job->{rotation};
|
|
|
|
unless (exists $config{rotations}{$rotation_name}) {
|
|
die "Rotation '$rotation_name' not defined.";
|
|
}
|
|
|
|
my $rotation_cfg = $config{rotations}{$rotation_name};
|
|
|
|
if (exists $rotation_cfg->{count}) {
|
|
$rotation_count = $rotation_cfg->{count};
|
|
}
|
|
if (exists $rotation_cfg->{days}) {
|
|
$rotation_days = $rotation_cfg->{days};
|
|
}
|
|
}
|
|
|
|
my @machines = @{$backup_job->{machines}};
|
|
|
|
foreach my $machine (@machines) {
|
|
eval {
|
|
$vm_name = $machine;
|
|
open_machine();
|
|
backup_machine();
|
|
rotate_backup();
|
|
};
|
|
if ($@) {
|
|
log_error $@;
|
|
}
|
|
}
|
|
|
|
log_message "Backup job '$job' finished.";
|
|
}
|
|
|
|
sub clone_job() {
|
|
unless ($job) {
|
|
die "Job not defined.";
|
|
}
|
|
unless (exists $config{clone_jobs}{$job}) {
|
|
die "Clone job '$job' doesn't exists.";
|
|
}
|
|
|
|
log_message "Clone job '$job' started.";
|
|
|
|
my $clone_job = $config{clone_jobs}{$job};
|
|
|
|
$vm_name = $clone_job->{vm};
|
|
$dst_name = $clone_job->{dst_name};
|
|
$dst_host = $clone_job->{dst_host};
|
|
$dst_datastore = $clone_job->{dst_datastore};
|
|
$memory = $clone_job->{memory};
|
|
$num_cpus = $clone_job->{num_cpus};
|
|
$mac = $clone_job->{mac};
|
|
$poweron = $clone_job->{poweron};
|
|
$overwrite = $clone_job->{overwrite};
|
|
|
|
open_machine();
|
|
clone_machine();
|
|
|
|
log_message "Clone job '$job' finished.";
|
|
}
|
|
|
|
sub backup_machine() {
|
|
log_message "Backup of '$vm_name' started.";
|
|
|
|
my $time = Time::Piece->new;
|
|
my $time_mark = $time->strftime($time_pattern);
|
|
my $tmpDir = ".$time_mark";
|
|
my $backup_datastore = $config{backup_datastore};
|
|
my $ds_tmp_dir = "[$backup_datastore] $vm_name/$tmpDir";
|
|
my $local_dir = "$local_backup_dir/$vm_name";
|
|
my $local_tmp_dir = "$local_dir/$tmpDir";
|
|
my $tar_file = "$local_dir/${vm_name}_$time_mark.tar.gz";
|
|
|
|
if (-e $local_tmp_dir) {
|
|
die "Temporary backup directory already exists: $local_tmp_dir";
|
|
}
|
|
|
|
my $service_content = Vim::get_service_content();
|
|
my $file_manager = Vim::get_view(mo_ref => $service_content->fileManager);
|
|
my $dc = Vim::find_entity_view(
|
|
view_type => 'Datacenter',
|
|
filter => {'name' => $config{datacenter}}
|
|
);
|
|
|
|
log_message "Creating temporary backup directory: $ds_tmp_dir";
|
|
$file_manager->MakeDirectory(
|
|
name => $ds_tmp_dir,
|
|
datacenter => $dc,
|
|
createParentDirectories => true
|
|
);
|
|
|
|
eval {
|
|
my $vm_path_name = $vm->config->files->vmPathName;
|
|
log_message "Copying machine configuration file: $vm_path_name";
|
|
|
|
$file_manager->CopyDatastoreFile(
|
|
sourceName => $vm_path_name,
|
|
sourceDatacenter => $dc,
|
|
destinationName => "$ds_tmp_dir/$vm_name.vmx",
|
|
destinationDatacenter => $dc
|
|
);
|
|
|
|
my $vdm = Vim::get_view(mo_ref => $service_content->virtualDiskManager);
|
|
my $devices = $vm->config->hardware->device;
|
|
|
|
log_message "Consolidating and removing existing snapshots.";
|
|
$vm->RemoveAllSnapshots();
|
|
|
|
log_message "Creating backup snapshot.";
|
|
$vm->CreateSnapshot(
|
|
name => "backup",
|
|
description => "Scheduled backup",
|
|
memory => 0,
|
|
quiesce => 0
|
|
);
|
|
|
|
foreach my $device (@$devices) {
|
|
if ($device->isa('VirtualDisk')) {
|
|
my $disk_path = $device->backing->fileName;
|
|
my $disk_file = basename($disk_path);
|
|
log_message "Copying virtual disk file: $disk_path";
|
|
|
|
# XXX: Not implemented by Perl vSphere SDK
|
|
#my $disk_spec = VirtualDiskSpec->new(
|
|
# adapterType => 'busLogic',
|
|
# diskType => 'thin'
|
|
#);
|
|
$vdm->CopyVirtualDisk(
|
|
sourceName => $disk_path,
|
|
sourceDatacenter => $dc,
|
|
destName => "$ds_tmp_dir/$disk_file",
|
|
destDatacenter => $dc
|
|
#destSpec => $disk_spec
|
|
);
|
|
}
|
|
}
|
|
|
|
log_message "Consolidating and removing snapshots.";
|
|
$vm->RemoveAllSnapshots();
|
|
|
|
my $gzip_cpus = max(1, int(Sys::CPU::cpu_count()) - 1);
|
|
my $tar_command = "tar -I \"pigz -p $gzip_cpus\" -cf $tar_file -C $local_tmp_dir .";
|
|
|
|
log_message "Compressing with Gzip (using $gzip_cpus CPUs) to TAR file: $tar_file";
|
|
log_message $tar_command;
|
|
my $tar_status = system($tar_command);
|
|
|
|
unless ($tar_status == 0) {
|
|
die "An error occurred when trying to compress the file.";
|
|
}
|
|
};
|
|
my $err = $@;
|
|
|
|
if ($err) {
|
|
log_message "An error ocurred during '$vm_name' backup, aborting.";
|
|
|
|
log_message "Consolidating and removing snapshots.";
|
|
$vm->RemoveAllSnapshots();
|
|
}
|
|
|
|
log_message "Removing temporary directory: $local_tmp_dir";
|
|
rmtree($local_tmp_dir);
|
|
|
|
if ($err) {
|
|
die $err;
|
|
}
|
|
|
|
log_message "Backup of '$vm_name' successfully created.";
|
|
}
|
|
|
|
sub rotate_backup() {
|
|
log_message "Rotating '$vm_name' backups.";
|
|
|
|
my $local_dir = "$local_backup_dir/$vm_name";
|
|
|
|
if ($rotation_days == 0 && $rotation_count == 0) {
|
|
return;
|
|
}
|
|
|
|
my $regex = qr/\Q$vm_name\E_(\d{4}-\d{2}-\d{2}_\d{2}-\d{2})\.tar\.gz$/;
|
|
|
|
my @delete_files;
|
|
my $file_count = 0;
|
|
|
|
my $rotateTime = Time::Piece->new();
|
|
$rotateTime -= ONE_DAY * $rotation_days;
|
|
|
|
opendir(my $dh, $local_dir);
|
|
while (my $file = readdir($dh)) {
|
|
my @reResult;
|
|
unless (@reResult = $file =~ $regex) {
|
|
next;
|
|
}
|
|
$file_count++;
|
|
my ($fileMatch) = @reResult;
|
|
my $fileTime = Time::Piece->strptime($fileMatch, $time_pattern);
|
|
|
|
if ($fileTime < $rotateTime) {
|
|
push(@delete_files, $file);
|
|
}
|
|
}
|
|
|
|
my $delete_count = scalar(@delete_files);
|
|
my $kept_count = $file_count - $delete_count;
|
|
|
|
if ($kept_count < $rotation_count) {
|
|
@delete_files = sort @delete_files;
|
|
splice(@delete_files, -min($rotation_count - $kept_count, $delete_count));
|
|
}
|
|
|
|
$delete_count = scalar(@delete_files);
|
|
|
|
if ($delete_count == $file_count) {
|
|
die "Rotation aborted, because is trying to remove all backups.";
|
|
}
|
|
foreach my $deleteFile (@delete_files) {
|
|
log_message "Removing $deleteFile (Not done until script is tested for a while).";
|
|
unlink $deleteFile;
|
|
}
|
|
|
|
if (scalar(@delete_files) == 0) {
|
|
log_message "No backups to clean.";
|
|
} else {
|
|
log_message "Total: $delete_count backups cleaned.";
|
|
}
|
|
|
|
closedir($dh);
|
|
}
|
|
|
|
sub clone_machine {
|
|
log_message "Cloning '$vm_name' to '$dst_name'.";
|
|
log_message "Doing some previous checkings.";
|
|
|
|
my $original_vm = Vim::find_entity_view(
|
|
view_type => 'VirtualMachine',
|
|
filter => {'name' => $dst_name}
|
|
);
|
|
|
|
if ($original_vm) {
|
|
if ($overwrite) {
|
|
set_power_state($original_vm, 'poweredOff');
|
|
|
|
log_message "Deleting machine '$dst_name'.";
|
|
$original_vm->Destroy();
|
|
} else {
|
|
die "Machine with same name exists.";
|
|
}
|
|
}
|
|
|
|
# If MAC is not especified, it is generated by VMWare
|
|
|
|
if ($mac) {
|
|
my $vmView = Vim::find_entity_views(view_type => 'VirtualMachine');
|
|
foreach my $vmRes (@$vmView) {
|
|
my @nics = grep { $_->isa("VirtualEthernetCard") } @{ $vm->config->hardware->device };
|
|
|
|
for my $nic (@nics) {
|
|
if ($nic->macAddress eq $mac) {
|
|
my $machineName = $vmRes->name;
|
|
die "Machine '$machineName' with same MAC exists.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log_message "Defining clone specifications.";
|
|
|
|
# Hostname
|
|
|
|
my $extra_conf = OptionValue->new(
|
|
key => 'guestinfo.hostname',
|
|
value => $dst_name
|
|
);
|
|
|
|
# Operating system
|
|
|
|
my $guest_id = $vm->guest->guestId;
|
|
$guest_id = $guest_id ? $guest_id : undef;
|
|
|
|
# CPU & memory
|
|
|
|
my $cpu;
|
|
my $mem_res;
|
|
my $sl = SharesLevel->new('normal');
|
|
my $sh = SharesInfo->new(level => $sl, shares => 1);
|
|
|
|
if (defined($cpu_reservation)) {
|
|
$cpu = ResourceAllocationInfo->new(
|
|
reservation => $cpu_reservation,
|
|
limit => -1,
|
|
shares => $sh
|
|
);
|
|
}
|
|
if (defined($mem_reservation)) {
|
|
$mem_res = ResourceAllocationInfo->new(
|
|
reservation => $mem_reservation,
|
|
limit => -1,
|
|
shares => $sh
|
|
);
|
|
}
|
|
|
|
# Datastore
|
|
|
|
my $relocate_spec;
|
|
my $host_view = $vm->summary->runtime->host;
|
|
|
|
if (defined($dst_host)) {
|
|
$host_view = Vim::find_entity_view(
|
|
view_type => 'HostSystem',
|
|
filter => {'name' => $dst_host}
|
|
);
|
|
}
|
|
|
|
my $comp_res_view = Vim::get_view(mo_ref => $host_view->parent);
|
|
|
|
if (defined($dst_datastore)) {
|
|
my $ds_new = Vim::find_entity_view(
|
|
view_type => 'Datastore',
|
|
filter => {'name' => $dst_datastore},
|
|
properties => ['name']
|
|
);
|
|
$relocate_spec = VirtualMachineRelocateSpec->new(
|
|
pool => $comp_res_view->resourcePool,
|
|
host => $host_view,
|
|
datastore => $ds_new
|
|
);
|
|
} else {
|
|
$relocate_spec = VirtualMachineRelocateSpec->new(
|
|
pool => $comp_res_view->resourcePool,
|
|
host => $host_view
|
|
);
|
|
}
|
|
|
|
# Network card
|
|
|
|
my $mac_type = 'Generated';
|
|
|
|
if (defined($mac)) {
|
|
$mac_type = 'Manual';
|
|
}
|
|
|
|
my $vnic_device;
|
|
my $devices = $vm->config->hardware->device;
|
|
my $vnic_name = "Network adapter $vnic";
|
|
|
|
foreach my $device (@$devices) {
|
|
if ($device->deviceInfo->label eq $vnic_name) {
|
|
$vnic_device = $device;
|
|
}
|
|
}
|
|
|
|
my $curr_mac = $vnic_device->macAddress;
|
|
my $network = Vim::get_view(mo_ref => $vnic_device->backing->network, properties => ['name']);
|
|
|
|
my $backing_info = VirtualEthernetCardNetworkBackingInfo->new(deviceName => $network->{'name'});
|
|
|
|
my $nic_type = ref($vnic_device);
|
|
my @nic_classes = (
|
|
'VirtualE1000',
|
|
'VirtualPCNet32',
|
|
'VirtualVmxnet2',
|
|
'VirtualVmxnet3'
|
|
);
|
|
|
|
if (not($nic_type ~~ @nic_classes)) {
|
|
die "Unable to retrieve NIC type.";
|
|
}
|
|
|
|
my $config_spec_operation = VirtualDeviceConfigSpecOperation->new('edit');
|
|
my $new_network_device = $nic_type->new(
|
|
key => $vnic_device->key,
|
|
unitNumber => $vnic_device->unitNumber,
|
|
controllerKey => $vnic_device->controllerKey,
|
|
backing => $backing_info,
|
|
addressType => $mac_type,
|
|
macAddress => $mac
|
|
);
|
|
my $vm_dev_spec = VirtualDeviceConfigSpec->new(
|
|
operation => $config_spec_operation,
|
|
device => $new_network_device
|
|
);
|
|
|
|
# Gathering specifications and cloning
|
|
|
|
my $change_spec = VirtualMachineConfigSpec->new(
|
|
deviceChange => [$vm_dev_spec],
|
|
name => $dst_name,
|
|
memoryMB => $memory,
|
|
numCPUs => $num_cpus,
|
|
guestId => $guest_id,
|
|
cpuAllocation => $cpu,
|
|
memoryAllocation => $mem_res,
|
|
extraConfig => [$extra_conf]
|
|
);
|
|
my $clone_spec = VirtualMachineCloneSpec->new(
|
|
powerOn => $poweron,
|
|
template => 0,
|
|
location => $relocate_spec,
|
|
config => $change_spec
|
|
);
|
|
|
|
log_message "Cloning machine.";
|
|
$vm->CloneVM(
|
|
folder => $vm->parent,
|
|
name => $dst_name,
|
|
spec => $clone_spec
|
|
);
|
|
|
|
log_message "Clone '$dst_name' of '$vm_name' successfully created.";
|
|
}
|
|
|
|
sub snapshot_machine() {
|
|
log_message "Creating snapshot of '$vm_name' with CBT $cbt.";
|
|
|
|
$vm->CreateSnapshot(
|
|
name => $snapshot_name,
|
|
description => $snapshot_desc,
|
|
memory => 0,
|
|
quiesce => 0
|
|
);
|
|
|
|
log_message "Snapshot of '$vm_name' successfully created.";
|
|
}
|
|
|
|
sub migrate_machine {
|
|
log_message "Migrating '$vm_name' to $dst_host.";
|
|
|
|
unless ($priority) {
|
|
$priority = "highPriority";
|
|
}
|
|
my $host_view = Vim::find_entity_view(
|
|
view_type => "HostSystem",
|
|
filter => {name => $dst_host}
|
|
);
|
|
|
|
$vm->SuspendVM();
|
|
|
|
if (defined($dst_datastore)) {
|
|
my $datastore_view = Vim::find_entity_view(
|
|
view_type => 'Datastore',
|
|
filter => {'name' => $dst_datastore},
|
|
properties => ['name']
|
|
);
|
|
my $spec = VirtualMachineRelocateSpec->new(
|
|
datastore => $datastore_view,
|
|
host => $host_view
|
|
);
|
|
$vm->RelocateVM_Task(
|
|
spec => $spec,
|
|
priority => VirtualMachineMovePriority->new($priority)
|
|
);
|
|
}
|
|
|
|
$vm->MigrateVM(
|
|
pool => $vm->resourcePool,
|
|
host => $host_view,
|
|
priority => VirtualMachineMovePriority->new('defaultPriority')
|
|
);
|
|
$vm->PowerOnVM();
|
|
|
|
log_message "Migration of '$vm_name' successfull.";
|
|
}
|
|
|
|
#--------------------------------- Utils
|
|
|
|
sub open_machine() {
|
|
unless ($vm_name) {
|
|
die "Machine name not set.";
|
|
}
|
|
|
|
$vm = Vim::find_entity_view(
|
|
view_type => 'VirtualMachine',
|
|
filter => {name => $vm_name}
|
|
);
|
|
|
|
unless ($vm) {
|
|
die "Machine '$vm_name' not found.";
|
|
}
|
|
|
|
my $vm_remote_host = $vm->runtime->host;
|
|
$remote_host = Vim::get_view(mo_ref => $vm_remote_host)->name;
|
|
my $datastores = eval {$vm->{datastore} || []};
|
|
my $dsview = Vim::get_views(mo_ref_array => $datastores, properties => ['name']);
|
|
|
|
foreach (@$dsview) {
|
|
$vm_datastore = $_->{name};
|
|
}
|
|
|
|
log_message "Found machine '$vm_name' at host $remote_host at datastore '$vm_datastore'.";
|
|
}
|
|
|
|
sub set_power_state {
|
|
my ($vm_power, $state) = @_;
|
|
|
|
eval {
|
|
if ($vm_power->runtime->powerState->val ne $state) {
|
|
given ($state) {
|
|
when ('poweredOff') {
|
|
$vm_power->ShutdownGuest();
|
|
log_message "Turning off ".$vm_power->name.".";
|
|
}
|
|
when ('poweredOn') {
|
|
$vm_power->PowerOnVM();
|
|
log_message "Turning on ".$vm_power->name.".";
|
|
}
|
|
}
|
|
sleep(50);
|
|
}
|
|
};
|
|
}
|