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 2011/04/25 17:30:51 UTC

svn commit: r1096498 - /incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm

Author: arkurth
Date: Mon Apr 25 15:30:50 2011
New Revision: 1096498

URL: http://svn.apache.org/viewvc?rev=1096498&view=rev
Log:
VCL-450
Added default minimum memory sizes for each OS/architecture which requires more than 512 MB of RAM in the %VM_OS_CONFIGURATION hash.  Updated get_vm_ram to use this value.

Added code to capture subroutine to save the vmx file used on the VM being captured along with the vmdk files. This vmx file can later be used as a reference. Added get_reference_vmx_file_name, get_reference_vmx_file_path, and get_reference_vmx_info subroutines.

Updated copy_vmdk to also copy the reference vmx file when copying a vmdk.

Updated get_vmdk_parameter_value to allow it to retrieve a parameter from the vmdk file if it resides on the VM host. It previously only checked the files on the management node.

Updated get_vm_disk_adapter_type and get_vm_virtual_hardware_version to try to first retrieve the type from the reference vmx file. This should allow VMs using the LSI SAS adapter type to work correctly.

Modified:
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm?rev=1096498&r1=1096497&r2=1096498&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm Mon Apr 25 15:30:50 2011
@@ -87,17 +87,17 @@ our %VM_OS_CONFIGURATION = (
 		"scsi-virtualDev" => "lsiLogic",
 	},
 	# Windows configurations:
