You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2014/09/18 21:38:55 UTC
svn commit: r1626057 - in
/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware: VIM_SSH.pm
VMware.pm vSphere_SDK.pm
Author: arkurth
Date: Thu Sep 18 19:38:54 2014
New Revision: 1626057
URL: http://svn.apache.org/r1626057
Log:
VCL-685
Updated VIM_SSH.pm::_services_restart and _check_service_pid. Occasionally '1' was returned as the PID of a service and the code had been writing this to the .pid file. This caused problems. Added a check for a valid value. Added additional services to check.
Updated VMware.pm::is_vm_registered to accept datastore formatted paths.
Updated VMware.pm::delete_vm to remove the VM's parent directory if it was named after the VM.
Added VIM_SSH.pm::vm_suspend for a script I wrote to migrate VMs. This subroutine may be used at some point in the future.
Updated VMware.pm::get_datastore_info to strip away the leading 'ds://' in the vmdk's URL if it was present. This allows paths containing the URL to be used.
Updated vSphere_SDK.pm::copy_virtual_disk to delete the source VM it creates during a cloning operation if the clone fails.
VCL-724
Added VMware.pm::check_multiextent and vSphere_SDK.pm::is_multiextent_disabled. These are called by the copy/move_vmdk subroutines to detect if a 2gbsparse vmdk operation fails because multiextent is not enabled on the host.
Modified:
vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm?rev=1626057&r1=1626056&r2=1626057&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm Thu Sep 18 19:38:54 2014
@@ -166,7 +166,7 @@ sub _run_vim_cmd {
return;
}
- my $timeout_seconds = shift || 30;
+ my $timeout_seconds = shift || 60;
my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name();
@@ -255,11 +255,6 @@ sub _services_restart {
my $vmhost_computer_name = $self->vmhost_os->data->get_computer_short_name();
- # Check if the PID files for the following services are correct
- $self->_check_service_pid('hostd-worker', '/var/run/vmware/vmware-hostd.PID');
- $self->_check_service_pid('sfcb-vmware_bas', '/var/run/vmware/vicimprovider.PID');
-
- my $services_command = "services.sh restart";
my $semaphore = $self->get_semaphore("$vmhost_computer_name-vmware_services_restart", 0);
if (!$semaphore) {
notify($ERRORS{'OK'}, 0, "unable to obtain semaphore, another process is likely running '$services_command' on $vmhost_computer_name, sleeping for 30 seconds and then proceeding");
@@ -267,8 +262,26 @@ sub _services_restart {
return 1;
}
+ my $check_services = {
+ 'hostd-worker' => '/var/run/vmware/vmware-hostd.PID',
+ 'sfcb-vmware_bas' => '/var/run/vmware/vicimprovider.PID',
+ 'vmkdevmgr' => '/var/run/vmware/vmkdevmgr.pid',
+ 'vmkeventd' => '/var/run/vmware/vmkeventd.pid',
+ 'vmsyslogd' => '/var/run/vmware/vmsyslogd.pid',
+ 'rhttpproxy-work' => '/var/run/vmware/vmware-rhttpproxy.PID',
+ 'vpxa-worker' => '/var/run/vmware/vmware-vpxa.PID',
+ };
+
+ # Check if the PID files for the following services are correct
+ for my $service_name (keys %$check_services) {
+ my $pid_file_path = $check_services->{$service_name};
+ $self->_check_service_pid($service_name, $pid_file_path);
+ }
+
+ my $services_command = "services.sh restart";
+
notify($ERRORS{'DEBUG'}, 0, "restarting VMware services on $vmhost_computer_name");
- my ($services_exit_status, $services_output) = $self->vmhost_os->execute($services_command, 1, 90);
+ my ($services_exit_status, $services_output) = $self->vmhost_os->execute($services_command, 1, 120);
if (!defined($services_output)) {
notify($ERRORS{'WARNING'}, 0, "failed to run command on VM host $vmhost_computer_name: $services_command");
return;
@@ -322,11 +335,15 @@ sub _check_service_pid {
}
else {
($running_pid) = "@$ps_output" =~ /(\d+)/g;
- if ($running_pid) {
+ if (!$running_pid) {
+ notify($ERRORS{'DEBUG'}, 0, "parent $process_name PID is not running");
+ return;
+ }
+ elsif ($running_pid > 1) {
notify($ERRORS{'DEBUG'}, 0, "retrieved parent $process_name PID: $running_pid");
}
else {
- notify($ERRORS{'WARNING'}, 0, "unable to determine parent $process_name PID, command: '$ps_command', output:\n" . join("\n", @$ps_output));
+ notify($ERRORS{'WARNING'}, 0, "parent $process_name PID not valid: $running_pid, command: '$ps_command', output:\n" . join("\n", @$ps_output));
return;
}
}
@@ -428,7 +445,7 @@ sub _get_vm_list {
my $vmx_normal_path = $self->_get_normal_path($vmx_file_path);
if (!$vmx_normal_path) {
notify($ERRORS{'WARNING'}, 0, "unable to determine normal path: $vmx_file_path");
- return;
+ #return;
}
$vms{$vm_id} = $vmx_normal_path;
@@ -469,7 +486,7 @@ sub _get_vm_id {
}
for my $vm_id (keys %$vm_list) {
- return $vm_id if ($vmx_file_path eq $vm_list->{$vm_id});
+ return $vm_id if ($vm_list->{$vm_id} && $vmx_file_path eq $vm_list->{$vm_id});
}
notify($ERRORS{'WARNING'}, 0, "unable to determine VM ID, vmx file is not registered: $vmx_file_path, registered VMs:\n" . format_data($vm_list));
@@ -1207,7 +1224,7 @@ sub vm_power_off {
# Get the task ID
my @task_ids = $self->_get_task_ids($vmx_file_path, 'powerOff');
if (!@task_ids) {
- notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to power on the VM");
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to power off the VM");
return;
}
@@ -1224,6 +1241,84 @@ sub vm_power_off {
#/////////////////////////////////////////////////////////////////////////////
+=head2 vm_suspend
+
+ Parameters : $vmx_file_path
+ Returns : boolean
+ Description : Powers off the VM indicated by the vmx file path argument.
+
+=cut
+
+sub vm_suspend {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ # Get the vmx file path argument
+ my $vmx_file_path = shift;
+ if (!$vmx_file_path) {
+ notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not supplied");
+ return;
+ }
+
+ # Check if the VM is already powered off
+ my $vm_power_state = $self->get_vm_power_state($vmx_file_path);
+ if ($vm_power_state) {
+ if ($vm_power_state =~ /off/i) {
+ notify($ERRORS{'DEBUG'}, 0, "VM is already powered off: $vmx_file_path");
+ return 1;
+ }
+ elsif ($vm_power_state =~ /suspend/i) {
+ notify($ERRORS{'DEBUG'}, 0, "VM is already suspended: $vmx_file_path");
+ return 1;
+ }
+ }
+
+ # Get the VM ID
+ my $vm_id = $self->_get_vm_id($vmx_file_path);
+ if (!defined($vm_id)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to power off VM because VM ID could not be determined");
+ return;
+ }
+
+ my $vim_cmd_arguments = "vmsvc/power.suspend $vm_id";
+ my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 400);
+ return if !$output;
+
+ # Expected output if the VM was not previously suspended:
+ # Suspending VM:
+
+ # Expected output if the VM was previously suspended or powered off:
+ # Suspending VM:
+ # Suspend failed
+
+ if (!grep(/Suspending VM/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to suspend VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
+ return;
+ }
+
+ # Get the task ID
+ my @task_ids = $self->_get_task_ids($vmx_file_path, 'suspend');
+ if (!@task_ids) {
+ notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the task created to suspend the VM");
+ return;
+ }
+
+ # Wait for the task to complete
+ if ($self->_wait_for_task($task_ids[0])) {
+ notify($ERRORS{'OK'}, 0, "suspended VM: $vmx_file_path");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to suspend VM: $vmx_file_path, the vim power off task did not complete successfully, vim-cmd $vim_cmd_arguments output:\n" . join("\n", @$output));
+ return;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 vm_register
Parameters : $vmx_file_path
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm?rev=1626057&r1=1626056&r2=1626057&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm Thu Sep 18 19:38:54 2014
@@ -1317,7 +1317,7 @@ sub get_vmhost_api_object {
return 0;
}
notify($ERRORS{'DEBUG'}, 0, "loaded VMware control module: $api_perl_package");
-
+
# Create an API object to control the VM host and VMs
my $api;
eval { $api = ($api_perl_package)->new({data_structure => $self->data,
@@ -4153,12 +4153,12 @@ sub is_vm_registered {
notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not specified and default vmx file path could not be determined");
return;
}
- $vmx_file_path = normalize_file_path($vmx_file_path);
+ $vmx_file_path = $self->_get_normal_path($vmx_file_path);
my @registered_vmx_file_paths = $self->api->get_registered_vms();
for my $registered_vmx_file_path (@registered_vmx_file_paths) {
- $registered_vmx_file_path = normalize_file_path($registered_vmx_file_path);
- if ($vmx_file_path eq $registered_vmx_file_path) {
+ $registered_vmx_file_path = $self->_get_normal_path($registered_vmx_file_path);
+ if ($registered_vmx_file_path && $vmx_file_path eq $registered_vmx_file_path) {
notify($ERRORS{'DEBUG'}, 0, "VM is registered: $vmx_file_path");
return 1;
}
@@ -5305,7 +5305,11 @@ sub delete_vm {
# Get the vmx info
my $vmx_info = $self->get_vmx_info($vmx_file_path);
- if (!$vmx_info) {
+ my $vmx_directory_path;
+ if ($vmx_info) {
+ $vmx_directory_path = $vmx_info->{vmx_directory_path};
+ }
+ else {
# Failed to retrieve vmx info
# VM may have been registered but the vmx file/directory was deleted
# Check if the vmx file exists
@@ -5316,12 +5320,19 @@ sub delete_vm {
return;
}
- notify($ERRORS{'OK'}, 0, "deleted VM, successfully unregistered VM but it vmx directory was not deleted because the vmx file does not exist: $vmx_file_path");
- return 1;
+ # Check if the vmx parent directory is named after the vmx file
+ # If so, it's safe to delete the directory
+ my $vmx_file_base_name = $self->_get_file_base_name($vmx_file_path);
+ $vmx_directory_path = $self->_get_parent_directory_normal_path($vmx_file_path) || '';
+ if ($vmx_directory_path && $vmx_directory_path =~ /\/$vmx_file_base_name$/) {
+ notify($ERRORS{'OK'}, 0, "deleted VM, successfully unregistered VM, vmx file does not exist: $vmx_file_path, deleting vmx parent directory: $vmx_directory_path, directory name matches the vmx file base name: $vmx_file_base_name");
+ }
+ else {
+ notify($ERRORS{'OK'}, 0, "deleted VM, successfully unregistered VM but it vmx directory was not deleted because the vmx file does not exist: $vmx_file_path, vmx parent directory name ($vmx_directory_path) does not match the vmx file base name: $vmx_file_base_name");
+ return 1;
+ }
}
- my $vmx_directory_path = $vmx_info->{vmx_directory_path};
-
# Delete the vmx directory
my $attempt = 0;
my $attempt_limit = 5;
@@ -5356,7 +5367,11 @@ sub delete_vm {
}
# Check if the directory containing the vmdk is shared among different VMs or dedicated to the VM being deleted
- if ($self->is_vmdk_directory_shared($vmdk_directory_path)) {
+ my $vmdk_directory_shared = $self->is_vmdk_directory_shared($vmdk_directory_path);
+ if (!defined($vmdk_directory_shared)) {
+ notify($ERRORS{'DEBUG'}, 0, "vmdk directory will NOT be deleted, unable to determine if vmdk appears to be shared: $vmdk_directory_path");
+ }
+ elsif ($vmdk_directory_shared) {
# Directory is shared, entire directory can't be deleted
notify($ERRORS{'DEBUG'}, 0, "vmdk directory will NOT be deleted because the vmdk appears to be shared: $vmdk_directory_path");
@@ -5521,7 +5536,7 @@ sub copy_vmdk {
my $vmhost_product_name = $self->get_vmhost_product_name();
# Get the arguments
- my ($source_vmdk_file_path, $destination_vmdk_file_path, $virtual_disk_type) = @_;
+ my ($source_vmdk_file_path, $destination_vmdk_file_path, $destination_virtual_disk_type) = @_;
if (!$source_vmdk_file_path || !$destination_vmdk_file_path) {
notify($ERRORS{'WARNING'}, 0, "source and destination vmdk file path arguments were not specified");
return;
@@ -5546,12 +5561,12 @@ sub copy_vmdk {
my $destination_reference_vmx_file_path = "$destination_directory_path/$destination_reference_vmx_file_name";
# Set the default virtual disk type if the argument was not specified
- if (!$virtual_disk_type) {
+ if (!$destination_virtual_disk_type) {
if ($vmhost_product_name =~ /esx/i) {
- $virtual_disk_type = 'thin';
+ $destination_virtual_disk_type = 'thin';
}
else {
- $virtual_disk_type = '2gbsparse';
+ $destination_virtual_disk_type = '2gbsparse';
}
}
@@ -5577,7 +5592,7 @@ sub copy_vmdk {
my $end_time;
# Attempt to use the API's copy_virtual_disk subroutine
if ($self->api->can('copy_virtual_disk')) {
- my $copied_destination_vmdk_file_path = $self->api->copy_virtual_disk($source_vmdk_file_path, $destination_vmdk_file_path, $virtual_disk_type);
+ my $copied_destination_vmdk_file_path = $self->api->copy_virtual_disk($source_vmdk_file_path, $destination_vmdk_file_path, $destination_virtual_disk_type);
if ($copied_destination_vmdk_file_path) {
$end_time = time;
$copied_destination_vmdk_file_path = $self->_get_normal_path($copied_destination_vmdk_file_path);
@@ -5615,6 +5630,14 @@ sub copy_vmdk {
}
if (!$end_time) {
+ # If the source disk is 2gb sparse, make sure multiextent is loaded
+ my $source_virtual_disk_type = $self->api->get_virtual_disk_type($source_vmdk_file_path);
+ if ($source_virtual_disk_type =~ /sparse/i || $destination_virtual_disk_type =~ /sparse/) {
+ if (!$self->check_multiextent()) {
+ notify($ERRORS{'WARNING'}, 0, "copy will likely fail, multiextent kernel module is disabled on VM host $vmhost_name");
+ }
+ }
+
# Create the destination directory
if (!$self->vmhost_os->create_directory($destination_directory_path)) {
notify($ERRORS{'WARNING'}, 0, "unable to copy vmdk, destination directory could not be created on VM host $vmhost_name: $destination_directory_path");
@@ -5622,8 +5645,8 @@ sub copy_vmdk {
}
# Try to use vmkfstools
- my $command = "vmkfstools -i \"$source_vmdk_file_path\" \"$destination_vmdk_file_path\" -d $virtual_disk_type";
- notify($ERRORS{'DEBUG'}, 0, "attempting to copy virtual disk using vmkfstools, disk type: $virtual_disk_type:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
+ my $command = "vmkfstools -i \"$source_vmdk_file_path\" \"$destination_vmdk_file_path\" -d $destination_virtual_disk_type";
+ notify($ERRORS{'DEBUG'}, 0, "attempting to copy virtual disk using vmkfstools, disk type: $destination_virtual_disk_type:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
$start_time = time;
my ($exit_status, $output) = $self->vmhost_os->execute($command, 1, 7200);
@@ -5672,7 +5695,7 @@ sub copy_vmdk {
}
else {
$end_time = time;
- notify($ERRORS{'OK'}, 0, "copied virtual disk on VM host using vmkfstools, destination disk type: $virtual_disk_type:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
+ notify($ERRORS{'OK'}, 0, "copied virtual disk on VM host using vmkfstools, destination disk type: $destination_virtual_disk_type:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
}
}
@@ -5944,6 +5967,10 @@ sub move_vmdk {
return;
}
+ # Normalize the file paths
+ $source_vmdk_file_path = $self->_get_normal_path($source_vmdk_file_path) || return;
+ $destination_vmdk_file_path = $self->_get_normal_path($destination_vmdk_file_path) || return;
+
# Make sure the source vmdk file exists
if (!$self->vmhost_os->file_exists($source_vmdk_file_path)) {
notify($ERRORS{'WARNING'}, 0, "source vmdk file path does not exist: $source_vmdk_file_path");
@@ -5958,8 +5985,10 @@ sub move_vmdk {
notify($ERRORS{'DEBUG'}, 0, "attempting to move vmdk: '$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
+ my $source_vmdk_directory_path = $self->_get_parent_directory_normal_path($source_vmdk_file_path);
+
# Determine the destination vmdk directory path and create the directory
- my ($destination_vmdk_directory_path) = $destination_vmdk_file_path =~ /(.+)\/[^\/]+\.vmdk$/;
+ my $destination_vmdk_directory_path = $self->_get_parent_directory_normal_path($destination_vmdk_file_path);
if (!$destination_vmdk_directory_path) {
notify($ERRORS{'WARNING'}, 0, "unable to determine destination vmdk directory path from vmdk file path: $destination_vmdk_file_path");
return;
@@ -5976,6 +6005,12 @@ sub move_vmdk {
# Check if the VM host OS object implements an execute subroutine and attempt to run vmware-vdiskmanager
if ($self->vmhost_os->can("execute")) {
+ # If the source disk is 2gb sparse, make sure multiextent is loaded
+ my $source_virtual_disk_type = $self->api->get_virtual_disk_type($source_vmdk_file_path);
+ if ($source_virtual_disk_type =~ /sparse/i) {
+ $self->check_multiextent();
+ }
+
# Try vmware-vdiskmanager
notify($ERRORS{'OK'}, 0, "attempting to move vmdk file using vmware-vdiskmanager: $source_vmdk_file_path --> $destination_vmdk_file_path");
my $vdisk_command = "vmware-vdiskmanager -n \"$source_vmdk_file_path\" \"$destination_vmdk_file_path\"";
@@ -5984,6 +6019,15 @@ sub move_vmdk {
notify($ERRORS{'WARNING'}, 0, "failed to execute 'vmware-vdiskmanager' command on VM host to move vmdk file:\n$vdisk_command");
}
elsif (grep(/success/i, @$vdisk_output)) {
+ # Check if the source directory still exists and contains files
+ my @source_directory_files = $self->vmhost_os->find_files($source_vmdk_directory_path, '*');
+ if (@source_directory_files) {
+ notify($ERRORS{'DEBUG'}, 0, "source directory will not be deleted, it still contains files: $source_vmdk_directory_path\n" . join("\n", @source_directory_files));
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "source directory is empty, attempting to delete: $source_vmdk_directory_path");
+ $self->vmhost_os->delete_file($source_vmdk_directory_path);
+ }
notify($ERRORS{'OK'}, 0, "moved vmdk file by executing 'vmware-vdiskmanager' command on VM host:\ncommand: $vdisk_command\noutput: " . join("\n", @$vdisk_output));
return 1;
}
@@ -5994,7 +6038,6 @@ sub move_vmdk {
notify($ERRORS{'WARNING'}, 0, "failed to execute 'vmware-vdiskmanager' command on VM host to move vmdk file:\n$vdisk_command\noutput:\n" . join("\n", @$vdisk_output));
}
-
# Try vmkfstools
notify($ERRORS{'DEBUG'}, 0, "attempting to move vmdk file using vmkfstools: $source_vmdk_file_path --> $destination_vmdk_file_path");
my $vmkfs_command = "vmkfstools -E \"$source_vmdk_file_path\" \"$destination_vmdk_file_path\"";
@@ -6015,6 +6058,15 @@ sub move_vmdk {
notify($ERRORS{'WARNING'}, 0, "failed to move vmdk file using vmkfstools, destination file does not exist: '$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
}
else {
+ # Check if the source directory still exists and contains files
+ my @source_directory_files = $self->vmhost_os->find_files($source_vmdk_directory_path, '*');
+ if (@source_directory_files) {
+ notify($ERRORS{'DEBUG'}, 0, "source directory will not be deleted, it still contains files: $source_vmdk_directory_path\n" . join("\n", @source_directory_files));
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "source directory is empty, attempting to delete: $source_vmdk_directory_path");
+ $self->vmhost_os->delete_file($source_vmdk_directory_path);
+ }
notify($ERRORS{'OK'}, 0, "moved vmdk file using vmkfstools: '$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
return 1;
}
@@ -6026,13 +6078,6 @@ sub move_vmdk {
# Unable to move vmdk file using any VMware utilities or APIs
# Attempt to manually move the files
- # Determine the source vmdk directory path
- my ($source_vmdk_directory_path) = $source_vmdk_file_path =~ /(.+)\/[^\/]+\.vmdk$/;
- if (!$source_vmdk_directory_path) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine source vmdk directory path from vmdk file path: $source_vmdk_file_path");
- return;
- }
-
# Determine the source vmdk file name
my ($source_vmdk_file_name) = $source_vmdk_file_path =~ /\/([^\/]+\.vmdk)$/;
if (!$source_vmdk_file_name) {
@@ -6169,12 +6214,85 @@ sub move_vmdk {
return;
}
+ # Check if the source directory still exists and contains files
+ my @source_directory_files = $self->vmhost_os->find_files($source_vmdk_directory_path, '*');
+ if (@source_directory_files) {
+ notify($ERRORS{'DEBUG'}, 0, "source directory will not be deleted, it still contains files: $source_vmdk_directory_path\n" . join("\n", @source_directory_files));
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "source directory is empty, attempting to delete: $source_vmdk_directory_path");
+ $self->vmhost_os->delete_file($source_vmdk_directory_path);
+ }
+
notify($ERRORS{'OK'}, 0, "moved vmdk file: '$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
return 1;
}
#/////////////////////////////////////////////////////////////////////////////
+=head2 check_multiextent
+
+ Parameters : none
+ Returns : boolean
+ Description : Checks if the multiextent kernel module is loaded on the VM
+ host. This is required to operate on 2GB sparse vmdk files. If
+ not loaded, an attempt is made to load it.
+
+=cut
+
+sub check_multiextent {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my $vmhost_hostname = $self->data->get_vmhost_hostname();
+
+ my $list_command = 'vmkload_mod -l | grep multiextent';
+ my ($list_exit_status, $list_output) = $self->vmhost_os->execute($list_command);
+ if (!defined($list_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to determine if multiextent kernel module is loaded on $vmhost_hostname");
+ return;
+ }
+ elsif (grep(/^multiextent/, @$list_output)) {
+ notify($ERRORS{'DEBUG'}, 0, "multiextent kernel module is loaded on $vmhost_hostname");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "multiextent kernel module is not loaded on $vmhost_hostname, attempting to load it");
+ }
+
+ my $load_command = 'vmkload_mod multiextent';
+ my ($load_exit_status, $load_output) = $self->vmhost_os->execute($load_command);
+ if (!defined($load_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to load multiextent kernel module on $vmhost_hostname");
+ }
+ elsif (grep(/loaded successfully/, @$load_output)) {
+ notify($ERRORS{'DEBUG'}, 0, "loaded multiextent kernel module on $vmhost_hostname");
+ return 1;
+ }
+ elsif (grep(/already loaded/, @$load_output)) {
+ notify($ERRORS{'DEBUG'}, 0, "multiextent kernel module already loaded on $vmhost_hostname");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to load multiextent kernel module on $vmhost_hostname, exit status: $load_exit_status, output:\n" . join("\n", @$load_output));
+ }
+
+ notify($ERRORS{'CRITICAL'}, 0, "multiextent kernel module is disabled on VM host $vmhost_hostname, operations on 2GB sparse virtual disk files will fail\n" .
+ '*' x 100 . "\n" .
+ "DO THE FOLLOWING TO FIX THIS PROBLEM:\n" .
+ "Enable the module by running the following command on each VMware host: 'vmkload_mod -u multiextent'\n" .
+ "Add a line containing 'vmkload_mod -u multiextent' to /etc/rc.local.d/local.sh on each ESXi host\n" .
+ '*' x 100
+ );
+
+ return 0;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 power_on
Parameters : $vmx_file_path (optional)
@@ -6499,11 +6617,18 @@ sub get_datastore_info {
notify($ERRORS{'WARNING'}, 0, "failed to retrieve datastore info from " . ref($self->api) . " API object");
return;
}
- else {
- notify($ERRORS{'DEBUG'}, 0, "retrieved datastore info from VM host: " . join(", ", sort keys %$datastore_info));
- $self->{datastore_info} = $datastore_info;
- return $datastore_info;
+
+ for my $datastore_name (keys %$datastore_info) {
+ # URL may be in the format: 'ds:///vmfs/volumes/51938b70-d1df1a73-459a-3640b58306bb/'
+ # Remove the ds:// from the beginning
+ if ($datastore_info->{$datastore_name}{url}) {
+ $datastore_info->{$datastore_name}{url} =~ s/^.+\/vmfs/\/vmfs/;
+ }
}
+
+ notify($ERRORS{'DEBUG'}, 0, "retrieved datastore info from VM host: " . join(", ", sort keys %$datastore_info));
+ $self->{datastore_info} = $datastore_info;
+ return $datastore_info;
}
#/////////////////////////////////////////////////////////////////////////////
@@ -6798,7 +6923,7 @@ sub _get_datastore_name {
# Get the datastore information
my $datastore_info = $self->get_datastore_info() || return;
my @datastore_normal_paths;
-
+
# Loop through the datastores, check if the path begins with the datastore path
for my $datastore_name (keys(%{$datastore_info})) {
my $datastore_normal_path = $datastore_info->{$datastore_name}{normal_path};
@@ -6809,7 +6934,7 @@ sub _get_datastore_name {
$datastore_normal_path = normalize_file_path($datastore_normal_path);
my $datastore_url = $datastore_info->{$datastore_name}{url};
- $datastore_url = normalize_file_path($datastore_url);
+ $datastore_url = normalize_file_path($datastore_url) || '';
if ($path =~ /^($datastore_name|\[$datastore_name\]|$datastore_normal_path|$datastore_url)(\s|\/|$)/) {
return $datastore_name;
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm?rev=1626057&r1=1626056&r2=1626057&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm Thu Sep 18 19:38:54 2014
@@ -685,7 +685,11 @@ sub copy_virtual_disk {
# Delete the destination directory path previously created
$self->delete_file($destination_directory_path);
- if ($copy_virtual_disk_fault =~ /No space left/i) {
+ if ($source_disk_type =~ /sparse/i && $copy_virtual_disk_fault =~ /FileNotFound/ && $self->is_multiextent_disabled()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk on VM host $vmhost_name using CopyVirtualDisk function, likely because multiextent kernel module is disabled");
+ return;
+ }
+ elsif ($copy_virtual_disk_fault =~ /No space left/i) {
# Check if the output indicates there is not enough space to copy the vmdk
# Output will contain:
# Fault string: A general system error occurred: No space left on device
@@ -854,6 +858,7 @@ EOF
return;
}
+ my $source_vm_vmx_path = $source_vm_view->config->files->vmPathName;
# Create the specification for cloning the VM
my $clone_spec = VirtualMachineCloneSpec->new(
@@ -884,36 +889,54 @@ EOF
# Clone the temporary VM, thus creating a copy of its virtual disk
notify($ERRORS{'DEBUG'}, 0, "attempting to clone VM: $source_vm_name --> $clone_vm_name\nclone VM directory path: '$clone_vm_directory_path'");
+ my $clone_vm;
my $clone_vm_view;
eval {
- my $clone_vm = $source_vm_view->CloneVM(
+ $clone_vm = $source_vm_view->CloneVM(
folder => $vm_folder_view,
name => $clone_vm_name,
spec => $clone_spec
);
- if ($clone_vm) {
- $clone_vm_view = Vim::get_view(mo_ref => $clone_vm);
- notify($ERRORS{'DEBUG'}, 0, "cloned VM: $source_vm_name --> $clone_vm_name");
+ };
+
+ if ($clone_vm) {
+ $clone_vm_view = Vim::get_view(mo_ref => $clone_vm);
+ notify($ERRORS{'DEBUG'}, 0, "cloned VM: $source_vm_name --> $clone_vm_name");
+ }
+ else {
+ if (my $fault = $@) {
+ if ($source_disk_type =~ /sparse/i && $fault =~ /FileNotFound/ && $self->is_multiextent_disabled()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to clone VM on VM host $vmhost_name, likely because multiextent kernel module is disabled");
+ }
+ elsif ($fault =~ /No space left/i) {
+ # Check if the output indicates there is not enough space to copy the vmdk
+ # Output will contain:
+ # Fault string: A general system error occurred: No space left on device
+ # Fault detail: SystemError
+ notify($ERRORS{'CRITICAL'}, 0, "failed to clone VM on VM host $vmhost_name, no space is left on the destination device: '$destination_path'\nerror:\n$fault");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to clone VM on VM host $vmhost_name: '$source_path' --> '$destination_path'\nerror:\n$fault");
+ }
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to clone VM: $source_vm_name --> $clone_vm_name");
- return;
}
- };
- if (my $fault = $@) {
- if ($fault =~ /No space left/i) {
- # Check if the output indicates there is not enough space to copy the vmdk
- # Output will contain:
- # Fault string: A general system error occurred: No space left on device
- # Fault detail: SystemError
- notify($ERRORS{'CRITICAL'}, 0, "failed to copy vmdk on VM host $vmhost_name, no space is left on the destination device: '$destination_path'\nerror:\n$fault");
- return;
+
+ # Delete the source VM which could not be cloned
+ if (!$source_vm_vmx_path) {
+ notify($ERRORS{'WARNING'}, 0, "source VM not deleted, unable to determine vmx file path");
+ }
+ elsif ($source_vm_vmx_path !~ /\.vmx$/i) {
+ notify($ERRORS{'WARNING'}, 0, "source VM not deleted, vmPathName does not end with '.vmx': $source_vm_vmx_path");
}
else {
- notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk on VM host $vmhost_name: '$source_path' --> '$destination_path'\nerror:\n$fault");
+ $self->delete_vm($source_vm_vmx_path);
}
+
return;
}
+
notify($ERRORS{'DEBUG'}, 0, "deleting source VM: $source_vm_name");
$self->vm_unregister($source_vm_view);
@@ -1029,9 +1052,13 @@ sub move_virtual_disk {
if (my $fault = $@) {
# Get the source file info
my $source_file_info = $self->_get_file_info($source_path)->{$source_path};
+ my $source_disk_type = $source_file_info->{diskType};
# A FileNotFound fault will be generated if the source vmdk file exists but there is a problem with it
- if ($fault->isa('SoapFault') && ref($fault->detail) eq 'FileNotFound' && defined($source_file_info->{type}) && $source_file_info->{type} !~ /vmdisk/i) {
+ if ($source_disk_type =~ /sparse/i && $fault =~ /FileNotFound/ && $self->is_multiextent_disabled()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to move $source_disk_type virtual disk on VM host $vmhost_name, likely because multiextent kernel module is disabled");
+ }
+ elsif ($fault->isa('SoapFault') && ref($fault->detail) eq 'FileNotFound' && defined($source_file_info->{type}) && $source_file_info->{type} !~ /vmdisk/i) {
notify($ERRORS{'WARNING'}, 0, "failed to move virtual disk on VM host $vmhost_name, source file is either not a virtual disk file or there is a problem with its configuration, check the 'Extent description' section of the vmdk file: '$source_path'\nsource file info:\n" . format_data($source_file_info));
}
elsif ($fault =~ /No space left/i) {
@@ -1950,7 +1977,13 @@ sub file_exists {
}
# Get and check the file path argument
- my $file_path = $self->_get_datastore_path(shift) || return;
+ my $file_path_argument = shift;
+
+ my $file_path = $self->_get_datastore_path($file_path_argument);
+ if (!$file_path) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine if file exists: $file_path_argument, datastore path could not be determined");
+ return;
+ }
# Check if the path argument is the root of a datastore
if ($file_path =~ /^\[(.+)\]$/) {
@@ -2673,6 +2706,30 @@ sub _get_cluster_view {
#/////////////////////////////////////////////////////////////////////////////
+=head2 _get_host_system_views
+
+ Parameters : none
+ Returns : array of vSphere SDK HostSystem view object
+ Description : Retrieves an array of vSphere SDK HostSystem view objects. There
+ may be multiple if vCenter is used.
+
+=cut
+
+sub _get_host_system_views {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ return @{$self->{host_system_views}} if $self->{host_system_views};
+ my @host_system_views = @{Vim::find_entity_views(view_type => 'HostSystem')};
+ $self->{host_system_views} = \@host_system_views;
+ return @host_system_views;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 _get_host_system_view
Parameters :
@@ -2698,7 +2755,7 @@ sub _get_host_system_view {
my $vmhost_name = $self->data->get_vmhost_short_name();
- my @host_system_views = @{Vim::find_entity_views(view_type => 'HostSystem')};
+ my @host_system_views = $self->_get_host_system_views();
if (!scalar(@host_system_views)) {
notify($ERRORS{'WARNING'}, 0, "failed to retrieve HostSystem views");
return;
@@ -3749,6 +3806,77 @@ sub add_ethernet_adapter {
#/////////////////////////////////////////////////////////////////////////////
+=head2 is_multiextent_disabled
+
+ Parameters : none
+ Returns : boolean
+ Description : Checks if the multiextent kernel module is loaded on all hosts.
+ This is required to operate on 2GB sparse vmdk files.
+
+=cut
+
+sub is_multiextent_disabled {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+ return;
+ }
+
+ my @host_system_views = $self->_get_host_system_views();
+ if (!scalar(@host_system_views)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve HostSystem views");
+ return;
+ }
+
+ my $multiextent_disabled = 0;
+ my $multiextent_info = {};
+ HOST: for my $host_system_view (@host_system_views) {
+ my $host_system_name = $host_system_view->{name};
+
+ my $kernel_module_system = Vim::get_view(mo_ref => $host_system_view->configManager->kernelModuleSystem);
+ if (!$kernel_module_system) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine if multiextent kernel module is enabled on $host_system_name, kernelModuleSystem could not be retrieved");
+ $multiextent_info->{$host_system_name} = 'unknown';
+ next;
+ }
+
+ my $kernel_modules = $kernel_module_system->QueryModules;
+ if (!$kernel_modules) {
+ notify($ERRORS{'WARNING'}, 0, "unable to determine if multiextent kernel module is enabled on $host_system_name, kernelModuleSystem failed to query modules");
+ $multiextent_info->{$host_system_name} = 'unknown';
+ next;
+ }
+
+ for my $kernel_module (@$kernel_modules) {
+ my $kernel_module_name = $kernel_module->name;
+ if ($kernel_module_name eq 'multiextent') {
+ $multiextent_info->{$host_system_name} = 'loaded';
+ next HOST;
+ }
+ }
+
+ $multiextent_info->{$host_system_name} = 'not loaded';
+ $multiextent_disabled = 1;
+ }
+
+ if ($multiextent_disabled) {
+ notify($ERRORS{'CRITICAL'}, 0, "multiextent kernel module is disabled on ESXi hosts, operations on sparse virtual disk files will continue to fail:\n" . format_data($multiextent_info) . "\n" .
+ '*' x 100 . "\n" .
+ "DO THE FOLLOWING TO FIX THIS PROBLEM:\n" .
+ "Enable the module by running the following command on each ESXi host: 'vmkload_mod -u multiextent'\n" .
+ "Add a line containing 'vmkload_mod -u multiextent' to /etc/rc.local.d/local.sh on each ESXi host\n" .
+ '*' x 100
+ );
+ return 1;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "multiextent kernel module is not disabled:\n" . format_data($multiextent_info));
+ return 0;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
1;
__END__