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 2010/08/06 21:38:53 UTC

svn commit: r983104 - in /incubator/vcl/trunk/managementnode/lib/VCL: Module.pm Module/OS.pm Module/OS/Linux.pm Module/Provisioning.pm Module/Provisioning/VMware/VMware.pm Module/Semaphore.pm utils.pm

Author: arkurth
Date: Fri Aug  6 19:38:53 2010
New Revision: 983104

URL: http://svn.apache.org/viewvc?rev=983104&view=rev
Log:
VCL-289
Created Provisioning.pm::retrieve_image subroutine based on the code from the existing xCAT and VMware modules.  Added Semaphore.pm which contains code to create an exclusively locked file and Module.pm::get_semaphore for easy access/creation.  The retrieve_image subroutine creates a Semaphore object before retrieving an image from another management node.  This ensures that only 1 retrieval for a given image happens at a time.  Added get_image_repository_path and get_image_repository_search_paths subroutines to VMware.pm allowing it to use the new retrieve_image subroutine.  Also removed get_lockfile and release_lockfile from VMware.pm. They were put there during development.

VCL-100
Updated Linux.pm::get_file_size to return the bytes actually used rather than the apparent size of a file.  This allows an accurate size to be returned when checking the size of thin vmdk files whose different size values may differ widely.

VCL-298
Updated VMware.pm:
Modified is_vm_persistent to return true if the reservation end time is more than 24 hours in the future.
Updated in get_image_size so that it works correctly if an image name is passed as an argument. This is done by image.pm.
Fixed bug in get_vm_disk_adapter_type.  It was returning IDE if using ESX in some situations which doesn't work.

Other
Made minor change to OS.pm::is_ssh_responding so that it doesn't rely on the exit status of the SSH command.
In utils.pm::run_ssh_command, added '2>&1' so it is applied to both the remote command being run and the local SSH command.  This should allow STDOUT and STDERR output to be captured and synchronized.

Added:
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm   (with props)
Modified:
    incubator/vcl/trunk/managementnode/lib/VCL/Module.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
    incubator/vcl/trunk/managementnode/lib/VCL/utils.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module.pm?rev=983104&r1=983103&r2=983104&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module.pm Fri Aug  6 19:38:53 2010
@@ -86,6 +86,7 @@ use English '-no_match_vars';
 
 use VCL::utils qw($SETUP_MODE $VERBOSE %ERRORS &notify &getnewdbh format_data);
 use VCL::DataStructure;
+use VCL::Module::Semaphore;
 
 ##############################################################################
 
