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 2015/04/22 21:59:25 UTC

svn commit: r1675483 [1/3] - in /vcl/trunk/managementnode/lib/VCL: DataStructure.pm Module.pm Module/Provisioning/VMware/VIM_SSH.pm Module/Provisioning/VMware/VMware.pm utils.pm

Author: arkurth
Date: Wed Apr 22 19:59:24 2015
New Revision: 1675483

URL: http://svn.apache.org/r1675483
Log:
VCL-844
Added subroutines to VMware.pm:
* get_vmx_url_base_directory_path
* get_vmx_intermediate_directory_path
* _get_datastore_url
* _get_url_path
* configure_vmhost_ssh_keys
* copy_vmhost_ssh_public_key_to_another_host
* copy_file_to_another_host
* setup_migrate_vm
* migrate_vm
* migrate_revert_source

Added subroutines to utils.pm:
* update_computer_vmhost_id.
* setup_get_hash_multiple_choice.

Updated Module.pm::new to call initialize on ManagementNode.pm objects even if $SETUP_MODE is true.

Updated Module.pm::create_object to accept an additional argument which allows arguments to be passed to the constructor of the object being created as well as the DataStructure.pm object.

Updated Module.pm::create_vmhost_os_object to accept an argument specifying which VM host to create an object for.

Updated DataStructure.pm to accept a vmhost_identifier argument rather than use a vmhost_id argument.

Changed where cached data is stored from %ENV to $self in order to allow it to work with multiple VMware.pm objects.


VCL-851
Added check to skip the attempt to use vSphere_SDK.pm if vmprofile.password is not configured.

