#!/usr/bin/perl -w ################################################################## # Author: William Lam # Email: william2003[at]gmail[dot]com # Created on: 02/21/2009 # http://www.engineering.ucsb.edu/~duonglt/vmware/ ################################################################## use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../"; use Text::ParseWords; use VMware::VIRuntime; use VMware::VIFPLib; ############################# ### USER CONFIGURATIONS ### ############################# ############################################################### # NAME OF THE BACKUP DATASTORE ############################################################### my $VM_BACKUP_DATASTORE = "dlgCore-NFS-bigboi.VM-Backups"; ############################################################### # BACKUP DIRECTORY NAME ON DATASTORE ############################################################### my $VM_BACKUP_DIRECTORY = "WILLIAM_BACKUPS"; #################################################### # Number of backups for a given VM before deleting #################################################### my $VM_BACKUP_ROTATION_COUNT = "3"; ################################################### # Supported backup types # 'zeroedthick' 'eagerzeroedthick' 'thin' '2gbsparse' ################################################### my $DISK_BACKUP_FORMAT = "zeroedthick"; ################################################### # Supported adapter types # 'buslogic' 'lsilogic' ################################################### my $ADAPTER_FORMAT = "buslogic"; ################################################################################################### # Shutdown guestOS prior to running backups and power them back on afterwards # This feature assumes VMware Tools are installed, else hard power down will be initiated # 1=enable, 0=disable (disable by default) ################################################################################################### my $POWER_VM_DOWN_BEFORE_BACKUP = "0"; ############################################################## # VM BACKUP DIRECTORY NAMING CONVENTION (default -YYYY-MM-DD) ############################################################## my $VM_BACKUP_DIR_NAMING_CONVENTION = timeStamp('YMD'); ################################################### # VM Snapshot Memory & Quiesce # 1=enable, 0=disable (disable by default) ################################################### my $VM_SNAPSHOT_MEMORY = "0"; my $VM_SNAPSHOT_QUIESCE = "0"; ################################################ # LOG LEVEL VERBOSITY : "verbose" or "normal" ################################################ my $LOG_LEVEL = "verbose"; ########################## DO NOT MODIFY PAST THIS LINE ########################## $SIG{__DIE__} = sub{Util::disconnect()}; my $ver = "2.5.1"; $Util::script_version = $ver; my %opts = ( vmlist => { type => "=s", help => "A file containing a list of virtual machine(s) to be backed up on host", required => 1, }, output => { type => "=s", default => "/tmp/ghettoVCBg2.log", help => "Full path to output log (default /tmp/ghettoVCBg2.log)", required => 0, }, debug => { type => "=s", default => "0", help => "Set to 1 to enable debug mode (default 0)", required => 0, }, config_dir => { type => "=s", help => "Name of directory containing VM(s) backup configurations", required => 0, }, ); Opts::add_options(%opts); Opts::parse(); Opts::set_option("passthroughauth", 1); Opts::validate(); ##################### # GLOBAL VARIABLES #################### my @vm_backup_list = (); my %success_backups = (); my $host_view; my $host_type; my $content; my $host; my $vmlist = Opts::get_option('vmlist'); my $enable_debug = Opts::get_option('debug'); my $backup_log_output = Opts::get_option('output'); my $configDir; my $host_username; my $host_password; my $vima_ver; my %host_to_vm = (); my %vmdk_type = (); if(Opts::option_is_set('config_dir')) { $configDir = Opts::get_option('config_dir'); } #only validate if we're not using a config if(defined($configDir)) { #validate all required params are populated if( $VM_BACKUP_DATASTORE eq "" || $VM_BACKUP_DIRECTORY eq "" || $VM_BACKUP_ROTATION_COUNT eq "" || $DISK_BACKUP_FORMAT eq "" || $ADAPTER_FORMAT eq "" || $POWER_VM_DOWN_BEFORE_BACKUP eq "" || $LOG_LEVEL eq "" ) { print "\nA required variable has not been defined, plesae go back and verify!\n"; exit; } } #retrieve all VIMA targets my @vima_targets = VIFPLib::enumerate_targets(); #validate vima host to figure out which version to setup the commands my $vifs_cmd; my $vmkfstools_cmd; open(VIMA_REL, "/etc/vima-release") || die "Couldn't open the /etc/vima-release!"; while () { my $line = $_; my ($prod, $ver, $build) = split(' ',$line); $vima_ver = $ver; last if $. == 1; } close(VIMA_REL); if($vima_ver eq "1.0.0") { $vifs_cmd = "/usr/bin/vifs.pl"; $vmkfstools_cmd = "/usr/bin/vmkfstools.pl"; } elsif($vima_ver eq "4.0.0") { $vifs_cmd = "/usr/bin/vifs"; $vmkfstools_cmd = "/usr/bin/vmkfstools"; } else { die "Script only supports VMware VIMA 1.0.0 and vMA 4.0.0\n"; } if($vmlist) { &processFile($vmlist); } open(LOG,">>$backup_log_output"); print LOG "============================== ",timeStamp('MDYHMS'), "ghettoVCBg2 LOG START ==============================\n"; if(!defined($configDir)) { if( ($LOG_LEVEL eq "verbose") || ($enable_debug eq 1) ) { print LOG timeStamp('MDYHMS'), "CONFIG - BACKUP_LOG_OUTPUT = $backup_log_output\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DATASTORE = $VM_BACKUP_DATASTORE\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DIRECTORY = $VM_BACKUP_DIRECTORY\n"; print LOG timeStamp('MDYHMS'), "CONFIG - DISK_BACKUP_FORMAT = $DISK_BACKUP_FORMAT\n"; print LOG timeStamp('MDYHMS'), "CONFIG - ADAPTER_FORMAT = $ADAPTER_FORMAT\n"; my $powerDowntext = ($POWER_VM_DOWN_BEFORE_BACKUP ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - POWER_VM_DOWN_BEFORE_BACKUP = $powerDowntext\n"; my $memtext = ($VM_SNAPSHOT_QUIESCE ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - VM_SNAPSHOT_QUIESCE = $memtext\n"; my $quitext = ($VM_SNAPSHOT_MEMORY ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - VM_SNAPSHOT_QUIESCE = $quitext\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DIR_NAMING_CONVENTION = $VM_BACKUP_DIR_NAMING_CONVENTION\n"; } } foreach my $vima_host (@vima_targets) { $host = $vima_host; # login using fastpass VIFPLib::login_by_fastpass($host); #validate ESX/ESXi host $content = Vim::get_service_content(); $host_type = $content->about->apiType; if($host_type eq 'HostAgent') { $host_view = Vim::find_entity_views(view_type => 'HostSystem'); if (!$host_view) { print LOG timeStamp('MDYHMS'), "ESX/ESXi host was not found\n"; } else { my $viuser = vifplib_perl::CreateVIUserInfo(); my $vifplib = vifplib_perl::CreateVIFPLib(); eval { $vifplib->QueryTarget($host, $viuser); }; if(!$@) { $host_username = $viuser->GetUsername(); $host_password = $viuser->GetPassword(); } if($vmlist) { &backUpVMs(@vm_backup_list); } } } } getFinalList(@vm_backup_list); print LOG "\n============================== ",timeStamp('MDYHMS'), "ghettoVCBg2 LOG END ==============================\n\n"; close(LOG); Util::disconnect(); ######################## # HELPER FUNCTIONS ######################## sub getFinalList { for my $key ( keys %success_backups ) { my $value = $success_backups{$key}; if($value ne 1 && $value ne -1) { print LOG "\n", timeStamp('MDYHMS'), "ERROR - Unable to locate VM: $key\n"; } } } # Subroutine to process the input file sub processFile { my ($vmlist) = @_; my $HANDLE; open (HANDLE, $vmlist) or die(timeStamp('MDYHMS'), "ERROR: Can not locate \"$vmlist\" input file!\n"); my @lines = ; my @errorArray; my $line_no = 0; close(HANDLE); foreach my $line (@lines) { $line_no++; &TrimSpaces($line); if($line) { if($line =~ /^\s*:|:\s*$/){ print LOG timeStamp('MDYHMS'), "Error in Parsing File at line: $line_no\n"; print LOG timeStamp('MDYHMS'), "Continuing to the next line\n"; next; } my $vm = $line; &TrimSpaces($vm); #only update the list on the following cases: #If VM has not been found or backedup #If --config_dir was used but config file for VM was not found if (!exists $success_backups{$vm} || ($success_backups{$vm} ne -1 && $success_backups{$vm} ne 1) ) { push @vm_backup_list,$vm; $success_backups{$vm} = 0; } } } } sub TrimSpaces { foreach (@_) { s/^\s+|\s*$//g } } sub backUpVMs { my @vm_backup_list = @_; my $vm_backup_dir; my $snapshot_name; my $original_vm_state; my $returnval; my $count = 0; foreach my $vm_name (@vm_backup_list) { if(defined($configDir)) { my @goodparam = qw(VM_BACKUP_DATASTORE VM_BACKUP_DIRECTORY VM_BACKUP_ROTATION_COUNT DISK_BACKUP_FORMAT ADAPTER_FORMAT POWER_VM_DOWN_BEFORE_BACKUP LOG_LEVEL VM_SNAPSHOT_MEMORY VM_SNAPSHOT_QUIESCE); my $file = "$configDir/$vm_name"; if(-e $file && $success_backups{$vm_name} ne -1 && $success_backups{$vm_name} ne 1) { my %config; open(CONFIG, "$file") || print LOG "Couldn't open the $file!\n"; while () { chomp; s/#.*//; # Remove comments s/^\s+//; # Remove opening whitespace s/\s+$//; # Remove closing whitespace next unless length; my ($key, $value) = split(/\s*=\s*/, $_, 2); if( grep $key eq $_, @goodparam ) { $value =~ s/"//g; $config{$key} = $value; } #reconfigure variables $VM_BACKUP_DATASTORE = $config{VM_BACKUP_DATASTORE}; $VM_BACKUP_DIRECTORY = $config{VM_BACKUP_DIRECTORY}; $VM_BACKUP_ROTATION_COUNT = $config{VM_BACKUP_ROTATION_COUNT}; $DISK_BACKUP_FORMAT = $config{DISK_BACKUP_FORMAT}; $ADAPTER_FORMAT = $config{ADAPTER_FORMAT}; $POWER_VM_DOWN_BEFORE_BACKUP = $config{POWER_VM_DOWN_BEFORE_BACKUP}; $VM_SNAPSHOT_MEMORY = $config{VM_SNAPSHOT_MEMORY}; $VM_SNAPSHOT_QUIESCE = $config{VM_SNAPSHOT_QUIESCE}; $LOG_LEVEL = $config{LOG_LEVEL}; } close(CONFIG); } elsif($success_backups{$vm_name} ne -1 && $success_backups{$vm_name} ne 1) { $success_backups{$vm_name} = -1; print LOG "\n", timeStamp('MDYHMS'), "ERROR - Unable to locate configuration file for VM: $vm_name!\n"; } } if($success_backups{$vm_name} ne -1 && $success_backups{$vm_name} ne 1) { my $vm_view = Vim::find_entity_view(view_type => 'VirtualMachine',filter => {"config.name" => $vm_name}); if(defined($vm_view)) { #do not backup if snapshots have been found if($vm_view->snapshot) { print LOG "\n", timeStamp('MDYHMS'), "WARN - Snapshot found for $vm_name, backup will not take place\n"; $success_backups{$vm_name} = 1; } else { my $devices = $vm_view->config->hardware->device; my $vm_has_rdm = 0; foreach my $device (@$devices) { #verify device is virtual disk if ( ($device->isa('VirtualDisk')) ) { #verify thick/eagerzeroedthick if( ($device->backing->isa('VirtualDiskFlatVer1BackingInfo')) || ($device->backing->isa('VirtualDiskFlatVer2BackingInfo')) ) { $vmdk_type{$device->key} = "flat"; } #verify 2gbsparse elsif ( ($device->backing->isa('VirtualDiskSparseVer1BackingInfo')) || ($device->backing->isa('VirtualDiskSparseVer2BackingInfo')) ) { $vmdk_type{$device->key} = "sparse"; } #verify RDM w/virtual compatiablity mode elsif ( ($device->backing->isa('VirtualDiskRawDiskMappingVer1BackingInfo')) && ($device->backing->compatibilityMode eq 'virtualMode') ) { $vmdk_type{$device->key} = "vrdm"; } elsif ( ($device->backing->isa('VirtualDiskRawDiskMappingVer1BackingInfo')) && ($device->backing->compatibilityMode eq 'physicalMode') ) { $vmdk_type{$device->key} = "prdm"; $vm_has_rdm = 1; } } } #do not backup if VM has RDM in physicalMode if($vm_has_rdm ne 0) { print LOG "\n", timeStamp('MDYHMS'), "WARN - physical RDM was found for $vm_name, backup will not take place\n"; $success_backups{$vm_name} = 1; } else { my $vmx_config = $vm_view->config->files->vmPathName; $vm_backup_dir = "[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY/$vm_name/$vm_name\-$VM_BACKUP_DIR_NAMING_CONVENTION"; my $vmx_file = $vm_view->config->files->vmPathName; ($vmx_file) = ($vmx_file =~ m|.*/(.*)|); my ($vm_datastore) = ($vmx_config=~ /\[([^]]+)/); if(defined($configDir)) { if( ($LOG_LEVEL eq "verbose") || ($enable_debug eq 1) ) { print LOG "\n", timeStamp('MDYHMS'), "CONFIG - USING CONFIGURATION FILE = $vm_name\n"; print LOG timeStamp('MDYHMS'), "CONFIG - BACKUP_LOG_OUTPUT = $backup_log_output\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DATASTORE = $VM_BACKUP_DATASTORE\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DIRECTORY = $VM_BACKUP_DIRECTORY\n"; print LOG timeStamp('MDYHMS'), "CONFIG - DISK_BACKUP_FORMAT = $DISK_BACKUP_FORMAT\n"; print LOG timeStamp('MDYHMS'), "CONFIG - ADAPTER_FORMAT = $ADAPTER_FORMAT\n"; my $powerDowntext = ($POWER_VM_DOWN_BEFORE_BACKUP ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - POWER_VM_DOWN_BEFORE_BACKUP = $powerDowntext\n"; my $memtext = ($VM_SNAPSHOT_QUIESCE ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - VM_SNAPSHOT_QUIESCE = $memtext\n"; my $quitext = ($VM_SNAPSHOT_MEMORY ? "YES" : "NO"); print LOG timeStamp('MDYHMS'), "CONFIG - VM_SNAPSHOT_QUIESCE = $quitext\n"; print LOG timeStamp('MDYHMS'), "CONFIG - VM_BACKUP_DIR_NAMING_CONVENTION = $VM_BACKUP_DIR_NAMING_CONVENTION"; } } if($enable_debug eq 1) { print LOG "\n", timeStamp('MDYHMS'), "---------- DEBUG INFO $vm_name ----------\n"; print LOG timeStamp('MDYHMS'), "DEBUG - Host Build: ", $content->about->fullName,"\n"; print LOG timeStamp('MDYHMS'), "DEBUG - Host: ",$host,"\n"; print LOG timeStamp('MDYHMS'), "DEBUG - Virtual Machine: $vm_name\n"; print LOG timeStamp('MDYHMS'), "DEBUG - VM ConfigPath: $vmx_config\n"; print LOG timeStamp('MDYHMS'), "DEBUG - VMX File: $vmx_file\n"; print LOG timeStamp('MDYHMS'), "DEBUG - BackupConfigPath: $vm_backup_dir/$vmx_file\n"; print LOG timeStamp('MDYHMS'), "DEBUG - BackupPath: $vm_backup_dir\n"; print LOG timeStamp('MDYHMS'), "DEBUG - VM Datastore: $vm_datastore\n"; print LOG timeStamp('MDYHMS'), "DEBUG - VMDK(s):\n"; my $vm_disks = $vm_view->layout->disk; foreach(@$vm_disks) { my $disk_files = $_->diskFile; foreach(@$disk_files) { print LOG timeStamp('MDYHMS'), "DEBUG - $_\n"; } } $success_backups{$vm_name} = 1; print LOG timeStamp('MDYHMS'), "---------- DEBUG INFO $vm_name ----------\n"; } else { ##################### # CREATE BACKUP DIR ##################### print LOG "\n",timeStamp('MDYHMS'), "Initiate backup for \"$vm_name\" found on $host!\n"; my $dir_result = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --mkdir "[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY" 2>&1`; $dir_result = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --mkdir "[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY/$vm_name" 2>&1`; $dir_result = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --mkdir "[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY/$vm_name/$vm_name\-$VM_BACKUP_DIR_NAMING_CONVENTION" 2>&1`; ##################### # COPY VMX FILE ##################### my $vmx_copy = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --copy "$vmx_config" "$vm_backup_dir/$vmx_file" 2>&1`; ##################### # GET STATE ##################### $original_vm_state = $vm_view->runtime->powerState->val; if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "$vm_name original powerState: $original_vm_state\n"; } ##################### # POWER OFF IF SET ##################### if( ($POWER_VM_DOWN_BEFORE_BACKUP eq 1) && ($original_vm_state eq 'poweredOn') ) { &shutdownVM($vm_view,$vm_name); } elsif( ($original_vm_state eq 'poweredOn') || ($original_vm_state eq 'suspended') ) { ##################### # CREATE SNAPSHOT ##################### $snapshot_name = "ghettoVCBg2-snapshot-".timeStamp('YMD'); &create_snapshot($vm_view,$snapshot_name,$vm_name,$VM_SNAPSHOT_MEMORY,$VM_SNAPSHOT_QUIESCE); } ##################### # BACKUP VMDK ##################### my $vm_disks = $vm_view->layout->disk; my @num_disks_ref = @$vm_disks; my $num_disks = @num_disks_ref; print LOG timeStamp('MDYHMS'), "$vm_name has $num_disks VMDK(s)\n"; &backupVMDK($vm_disks,$vm_datastore,$vm_backup_dir); ##################### # UPDATE VM VIEW ##################### $vm_view->update_view_data(); ##################### # POWER ON VM IF SET ##################### if( ($POWER_VM_DOWN_BEFORE_BACKUP eq 1) && ($original_vm_state eq 'poweredOn') ) { &poweronVM($vm_view,$vm_name); } elsif( ($original_vm_state eq 'poweredOn') || ($original_vm_state eq 'suspended') ) { ##################### # REMOVE SNAPSHOT ##################### &remove_snapshot($vm_view,$snapshot_name,$vm_name); } ##################### # CHECK ROTATION ##################### &checkVMBackupRotation($vm_view,"[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY/$vm_name","[$VM_BACKUP_DATASTORE] $VM_BACKUP_DIRECTORY/$vm_name/$vm_name\-$VM_BACKUP_DIR_NAMING_CONVENTION",$vm_name,$vmx_file); print LOG timeStamp('MDYHMS'), "Backup completed for \"$vm_name\"!\n"; $success_backups{$vm_name} = 1; %vmdk_type = (); } } } } } } return $returnval; } sub poweronVM { my ($vm_view,$vm_name) = @_; eval { $vm_view->PowerOnVM(); my $continue = 1; while ($continue) { my $vm_state = $vm_view->runtime->powerState->val; if($vm_state eq 'poweredOn') { if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Successfully powered back on $vm_name!\n"; } $continue = 0; } sleep 2; $vm_view->update_view_data(); } }; if($@) { if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "FAULT ERROR: Unable to power back on $vm_name!\n"; } } } sub shutdownVM { my ($vm_view,$vm_name) = @_; if($vm_view->summary->guest->toolsStatus->val eq 'toolsOk') { eval { $vm_view->ShutdownGuest(); my $continue = 1; while($continue) { my $vm_state = $vm_view->runtime->powerState->val; if($vm_state eq 'poweredOff') { if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Successfully shutdown $vm_name!\n"; } $continue = 0; } sleep 2; $vm_view->update_view_data(); } }; if($@) { print LOG timeStamp('MDYHMS'), "FAULT ERROR: $@ \n"; } } else { eval { $vm_view->PowerOffVM(); my $continue = 1; while ($continue) { my $vm_state = $vm_view->runtime->powerState->val; if($vm_state eq 'poweredOff') { if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Hard power off, VMware Tools is not installed on $vm_name!\n"; } $continue = 0; } sleep 2; $vm_view->update_view_data(); } }; if($@) { print LOG timeStamp('MDYHMS'), "FAULT ERROR: $@ \n"; } } } sub checkVMBackupRotation { my ($vm_view, $BACKUP_DIR_PATH,$BACKUP_VM_NAMING_CONVENTION,$vm_name,$vmx_file) = @_; my @LIST_BACKUPS = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --dir "$BACKUP_DIR_PATH" 2>&1`; #default rotation if variable is not defined if(!defined($VM_BACKUP_ROTATION_COUNT)) { $VM_BACKUP_ROTATION_COUNT = "1"; } chomp(@LIST_BACKUPS); foreach my $DIR (reverse(@LIST_BACKUPS)) { $DIR =~ s/\///g; ################################# # VMware bug in vCLI vifs --dir # SR 1291801391 ################################# # tmp fix if($DIR !~ /^Parent Directory/) { my $NEW; my $mv_dir; my $TMP_DIR="$BACKUP_DIR_PATH/$DIR"; my ($BAD, $TMP) = split('--', $TMP_DIR); if(!defined($TMP)) { $TMP = $TMP_DIR; } if($TMP eq $BACKUP_VM_NAMING_CONVENTION) { $NEW=$TMP."--1"; $mv_dir = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --move "$TMP_DIR" "$NEW" 2>&1`; } elsif($TMP ge $VM_BACKUP_ROTATION_COUNT) { my $path = $TMP_DIR; my $service_content = Vim::get_service_content(); my $fm = Vim::get_view (mo_ref => $service_content->{fileManager}); eval { $fm->DeleteDatastoreFile(name => $path); if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Purging \"$path\" due to rotation max\n"; } }; if($@) { if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Unable to purge \"$path\" due to rotation max\n"; } } } else { my ($BASE, $BAD) = split('--',$TMP_DIR); $NEW = $BASE."--".($TMP+1); $mv_dir = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --move "$TMP_DIR" "$NEW" 2>&1`; } } } } sub backupVMDK { my ($vm_disks,$vm_datastore,$vm_backup_dir) = @_; foreach(@$vm_disks) { my $disk_files = $_->diskFile; my $diskKey = $_->key; foreach(@$disk_files) { if($vmdk_type{$diskKey} ne 'prdm') { my $curr_vmdk_path = $_; my ($tmp_vm_datastore) = ($curr_vmdk_path =~ /\[([^]]+)/); my ($tmp_vm_vmdk) = ($curr_vmdk_path =~ m|.*/(.*)|); my $vmdk_backup_destination; if($tmp_vm_datastore eq $vm_datastore) { $vmdk_backup_destination = $vm_backup_dir."/".$tmp_vm_vmdk; } else { $vmdk_backup_destination = $vm_backup_dir."/".$vm_datastore."/".$tmp_vm_vmdk; my $ds_mkdir = `$vifs_cmd --server "$host" --username "$host_username" --password "$host_password" --mkdir "$vm_backup_dir/$vm_datastore" 2>&1`; } if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Backing up \"",$_,"\" to \"",$vmdk_backup_destination,"\" \n"; } my $vmkfstools_cpy; #case for legacy VIMA 1.0 if($vima_ver eq "1.0.0" && ($DISK_BACKUP_FORMAT eq 'zeroedthick' || $DISK_BACKUP_FORMAT eq 'eagezeroedthick')) { $vmkfstools_cpy = `$vmkfstools_cmd --server "$host" --username "$host_username" --password "$host_password" -i "$_" -a $ADAPTER_FORMAT -d '' "$vmdk_backup_destination" 2>&1`; } else { #vMA 4.0.0 should have fixed the issue $vmkfstools_cpy = `$vmkfstools_cmd --server "$host" --username "$host_username" --password "$host_password" -i "$_" -a $ADAPTER_FORMAT -d $DISK_BACKUP_FORMAT "$vmdk_backup_destination" 2>&1`; } if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Backup completed for ",$_,"\n"; } } } } } # Create: Creates a snapshot for one or more VMs. #=========================================================== sub create_snapshot { my ($vm_view, $snapshot_name, $vm_name, $mem, $qui) = @_; eval { $vm_view->CreateSnapshot(name => $snapshot_name, description => 'Snapshot created for Virtual Machine '.$vm_view->name, memory => $mem, quiesce => $qui); if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Creating Snapshot \"", $snapshot_name, "\" for ", $vm_name,"\n"; } }; if ($@) { print LOG timeStamp('MDYHMS'), "ERROR FAULT: $@\n"; } } # Remove: removes a named snapshot for one or more virtual machines. # ================================================================== sub remove_snapshot { my ($vm_view, $remove_snapshot, $vm_name) = @_; my $children = 0; my $ref = undef; my $nRefs = 0; if(defined $vm_view->snapshot) { ($ref, $nRefs) = find_snapshot_name ($vm_view->snapshot->rootSnapshotList, $remove_snapshot); } if (defined $ref && $nRefs == 1) { my $snapshot = Vim::get_view (mo_ref =>$ref->snapshot); eval { $snapshot->RemoveSnapshot (removeChildren => $children); if($LOG_LEVEL eq "verbose") { print LOG timeStamp('MDYHMS'), "Removing Snapshot \"",$remove_snapshot,"\" for ", $vm_name,"\n"; } }; if ($@) { print LOG timeStamp('MDYHMS'), "ERROR FAULT: $@\n"; } } else { if ($nRefs > 1) { print LOG timeStamp('MDYHMS'), "WARNING: More than one snapshot exists with name \"",$remove_snapshot,"\"\n"; } if($nRefs == 0 ) { print LOG timeStamp('MDYHMS'), "WARNING: Snapshot \"",$remove_snapshot,"\" not found\n"; } } } # Find a snapshot with the name # This either returns: The reference to the snapshot # 0 if not found & 1 if it's a duplicate # Duplicacy check is required for rename, remove and revert operations # For these operation specified snapshot name must be unique # ================================================== sub find_snapshot_name { my ($tree, $name) = @_; my $ref = undef; my $count = 0; foreach my $node (@$tree) { if ($node->name eq $name) { $ref = $node; $count++; } my ($subRef, $subCount) = find_snapshot_name($node->childSnapshotList, $name); $count = $count + $subCount; $ref = $subRef if ($subCount); } return ($ref, $count); } sub timeStamp { my ($date_format) = @_; my %dttime = (); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $my_time; my $time_string; ### begin_: initialize DateTime number formats $dttime{year } = sprintf "%04d",($year + 1900); ## four digits to specify the year $dttime{mon } = sprintf "%02d",($mon + 1); ## zeropad months $dttime{mday } = sprintf "%02d",$mday; ## zeropad day of the month $dttime{wday } = sprintf "%02d",$wday + 1; ## zeropad day of week; sunday = 1; $dttime{yday } = sprintf "%02d",$yday; ## zeropad nth day of the year $dttime{hour } = sprintf "%02d",$hour; ## zeropad hour $dttime{min } = sprintf "%02d",$min; ## zeropad minutes $dttime{sec } = sprintf "%02d",$sec; ## zeropad seconds $dttime{isdst} = $isdst; if($date_format eq 'MDYHMS') { $my_time = "$dttime{mon}-$dttime{mday}-$dttime{year} $dttime{hour}:$dttime{min}:$dttime{sec}"; $time_string = "\t".$my_time." -- "; } elsif ($date_format eq 'YMD') { $my_time = "$dttime{year}-$dttime{mon}-$dttime{mday}"; $time_string = $my_time; } return $time_string; }