-	"xp-x86" => {
+	"winxp-x86" => {
 		"guestOS" => "winXPPro",
 		"ethernet-virtualDev" => "vlance",
 		"scsi-virtualDev" => "busLogic",
 	},
-	"xp-x86_64" => {
+	"winxp-x86_64" => {
 		"guestOS" => "winXPPro-64",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
 	},
-	"vista-x86" => {
+	"winvista-x86" => {
 		"guestOS" => "winvista",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
@@ -109,34 +109,35 @@ our %VM_OS_CONFIGURATION = (
 		"scsi-virtualDev" => "lsiLogic",
 		"memsize" => 1024,
 	}, 
-	"7-x86" => {
+	"win7-x86" => {
 		"guestOS" => "windows7",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
 		"memsize" => 1024,
 	},
-	"7-x86_64" => {
+	"win7-x86_64" => {
 		"guestOS" => "windows7-64",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
 		"memsize" => 2048,
 	}, 
-	"2003-x86" => {
+	"win2003-x86" => {
 		"guestOS" => "winNetEnterprise",
 		"ethernet-virtualDev" => "vlance",
 		"scsi-virtualDev" => "lsiLogic",
 	},
-	"2003-x86_64" => {
+	"win2003-x86_64" => {
 		"guestOS" => "winNetEnterprise-64",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
+		"memsize" => 1024,
 	},
-	"2008-x86" => {
+	"win2008-x86" => {
 		"guestOS" => "winServer2008Enterprise-32",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
 	},
-	"2008-x86_64" => {
+	"win2008-x86_64" => {
 		"guestOS" => "winServer2008Enterprise-64",
 		"ethernet-virtualDev" => "e1000",
 		"scsi-virtualDev" => "lsiLogic",
@@ -469,7 +470,7 @@ sub capture {
 		notify($ERRORS{'WARNING'}, 0, "failed to determine the vmx file path actively being used by VM $computer_name");
 		return;
 	}
-
+	
 	# Set the vmx file path in this object so that it overrides the default value that would normally be constructed
 	if (!$self->set_vmx_file_path($vmx_file_path_original)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to set the vmx file to the path that was determined to be in use by VM $computer_name being captured: $vmx_file_path_original");
@@ -535,6 +536,11 @@ sub capture {
 	my $vmdk_directory_path_renamed = "$vmdk_base_directory_path/$image_name";
 	my $vmdk_file_path_renamed = "$vmdk_directory_path_renamed/$image_name.vmdk";
 	
+	# Construct the path of the reference vmx file to be saved with the vmdk
+	# The .vmx file is only saved so that it can be referenced later
+	my $reference_vmx_file_name = $self->get_reference_vmx_file_name();
+	my $vmx_file_path_renamed = "$vmdk_directory_path_renamed/$reference_vmx_file_name";
+	
 	# Make sure the vmdk file path for the captured image doesn't already exist
 	# Do this before calling pre_capture and shutting down the VM
 	if ($vmdk_file_path_original ne $vmdk_file_path_renamed && $self->vmhost_os->file_exists($vmdk_file_path_renamed)) {
@@ -585,6 +591,18 @@ sub capture {
 		}
 	}
 	
+	# Copy the vmx file to the new image directory for later reference
+	# First check if vmx file already exists (could happen if base image VM was manually created)
+	if ($vmx_file_path_original eq $vmx_file_path_renamed) {
+		notify($ERRORS{'DEBUG'}, 0, "vmx file will not be copied, vmx file path being captured is already named as the image being captured: '$vmx_file_path_original'");
+	}
+	else {
+		if (!$self->vmhost_os->copy_file($vmx_file_path_original, $vmx_file_path_renamed)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to copy the reference vmx file after the VM was powered off: '$vmx_file_path_original' --> '$vmx_file_path_renamed'");
+			return;
+		}
+	}
+	
 	# Copy the vmdk to the image repository if the repository path is defined in the VM profile
 	my $repository_directory_path = $self->get_repository_vmdk_directory_path();
 	if ($repository_directory_path) {
@@ -598,10 +616,18 @@ sub capture {
 			# Files can be copied directly to the image repository and converted while they are copied
 			my $repository_vmdk_file_path = $self->get_repository_vmdk_file_path();
 			if ($self->copy_vmdk($vmdk_file_path_renamed, $repository_vmdk_file_path, '2gbsparse')) {
-				$repository_copy_successful = 1;
+				
+				# Copy the reference vmx file of the VM being captured to the vmdk directory
+				my $repository_vmx_file_path = "$repository_directory_path/$image_name.vmx";
+				if ($self->vmhost_os->copy_file($vmx_file_path_renamed, $repository_vmx_file_path)) {
+					$repository_copy_successful = 1;
+				}
+				else {
+					notify($ERRORS{'WARNING'}, 0, "failed to copy the reference vmx file to the repository mounted on the VM host after the VM was powered off: '$vmx_file_path_renamed' --> '$repository_vmx_file_path'");
+				}
 			}
 			else {
-				notify($ERRORS{'WARNING'}, 0, "failed to copy the vmdk files after the VM was powered off: '$vmdk_file_path_renamed' --> '$repository_vmdk_file_path'");
+				notify($ERRORS{'WARNING'}, 0, "failed to copy the vmdk files to the repository mounted on the VM host after the VM was powered off: '$vmdk_file_path_renamed' --> '$repository_vmdk_file_path'");
 			}
 		}
 		else {
@@ -619,7 +645,7 @@ sub capture {
 			elsif ($virtual_disk_type =~ /sparse/) {
 				# Virtual disk is sparse, get a list of the vmdk file paths
 				notify($ERRORS{'DEBUG'}, 0, "vmdk can be copied directly from VM host $vmhost_hostname to the image repository because the virtual disk type is sparse: $virtual_disk_type");
-				@vmdk_copy_paths = $self->vmhost_os->find_files($vmdk_directory_path_original, '*.vmdk');
+				@vmdk_copy_paths = $self->vmhost_os->find_files($vmdk_directory_path_renamed, '*.vmdk');
 			}
 			else {
 				# Virtual disk is NOT sparse - a sparse copy must first be created before being copied to the repository
@@ -628,7 +654,7 @@ sub capture {
 				# Construct the vmdk file path where the 2gbsparse copy will be created
 				# The vmdk files are copied to a directory with the same name but with '_2gbsparse' appended to the directory name
 				# The vmdk files in the '_2gbsparse' are named the same as the original non-sparse directory
-				$vmdk_directory_path_sparse = "$vmdk_directory_path_original\_2gbsparse";
+				$vmdk_directory_path_sparse = "$vmdk_directory_path_renamed\_2gbsparse";
 				$vmdk_file_path_sparse = "$vmdk_directory_path_sparse/$image_name.vmdk";
 				
 				# Create a sparse copy of the virtual disk
@@ -643,6 +669,9 @@ sub capture {
 			
 			# Copy the vmdk directory from the VM host to the image repository
 			if (@vmdk_copy_paths) {
+				# Add the reference vmx file path to the array so that the vmx is copied to the repository
+				push @vmdk_copy_paths, $vmx_file_path_renamed;
+				
 				# Loop through the files, copy each to the management node's repository directory
 				notify($ERRORS{'DEBUG'}, 0, "vmdk files will be copied from VM host $vmhost_hostname to the image repository on the management node:\n" . join("\n", sort @vmdk_copy_paths));
 				VMDK_COPY_PATH: for my $vmdk_copy_path (@vmdk_copy_paths) {
@@ -1849,7 +1878,7 @@ sub prepare_vmdk {
 	
 	# Find the vmdk file paths in the image repository directory
 	my @vmdk_repository_file_paths;
-	my $command = "find \"$repository_vmdk_directory_path\" -type f -iname \"$image_name*.vmdk\"";
+	my $command = "find \"$repository_vmdk_directory_path\" -type f -iname \"$image_name*\"";
 	my ($exit_status, $output) = run_command($command, 1);
 	if (!defined($output)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to run command to find files in image repository directory: '$repository_vmdk_directory_path', pattern: '*.vmdk', command:\n$command");
@@ -1888,7 +1917,7 @@ sub prepare_vmdk {
 	
 	# Check if the vmdk disk type is compatible with the VMware product installed on the host
 	return 1 if $self->is_vmdk_compatible();
-
+	
 	# Disk type is not compatible with the VMware product installed on the host
 	# Attempt to make a copy - copy_vmdk should create a copy in a compatible format
 	# The destination copy is stored in a directory with the same name as the normal vmdk directory followed by a ~
@@ -2941,6 +2970,69 @@ sub set_vmx_file_path {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_reference_vmx_file_name
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns the name of the reference vmx file that was used when the
+               image was captured.
+
+=cut
+
+sub get_reference_vmx_file_name {
+	my $self = shift;
+	if (ref($self) !~ /vmware/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	return $ENV{reference_vmx_file_name} if defined($ENV{reference_vmx_file_name});
+	
+	my $image_name = $self->data->get_image_name() || return;
+	
+	$ENV{reference_vmx_file_name} = "$image_name.vmx.reference";
+	return $ENV{reference_vmx_file_name};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_reference_vmx_file_path
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns the path to the reference vmx file that was used when the
+               image was captured.
+
+=cut
+
+sub get_reference_vmx_file_path {
+	my $self = shift;
+	if (ref($self) !~ /vmware/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	return $ENV{reference_vmx_file_path} if defined($ENV{reference_vmx_file_path});
+	
+	my $vmdk_directory_path = $self->get_vmdk_directory_path();
+	if (!$vmdk_directory_path) {
+		notify($ERRORS{'WARNING'}, 0, "unable to construct reference vmx file path, vmdk directory path could not be determined");
+		return;
+	}
+	
+	my $reference_vmx_file_name = $self->get_reference_vmx_file_name();
+	if (!$reference_vmx_file_name) {
+		notify($ERRORS{'WARNING'}, 0, "unable to construct reference vmx file path, reference vmx file name could not be determined");
+		return;
+	}
+	
+	$ENV{reference_vmx_file_path} = "$vmdk_directory_path/$reference_vmx_file_name";
+	notify($ERRORS{'DEBUG'}, 0, "determined reference vmx file path: $ENV{reference_vmx_file_path}");
+	return $ENV{reference_vmx_file_path};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 get_vmdk_file_path
 
  Parameters  : none
@@ -3989,54 +4081,41 @@ sub get_vmdk_parameter_value {
 		return;
 	}
 	
-	my $vmdk_file_path = $self->get_vmdk_file_path() || return;
-	my $image_repository_vmdk_file_path = $self->get_repository_vmdk_file_path() || return;
+	my $vmhost_hostname = $self->data->get_vmhost_hostname();
 	
-	# Open the vmdk file for reading
-	if (open FILE, "<", $vmdk_file_path) {
-		notify($ERRORS{'DEBUG'}, 0, "attempting to locate $vmdk_parameter value in vmdk file: $vmdk_file_path");
-	}
-	elsif (open FILE, "<", $image_repository_vmdk_file_path) {
-		notify($ERRORS{'DEBUG'}, 0, "attempting to locate $vmdk_parameter value in vmdk file: $image_repository_vmdk_file_path");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "unable to open either vmdk file for reading: $vmdk_file_path, $image_repository_vmdk_file_path");
-		return;
-	}
+	# Try to get the file contents from the VM host
+	my $vmdk_file_path = $self->get_vmdk_file_path();
+	my @vmdk_file_lines = $self->vmhost_os->get_file_contents($vmdk_file_path);
 	
-	# Read the file line by line - do not read the file all at once
-	# The vmdk file may be very large depending on the type - it may not be split up into a descriptor file and extents
-	# If the vmdk file isn't split, the descriptor section will be at the beginning
-	my $line_count = 0;
-	my $value;
-	while ($line_count < 100) {
-		$line_count++;
-		my $line = <FILE>;
-		chomp $line;
+	if (!@vmdk_file_lines) {
+		my $head_command = "head -n 100 $vmdk_file_path";
+		
+		my $image_repository_vmdk_file_path = $self->get_repository_vmdk_file_path();
+		$head_command .= " $image_repository_vmdk_file_path" if $image_repository_vmdk_file_path;
 		
+		my ($head_exit_status, $head_output) = $self->mn_os->execute($head_command);
+		if (!defined($head_output)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to run command on management node while attempting to locate $vmdk_parameter value in vmdk file: '$head_command'");
+			return;
+		}
+		@vmdk_file_lines = @$head_output;
+	}
+	
+	for my $vmdk_file_line (@vmdk_file_lines) {
 		# Ignore comment lines
-		next if ($line =~ /^\s*#/);
+		next if ($vmdk_file_line =~ /^\s*#/);
 		
 		# Check if the line contains the parameter name
-		if ($line =~ /(^|\.)$vmdk_parameter[\s=]+/i) {
-			notify($ERRORS{'DEBUG'}, 0, "found line containing $vmdk_parameter: '$line'");
-			
-			# Extract the value from the line
-			($value) = $line =~ /\"(.+)\"/;
-			last;
+		if ($vmdk_file_line =~ /(?:^|\.)$vmdk_parameter[=\s\"]*([^\"]*)/ig) {
+			my $value = $1;
+			chomp $value;
+			notify($ERRORS{'DEBUG'}, 0, "found '$vmdk_parameter' value in vmdk file:\nline: '$vmdk_file_line'\nvalue: '$value'");
+			return $value;
 		}
 	}
 	
-	close FILE;
-	
-	if (defined($value)) {
-		notify($ERRORS{'DEBUG'}, 0, "found $vmdk_parameter value in vmdk file: '$value'");
-		return $value;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "did not find $vmdk_parameter value in vmdk file");
-		return;
-	}
+	notify($ERRORS{'WARNING'}, 0, "did not find '$vmdk_parameter' value in vmdk file");
+	return;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -4062,31 +4141,44 @@ sub get_vm_disk_adapter_type {
 	
 	my $vmdk_controller_type;
 	
-	if ($self->api->can("get_virtual_disk_controller_type") && ($vmdk_controller_type = $self->api->get_virtual_disk_controller_type($self->get_vmdk_file_path()))) {
-		notify($ERRORS{'DEBUG'}, 0, "retrieved VM disk adapter type from api object: $vmdk_controller_type");
-	}
-	elsif ($vmdk_controller_type = $self->get_vmdk_parameter_value('adapterType')) {
-		notify($ERRORS{'DEBUG'}, 0, "retrieved VM disk adapter type from vmdk file: $vmdk_controller_type");
+	# Attempt to retrieve the type from the reference vmx file for the image
+	my $reference_vmx_file_info = $self->get_reference_vmx_info();
+	if ($reference_vmx_file_info) {
+		for my $vmx_key (keys %$reference_vmx_file_info) {
+			if ($vmx_key =~ /scsi\d+\.virtualdev/i) {
+				$vmdk_controller_type = $reference_vmx_file_info->{$vmx_key};
+				notify($ERRORS{'DEBUG'}, 0, "retrieved VM disk adapter type from reference vmx file: $vmdk_controller_type");
+				return $vmdk_controller_type;
+			}
+		}
+		notify($ERRORS{'DEBUG'}, 0, "unable to retrieve VM disk adapter type from reference vmx file, 'scsi*.virtualDev' key does not exist");
 	}
 	
-	if (!$vmdk_controller_type) {
-		my $vm_os_configuration = $self->get_vm_os_configuration();
-		if (!$vm_os_configuration) {
-			notify($ERRORS{'WARNING'}, 0, "unable to determine VM disk adapter type because unable to retrieve default VM OS configuration");
-			return;
+	# Try to get the type from the API module's get_virtual_disk_controller_type subroutine
+	if ($self->api->can("get_virtual_disk_controller_type")) {
+		if ($vmdk_controller_type = $self->api->get_virtual_disk_controller_type($self->get_vmdk_file_path())) {
+			notify($ERRORS{'DEBUG'}, 0, "retrieved VM disk adapter type from api object: $vmdk_controller_type");
+			return $vmdk_controller_type;
 		}
-		
-		$vmdk_controller_type = $vm_os_configuration->{"scsi-virtualDev"};
-		notify($ERRORS{'DEBUG'}, 0, "retrieved default VM disk adapter type for VM OS: $vmdk_controller_type");
 	}
 	
-	if ($vmdk_controller_type) {
+	# Try to retrieve the adapter type by reading the vmdk descriptor
+	if ($vmdk_controller_type = $self->get_vmdk_parameter_value('adapterType')) {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved VM disk adapter type from vmdk file: $vmdk_controller_type");
+		return $vmdk_controller_type;
+	}
+	
+	# Try to retrieve the default adapter type for the image OS
+	my $vm_os_configuration = $self->get_vm_os_configuration();
+	if ($vm_os_configuration && ($vmdk_controller_type = $vm_os_configuration->{"scsi-virtualDev"})) {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved default VM disk adapter type for VM OS: $vmdk_controller_type");
 		return $vmdk_controller_type;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "unable to determine VM disk adapter type");
+		notify($ERRORS{'WARNING'}, 0, "unable to determine VM disk adapter type from default VM OS configuration");
 		return;
 	}
+	
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -4107,6 +4199,21 @@ sub get_vm_virtual_hardware_version {
 	}
 	
 	my $hardware_version;
+	
+	# Attempt to retrieve the type from the reference vmx file for the image
+	my $reference_vmx_file_info = $self->get_reference_vmx_info();
+	if ($reference_vmx_file_info) {
+		for my $vmx_key (keys %$reference_vmx_file_info) {
+			if ($vmx_key =~ /virtualHW\.version/i) {
+				$hardware_version = $reference_vmx_file_info->{$vmx_key};
+				notify($ERRORS{'DEBUG'}, 0, "retrieved VM virtual hardware version from reference vmx file: $hardware_version");
+				return $hardware_version;
+			}
+		}
+		notify($ERRORS{'DEBUG'}, 0, "unable to retrieve VM virtual hardware version from reference vmx file, 'virtualHW.version' key does not exist");
+	}
+	
+	
 	if ($self->api->can("get_virtual_disk_hardware_version")) {
 		$hardware_version = $self->api->get_virtual_disk_hardware_version($self->get_vmdk_file_path());
 		notify($ERRORS{'DEBUG'}, 0, "retrieved hardware version from api object: $hardware_version");
@@ -4165,8 +4272,8 @@ sub get_vm_virtual_hardware_version {
  Returns     : hash
  Description : Returns the information stored in %VM_OS_CONFIGURATION for
                the guest OS. The guest OS type, OS name, and archictecture are
-               used to determine the appropriate guestOS and ethernet-virtualDev
-               values to be used in the vmx file.
+               used to determine some of the appropriate values to be used in
+               the vmx file.
 
 =cut
 
@@ -4180,40 +4287,52 @@ sub get_vm_os_configuration {
 	# Return previously retrieved data if it exists
 	return $self->{vm_os_configuration} if $self->{vm_os_configuration};
 	
-	my $image_os_type = $self->data->get_image_os_type() || return;
 	my $image_os_name = $self->data->get_image_os_name() || return;
+	my $image_os_type = $self->data->get_image_os_type();
 	my $image_architecture = $self->data->get_image_architecture() || return;
 	
 	# Figure out the key name in the %VM_OS_CONFIGURATION hash for the guest OS
-	my $vm_os_configuration_key;
-	if ($image_os_type =~ /linux/i) {
-		$vm_os_configuration_key = "linux-$image_architecture";
-	}
-	elsif ($image_os_type =~ /windows/i) {
-		my $regex = 'xp|2003|2008|vista|7';
-		$image_os_name =~ /($regex)/i;
-		my $windows_product = $1;
-		if (!$windows_product) {
-			notify($ERRORS{'WARNING'}, 0, "unsupported Windows product: $image_os_name, it does not contain ($regex), using default values for Windows");
-			$windows_product = 'windows';
+	for my $vm_os_configuration_key (keys(%VM_OS_CONFIGURATION)) {
+		my ($os_product_name, $os_architecture) = $vm_os_configuration_key =~ /(.+)-(.+)/;
+		if (!$os_product_name || !$os_architecture) {
+			notify($ERRORS{'WARNING'}, 0, "failed to parse VM OS configuration key: $vm_os_configuration_key, format should be <OS product name>-<architecture>");
+			next;
+		}
+		elsif ($image_architecture ne $os_architecture) {
+			next;
+		}
+		elsif ($image_os_name !~ /$os_product_name/) {
+			next;
+		}
+		else {
+			$self->{vm_os_configuration} = $VM_OS_CONFIGURATION{$vm_os_configuration_key};
+			notify($ERRORS{'DEBUG'}, 0, "returning matching '$vm_os_configuration_key' OS configuration: $image_os_name, image architecture: $image_architecture\n" . format_data($self->{vm_os_configuration}));
 		}
-		$vm_os_configuration_key = "$windows_product-$image_architecture";
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "unsupported OS type: $image_os_type, using default values");
-		$vm_os_configuration_key = "default-$image_architecture";
 	}
 	
-	# Retrieve the information from the hash, set an object variable
-	$self->{vm_os_configuration} = $VM_OS_CONFIGURATION{$vm_os_configuration_key};
-	if ($self->{vm_os_configuration}) {
-		notify($ERRORS{'DEBUG'}, 0, "retrieved default VM configuration for OS: $vm_os_configuration_key\n" . format_data($self->{vm_os_configuration}));
-		return $self->{vm_os_configuration};
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "failed to find default VM configuration for OS: $vm_os_configuration_key");
-		return;
+	if (!$self->{vm_os_configuration}) {
+		# Check if the default key exists for the OS type - 'windows', 'linux', etc.
+		if ($self->{vm_os_configuration} = $VM_OS_CONFIGURATION{"$image_os_type-$image_architecture"}) {
+			notify($ERRORS{'DEBUG'}, 0, "returning default '$image_os_type' OS configuration, architecture: $image_architecture\n" . format_data($self->{vm_os_configuration}));
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "default VM OS configuration key '$image_os_type-$image_architecture' does not exist for image OS type: $image_os_type, image architecture: $image_architecture");
+			
+			# Check if the default key exists for the image architecture
+			if ($self->{vm_os_configuration} = $VM_OS_CONFIGURATION{"default-$image_architecture"}) {
+				notify($ERRORS{'DEBUG'}, 0, "returning default OS configuration, architecture: $image_architecture\n" . format_data($self->{vm_os_configuration}));
+			}
+			else {
+				notify($ERRORS{'WARNING'}, 0, "default VM OS configuration key 'default-$image_architecture' does not exist for image architecture: $image_architecture");
+				
+				# Unable to locate closest matching key, return default x86 configuration
+				$self->{vm_os_configuration} = $VM_OS_CONFIGURATION{"default-x86"};
+				notify($ERRORS{'DEBUG'}, 0, "returning default x86 OS configuration\n" . format_data($self->{vm_os_configuration}));
+			}
+		}
 	}
+	
+	return $self->{vm_os_configuration};
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -4285,8 +4404,6 @@ sub get_vm_ram {
 		return;
 	}
 	
-	my $minimum_vm_ram_mb = 512;
-	
 	# Get the image minram setting
 	my $image_minram_mb = $self->data->get_image_minram();
 	if (!defined($image_minram_mb)) {
@@ -4298,16 +4415,21 @@ sub get_vm_ram {
 	if ($image_minram_mb % 4) {
 		my $image_minram_mb_original = $image_minram_mb;
 		$image_minram_mb -= ($image_minram_mb % 4);
-		notify($ERRORS{'DEBUG'}, 0, "image minram value is not a multiple of 4: $image_minram_mb_original, adjusting to $image_minram_mb");
+		notify($ERRORS{'DEBUG'}, 0, "image minimum RAM value ($image_minram_mb_original MB) is not a multiple of 4, adjusting to $image_minram_mb MB");
 	}
 	
 	# Check if the image setting is too low
-	if ($image_minram_mb < $minimum_vm_ram_mb) {
-		notify($ERRORS{'DEBUG'}, 0, "image ram setting is too low: $image_minram_mb MB, $minimum_vm_ram_mb MB will be used");
-		return $minimum_vm_ram_mb;
+	# Get the minimum memory size for the OS
+	my $vm_os_configuration = $self->get_vm_os_configuration();
+	my $vm_guest_os = $vm_os_configuration->{guestOS} || 'unknown';
+	my $vm_os_memsize = $vm_os_configuration->{memsize} || 512;
+	if ($image_minram_mb < $vm_os_memsize) {
+		notify($ERRORS{'DEBUG'}, 0, "image minimum RAM value ($image_minram_mb MB) is too low for the $vm_guest_os guest OS, adjusting to $vm_os_memsize MB");
+		return $vm_os_memsize;
+	}
+	else {
+		return $image_minram_mb;
 	}
-	
-	return $image_minram_mb;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -4507,6 +4629,58 @@ sub get_vmx_info {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_reference_vmx_info
+
+ Parameters  : none
+ Returns     : hash reference
+ Description : Checks if the reference vmx file exists for the image and returns
+               a hash reference containing the data contained in the file. This
+               data is the configuration used when the image was captured.
+
+=cut
+
+sub get_reference_vmx_info {
+	my $self = shift;
+	if (ref($self) !~ /module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Check if the reference vmx info has already been retrieved
+	if ($self->{reference_vmx_info}) {
+		return $self->{reference_vmx_info};
+	}
+	
+	# Check if it was already determined that the reference vmx file doesn't exist
+	# $self->{reference_vmx_info} is set to 0 if the file doesn't exist
+	if (defined($self->{reference_vmx_info}) && !$self->{reference_vmx_info}) {
+		return 0;
+	}
+	
+	# Get the reference vmx file path and check if the file exists
+	my $reference_vmx_file_path = $self->get_reference_vmx_file_path();
+	if (!$self->vmhost_os->file_exists($reference_vmx_file_path)) {
+		notify($ERRORS{'DEBUG'}, 0, "reference vmx file does not exist: $reference_vmx_file_path");
+		# Set $self->{reference_vmx_info} to 0 so this subroutine doesn't have to check if it exists again on subsequent calls
+		$self->{reference_vmx_info} = 0;
+		return 0;
+	}
+	
+	# Retrieve the info from the file
+	my $reference_vmx_info = $self->get_vmx_info($reference_vmx_file_path);
+	if ($reference_vmx_info) {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved reference vmx info from file: $reference_vmx_file_path");
+		$self->{reference_vmx_info} = $reference_vmx_info;
+		return $reference_vmx_info;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve reference vmx info from file: $reference_vmx_file_path");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 delete_vm
 
  Parameters  : $vmx_file_path
@@ -4751,9 +4925,19 @@ sub copy_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;
 	
+	my $source_directory_path = $self->_get_parent_directory_normal_path($source_vmdk_file_path) || return;
+	my $destination_directory_path = $self->_get_parent_directory_normal_path($destination_vmdk_file_path) || return;
+	
+	# Construct the source and destination reference vmx file paths
+	# The reference vmx file is copied to the vmdk directory if it exists
+	my $reference_vmx_file_name = $self->get_reference_vmx_file_name();
+	my $source_reference_vmx_file_path = "$source_directory_path/$reference_vmx_file_name";
+	my $destination_reference_vmx_file_path = "$destination_directory_path/$reference_vmx_file_name";
+	
 	# Set the default virtual disk type if the argument was not specified
 	if (!$virtual_disk_type) {
 		if ($vmhost_product_name =~ /esx/i) {
@@ -4782,16 +4966,9 @@ sub copy_vmdk {
 		return;
 	}
 	
-	# Get the destination parent directory path and create the directory
-	my ($destination_directory_path) = $destination_vmdk_file_path =~ /(.+)\/[^\/]+/;
-	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");
-		return;
-	}
-	
 	my $start_time = time;
 	my $copy_result = 0;
-	
+
 	# Attempt to use the API's copy_virtual_disk subroutine
 	if ($self->api->can('copy_virtual_disk')) {
 		if ($self->api->copy_virtual_disk($source_vmdk_file_path, $destination_vmdk_file_path, $virtual_disk_type)) {
@@ -4809,6 +4986,13 @@ sub copy_vmdk {
 		notify($ERRORS{'WARNING'}, 0, "failed to copy vmdk on VM host $vmhost_name, unable to copy using API's copy_virtual_disk subroutine and an 'execute' subroutine is not implemented by the VM host OS object");
 		return;
 	}
+	elsif (!$copy_result) {
+		# 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");
+			return;
+		}
+	}
 	
 	if (!$copy_result) {
 		# Try to use vmkfstools
@@ -4905,6 +5089,12 @@ sub copy_vmdk {
 	# Check if any of the methods was successful
 	if (!$copy_result) {
 		notify($ERRORS{'WARNING'}, 0, "failed to copy virtual disk on VM host $vmhost_name using any available methods:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
+		
+		# Delete the destination directory
+		if (!$self->vmhost_os->delete_file($destination_directory_path)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to delete destination directory after failing to copy virtual disk on VM host $vmhost_name: $destination_directory_path");
+		}
+		
 		return;
 	}
 	
@@ -4920,11 +5110,21 @@ sub copy_vmdk {
 		$seconds = "0$seconds";
 	}
 	
+	# Check if the reference vmx file exists in the source directory
+	# Copy it to the destination directory if it does exist
+	if ($self->vmhost_os->file_exists($source_reference_vmx_file_path)) {
+		notify($ERRORS{'DEBUG'}, 0, "copying reference vmx file to vmdk directory: '$source_reference_vmx_file_path' --> '$destination_reference_vmx_file_path'");
+		if (!$self->vmhost_os->copy_file($source_reference_vmx_file_path, $destination_reference_vmx_file_path)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to copy reference vmx file to vmdk directory: '$source_reference_vmx_file_path' --> '$destination_reference_vmx_file_path'");
+		}
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "reference vmx file not copied to vmdk directory because it does not exist: '$source_reference_vmx_file_path'");
+	}
+	
 	# Get the size of the copied vmdk files
 	my $search_path = $destination_vmdk_file_path;
-	$search_path =~ s/\.vmdk$//g;
-	$search_path .= '*.vmdk';
-	
+	$search_path =~ s/(\.vmdk)$/\*$1/i;
 	my $image_size_bytes = $self->vmhost_os->get_file_size($search_path);
 	if (!defined($image_size_bytes) || $image_size_bytes !~ /^\d+$/) {
 		notify($ERRORS{'WARNING'}, 0, "copied vmdk on VM host $vmhost_name but failed to retrieve destination file size:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");