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/02/24 20:00:23 UTC

svn commit: r1662083 - in /vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware: VIM_SSH.pm VMware.pm

Author: arkurth
Date: Tue Feb 24 19:00:23 2015
New Revision: 1662083

URL: http://svn.apache.org/r1662083
Log:
VCL-685
Updated VMware.pm::is_vmdk_file_shared and is_vmdk_directory_shared to check for an existing .vmdk file beginning with one of the OS.name values and also the OS.name value with 'vmware' omitted from the beginning. The beginning 'vmware' gets trimmed if an image is created on KVM and then loaded on VMware.

Updated VIM_SSH.pm to detect some common errors if a host is overloaded with multiple vim-cmd commands being run at the same time.

Added subroutines to VIM_SSH.pm which retrieve the host's capabilities. These are not currently being called (may use these in the future):
get_config_option_descriptor_info
get_config_option_info
get_config_option_guest_os_info
_print_compatible_guest_os_hardware_versions
_parse_vim_cmd_output

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
    vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.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=1662083&r1=1662082&r2=1662083&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 Tue Feb 24 19:00:23 2015
@@ -175,25 +175,30 @@ sub _run_vim_cmd {
 	
 	my $attempt = 0;
 	my $attempt_limit = 5;
-	my $wait_seconds = 2;
+	my $wait_seconds = 5;
 	
 	my $connection_reset_errors = 0;
+	
 	ATTEMPT: while ($attempt++ < $attempt_limit) {
+		
+		my $semaphore;
 		if ($attempt > 1) {
 			# Wait before making next attempt
 			notify($ERRORS{'OK'}, 0, "sleeping $wait_seconds seconds before making attempt $attempt/$attempt_limit");
-			sleep $wait_seconds;
-			
-			my $semaphore_id = "$vmhost_computer_name-vmware_services_restart";
-			if ($self->does_semaphore_exist($semaphore_id)) {
-				$self->{services_restarted} = 1;
-				notify($ERRORS{'DEBUG'}, 0, "detected another process is restarting VMware services, sleeping for 10 seconds");
-				sleep_uninterrupted(10);
-				my $wait_message = "another process is restarting VMware services on $vmhost_computer_name";
-				$self->code_loop_timeout(sub{!$self->does_semaphore_exist(@_)}, [$semaphore_id], $wait_message, 140, 5);
-			}
+			sleep_uninterrupted($wait_seconds);
+			$semaphore = $self->get_semaphore($vmhost_computer_name, 120, 1) || next ATTEMPT;
 		}
 		
+		#	my $semaphore_id = "$vmhost_computer_name";
+		#	if ($self->does_semaphore_exist($semaphore_id)) {
+		#		
+		#		notify($ERRORS{'DEBUG'}, 0, "blocked by another process controlling $vmhost_computer_name, sleeping for 10 seconds");
+		#		sleep_uninterrupted(10);
+		#		my $wait_message = "blocked by another process controlling $vmhost_computer_name";
+		#		$self->code_loop_timeout(sub{!$self->does_semaphore_exist(@_)}, [$semaphore_id], $wait_message, 140, 5);
+		#	}
+		#}
+		
 		# The following error is somewhat common if several processes are adding/removing VMs at the same time:
 		# (vmodl.fault.ManagedObjectNotFound) {
 		#	 dynamicType = <unset>,
@@ -252,13 +257,20 @@ sub _run_vim_cmd {
 			}
 			return;
 		}
+		elsif ($exit_status != 0 || grep(/^(vim-cmd:|Killed|terminate called|Aborted|what\()/i, @$output)) {
+			# terminate called after throwing an instance of 'std::bad_alloc'
+			# what():  std::bad_alloc
+			# Aborted
+			notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: failed to execute command on VM host $vmhost_computer_name: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
+			next ATTEMPT;
+		}
 		else {
 			# VIM command command was executed
 			if ($attempt > 1) {
-				notify($ERRORS{'DEBUG'}, 0, "attempt $attempt/$attempt_limit: executed command on VM host $vmhost_computer_name: $command");
+				notify($ERRORS{'DEBUG'}, 0, "attempt $attempt/$attempt_limit: executed command on VM host $vmhost_computer_name: $command, exit status: $exit_status");
 			}
 			else {
-				notify($ERRORS{'DEBUG'}, 0, "executed command on VM host $vmhost_computer_name: $command");
+				notify($ERRORS{'DEBUG'}, 0, "executed command on VM host $vmhost_computer_name: $command, exit status: $exit_status");
 			}
 			return ($exit_status, $output);
 		}
@@ -739,7 +751,7 @@ sub _get_datastore_info {
 		# Check if the accessible value was retrieved and is not false
 		my $datastore_accessible = $datastore_info->{$datastore_name}{accessible};
 		if (!$datastore_accessible || $datastore_accessible =~ /false/i) {
-			notify($ERRORS{'WARNING'}, 0, "datastore '$datastore_name' is mounted on $vmhost_hostname but not accessible");
+			notify($ERRORS{'DEBUG'}, 0, "datastore '$datastore_name' is mounted on $vmhost_hostname but not accessible");
 			delete $datastore_info->{$datastore_name};
 			next;
 		}
@@ -1958,7 +1970,13 @@ sub get_network_names {
 	
 	# Convert the output line array to a string then split it by network sections
 	my ($network_info) = join("\n", @$output) =~ /(vim\.vm\.NetworkInfo[^\]]+)/;
-	notify($ERRORS{'DEBUG'}, 0, "network info:\n$network_info");
+	if ($network_info) {
+		notify($ERRORS{'DEBUG'}, 0, "network info:\n$network_info");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve network info, vim-cmd arguments: '$vim_cmd_arguments', $exit_status: $exit_status, output:\n" . join("\n", @$output));
+		return;
+	}
 	
 	my (@network_sections) = split(/vim.vm.NetworkInfo/, $network_info);
 	
@@ -2406,6 +2424,323 @@ sub get_license_info {
 }
 
 #/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_config_option_descriptor_info
+
+ Parameters  : none
+ Returns     : hash reference
+ Description : Retrieves information about the VM configuration options that are
+               supported on the host.
+               }
+                  "vmx-09" => {
+                    "createSupported" => "true",
+                    "defaultConfigOption" => "false",
+                    "description" => "ESXi 5.1 virtual machine",
+                    "dynamicType" => "<unset>",
+                    "key" => "vmx-09",
+                    "runSupported" => "true",
+                    "upgradeSupported" => "true"
+                  },
+                  "vmx-10" => {
+                    "createSupported" => "true",
+                    "defaultConfigOption" => "true",
+                    "description" => "ESXi 5.5 virtual machine",
+                    "dynamicType" => "<unset>",
+                    "key" => "vmx-10",
+                    "runSupported" => "true",
+                    "upgradeSupported" => "true"
+                  }
+               }
+
+=cut
+
+sub get_config_option_descriptor_info {
+	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 $vim_cmd_arguments = "solo/querycfgoptdesc";
+	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
+	return if !$output;
+	
+	my $result = $self->_parse_vim_cmd_output($output);
+	if (!$result) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option descriptor info");
+		return;
+	}
+	
+	my $type = ref($result);
+	if (!$type) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option descriptor info, parsed result is not a reference:\n" . $result);
+		return;
+	}
+	
+	# If a single entry is returned a hash reference may be returned instead of an array, convert it to an array
+	if ($type eq 'HASH') {
+		$result = [$result];
+	}
+	
+	my $config_option_descriptor_info = {};
+	for my $config_option_descriptor (@$result) {
+		my $key = $config_option_descriptor->{key};
+		if (!defined($key)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option descriptor info, result does not contain a 'key' element:\n" . format_data($config_option_descriptor));
+			return;
+		}
+		$config_option_descriptor_info->{$key} = $config_option_descriptor;
+	}
+	
+	#notify($ERRORS{'DEBUG'}, 0, "retrieved config option descriptor info:\n" . format_data($config_option_descriptor_info));
+	return $config_option_descriptor_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_config_option_info
+
+ Parameters  : $key
+ Returns     : hash reference
+ Description : Retrieves info about the VM configuration options available for a
+               particular hardware version key (ex: vmx-09).
+
+=cut
+
+sub get_config_option_info {
+	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 ($key) = @_;
+	if (!defined($key)) {
+		notify($ERRORS{'WARNING'}, 0, "key argument was not provided");
+		return;
+	}
+	
+	my $vim_cmd_arguments = "solo/querycfgopt $key";
+	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
+	return if !$output;
+	
+	my $result = $self->_parse_vim_cmd_output($output);
+	if (!$result) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option info for $key");
+		return;
+	}
+	
+	return $result;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_config_option_guest_os_info
+
+ Parameters  : $key
+ Returns     : hash reference
+ Description : Retrieves info about the guest OS's supported for the given key
+               (ex: vmx-09).
+					{
+					  "windows8_64Guest" => {
+						 "family" => "windowsGuest",
+						 "fullName" => "Microsoft Windows 8 (64-bit)",
+						 "id" => "windows8_64Guest",
+						 ...
+					  },
+					  "windows8Server64Guest" => {
+					  ...
+					  },
+					  ...
+					}
+
+=cut
+
+sub get_config_option_guest_os_info {
+	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 ($key) = @_;
+	if (!defined($key)) {
+		notify($ERRORS{'WARNING'}, 0, "key argument was not provided");
+		return;
+	}
+	
+	my $config_option_info = $self->get_config_option_info($key) || return;
+	
+	my $guest_os_descriptor_array_ref = $config_option_info->{guestOSDescriptor};
+	if (!defined($guest_os_descriptor_array_ref)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option guest OS info, config option info does not contain a 'guestOSDescriptor' key:\n" . format_data($config_option_info));
+		return;
+	}
+	
+	my $type = ref($guest_os_descriptor_array_ref);
+	if (!$type || $type ne 'ARRAY') {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option guest OS info, guestOSDescriptor value is not an array reference:\n" . format_data($guest_os_descriptor_array_ref));
+		return;
+	}
+	
+	my $config_option_guest_os_info = {};
+	for my $guest_os_descriptor (@$guest_os_descriptor_array_ref) {
+		my $id = $guest_os_descriptor->{id};
+		if (!defined($id)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to retrieve config option guest OS info, guest OS descriptor does not contain an 'id' key:\n" . format_data($guest_os_descriptor));
+			return;
+		}
+		$config_option_guest_os_info->{$id} = $guest_os_descriptor;
+	}
+	
+	#notify($ERRORS{'DEBUG'}, 0, "retrieved config option guest OS info:\n" . format_data($config_option_guest_os_info));
+	return $config_option_guest_os_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _print_compatible_guest_os_hardware_versions
+
+ Parameters  : $print_code (optional)
+ Returns     : true
+ Description : Used for development/testing only. Prints list of possible
+               guestOS values.
+
+=cut
+
+sub _print_compatible_guest_os_hardware_versions {
+	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 $print_code = shift;
+	
+	my $guest_os_info = {};
+	my $config_option_descriptor_info = $self->get_config_option_descriptor_info();
+	for my $version_key (sort keys %$config_option_descriptor_info) {
+		my $config_option_guest_os_info = $self->get_config_option_guest_os_info($version_key);
+		for my $guest_os (keys %$config_option_guest_os_info) {
+			$guest_os_info->{$guest_os}{$version_key} = 1;
+		}
+	}
+	
+	for my $guest_os (sort keys %$guest_os_info) {
+		if ($print_code) {
+			print "'$guest_os' => { ";
+			for my $version_key (sort keys %{$guest_os_info->{$guest_os}}) {
+				$version_key =~ s/vmx-0?//;
+				print "$version_key => 1, ";
+			}
+			print "},\n";
+		}
+		else {
+			my $length = length($guest_os);
+			print "$guest_os ";
+			print (' ' x (25-$length));
+			print (' ' x (50-scalar(@{$guest_os_info->{$guest_os}})*8));
+			print join(", ", sort keys %{$guest_os_info->{$guest_os}});
+			print "\n";
+		}
+	}
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _parse_vim_cmd_output
+
+ Parameters  : $vim_cmd_output
+ Returns     : varies
+ Description : Parses the Data::Dumper-like output returned for some vim-cmd
+               commands and attempts to parse the output into a data structure.
+
+=cut
+
+sub _parse_vim_cmd_output {
+	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 ($argument) = @_;
+	if (!defined($argument)) {
+		notify($ERRORS{'WARNING'}, 0, "vim-cmd output argument was not supplied");
+		return;
+	}
+	my @lines;
+	if (my $type = ref($argument)) {
+		if ($type eq 'ARRAY') {
+			@lines = @$argument;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "argument is a $type reference, only an ARRAY reference or string are supported");
+			return;
+		}
+	}
+	elsif (scalar(@_) > 1) {
+		@lines = @_;
+	}
+	else {
+		@lines = split("\n", $argument);
+	}
+	
+	my $statement;
+	my $numbered_statement;
+	my $line_number = 0;
+	for my $line (@lines) {
+		# Skip blank lines
+		if ($line !~ /\S/) {
+			next;
+		}
+		
+		$line_number++;
+		
+		# Remove trailing newlines
+		$line =~ s/\n+$//g;
+		
+		# Remove class names at beginning of line surrounded by parenthesis
+		# '(vim.vm.device.VirtualPointingDevice) {' --> '{'
+		$line =~ s/^(\s*)\([^\)]*\)\s*/$1/g;
+		
+		# Remove class names after an equals sign
+		# 'backing = (vim.vm.device.VirtualDevice.BackingInfo) null,' --> 'backing = null'
+		$line =~ s/(=\s+)\([^\)]+\)\s*//g;
+		
+		# Surround values after equals sign in quotes
+		# 'value = xxx,' --> 'value = "xxx",'
+		$line =~ s/(=\s+)([^"].+),/$1"$2",/g;
+		
+		# Change 'null' to undef and add =>
+		# 'busSlotOption null,' --> 'busSlotOption => undef,'
+		$line =~ s/(\w\s+)null,/$1 => undef,/g;
+		
+		# Change = to =>
+		$line =~ s/=(\s+.+),/=>$1,/g;
+		
+		# Add => before array and hash references
+		# 'guestOSDescriptor [' --> 'guestOSDescriptor => ['
+		$line =~ s/(\w\s+)([\[{])/$1=>$2/g;
+		
+		$statement .= "$line\n";
+		$numbered_statement .= "$line_number:$line\n";
+	}
+	
+	# The statement variable should contain a valid definition
+	my $result = eval($statement);
+	if ($EVAL_ERROR) {
+		notify($ERRORS{'WARNING'}, 0, "failed to parse vim-cmd output, error:\n$EVAL_ERROR\n$numbered_statement");
+		return;
+	}
+	else {
+		#notify($ERRORS{'DEBUG'}, 0, "parsed vim-cmd output:\n" . format_data($result));
+		return $result;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
 
 =head2 DESTROY
 

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=1662083&r1=1662082&r2=1662083&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm Tue Feb 24 19:00:23 2015
@@ -4086,7 +4086,7 @@ sub is_vmdk_file_shared {
 	#    *-00000*.vmdk        (vmwarewinxp-base234-v23-000001.vmdk)
 	#    *-00000*-delta.vmdk  (vmwarewinxp-base234-v23-000001-delta.vmdk)
 	
-	if (my @matching_os_names = map { $vmdk_file_name =~ /^($_)-/ } @os_names) {
+	if (my @matching_os_names = map { $vmdk_file_name =~ /^($_)-/ || "vmware$vmdk_file_name" =~ /^($_)-/ } @os_names) {
 		if ($vmdk_file_name =~ /-\d+(-delta|-s\d+)?\.vmdk$/i) {
 			notify($ERRORS{'DEBUG'}, 0, "vmdk file does NOT appear to be shared, it is a snapshot file: '$vmdk_file_name'");
 			return 0;
@@ -4144,12 +4144,13 @@ sub is_vmdk_directory_shared {
 	my $os_info = get_os_info();
 	my @os_names = sort(map { $os_info->{$_}{name} } keys %$os_info);
 	
-	if (my @matching_os_names = map { $vmdk_directory_name =~ /^($_)-/ } @os_names) {
+	
+	if (my @matching_os_names = map { $vmdk_directory_name =~ /^($_)-/ || "vmware$vmdk_directory_name" =~ /^($_)-/ } @os_names) {
 		notify($ERRORS{'DEBUG'}, 0, "vmdk directory appears to be shared: '$vmdk_directory_path', it begins with the name of an OS in the database: " . join(' ,', @matching_os_names));
 		return 1;
 	}
 	else {
-		notify($ERRORS{'DEBUG'}, 0, "vmdk directory does NOT appear to be shared: '$vmdk_directory_path', it does NOT begin with the name of an OS in the database");
+		notify($ERRORS{'DEBUG'}, 0, "vmdk directory does NOT appear to be shared: '$vmdk_directory_path', directory name '$vmdk_directory_name' does NOT begin with the name of an OS in the database:\n" . join("\n", @os_names));
 		return 0;
 	}
 }