Test mode, backup only specific disks, default rotation
gitea/vn-vmware/master This commit looks good Details

This commit is contained in:
Juan Ferrer 2020-02-17 13:31:17 +01:00
parent 748d6387be
commit 6c9b098c62
6 changed files with 213 additions and 134 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
config.my.pl
config.my.pl
visdkrc

24
Jenkinsfile vendored
View File

@ -15,7 +15,7 @@ pipeline {
stages {
stage('Checkout') {
steps {
sh 'printenv'
setEnv()
}
}
stage('Deploy') {
@ -30,27 +30,7 @@ pipeline {
}
post {
always {
script {
if (!env.GIT_COMMITTER_EMAIL) {
env.COMMITTER_EMAIL = sh(
script: 'git --no-pager show -s --format="%ae"',
returnStdout: true
).trim()
} else {
env.COMMITTER_EMAIL = env.GIT_COMMITTER_EMAIL;
}
if (!env.COMMITTER_EMAIL) return
try {
mail(
to: env.COMMITTER_EMAIL,
subject: "Pipeline: ${env.JOB_NAME} (${env.BUILD_NUMBER}): ${currentBuild.currentResult}",
body: "Check status at ${env.BUILD_URL}"
)
} catch (e) {
echo e.toString()
}
}
sendEmail()
}
}
}

View File

@ -17,19 +17,40 @@ local_backup_dir => '/mnt/vm-backups',
# Number of processes used by pigz to compress backups
pigz_processes => 1,
# Backup jobs
backup_jobs => {
schedule1 => {
machines => ['test'],
rotation => 'lastMonth'
}
},
# The default rotation to use if none is specified on job
rotation => 'lastMonth',
# Backup rotation configuration
rotations => {
lastMonth => {
days => 31,
count => 31
},
lastWeek => {
days => 7
},
lastYear => {
days => 365
}
},
# Backup jobs
backup_jobs => {
fooJob => {
machines => ['foo'],
rotation => 'lastYear'
},
barJob => {
machines => [{
name => 'bar',
disks => 'bar.vmdk'
}]
},
bazJob => {
machines => [{
name => 'baz',
rotation => 'lastWeek'
}]
}
},

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
vn-vmware (1.1.13) stable; urgency=low
vn-vmware (1.1.14) stable; urgency=low
* Initial Release.

2
debian/cron.d vendored
View File

@ -3,4 +3,4 @@
MAILFROM="vn-vmware@domain.local"
MAILTO="sysadmin@domain.local"
00 04 * * tue-fri root vn-vmware.pl --operation test
00 04 * * tue-fri root vn-vmware.pl --operation none

View File

@ -55,7 +55,7 @@ foreach my $vi_config_file (@vi_config_files) {
my %opts = (
'operation' => {
type => "=s",
help => "Operation to perform: backup-job, clone-job, backup, rotate, clone, snapshot, migrate, test",
help => "Operation to perform: none, backup-job, clone-job, backup, rotate, clone, snapshot, migrate",
required => true
},
'job' => {
@ -132,6 +132,11 @@ my %opts = (
help => "Whether to power on machine after operation",
default => 1
},
'test' => {
type => "",
help => "Test mode, don't perform actions",
default => 0
},
'snapshot-name' => {
type => "=s",
help => "Name of the snapshot",
@ -155,7 +160,7 @@ 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_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');
@ -166,11 +171,13 @@ my $mem_reservation = Opts::get_option('mem-reservation');
my $cpu_reservation = Opts::get_option('cpu-reservation');
my $overwrite = Opts::option_is_set('overwrite');
my $poweron = Opts::option_is_set('poweron');
my $test = Opts::option_is_set('test');
my $snapshot_name = Opts::get_option('snapshot-name');
my $snapshot_desc = Opts::get_option('snapshot-desc');
my $vm;
my $log_fh;
my $backup_disks;
my $time_pattern = '%Y-%m-%d_%H-%M';
my $local_backup_dir = $config{local_backup_dir};
@ -226,6 +233,10 @@ if ($log_fh) {
}
sub main {
if ($test) {
log_message "Test mode enabled, all actions will be simulated.";
}
Util::connect();
log_message "Connected to $server.";
@ -270,8 +281,8 @@ sub main {
open_machine();
migrate_machine();
}
when ('test') {
test_operation();
when ('none') {
no_operation();
}
default {
die "Unknown operation '$operation'.";
@ -301,29 +312,44 @@ sub backup_job() {
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;
my $rotation_name;
if (exists $backup_job->{rotation}) {
$rotation_name = $backup_job->{rotation};
} elsif (exists $config{rotation}) {
$rotation_name = $config{rotation};
}
if (ref($machine) eq 'HASH') {
$vm_name = $machine->{name};
$backup_disks = $machine->{disks};
if (exists $machine->{rotation}) {
$rotation_name = $machine->{rotation};
}
} else {
$vm_name = $machine;
}
if (defined ($rotation_name)) {
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};
}
}
open_machine();
backup_machine();
rotate_backup();
@ -388,20 +414,23 @@ sub backup_machine() {
);
log_message "Creating temporary backup directory: $ds_tmp_dir";
$file_manager->MakeDirectory(
name => $ds_tmp_dir,
datacenter => $dc,
createParentDirectories => true
);
unless (-e $local_tmp_dir) {
log_message "Aborting, removing temporary directory: $ds_tmp_dir";
$file_manager->DeleteDatastoreFile(
unless ($test) {
$file_manager->MakeDirectory(
name => $ds_tmp_dir,
datacenter => $dc
datacenter => $dc,
createParentDirectories => true
);
die "Local backup directory is not accessible: $local_tmp_dir";
unless (-e $local_tmp_dir) {
log_message "Aborting, removing temporary directory: $ds_tmp_dir";
$file_manager->DeleteDatastoreFile(
name => $ds_tmp_dir,
datacenter => $dc
);
die "Local backup directory is not accessible: $local_tmp_dir";
}
}
my $snapshot;
@ -430,12 +459,14 @@ sub backup_machine() {
log_message $file_path;
eval {
$file_manager->CopyDatastoreFile(
sourceName => $file_path,
sourceDatacenter => $dc,
destinationName => "$ds_tmp_dir/$file_name",
destinationDatacenter => $dc
);
unless ($test) {
$file_manager->CopyDatastoreFile(
sourceName => $file_path,
sourceDatacenter => $dc,
destinationName => "$ds_tmp_dir/$file_name",
destinationDatacenter => $dc
);
}
};
my $err = $@;
@ -449,17 +480,25 @@ sub backup_machine() {
}
log_message "Creating backup snapshot.";
my $snapshot_ref = $vm->CreateSnapshot(
name => "backup",
description => "Scheduled backup",
memory => false,
quiesce => true
);
$snapshot = Vim::get_view(mo_ref => $snapshot_ref);
my $config;
unless ($test) {
my $snapshot_ref = $vm->CreateSnapshot(
name => "backup",
description => "Scheduled backup",
memory => false,
quiesce => true
);
$snapshot = Vim::get_view(mo_ref => $snapshot_ref);
$config = $snapshot->config;
} else {
$config = $vm->config;
}
log_message "Copying virtual disk files.";
my $devices = $snapshot->config->hardware->device;
my $devices = $config->hardware->device;
my $vdm = Vim::get_view(mo_ref => $service_content->virtualDiskManager);
my $i = -1;
@ -473,6 +512,12 @@ sub backup_machine() {
$i++;
my $disk_path = $device->backing->fileName;
my $disk_file = basename($disk_path);
if (defined($backup_disks) && !($disk_file ~~ @$backup_disks)) {
log_message "$disk_path (Ignored)";
next;
}
log_message $disk_path;
if ($disk_file ~~ @copied_devices) {
@ -487,20 +532,25 @@ sub backup_machine() {
# adapterType => 'busLogic',
# diskType => 'thin'
#);
$vdm->CopyVirtualDisk(
sourceName => $disk_path,
sourceDatacenter => $dc,
destName => "$ds_tmp_dir/$disk_file",
destDatacenter => $dc
#destSpec => $disk_spec
);
unless ($test) {
$vdm->CopyVirtualDisk(
sourceName => $disk_path,
sourceDatacenter => $dc,
destName => "$ds_tmp_dir/$disk_file",
destDatacenter => $dc
#destSpec => $disk_spec
);
}
}
log_message "Removing backup snapshot.";
$snapshot->RemoveSnapshot(
removeChildren => true,
consolidate => true
);
unless ($test) {
$snapshot->RemoveSnapshot(
removeChildren => true,
consolidate => true
);
}
$snapshot = undef;
my $pigz_processes;
@ -516,10 +566,13 @@ sub backup_machine() {
log_message "Compressing with Gzip (using $pigz_processes processes) to TAR file.";
log_message $tar_command;
my $tar_status = system($tar_command);
unless ($tar_status == 0) {
die "An error occurred when trying to compress '$vm_name' machine files.";
unless ($test) {
my $tar_status = system($tar_command);
unless ($tar_status == 0) {
die "An error occurred when trying to compress '$vm_name' machine files.";
}
}
};
@ -537,7 +590,9 @@ sub backup_machine() {
}
log_message "Removing temporary directory: $local_tmp_dir";
rmtree($local_tmp_dir);
unless ($test) {
rmtree($local_tmp_dir);
}
if ($err) {
die $err;
@ -548,6 +603,7 @@ sub backup_machine() {
sub rotate_backup() {
if ($rotation_days == 0 && $rotation_count == 0) {
log_message "No rotation days or count, aborting.";
return;
}
@ -597,7 +653,9 @@ sub rotate_backup() {
foreach my $deleteFile (@delete_files) {
my $deleteFilePath = "$local_dir/$deleteFile";
log_message $deleteFilePath;
unlink "$deleteFilePath";
unless ($test) {
unlink "$deleteFilePath";
}
}
log_message "Old backups cleaned.";
@ -761,37 +819,46 @@ sub clone_machine {
# Gathering specifications and cloning
log_message "Cloning machine.";
my $vm_clone;
my $clone_spec = VirtualMachineCloneSpec->new(
powerOn => false,
template => false,
location => $relocate_spec
);
my $vm_clone_ref = $vm->CloneVM(
folder => $vm->parent,
name => $dst_tmp_name,
spec => $clone_spec
);
my $vm_clone = Vim::get_view(mo_ref => $vm_clone_ref);
unless ($test) {
my $vm_clone_ref = $vm->CloneVM(
folder => $vm->parent,
name => $dst_tmp_name,
spec => $clone_spec
);
$vm_clone = Vim::get_view(mo_ref => $vm_clone_ref);
my $change_spec = VirtualMachineConfigSpec->new(
memoryMB => $memory,
numCPUs => $num_cpus,
cpuAllocation => $cpu,
memoryAllocation => $mem_res,
extraConfig => [$extra_conf]
);
$vm_clone->ReconfigVM(spec => $change_spec);
my $change_spec = VirtualMachineConfigSpec->new(
memoryMB => $memory,
numCPUs => $num_cpus,
cpuAllocation => $cpu,
memoryAllocation => $mem_res,
extraConfig => [$extra_conf]
);
$vm_clone->ReconfigVM(spec => $change_spec);
}
if ($original_vm) {
destroy_machine($original_vm);
log_message "Renaming '$dst_tmp_name' to '$dst_name'.";
$vm_clone->Rename(newName => $dst_name);
unless ($test) {
$vm_clone->Rename(newName => $dst_name);
}
}
if ($poweron) {
log_message "Powering on '$dst_name'.";
$vm_clone->PowerOnVM();
unless ($test) {
$vm_clone->PowerOnVM();
}
}
log_message "Clone '$dst_name' of '$vm_name' successfully created.";
@ -800,12 +867,14 @@ sub clone_machine {
sub snapshot_machine() {
log_message "Creating snapshot of '$vm_name'.";
$vm->CreateSnapshot(
name => $snapshot_name,
description => $snapshot_desc,
memory => 0,
quiesce => 0
);
unless ($test) {
$vm->CreateSnapshot(
name => $snapshot_name,
description => $snapshot_desc,
memory => 0,
quiesce => 0
);
}
log_message "Snapshot of '$vm_name' successfully created.";
}
@ -821,7 +890,9 @@ sub migrate_machine {
filter => {name => $dst_host}
);
$vm->SuspendVM();
unless ($test) {
$vm->SuspendVM();
}
if (defined($dst_datastore)) {
my $datastore_view = Vim::find_entity_view(
@ -833,27 +904,31 @@ sub migrate_machine {
datastore => $datastore_view,
host => $host_view
);
$vm->RelocateVM_Task(
spec => $spec,
priority => VirtualMachineMovePriority->new($priority)
);
unless ($test) {
$vm->RelocateVM_Task(
spec => $spec,
priority => VirtualMachineMovePriority->new($priority)
);
}
}
$vm->MigrateVM(
pool => $vm->resourcePool,
host => $host_view,
priority => VirtualMachineMovePriority->new('defaultPriority')
);
$vm->PowerOnVM();
unless ($test) {
$vm->MigrateVM(
pool => $vm->resourcePool,
host => $host_view,
priority => VirtualMachineMovePriority->new('defaultPriority')
);
$vm->PowerOnVM();
}
log_message "Migration of '$vm_name' successfull.";
}
sub test_operation() {
sub no_operation() {
if ($vm_name) {
open_machine();
}
log_message "Test operation, doing nothing.";
log_message "Doing nothing.";
}
#--------------------------------- Utils
@ -880,9 +955,11 @@ sub destroy_machine {
log_message "Deleting machine '$destroy_vm->{name}'.";
if ($destroy_vm->runtime->powerState->val eq 'poweredOn') {
$destroy_vm->PowerOffVM();
}
unless ($test) {
if ($destroy_vm->runtime->powerState->val eq 'poweredOn') {
$destroy_vm->PowerOffVM();
}
$destroy_vm->Destroy();
$destroy_vm->Destroy();
}
}