Modified:
    vcl/trunk/managementnode/lib/VCL/DataStructure.pm
    vcl/trunk/managementnode/lib/VCL/Module.pm
    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/utils.pm

Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1675483&r1=1675482&r2=1675483&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Wed Apr 22 19:59:24 2015
@@ -579,14 +579,14 @@ my @blockrequest_data : Field : Arg('Nam
 
 my @computer_identifier : Field : Arg('Name' => 'computer_identifier') : Type(scalar) : Get('Name' => 'computer_identifier', 'Private' => 1);
 
-=head3 @vmhost_id
+=head3 @vmhost_identifier
 
  Data type   : array of scalars
  Description :
 
 =cut
 
-my @vmhost_id : Field : Arg('Name' => 'vmhost_id') : Type(scalar) : Get('Name' => 'vmhost_id', 'Private' => 1);
+my @vmhost_identifier : Field : Arg('Name' => 'vmhost_identifier') : Type(scalar) : Get('Name' => 'vmhost_identifier', 'Private' => 1);
 
 =head3 @image_identifier
 
@@ -658,7 +658,7 @@ sub _initialize : Init {
 	}
 
 	my $computer_identifier = $self->computer_identifier;
-	my $vmhost_id = $self->vmhost_id;
+	my $vmhost_identifier = $self->vmhost_identifier;
 	my $image_identifier = $self->image_identifier;
 	my $imagerevision_identifier = $self->imagerevision_identifier;
 	
@@ -677,15 +677,15 @@ sub _initialize : Init {
 		$self->request_data->{reservation}{$self->reservation_id}{computer} = $computer_info;
 	}
 	
-	# Get the VM host info if the vmhost_id argument was specified and add it to this object
-	if ($vmhost_id) {
-		notify($ERRORS{'DEBUG'}, 0, "VM host identifier argument was specified, retrieving data for VM host: $vmhost_id");
-		my $vmhost_info = get_vmhost_info($vmhost_id, 1);
+	# Get the VM host info if the $vmhost_identifier argument was specified and add it to this object
+	if ($vmhost_identifier) {
+		notify($ERRORS{'DEBUG'}, 0, "VM host identifier argument was specified, retrieving data for VM host: $vmhost_identifier");
+		my $vmhost_info = get_vmhost_info($vmhost_identifier, 1);
 		if (!$vmhost_info) {
-			notify($ERRORS{'WARNING'}, 0, "DataStructure object could not be initialized, failed to retrieve data for VM host: $vmhost_id");
+			notify($ERRORS{'WARNING'}, 0, "DataStructure object could not be initialized, failed to retrieve data for VM host: $vmhost_identifier");
 			
 			# Throw an exception because simply returning undefined (return;) does not result in this DataStructure object being undefined
-			Exception::Class::Base->throw( error => "DataStructure object could not be initialized, failed to retrieve data for VM host: $vmhost_id");
+			Exception::Class::Base->throw( error => "DataStructure object could not be initialized, failed to retrieve data for VM host: $vmhost_identifier");
 			return;
 		}
 		$self->request_data->{reservation}{$self->reservation_id}{computer}{vmhost} = $vmhost_info;

Modified: vcl/trunk/managementnode/lib/VCL/Module.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module.pm?rev=1675483&r1=1675482&r2=1675483&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module.pm Wed Apr 22 19:59:24 2015
@@ -178,26 +178,28 @@ sub new {
 	# Get the memory address of this newly created object - useful for debugging object creation problems
 	my $address = sprintf('%x', $self);
 	
+	my $type = ref($self);
+	
 	# Display a message based on the type of object created
 	if ($self->isa('VCL::Module::State')) {
 		my $request_state_name = $self->data->get_request_state_name(0) || '<not set>';
-		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created for state $request_state_name, address: $address");
+		notify($ERRORS{'DEBUG'}, 0, "$type object created for state $request_state_name, address: $address");
 	}
 	elsif ($self->isa('VCL::Module::OS') && !$self->isa('VCL::Module::OS::Linux::ManagementNode')) {
 		my $image_name = $self->data->get_image_name(0) || '<not set>';
-		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created for image $image_name, address: $address");
+		notify($ERRORS{'DEBUG'}, 0, "$type object created for image $image_name, address: $address");
 	}
 	elsif ($self->isa('VCL::Module::Provisioning')) {
 		my $computer_name = $self->data->get_computer_short_name(0) || '<not set>';
-		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created for computer $computer_name, address: $address");
+		notify($ERRORS{'DEBUG'}, 0, "$type object created for computer $computer_name, address: $address");
 	}
 	else {
-		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created, address: $address");
+		notify($ERRORS{'DEBUG'}, 0, "$type object created, address: $address");
 	}
 	
 	# Create a management node OS object
 	# Check to make sure the object currently being created is not a MN OS object to avoid endless loop
-	if (!$self->isa('VCL::Module::OS::Linux::ManagementNode')) {
+	if (!$self->isa('VCL::Module::OS::Linux::ManagementNode') && !$self->isa('VCL::Module::State')) {
 		my $mn_os;
 		# Check if the mn_os argument was provided
 		if ($args->{mn_os}) {
@@ -221,10 +223,15 @@ sub new {
 	}
 	
 	# Check if not running in setup mode and if initialize() subroutine is defined for this module
-	if (!$SETUP_MODE && $self->can("initialize")) {
-		# Call the initialize() subroutine, if it returns 0, return 0
-		# If it doesn't return 0, return the object reference
-		return if (!$self->initialize($args));
+	if (!$SETUP_MODE || $self->isa('VCL::Module::OS::Linux::ManagementNode')) {
+		if ($self->can("initialize")) {
+			# Call the initialize() subroutine, if it returns 0, return 0
+			# If it doesn't return 0, return the object reference
+			return if (!$self->initialize($args));
+		}
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "initialize not called for $type object ($address) because \$SETUP_MODE is true");
 	}
 
 	return $self;
@@ -289,7 +296,7 @@ sub create_datastructure_object {
 
 =head2 create_object
 
- Parameters  : $perl_package, $data_structure_arguments (optional)
+ Parameters  : $perl_package, $data_structure_arguments (optional), $object_argument_hashref (optional)
  Returns     : VCL::Module object reference
  Description : This is a general constructor to create VCL::Module objects. It
                contains the code to call 'use $perl_package', instantiate an
@@ -321,8 +328,14 @@ sub create_object {
 	my $data;
 	my $data_structure_arguments = shift;
 	if ($data_structure_arguments) {
-		notify($ERRORS{'DEBUG'}, 0, "new DataStructure object will be created for the $perl_package object, data structure arguments passed:\n" . format_data($data_structure_arguments));
-		$data = create_datastructure_object($data_structure_arguments);
+		if (ref($data_structure_arguments) && ref($data_structure_arguments) =~ /DataStructure/) {
+			notify($ERRORS{'DEBUG'}, 0, "DataStructure object argument will be passed to the new $perl_package object");
+			$data = $data_structure_arguments;
+		}
+		else {
+			notify($ERRORS{'DEBUG'}, 0, "new DataStructure object will be created for the $perl_package object, data structure arguments passed:\n" . format_data($data_structure_arguments));
+			$data = create_datastructure_object($data_structure_arguments);
+		}
 	}
 	elsif (!$self) {
 		notify($ERRORS{'DEBUG'}, 0, "new DataStructure object will be created for the $perl_package object, data structure arguments not passed and not called as an object reference");
@@ -332,6 +345,20 @@ sub create_object {
 		notify($ERRORS{'DEBUG'}, 0, "existing DataStructure object will be passed to the new $perl_package object");
 		$data = $self->data;
 	}
+	
+	my $object_argument_hashref = shift;
+	if ($object_argument_hashref) {
+		my $type = ref($object_argument_hashref);
+		if (!$type) {
+			notify($ERRORS{'WARNING'}, 0, "3rd argument is not a reference, it must be a hash reference: $object_argument_hashref");
+			return;
+		}
+		elsif ($type ne 'HASH') {
+			notify($ERRORS{'WARNING'}, 0, "3rd argument is a $type reference, it must be a hash reference");
+			return;
+		}
+	}
+	$object_argument_hashref->{data_structure} = $data;
 
 	# Attempt to load the module
 	eval "use $perl_package";
@@ -344,7 +371,7 @@ sub create_object {
 	# Attempt to create the object
 	my $object;
 	eval {
-		$object = ($perl_package)->new({data_structure => $data})
+		$object = ($perl_package)->new($object_argument_hashref)
 	};
 	
 	if ($EVAL_ERROR) {
@@ -500,7 +527,7 @@ sub create_mn_os_object {
 
 =head2 create_vmhost_os_object
 
- Parameters  : none
+ Parameters  : $vmhost_identifier (optional)
  Returns     : boolean
  Description : Creates an OS object for the VM host.
 
@@ -513,9 +540,15 @@ sub create_vmhost_os_object {
 		return;
 	}
 	
-	# Check if an OS object has already been stored in the calling object
-	if (my $vmhost_os = $self->vmhost_os(0)) {
-		return $vmhost_os;
+	my $vmhost_identifier = shift;
+	
+	if (!$vmhost_identifier) {
+		# Check if an OS object has already been stored in the calling object
+		if (my $vmhost_os = $self->vmhost_os(0)) {
+			my $address = sprintf('%x', $vmhost_os);
+			notify($ERRORS{'DEBUG'}, 0, "returning existing VM host OS object ($address)");
+			return $vmhost_os;
+		}
 	}
 	
 	# Make sure calling object isn't an OS module to avoid an infinite loop
@@ -526,20 +559,56 @@ sub create_vmhost_os_object {
 	
 	my $request_data = $self->data->get_request_data();
 	my $reservation_id = $self->data->get_reservation_id();
-	my $vmhost_computer_id = $self->data->get_vmhost_computer_id();
-	my $vmhost_hostname = $self->data->get_vmhost_hostname();
-	my $vmhost_profile_image_id = $self->data->get_vmhost_profile_image_id();
+	
+	my $vmhost_computer_id;
+	my $vmhost_hostname;
+	my $vmhost_profile_image_id;
+	if ($vmhost_identifier) {
+		my $vmhost_info = get_vmhost_info($vmhost_identifier);
+		if (!$vmhost_info) {
+			notify($ERRORS{'WARNING'}, 0, "unable to create VM host OS object for host specified by argument: $vmhost_identifier, VM host info could not be retrieved");
+			return;
+		}
+		
+		$vmhost_computer_id = $vmhost_info->{computerid};
+		if (!$vmhost_computer_id) {
+			notify($ERRORS{'WARNING'}, 0, "unable to create VM host OS object for host specified by argument: $vmhost_identifier, VM host computer ID could not be determined from VM host info:\n" . format_data($vmhost_info));
+			return;
+		}
+		
+		$vmhost_hostname = $vmhost_info->{computer}{hostname};
+		if (!$vmhost_hostname) {
+			notify($ERRORS{'WARNING'}, 0, "unable to create VM host OS object for host specified by argument: $vmhost_identifier, VM host computer hostname could not be determined from VM host info:\n" . format_data($vmhost_info));
+			return;
+		}
+		
+		$vmhost_profile_image_id = $vmhost_info->{vmprofile}{imageid};
+		if (!$vmhost_profile_image_id) {
+			notify($ERRORS{'WARNING'}, 0, "unable to create VM host OS object for host specified by argument: $vmhost_identifier, VM host profile image ID could not be determined from VM host info:\n" . format_data($vmhost_info));
+			return;
+		}
+	}
+	else {
+		# Argument was not supplied, use reservation data
+		$vmhost_computer_id = $self->data->get_vmhost_computer_id();
+		$vmhost_hostname = $self->data->get_vmhost_hostname();
+		$vmhost_profile_image_id = $self->data->get_vmhost_profile_image_id();
+		if (!$vmhost_computer_id || !$vmhost_hostname || !defined($vmhost_profile_image_id)) {
+			notify($ERRORS{'WARNING'}, 0, "unable to create VM host OS object, VM host computer ID, hostname, and profile image ID could not be determined from reservation data");
+			return;
+		}
+	}
 	
 	# Create a DataStructure object containing computer data for the VM host
 	my $vmhost_data;
 	eval {
 		$vmhost_data = new VCL::DataStructure({
-															request_data => $request_data,
-															reservation_id => $reservation_id,
-															computer_identifier => $vmhost_computer_id,
-															image_identifier => $vmhost_profile_image_id
-															}
-														);
+			request_data => $request_data,
+			reservation_id => $reservation_id,
+			computer_identifier => $vmhost_computer_id,
+			image_identifier => $vmhost_profile_image_id
+			}
+		);
 	};
 	
 	if ($EVAL_ERROR) {

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=1675483&r1=1675482&r2=1675483&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 Wed Apr 22 19:59:24 2015
@@ -1068,7 +1068,7 @@ sub get_registered_vms {
 	}
 	
 	# Get the vmx path values for each VM
-	my @vmx_paths = values(%$vm_list);
+	my @vmx_paths = sort { lc($a) cmp lc($b) } values(%$vm_list);
 	
 	notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@vmx_paths) . " registered VMs");
 	return @vmx_paths;
@@ -1302,7 +1302,7 @@ sub vm_power_off {
 
  Parameters  : $vmx_file_path
  Returns     : boolean
- Description : Powers off the VM indicated by the vmx file path argument.
+ Description : Suspends the VM indicated by the vmx file path argument.
 
 =cut
 
@@ -1340,6 +1340,8 @@ sub vm_suspend {
 		return;
 	}
 	
+	notify($ERRORS{'DEBUG'}, 0, "suspending VM: $vmx_file_path ($vm_id)");
+	my $start_time = time;
 	my $vim_cmd_arguments = "vmsvc/power.suspend $vm_id";
 	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 400);
 	return if !$output;
@@ -1365,7 +1367,8 @@ sub vm_suspend {
 	
 	# Wait for the task to complete
 	if ($self->_wait_for_task($task_ids[0])) {
-		notify($ERRORS{'OK'}, 0, "suspended VM: $vmx_file_path");
+		my $duration = (time - $start_time);
+		notify($ERRORS{'OK'}, 0, "suspended VM: $vmx_file_path, took $duration seconds");
 		return 1;
 	}
 	else {
@@ -2702,9 +2705,17 @@ sub _parse_vim_cmd_output {
 		# Remove trailing newlines
 		$line =~ s/\n+$//g;
 		
-		# Remove class names at beginning of line surrounded by parenthesis
+		# Remove class names in parenthesis from beginning class name of indented lines
+		# '   (vim.vm.device.xxx) {' --> '   {'
+		$line =~ s/^(\s+)\([^\)]*\)\s*/$1/g;
+		
+		# Remove class names at beginning of a line surrounded by parenthesis
 		# '(vim.vm.device.VirtualPointingDevice) {' --> '{'
-		$line =~ s/^(\s*)\([^\)]*\)\s*/$1/g;
+		$line =~ s/^\(([^\)]+)\)\s*{/'$1' => {/gx;
+		
+		# Add comma to lines containing a closing curly bracket
+		# '   }' --> '   },'
+		$line =~ s/^(\s*)}\s*$/$1},/g;
 		
 		# Remove class names after an equals sign
 		# 'backing = (vim.vm.device.VirtualDevice.BackingInfo) null,' --> 'backing = null'
@@ -2729,6 +2740,9 @@ sub _parse_vim_cmd_output {
 		$numbered_statement .= "$line_number:$line\n";
 	}
 	
+	# Enclose the entire statement in curly brackets
+	$statement = "{\n$statement\n}";
+
 	# The statement variable should contain a valid definition
 	my $result = eval($statement);
 	if ($EVAL_ERROR) {
@@ -2743,6 +2757,169 @@ sub _parse_vim_cmd_output {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 _get_vm_virtual_disk_file_layout
+
+ Parameters  : $vmx_file_path
+ Returns     : hash reference
+ Description : Retrieves a VM's virtual disk file layout as reported by:
+               vim-cmd vmsvc/get.filelayout <VM ID>
+               A hash reference is returned:
+               {
+                 "vim.vm.FileInfo" => {
+                   "dynamicType" => "<unset>",
+                   "ftMetadataDirectory" => "<unset>",
+                   "logDirectory" => "[blade1e1-10-vmpath] arkvmm160_3868-v0",
+                   "snapshotDirectory" => "[blade1e1-10-vmpath] arkvmm160_3868-v0",
+                   "suspendDirectory" => "[blade1e1-10-vmpath] arkvmm160_3868-v0",
+                   "vmPathName" => "[blade1e1-10-vmpath] arkvmm160_3868-v0/arkvmm160_3868-v0.vmx"
+                 },
+                 "vim.vm.FileLayout" => {
+                   "configFile" => [
+                     "arkvmm160_3868-v0.vmxf",
+                     "nvram",
+                     "arkvmm160_3868-v0.vmsd"
+                   ],
+                   "disk" => [
+                     {
+                       "diskFile" => [
+                         "[datastore-compressed] vmwarewinxp-xpsp33868-v0/vmwarewinxp-xpsp33868-v0.vmdk",
+                         "[blade1e1-10-vmpath] arkvmm160_3868-v0/vmwarewinxp-xpsp33868-v0-000001.vmdk"
+                       ],
+                       "dynamicType" => "<unset>",
+                       "key" => 3000
+                     },
+                     {
+                       "diskFile" => [
+                         "[blade1e1-10-vmpath] arkvmm160_3868-v0/arkvmm160_3868-v0.vmdk"
+                       ],
+                       "dynamicType" => "<unset>",
+                       "key" => 2000
+                     }
+                   ],
+                   "dynamicType" => "<unset>",
+                   "logFile" => [
+                     "vmware-1.log",
+                     "vmware-2.log",
+                     "vmware-3.log",
+                     "vmware.log"
+                   ],
+                   "snapshot" => [
+                     {
+                       "dynamicType" => "<unset>",
+                       "key" => "'vim.vm.Snapshot:576-snapshot-1'",
+                       "snapshotFile" => [
+                         "[blade1e1-10-vmpath] arkvmm160_3868-v0/arkvmm160_3868-v0-Snapshot1.vmsn",
+                         "[datastore-compressed] vmwarewinxp-xpsp33868-v0/vmwarewinxp-xpsp33868-v0.vmdk"
+                       ]
+                     }
+                   ],
+                   "swapFile" => "<unset>"
+                 }
+               }
+
+=cut
+
+sub _get_vm_virtual_disk_file_layout {
+	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 $vmx_file_path = shift;
+	if (!$vmx_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not specified");
+		return;
+	}
+	
+	# 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/get.filelayout $vm_id";
+	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
+	return if !$output;
+	
+	my $virtual_disk_file_layout = $self->_parse_vim_cmd_output($output);
+	if ($virtual_disk_file_layout) {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved virtual disk file layout for VM $vm_id ($vmx_file_path)\n" . format_data($virtual_disk_file_layout));
+		return $virtual_disk_file_layout;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve virtual disk file layout for VM $vm_id ($vmx_file_path)");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_vm_virtual_disk_file_paths
+
+ Parameters  : $vmx_file_path
+ Returns     : array reference
+ Description : Retrieves a VM's virtual disk file layout and returns an array
+               reference. Each top-level array element represent entire virtual
+               disk and contains an array reference containing the virtual
+               disk's files:
+               [
+                 [
+                   "/vmfs/volumes/datastore/vmwarewin7-bare3844-v1/vmwarewin7-bare3844-v1.vmdk",
+                   "/vmfs/volumes/blade-vmpath/vm170_3844-v1/vmwarewin7-bare3844-v1-000001.vmdk",
+                   "/vmfs/volumes/blade-vmpath/vm170_3844-v1/vmwarewin7-bare3844-v1-000002.vmdk",
+                   "/vmfs/volumes/blade-vmpath/vm170_3844-v1/vmwarewin7-bare3844-v1-000003.vmdk"
+                 ],
+                 [
+                   "/vmfs/volumes/blade-vmpath/vm170_3844-v1/vm170_3844-v1.vmdk"
+                 ]
+               ]
+
+=cut
+
+sub get_vm_virtual_disk_file_paths {
+	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 $vmx_file_path = shift;
+	if (!$vmx_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not specified");
+		return;
+	}
+	
+	my $virtual_disk_file_layout = $self->_get_vm_virtual_disk_file_layout($vmx_file_path) || return;
+	
+	my $virtual_disk_array_ref = $virtual_disk_file_layout->{'vim.vm.FileLayout'}{'disk'};
+	if (!$virtual_disk_array_ref) {
+		notify($ERRORS{'WARNING'}, 0, "unable to determine virtual disk file paths, failed to retrieve {'vim.vm.FileLayout'}{'disk'} array reference from virtual disk file layout:\n" . format_data($virtual_disk_file_layout));
+		return;
+	}
+	elsif (!ref($virtual_disk_array_ref) || ref($virtual_disk_array_ref) ne 'ARRAY') {
+		notify($ERRORS{'WARNING'}, 0, "unable to determine virtual disk file paths, virtual disk file layout {'vim.vm.FileLayout'}{'disk'} key does not contain an array reference:\n" . format_data($virtual_disk_array_ref));
+		return;
+	}
+	
+	my @virtual_disks;
+	for my $virtual_disk_ref (@$virtual_disk_array_ref) {
+		my $disk_file_array_ref = $virtual_disk_ref->{'diskFile'};
+		my @virtual_disk_file_paths;
+		for my $virtual_disk_file_path (@$disk_file_array_ref) {
+			push @virtual_disk_file_paths, $self->_get_normal_path($virtual_disk_file_path);
+		}
+		push @virtual_disks, \@virtual_disk_file_paths;
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved virtual disk file paths for $vmx_file_path:\n" . format_data(\@virtual_disks));
+	return @virtual_disks;
+}
+
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 DESTROY
 
  Parameters  : none