@@ -130,10 +131,10 @@ sub new {
 	my $args  = shift;
 
 	notify($ERRORS{'DEBUG'}, 0, "constructor called, class=$class");
-
+	
 	# Create a variable to store the newly created class object
 	my $class_object;
-
+	
 	# Make sure the data structure was passed as an argument called 'data_structure'
 	if (!defined $args->{data_structure}) {
 		notify($ERRORS{'CRITICAL'}, 0, "required 'data_structure' argument was not passed");
@@ -145,7 +146,7 @@ sub new {
 		notify($ERRORS{'CRITICAL'}, 0, "'data_structure' argument passed is not a reference to a VCL::DataStructure object");
 		return;
 	}
-
+	
 	# Add the DataStructure reference to the class object
 	$class_object->{data} = $args->{data_structure};
 
@@ -334,7 +335,7 @@ sub get_package_hierarchy {
 
 sub code_loop_timeout {
 	my $self = shift;
-	if (ref($self) !~ /VCL::Module/i) {
+	unless (ref($self) && $self->isa('VCL::Module')) {
 		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
 		return;
 	}
@@ -422,6 +423,82 @@ sub code_loop_timeout {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_semaphore
+
+ Parameters  : $file_path, $total_wait_seconds (optional), $attempt_delay_seconds (optional)
+ Returns     : VCL::Module::Semaphore object
+ Description : This subroutine is used to ensure that only 1 process performs a
+               particular task at a time. An example would be the retrieval of
+               an image from another management node. If multiple reservations
+               are being processed for the same image, each reservation may
+               attempt to retrieve it via SCP at the same time. This subroutine
+               can be used to only allow 1 process to retrieve the image. The
+               others will wait until the semaphore is released by the
+               retrieving process.
+               
+               Attempts to open and obtain an exclusive lock on the file
+               specified by the file path argument. If unable to obtain an
+               exclusive lock, it will wait up to the value specified by the
+               total wait seconds argument (default: 30 seconds). The number of
+               seconds to wait in between retries can be specified (default: 15
+               seconds).
+               
+               A semaphore object is returned. The exclusive lock will be
+               retained as long as the semaphore object remains defined. Once
+               undefined, the exclusive lock is released and the file is
+               deleted.
+               
+               Examples:
+               
+               Semaphore is released when it is undefined:
+               my $semaphore = $self->get_semaphore('/tmp/test.lock');
+               ... <exclusive lock is in place>
+               undef $semaphore;
+               ... <exclusive lock released>
+               
+               Semaphore is released when it goes out of scope:
+               if (blah) {
+                  my $semaphore = $self->get_semaphore('/tmp/test.lock');
+                  ... <exclusive lock is in place>
+               }
+               ... <exclusive lock released>
+
+=cut
+
+sub get_semaphore {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get the file path argument
+	my ($file_path, $total_wait_seconds, $attempt_delay_seconds) = @_;
+	if (!$file_path) {
+		notify($ERRORS{'WARNING'}, 0, "file path argument was not supplied");
+		return;
+	}
+	
+	# Attempt to create a new semaphore object
+	my $semaphore = VCL::Module::Semaphore->new({'data_structure' => $self->data});
+	if (!$semaphore) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create semaphore object");
+		return;
+	}
+	
+	# Attempt to open and exclusively lock the file
+	if ($semaphore->get_lockfile($file_path, $total_wait_seconds, $attempt_delay_seconds)) {
+		# Return the semaphore object
+		return $semaphore;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "failed to open and optain exclusive lock on file: $file_path");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__
 

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=983104&r1=983103&r2=983104&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm Fri Aug  6 19:38:53 2010
@@ -426,7 +426,7 @@ sub is_ssh_responding {
 	});
 	
 	# The exit status will be 0 if the command succeeded
-	if (defined($exit_status) && $exit_status == 0) {
+	if (defined($output) && grep(/testing/, @$output)) {
 		notify($ERRORS{'DEBUG'}, 0, "$computer_node_name is responding to SSH, port 22: $port_22_status, port 24: $port_24_status");
 		return 1;
 	}

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=983104&r1=983103&r2=983104&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Fri Aug  6 19:38:53 2010
@@ -1859,7 +1859,7 @@ sub get_file_size {
 	my $computer_node_name = $self->data->get_computer_node_name() || return;
 	
 	# Run stat rather than du because du is not available on VMware ESX
-	my $command = 'stat -c "%F:%s:%n" ' . $escaped_file_path;
+	my $command = 'stat -c "%F:%s:%b:%B:%n" ' . $escaped_file_path;
 	my ($exit_status, $output) = $self->execute($command);
 	if (!defined($output)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to run command to determine file size on $computer_node_name: $file_path\ncommand: $command");
@@ -1871,23 +1871,25 @@ sub get_file_size {
 	}
 	
 	# Loop through the stat output lines
-	my $total_bytes = 0;
+	my $total_bytes_used = 0;
+	my $total_bytes_allocated = 0;
 	for my $line (@$output) {
 		# Take the stat output line apart
-		my ($type, $file_bytes, $path) = split(/:/, $line);
-		if (!defined($type) || !defined($file_bytes) || !defined($path)) {
+		my ($type, $file_bytes, $file_blocks, $block_size, $path) = split(/:/, $line);
+		if (!defined($type) || !defined($file_bytes) || !defined($file_blocks) || !defined($block_size) || !defined($path)) {
 			notify($ERRORS{'WARNING'}, 0, "unexpected output returned from stat, line: $line\ncommand: $command\noutput:\n" . join("\n", @$output));
 			return;
 		}
 		
 		# Add the size to the total if the type is file
 		if ($type =~ /file/) {
-			$total_bytes += $file_bytes;
+			$total_bytes_used += ($file_blocks * $block_size);
+			$total_bytes_allocated += $file_bytes;
 		}
 	}
 	
-	notify($ERRORS{'DEBUG'}, 0, "size of $file_path on $computer_node_name: " . format_number($total_bytes) . " bytes");
-	return $total_bytes;
+	notify($ERRORS{'DEBUG'}, 0, "size of $file_path on $computer_node_name: " . format_number($total_bytes_used) . " bytes, (" . format_number($total_bytes_allocated) . " bytes allocated)");
+	return $total_bytes_used;
 }
 
 #/////////////////////////////////////////////////////////////////////////////

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm?rev=983104&r1=983103&r2=983104&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm Fri Aug  6 19:38:53 2010
@@ -140,6 +140,182 @@ sub vmhost_os {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 retrieve_image
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Retrieves an image from another management node.
+
+=cut
+
+sub retrieve_image {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
+		return;	
+	}
+
+	# Make sure image library functions are enabled
+	my $image_lib_enable = $self->data->get_management_node_image_lib_enable();
+	if (!$image_lib_enable) {
+		notify($ERRORS{'OK'}, 0, "image retrieval skipped, image library functions are disabled for this management node");
+		return;
+	}
+
+	# Get the image name from the reservation data
+	my $image_name = $self->data->get_image_name();
+	if (!$image_name) {
+		notify($ERRORS{'WARNING'}, 0, "failed to determine image name from reservation data");
+		return;
+	}
+	
+	# Get the last digit of the reservation ID and sleep that number of seconds
+	# This is done in case 2 reservations for the same image were started at the same time
+	# Both may attempt to retrieve an image and execute the SCP command at nearly the same time
+	# does_image_exist() may not catch this and allow 2 SCP retrieval processes to start
+	# It's likely that the reservation IDs are consecutive and the the last digits will be different
+	my ($pre_retrieval_sleep) = $self->data->get_reservation_id() =~ /(\d)$/;
+	notify($ERRORS{'DEBUG'}, 0, "sleeping for $pre_retrieval_sleep seconds to prevent multiple SCP image retrieval processes");
+	sleep $pre_retrieval_sleep;
+	
+	# Get a semaphore so only 1 process is able to retrieve the image at a time
+	# Do this before checking if the image exists in case another process is retrieving the image
+	my $semaphore = $self->get_semaphore("/tmp/retrieve_$image_name.lock", (60 * 15)) || return;
+	
+	# Make sure image does not already exist on this management node
+	if ($self->does_image_exist($image_name)) {
+		notify($ERRORS{'OK'}, 0, "$image_name already exists on this management node");
+		return 1;
+	}
+
+	# Get the image library partner string
+	my $image_lib_partners = $self->data->get_management_node_image_lib_partners();
+	if (!$image_lib_partners) {
+		notify($ERRORS{'WARNING'}, 0, "image library partners could not be determined");
+		return;
+	}
+	
+	# Split up the partner list
+	my @partner_list = split(/,/, $image_lib_partners);
+	if ((scalar @partner_list) == 0) {
+		notify($ERRORS{'WARNING'}, 0, "image lib partners variable is not listed correctly or does not contain any information: $image_lib_partners");
+		return;
+	}
+	
+	# Get the local image repository path
+	my $image_repository_path_local = $self->get_image_repository_path();
+	if (!$image_repository_path_local) {
+		notify($ERRORS{'WARNING'}, 0, "unable to determine the local image repository path");
+		return;
+	}
+	
+	# Loop through the partners
+	# Find partners which have the image
+	# Check size for each partner
+	# Retrieve image from partner with largest image
+	# It's possible that another partner (management node) is currently copying the image from another managment node
+	# This should prevent copying a partial image
+	my %partner_info;
+	my $largest_partner_image_size = 0;
+	my @partners_with_image;
+	
+	foreach my $partner (@partner_list) {
+		# Get the connection information for the partner management node
+		$partner_info{$partner}{hostname} = $self->data->get_management_node_hostname($partner);
+		$partner_info{$partner}{user} = $self->data->get_management_node_image_lib_user($partner) || 'root';
+		$partner_info{$partner}{identity_key} = $self->data->get_management_node_image_lib_key($partner) || '';
+		$partner_info{$partner}{port} = $self->data->get_management_node_ssh_port($partner) || '22';
+		
+		# Call the provisioning module's get_image_repository_search_paths() subroutine
+		# This returns an array of strings to pass to du
+		$partner_info{$partner}{search_paths} = [$self->get_image_repository_search_paths($partner)];
+		if (!$partner_info{$partner}{search_paths}) {
+			notify($ERRORS{'WARNING'}, 0, "failed to retrieve image repository search paths for partner: $partner");
+			next;
+		}
+		
+		# Run du to get the size of the image files on the partner if the image exists in any of the search paths
+		my $du_command = "du -b " . join(" ", @{$partner_info{$partner}{search_paths}});
+		my ($du_exit_status, $du_output) = run_ssh_command($partner, $partner_info{$partner}{identity_key}, $du_command, $partner_info{$partner}{user}, $partner_info{$partner}{port}, 0);
+		if (!defined($du_output)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to determine if image $image_name exists on $partner_info{$partner}{hostname}");
+			next;
+		}
+		
+		# Loop through the du output lines, parse lines beginning with a number followed by a '/'
+		for my $line (@$du_output) {
+			my ($file_size, $file_path) = $line =~ /^(\d+)\s+(\/.+)/;
+			next if (!defined($file_size) || !defined($file_path));
+			$partner_info{$partner}{file_paths}{$file_path} = $file_size;
+			$partner_info{$partner}{image_size} += $file_size;
+		}
+		
+		# Display the image size if any files were found
+		if ($partner_info{$partner}{image_size}) {
+			notify($ERRORS{'OK'}, 0, "$image_name exists on $partner_info{$partner}{hostname}, size: " . format_number($partner_info{$partner}{image_size}) . " bytes");
+		}
+		else {
+			notify($ERRORS{'OK'}, 0, "$image_name does NOT exist on $partner_info{$partner}{hostname}");
+			next;
+		}
+
+		# Check if the image size is larger than any previously found on other partners
+		if ($partner_info{$partner}{image_size} > $largest_partner_image_size) {
+			@partners_with_image = ();
+		}
+		
+		# Check if the image size is larger than any previously found on other partners
+		if ($partner_info{$partner}{image_size} >= $largest_partner_image_size) {
+			push @partners_with_image, $partner;
+			$largest_partner_image_size = $partner_info{$partner}{image_size};
+		}
+	}
+	
+	# Check if any partner was found
+	if (!@partners_with_image) {
+		notify($ERRORS{'WARNING'}, 0, "unable to find $image_name on other management nodes");
+		return;
+	}
+	
+	notify($ERRORS{'OK'}, 0, "found $image_name on partner management nodes:\n" . join("\n", map { $partner_info{$_}{hostname} } (sort @partners_with_image)));
+	
+	# Choose a random partner so that the same management node isn't used for most transfers
+	my $random_index = int(rand(scalar(@partners_with_image)));
+	my $retrieval_partner = $partners_with_image[$random_index];
+	notify($ERRORS{'OK'}, 0, "selected random retrieval partner: $partner_info{$retrieval_partner}{hostname}");
+	
+	# Create the directory in the image repository
+	my $mkdir_command = "mkdir -pv $image_repository_path_local";
+	my ($mkdir_exit_status, $mkdir_output) = run_command($mkdir_command);
+	if (!defined($mkdir_output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to run command to create image repository directory: $mkdir_command");
+		return;
+	}
+	
+	# Copy each file path to the image repository directory
+	notify($ERRORS{'OK'}, 0, "attempting to retrieve $image_name from $partner_info{$retrieval_partner}{hostname}");
+	for my $partner_file_path (sort keys %{$partner_info{$retrieval_partner}{file_paths}}) {
+		my ($file_name) = $partner_file_path =~ /([^\/]+)$/;
+		if (run_scp_command("$partner_info{$retrieval_partner}{user}\@$retrieval_partner:$partner_file_path", "$image_repository_path_local/$file_name", $partner_info{$retrieval_partner}{key}, $partner_info{$retrieval_partner}{port})) {
+		notify($ERRORS{'OK'}, 0, "image $image_name was copied from $partner_info{$retrieval_partner}{hostname}");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to copy image $image_name from $partner_info{$retrieval_partner}{hostname}");
+			return;
+		}
+	}
+	
+	# Make sure image was retrieved
+	if (!$self->does_image_exist($image_name)) {
+		notify($ERRORS{'WARNING'}, 0, "does_image_exist subroutine returned false for $image_name after it should have been retrieved");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__
 

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=983104&r1=983103&r2=983104&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 Fri Aug  6 19:38:53 2010
@@ -276,6 +276,9 @@ sub initialize {
 	# Store the VM host API object in this object
 	$self->{api} = $vmware_api;
 	
+	# Make sure the VMware product name can be retrieved
+	$self->get_vmhost_product_name() || return;
+	
 	# Make sure the vmx and vmdk base directories can be accessed
 	my $vmx_base_directory_path = $self->get_vmx_base_directory_path() || return;
 	if (!$vmhost_os->file_exists($vmx_base_directory_path)) {
@@ -461,9 +464,6 @@ sub capture {
 		sleep 5;
 	}
 	
-	## Get a lockfile so that only 1 process is operating on VM host files at any one time
-	#my $lockfile = $self->get_lockfile("/tmp/$vmhost_hostname.lock", (60 * 10)) || return;
-	
 	# Rename the vmdk files on the VM host and change the vmdk directory name to the image name
 	# This ensures that the vmdk and vmx files now reside in different directories
 	#   so that the vmdk files can't be deleted when the vmx directory is deleted later on
@@ -1997,13 +1997,77 @@ sub get_vmdk_file_path {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_image_repository_path
+
+ Parameters  : $management_node_identifier (optional)
+ Returns     : 
+ Description :
+
+=cut
+
+sub get_image_repository_path {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
+		return;
+	}
+	
+	return $self->get_repository_vmdk_directory_path();
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_image_repository_search_paths
+
+ Parameters  : 
+ Returns     : 
+ Description : 
+
+=cut
+
+sub get_image_repository_search_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 $management_node_identifier = shift || $self->data->get_management_node_hostname();
+	
+	my $image_name = $self->data->get_image_name();
+	
+	my @repository_search_paths;
+	
+	if (my $vmhost_profile_repository_path = $self->data->get_vmhost_profile_repository_path()) {
+		push @repository_search_paths, "$vmhost_profile_repository_path/$image_name/$image_name*.vmdk";
+	}
+	
+	if (my $management_node_install_path = $self->data->get_management_node_install_path($management_node_identifier)) {
+		push @repository_search_paths, "$management_node_install_path/vmware_images/$image_name/$image_name*.vmdk";
+		push @repository_search_paths, "$management_node_install_path/$image_name/$image_name*.vmdk";
+	}
+	
+	push @repository_search_paths, "/install/vmware_images/$image_name/$image_name*.vmdk";
+	
+	my %seen;
+	@repository_search_paths = grep { !$seen{$_}++ } @repository_search_paths; 
+	#notify($ERRORS{'DEBUG'}, 0, "repository search paths on $management_node_identifier:\n" . join("\n", @repository_search_paths));
+	return @repository_search_paths;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 get_repository_vmdk_base_directory_path
 
  Parameters  : none
  Returns     : string
  Description : Returns the image repository directory path on the management
-               node under which the vmdk directories for all of the images
-               reside.  The preferred database value to use is vmprofile.repositorypath. If this is not available, managementnode.installpath is retrieved and "/vmware_images" is appended. If this is not available, "/install/vmware
+					node under which the vmdk directories for all of the images
+					reside. The preferred database value to use is
+					vmprofile.repositorypath. If this is not available,
+					managementnode.installpath is retrieved and "/vmware_images" is
+					appended. If this is not available, "/install/vmware_images" is
+					returned.
 					Example:
                repository vmdk file path: /install/vmware_images/vmwarewinxp-base234-v12/vmwarewinxp-base234-v12.vmdk
                repository vmdk base directory path: /install/vmware_images
@@ -2018,9 +2082,6 @@ sub get_repository_vmdk_base_directory_p
 		return;
 	}
 	
-	# Return the path stored in this object if it has already been determined
-	return $self->{repository_base_directory_path} if (defined $self->{repository_base_directory_path});
-	
 	my $repository_vmdk_base_directory_path;
 	
 	# Attempt the retrieve vmhost.repositorypath
@@ -2037,9 +2098,7 @@ sub get_repository_vmdk_base_directory_p
 		notify($ERRORS{'WARNING'}, 0, "unable to retrieve repository path from VM profile or management node install path, returning '/install/vmware_images'");
 	}
 	
-	# Set a value in this object so this doesn't have to be retrieved more than once
-	$self->{repository_base_directory_path} = $repository_vmdk_base_directory_path;
-	return $self->{repository_base_directory_path};
+	return $repository_vmdk_base_directory_path;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -2098,7 +2157,8 @@ sub get_repository_vmdk_file_path {
  Parameters  : none
  Returns     : boolean
  Description : Determines if a VM should be persistent or not based on whether
-               or not the reservation is an imaging reservation.
+               or not the reservation is an imaging reservation or if the end
+               time is more than 24 hours in the future.
 
 =cut
 
@@ -2113,9 +2173,17 @@ sub is_vm_persistent {
 	if ($request_forimaging) {
 		return 1;
 	}
-	else {
-		return 0;
+	
+	# Return true if the request end time is more than 24 hours in the future
+	my $end_epoch = convert_to_epoch_seconds($self->data->get_request_end_time());
+	my $now_epoch = time();
+	my $end_hours = (($end_epoch - $now_epoch) / 60 / 60);
+	if ($end_hours >= 24) {
+		notify($ERRORS{'DEBUG'}, 0, "request end time is " . format_number($end_hours, 1) . " hours in the future, returning true");
+		return 1;
 	}
+	
+	return 0;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -2158,7 +2226,7 @@ sub is_vm_registered {
 
 =head2 get_image_size
 
- Parameters  : $vmdk_file_path (optional)
+ Parameters  : $image_name (optional)
  Returns     : integer
  Description : Returns the size of the image in bytes. If the vmdk file path
                argument is not supplied and the VM disk type in the VM profile
@@ -2176,73 +2244,67 @@ sub get_image_size {
 	}
 	
 	my $vmhost_hostname = $self->data->get_vmhost_hostname() || return;
-	my $vmprofile_vmdisk = $self->data->get_vmhost_profile_vmdisk() || return;
-	my $image_name = $self->data->get_image_name() || return;
 	
-	# Attempt to get the vmdk file path argument
-	# If not supplied, use the default vmdk file path for this reservation
-	my $vmdk_file_path = shift;
-	
-	# Try to retrieve the image size from the repository if an argument was not supplied and localdisk is being used
-	if (!$vmdk_file_path && $vmprofile_vmdisk eq "localdisk") {
-		my $repository_vmdk_directory_path = $self->get_repository_vmdk_directory_path() || return;
-		notify($ERRORS{'DEBUG'}, 0, "vm disk type is $vmprofile_vmdisk, checking size of vmdk directory in image repository: $repository_vmdk_directory_path");
+	# Attempt to get the image name argument
+	my $image_name = shift;
+	if (!$image_name) {
+		$image_name = $self->data->get_image_name() || return;
+	}
+	
+	my $image_size_bytes;
+	
+	# Try to retrieve the image size from the repository if localdisk is being used
+	if (my $repository_vmdk_base_directory_path = $self->get_repository_vmdk_base_directory_path()) {
+		
+		my $search_path = "$repository_vmdk_base_directory_path/$image_name/$image_name*.vmdk";
+		
+		notify($ERRORS{'DEBUG'}, 0, "checking size of image in image repository: $search_path");
 		
 		# Run du specifying image repository directory as an argument
-		my ($exit_status, $output) = run_command("du -bc \"$repository_vmdk_directory_path\"", 1);
+		my ($exit_status, $output) = run_command("du -bc $search_path", 1);
 		if (!defined($output)) {
-			notify($ERRORS{'WARNING'}, 0, "failed to run command to determine size of vmdk directory in image repository: $repository_vmdk_directory_path");
+			notify($ERRORS{'WARNING'}, 0, "failed to run command to determine size of image in image repository: $search_path");
+		}
+		elsif (grep(/no such file/i, @$output)) {
+			notify($ERRORS{'DEBUG'}, 0, "image does not exist in image repository");
 		}
 		elsif (grep(/du: /i, @$output)) {
-			notify($ERRORS{'WARNING'}, 0, "error occurred attempting to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
+			notify($ERRORS{'WARNING'}, 0, "error occurred attempting to determine size of image in image repository: $search_path, output:\n" . join("\n", @$output));
 		}
-		else {
-			my ($total_line) = grep(/total/, @$output);
-			if (!$total_line) {
-				notify($ERRORS{'WARNING'}, 0, "unable to locate 'total' line in du output while attempting to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
+		elsif (my ($total_line) = grep(/total/, @$output)) {
+			($image_size_bytes) = $total_line =~ /(\d+)/;
+			if (defined($image_size_bytes)) {
+				notify($ERRORS{'DEBUG'}, 0, "retrieved size of image in image repository: $image_size_bytes");
 			}
 			else {
-				my ($bytes_used) = $total_line =~ /(\d+)/;
-				if ($bytes_used =~ /^\d+$/) {
-					my $mb_used = format_number(($bytes_used / 1024 / 1024), 1);
-					my $gb_used = format_number(($bytes_used / 1024 / 1024 / 1024), 2);
-					notify($ERRORS{'DEBUG'}, 0, "size of $image_name image in image repository: " . format_number($bytes_used) . " bytes ($mb_used MB, $gb_used GB)");
-					return $bytes_used;
-				}
-				else {
-					notify($ERRORS{'WARNING'}, 0, "failed to parse du output to determine size of vmdk directory in image repository: $repository_vmdk_directory_path, output:\n" . join("\n", @$output));
-					return;
-				}
+				notify($ERRORS{'WARNING'}, 0, "failed to parse du output to determine size of vmdk directory in image repository: $search_path, output:\n" . join("\n", @$output));
 			}
 		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "unable to locate 'total' line in du output while attempting to determine size of vmdk directory in image repository: $search_path, output:\n" . join("\n", @$output));
+		}
 	}
 	
-	# Get the vmdk file path if not specified as an argument
-	if (!$vmdk_file_path) {
-		$vmdk_file_path = $self->get_vmdk_file_path() || return;
-	}
-	
-	# Extract the directory path and file prefix from the vmdk file path
-	my ($vmdk_directory_path, $vmdk_file_prefix) = $vmdk_file_path =~ /^(.+)\/([^\/]+)\.vmdk$/;
-	if (!$vmdk_directory_path || !$vmdk_file_prefix) {
-		notify($ERRORS{'WARNING'}, 0, "unable to determine vmdk directory path and vmdk file prefix from vmdk file path: $vmdk_file_path");
-		return;
+	# Unable to determine the image size from the image repository, attempt to retrieve size from VM host
+	if (!defined($image_size_bytes)) {
+		# Assemble a search path
+		my $vmdk_base_directory_path = $self->get_vmdk_base_directory_path() || return;
+		my $search_path = "$vmdk_base_directory_path/$image_name/$image_name*.vmdk";
+		
+		# Get the size of the files on the VM host
+		$image_size_bytes = $self->vmhost_os->get_file_size($search_path);
 	}
 	
-	# Assemble a search path
-	my $vmdk_search_path = "$vmdk_directory_path/$vmdk_file_prefix*.vmdk";
 	
-	# Get the size of the files on the VM host
-	my $vmdk_size = $self->vmhost_os->get_file_size($vmdk_search_path);
-	if (!defined($vmdk_size)) {
-		notify($ERRORS{'WARNING'}, 0, "unable to determine the size of vmdk file on VM host $vmhost_hostname:
-			 vmdk file path: $vmdk_file_path
-			 search path: $vmdk_search_path");
+	if (!defined($image_size_bytes)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to determine the size of image in image repository or on VM host");
 		return;
 	}
 	
-	notify($ERRORS{'DEBUG'}, 0, "size of $image_name image on VM host $vmhost_hostname: " . format_number($vmdk_size) . " bytes");
-	return $vmdk_size;
+	my $mb_used = format_number(($image_size_bytes / 1024 / 1024));
+	my $gb_used = format_number(($image_size_bytes / 1024 / 1024 / 1024), 2);
+	notify($ERRORS{'DEBUG'}, 0, "size of $image_name image: " . format_number($image_size_bytes) . " bytes ($mb_used MB, $gb_used GB)");
+	return $image_size_bytes;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -2414,15 +2476,15 @@ sub get_vm_disk_adapter_type {
 	my $vm_host_product_name = $self->get_vmhost_product_name() || return;
 	
 	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());
+	
+	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");
 	}
-	elsif (!$vmdk_controller_type || ($vm_host_product_name =~ /esx/i && $vmdk_controller_type =~ /ide/i)) {
-		
+	
+	if (!$vmdk_controller_type || ($vm_host_product_name =~ /esx/i && $vmdk_controller_type =~ /ide/i)) {
 		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");
@@ -2622,113 +2684,6 @@ sub get_vm_ram {
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 get_lockfile
-
- Parameters  : $file_path, $total_wait_seconds (optional), $attempt_delay_seconds (optional)
- Returns     : filehandle
- Description : Attempts to open and obtain an exclusive lock on the file
-               specified by the file path argument. If unable to obtain an
-               exclusive lock, it will wait up to the value specified by the
-               total wait seconds argument (default: 30 seconds). The number of
-               seconds to wait in between retries can be specified (default: 15
-               seconds).
-
-=cut
-
-sub get_lockfile {
-	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;
-	}
-	
-	# Get the file path argument
-	my ($file_path, $total_wait_seconds, $attempt_delay_seconds) = @_;
-	if (!$file_path) {
-		notify($ERRORS{'WARNING'}, 0, "file path argument was not supplied");
-		return;
-	}
-	
-	# Set the wait defaults if not supplied as arguments
-	$total_wait_seconds = 30 if !$total_wait_seconds;
-	$attempt_delay_seconds = 5 if !$attempt_delay_seconds;
-	
-	# Attempt to open the file
-	notify($ERRORS{'DEBUG'}, 0, "attempting to open file to be exclusively locked: $file_path");
-	my $file_handle = new IO::File($file_path, O_RDONLY | O_CREAT);
-	if (!$file_handle) {
-		notify($ERRORS{'WARNING'}, 0, "failed to open file to be exclusively locked: $file_path, reason: $!");
-		return;
-	}
-	my $fileno = $file_handle->fileno;
-	notify($ERRORS{'DEBUG'}, 0, "opened file to be exclusively locked: $file_path");
-	
-	# Store the fileno and path in %ENV so we can retrieve the path later on
-	$ENV{fileno}{$fileno} = $file_path;
-	
-	# Attempt to lock the file
-	my $wait_message = "attempting to obtain lock on file: $file_path";
-	if ($self->code_loop_timeout(sub{flock($file_handle, LOCK_EX|LOCK_NB)}, [], $wait_message, $total_wait_seconds, $attempt_delay_seconds)) {
-		notify($ERRORS{'DEBUG'}, 0, "obtained an exclusive lock on file: $file_path");
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "failed to obtain lock on file: $file_path");
-		return;
-	}
-	
-	# Store the file handle as a variable and return it
-	return $file_handle;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 release_lockfile
-
- Parameters  : $file_handle
- Returns     : boolean
- Description : Closes the lockfile handle specified by the argument.
-
-=cut
-
-sub release_lockfile {
-	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;
-	}
-	
-	# Get the file handle argument
-	my ($file_handle) = @_;
-	if (!$file_handle) {
-		notify($ERRORS{'WARNING'}, 0, "file handle argument was not supplied");
-		return;
-	}
-	
-	# Make sure the file handle is opened
-	my $fileno = $file_handle->fileno;
-	if (!$fileno) {
-		notify($ERRORS{'WARNING'}, 0, "file handle is not opened");
-		return;
-	}
-	
-	# Get the file path previously stored in %ENV
-	my $file_path = $ENV{fileno}{$fileno} || 'unknown';
-	
-	# Close the file
-	if (close($file_handle)) {
-		notify($ERRORS{'DEBUG'}, 0, "closed file handle: $file_path");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to close file handle: $file_path, reason: $!");
-		return;
-	}
-	
-	delete $ENV{fileno}{$fileno};
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
 =head2 get_vmx_file_paths
 
  Parameters  : none

Added: incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm?rev=983104&view=auto
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm (added)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm Fri Aug  6 19:38:53 2010
@@ -0,0 +1,281 @@
+#!/usr/bin/perl -w
+###############################################################################
+# $Id$
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+=head1 NAME
+
+VCL::Module::Semaphore - VCL module to control semaphores
+
+=head1 SYNOPSIS
+
+ my $semaphore = VCL::Module::Semaphore->new({data_structure => $self->data});
+ $semaphore->get_lockfile($file_path, $total_wait_seconds, $attempt_delay_seconds);
+
+=head1 DESCRIPTION
+
+ A semaphore is used to ensure that only 1 process performs a particular task at
+ a time. An example would be the retrieval of an image from another management
+ node. If multiple reservations are being processed for the same image, each
+ reservation may attempt to retrieve it via SCP at the same time. A
+ VCL::Module::Semaphore can be used to only allow 1 process to retrieve the
+ image. The others will wait until the semaphore is released by the retrieving
+ process.
+
+=cut
+
+##############################################################################
+package VCL::Module::Semaphore;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../..";
+
+# Configure inheritance
+use base qw(VCL::Module);
+
+# Specify the version of this module
+our $VERSION = '2.00';
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+use English qw( -no_match_vars );
+use IO::File;
+use Fcntl qw(:DEFAULT :flock);
+
+use VCL::utils;
+
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_lockfile
+
+ Parameters  : $file_path, $total_wait_seconds (optional), $attempt_delay_seconds (optional)
+ Returns     : filehandle
+ Description : Attempts to open and obtain an exclusive lock on the file
+               specified by the file path argument. If unable to obtain an
+               exclusive lock, it will wait up to the value specified by the
+               total wait seconds argument (default: 30 seconds). The number of
+               seconds to wait in between retries can be specified (default: 15
+               seconds).
+
+=cut
+
+sub get_lockfile {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get the file path argument
+	my ($file_path, $total_wait_seconds, $attempt_delay_seconds) = @_;
+	if (!$file_path) {
+		notify($ERRORS{'WARNING'}, 0, "file path argument was not supplied");
+		return;
+	}
+	
+	# Set the wait defaults if not supplied as arguments
+	$total_wait_seconds = 30 if !$total_wait_seconds;
+	$attempt_delay_seconds = 5 if !$attempt_delay_seconds;
+	
+	# Attempt to lock the file
+	my $wait_message = "attempting to open lockfile";
+	if ($self->code_loop_timeout(\&open_lockfile, [$self, $file_path], $wait_message, $total_wait_seconds, $attempt_delay_seconds)) {
+		return $file_path;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "failed to open lockfile: $file_path");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 open_lockfile
+
+ Parameters  : $file_path
+ Returns     : If successful: IO::File file handle object
+               If failed: false
+ Description : Opens and obtains an exclusive lock on the file specified by the
+               argument.
+
+=cut
+
+sub open_lockfile {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get the file path argument
+	my ($file_path) = @_;
+	if (!$file_path) {
+		notify($ERRORS{'WARNING'}, 0, "file path argument was not supplied");
+		return;
+	}
+	
+	# Attempt to open and lock the file
+	if (my $file_handle = new IO::File($file_path, O_WRONLY|O_CREAT)) {
+		if (flock($file_handle, LOCK_EX | LOCK_NB)) {
+			notify($ERRORS{'DEBUG'}, 0, "opened and obtained an exclusive lock on file: $file_path");
+			
+			# Truncate and print the process information to the file
+			$file_handle->truncate(0);
+			print $file_handle "$$ $0\n";
+			
+			$self->{file_handles}{$file_path} = $file_handle;
+			return $file_handle;
+		}
+		else {
+			notify($ERRORS{'DEBUG'}, 0, "unable to obtain exclusive lock on file: $file_path");
+			$file_handle->close;
+		}
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to open file: $file_path, error:\n$!");
+		return;
+	}
+	
+	# Run lsof to determine which process is locking the file
+	my ($exit_status, $output) = run_command("lsof -Fp $file_path", 1);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to run losf command to determine which process is locking the file: $file_path");
+		return;
+	}
+	elsif (grep(/no such file/i, @$output)) {
+		notify($ERRORS{'WARNING'}, 0, "losf command reports that the file does not exist: $file_path");
+		return;
+	}
+	
+	# Parse the lsof output to determine the PID
+	my @locking_pids = map { /^p(\d+)/ } @$output;
+	if (@locking_pids && grep { $_ eq $PID } @locking_pids) {
+		# The current process already has an exclusive lock on the file
+		# This could happen if open_lockfile is called more than once for the same file in the same scope
+		notify($ERRORS{'WARNING'}, 0, "file is already locked by this process: @locking_pids");
+		return;
+	}
+	elsif (@locking_pids) {
+		notify($ERRORS{'DEBUG'}, 0, "file is locked by another process: @locking_pids");
+		return;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "unable to determine PIDs from lsof output\n:" . join("\n", @$output));
+	}
+	
+	return 0;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 release_lockfile
+
+ Parameters  : $file_path
+ Returns     : boolean
+ Description : Releases the exclusive lock and closes the lockfile handle
+               specified by the argument.
+
+=cut
+
+sub release_lockfile {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get the file path argument
+	my $file_path = shift;
+	if (!$file_path) {
+		notify($ERRORS{'WARNING'}, 0, "file path argument was not supplied");
+		return;
+	}
+	
+	my $file_handle = $self->{file_handles}{$file_path};
+	if (!$file_handle) {
+		notify($ERRORS{'WARNING'}, 0, "file handle is not saved in this object for file path: $file_path");
+		return;
+	}
+	
+	# Make sure the file handle is opened
+	my $fileno = $file_handle->fileno;
+	if (!$fileno) {
+		notify($ERRORS{'WARNING'}, 0, "file is not opened: $file_path");
+	}
+	
+	# Close the file
+	if (!close($file_handle)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to close file: $file_path, reason: $!");
+	}
+	
+	# Delete the file
+	if (unlink($file_path)) {
+		notify($ERRORS{'DEBUG'}, 0, "deleted file: $file_path");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete file: $file_path, reason: $!");
+	}
+	
+	delete $self->{file_handles}{$file_path};
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 DESTROY
+
+ Parameters  : none
+ Returns     : nothing
+ Description : Destroys the semaphore object. The files opened and exclusively
+               locked by the semaphore object are closed and deleted.
+
+=cut
+
+sub DESTROY {
+	my $self = shift;
+	notify($ERRORS{'DEBUG'}, 0, "destructor called: $self");
+	
+	for my $file_path (keys %{$self->{file_handles}}) {
+		$self->release_lockfile($file_path);
+	}
+	
+	# Check for an overridden destructor
+	$self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
+} ## end sub DESTROY
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Propchange: incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/vcl/trunk/managementnode/lib/VCL/Module/Semaphore.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Modified: incubator/vcl/trunk/managementnode/lib/VCL/utils.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=983104&r1=983103&r2=983104&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/utils.pm Fri Aug  6 19:38:53 2010
@@ -5404,11 +5404,8 @@ sub run_ssh_command {
 	# -p <port>, Port to connect to on the remote host.
 	# -x, Disables X11 forwarding.
 	# Dont use: -q, Quiet mode.  Causes all warning and diagnostic messages to be suppressed.
-	my $ssh_command = "$ssh_path $identity_paths -l $user -p $port -x $node '$command'";
-
-	# Redirect standard output and error output so all messages are captured
-	$ssh_command .= ' 2>&1';
-
+	my $ssh_command = "$ssh_path $identity_paths -l $user -p $port -x $node '$command 2>&1' 2>&1";
+	
 	# Execute the command
 	my $ssh_output;
 	my $ssh_output_formatted;
@@ -5443,10 +5440,10 @@ sub run_ssh_command {
 		else {
 			notify($ERRORS{'DEBUG'}, 0, "attempt $attempts/$max_attempts: executing SSH command on $node:\n$ssh_command") if $output_level;
 		}
-
+		
 		# Execute the command
 		$ssh_output = `$ssh_command`;
-		
+
 		# Bits 0-7 of $? are set to the signal the child process received that caused it to die
 		my $signal_number = $? & 127;