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/16 22:11:44 UTC

svn commit: r986121 - in /incubator/vcl/trunk/managementnode/lib/VCL: Module/OS/Windows.pm Module/Provisioning/VMware/VMware.pm Module/Provisioning/VMware/vSphere_SDK.pm utils.pm

Author: arkurth
Date: Mon Aug 16 20:11:44 2010
New Revision: 986121

URL: http://svn.apache.org/viewvc?rev=986121&view=rev
Log:
VCL-164
Added a few subroutines to Windows.pm which allow the product keys and KMS servers to be configured via the command line when vcld is run with the -setup argument. Added helper subroutines to utils.pm which display menus and gather input from the user.

VCL-298
Added calls to normalize_file_path when the datastore or VM paths are retrieved from the DataStructure object. This removes the escaping before spaces.

Modified:
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
    incubator/vcl/trunk/managementnode/lib/VCL/utils.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=986121&r1=986120&r2=986121&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Mon Aug 16 20:11:44 2010
@@ -7931,6 +7931,18 @@ sub get_product_key {
 		return;
 	}
 	
+	# Normalize the product name string from the registry
+	# Remove Microsoft from the beginning - some products have this and some don't
+	$product_name =~ s/Microsoft//ig;
+	# Remove anything in parenthesis such as (R) or (TM)
+	$product_name =~ s/\(.*\)//ig;
+	# Replace spaces with %
+	$product_name =~ s/\s/%/ig;
+	# Add % to the beginning and end
+	$product_name = "%$product_name%";
+	# Replace multiple % characters with a single %
+	$product_name =~ s/%+/%/ig;
+	
 	# Create the affiliation-specific select statement
 	# Check if the affiliation identifier is a number or word
 	# If a number, use affiliation.id directly
@@ -8003,12 +8015,87 @@ EOF
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_product_key_info
+
+ Parameters  : none
+ Returns     : hash reference
+ Description : Returns the contents of the winProductKey table as a hash
+               reference. The hash keys are the affiliation IDs.
+
+=cut
+
+sub get_product_key_info {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Create the select statement
+	my $select_statement = <<EOF;
+SELECT
+*
+FROM
+winProductKey
+EOF
+	
+	# Call the database select subroutine
+	my @selected_rows = database_select($select_statement);
+	
+	# Transform the array of database rows into a hash
+	my %product_key_info;
+	map { $product_key_info{$_->{affiliationid}}{$_->{productname}} = $_->{productkey} } @selected_rows;
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved product key info:\n" . format_data(\%product_key_info));
+	return \%product_key_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_kms_server_info
+
+ Parameters  : none
+ Returns     : hash reference
+ Description : Returns the contents of the winKMS table as a hash
+               reference. The hash keys are the affiliation IDs.
+
+=cut
+
+sub get_kms_server_info {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Create the select statement
+	my $select_statement = <<EOF;
+SELECT
+*
+FROM
+winKMS
+EOF
+	
+	# Call the database select subroutine
+	my @selected_rows = database_select($select_statement);
+	
+	# Transform the array of database rows into a hash
+	my %kms_server_info;
+	map { $kms_server_info{$_->{affiliationid}}{$_->{address}} = $_->{port} } @selected_rows;
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved KMS server info:\n" . format_data(\%kms_server_info));
+	return \%kms_server_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 set_product_key
 
  Parameters  : $affiliation_id, $product_name, $product_key
  Returns     : If successful: true
                If failed: false
- Description : Inserts or updates a row in the winKMS table in the database.
+ Description : Inserts or updates a row in the winProductKey table in the
+               database.
 
 =cut
 
@@ -8090,6 +8177,103 @@ EOF
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 delete_product_key
+
+ Parameters  : $affiliation_id, $product_name
+ Returns     : If successful: true
+               If failed: false
+ Description : Deletes a row from the winProductKey table in the database.
+
+=cut
+
+sub delete_product_key {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get and check the arguments
+	my ($affiliation_id, $product_name, $product_key) = @_;
+	if (!defined($affiliation_id) || !defined($product_name) || !defined($product_key)) {
+		notify($ERRORS{'WARNING'}, 0, "affiliation ID, product name, and product key arguments not passed correctly");
+		return;
+	}
+	
+	# Construct the delete statement
+	my $delete_statement = <<"EOF";
+DELETE FROM
+winProductKey
+WHERE
+affiliationid = $affiliation_id
+AND productname = '$product_name'
+AND productkey = '$product_key'
+EOF
+
+	# Execute the delete statement
+	my $delete_result = database_execute($delete_statement);
+	if (defined($delete_result)) {
+		notify($ERRORS{'DEBUG'}, 0, "deleted product key from database:\naffiliation ID: $affiliation_id\nproduct name: $product_name\nproduct key: $product_key, result: $delete_result");
+	
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete product key from database:\naffiliation ID: $affiliation_id\nproduct name: $product_name\nproduct key: $product_key");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_kms_server
+
+ Parameters  : $affiliation_id, $address
+ Returns     : If successful: true
+               If failed: false
+ Description : Deletes a row from the winKMS table in the database.
+
+=cut
+
+sub delete_kms_server {
+	my $self = shift;
+	if (ref($self) !~ /windows/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get and check the arguments
+	my ($affiliation_id, $address) = @_;
+	if (!defined($affiliation_id) || !defined($address)) {
+		notify($ERRORS{'WARNING'}, 0, "affiliation ID and KMS server address arguments not passed correctly");
+		return;
+	}
+	
+	# Construct the delete statement
+	my $delete_statement = <<"EOF";
+DELETE FROM
+winKMS
+WHERE
+affiliationid = $affiliation_id
+AND address = '$address'
+EOF
+
+	# Execute the delete statement
+	my $delete_result = database_execute($delete_statement);
+	if (defined($delete_result)) {
+		notify($ERRORS{'DEBUG'}, 0, "deleted KMS server from database:\naffiliation ID: $affiliation_id\naddress: $address, result: $delete_result");
+	
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete product key from database:\naffiliation ID: $affiliation_id\naddress: $address");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 get_kms_servers
 
  Parameters  : $affiliation_identifier (optional)
@@ -8593,6 +8777,558 @@ sub disable_shutdown_event_tracker {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 setup
+
+ Parameters  : none
+ Returns     : 
+ Description : Presents a command-line menu interface to the user to configure
+               the Windows OS modules when vcld is run in setup mode.
+
+=cut
+
+sub setup {
+	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;
+	}
+	
+	push @{$ENV{setup_path}}, 'Windows';
+	
+	my @operation_choices = (
+		'Configure Product Keys',
+		'Configure KMS Servers',
+	);
+	
+	my @setup_path = @{$ENV{setup_path}};
+	OPERATION: while (1) {
+		@{$ENV{setup_path}} = @setup_path;
+		
+		print '-' x 76 . "\n";
+		
+		$self->setup_check();
+		
+		print "Choose an operation:\n";
+		my $operation_choice_index = setup_get_array_choice(@operation_choices);
+		last if (!defined($operation_choice_index));
+		my $operation_name = $operation_choices[$operation_choice_index];
+		print "\n";
+		
+		push @{$ENV{setup_path}}, $operation_name;
+		
+		if ($operation_name =~ /product keys/i) {
+			$self->setup_product_keys();
+		}
+		elsif ($operation_name =~ /kms/i) {
+			$self->setup_kms_servers();
+		}
+	}
+	
+	pop @{$ENV{setup_path}};
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_check
+
+ Parameters  : none
+ Returns     :
+ Description : Checks various configuration settings and displays a message to
+					the user if any important settings are not configured. This gets
+					called every time Windows.pm::setup() is called.
+
+=cut
+
+sub setup_check {
+	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;
+	}
+	
+	my @messages;
+	
+	# Get a hash containing all of the information from the affiliation table
+	my $affiliation_info = get_affiliation_info();
+	if (!$affiliation_info) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve affiliation info");
+		return;
+	}
+	
+	my ($global_affiliation_id) = grep { $affiliation_info->{$_}{name} =~ /^global$/i } (keys %$affiliation_info);
+	if (!defined($global_affiliation_id)) {
+		print "ERROR: unable to determine global affiliation ID:\n" . format_data($affiliation_info) . "\n";
+		return;
+	}
+	
+	# Get the product key information from the database
+	my $product_key_info = $self->get_product_key_info();
+	if (!defined($product_key_info)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve product key information from the database");
+		return;
+	}
+	
+	my @product_names = keys %{$product_key_info->{$global_affiliation_id}};
+	
+	if (!grep(/Windows XP/, @product_names)) {
+		push @messages, "A Windows XP product key is not configured for the Global affiliation. Captured Windows XP images using Sysprep may fail to load if the product key is not configured.";
+	}
+	if (!grep(/Server 2003/, @product_names)) {
+		push @messages, "A Windows Server 2003 product key is not configured for the Global affiliation. Captured Windows Server 2003 images using Sysprep may fail to load if the product key is not configured.";
+	}
+	
+	for my $message (@messages) {
+		chomp $message;
+		setup_print_wrap("*** $message ***\n\n");
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_product_keys
+
+ Parameters  : none
+ Returns     : nothing
+ Description : Used to list, set, and delete product keys from the winProductKey
+               table in the database when vcld is run in setup mode.
+
+=cut
+
+sub setup_product_keys {
+	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 a hash containing all of the information from the affiliation table
+	my $affiliation_info = get_affiliation_info();
+	if (!$affiliation_info) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve affiliation info");
+		return;
+	}
+	
+	my @product_names = (
+		'Windows XP',
+		'Windows Server 2003',
+		'Windows Vista Business',
+		'Windows Vista Business N',
+		'Windows Vista Enterprise',
+		'Windows Vista Enterprise N',
+		'Windows Server 2008 Datacenter',
+		'Windows Server 2008 Datacenter without Hyper-V',
+		'Windows Server 2008 for Itanium-Based Systems',
+		'Windows Server 2008 Enterprise',
+		'Windows Server 2008 Enterprise without Hyper-V',
+		'Windows Server 2008 Standard',
+		'Windows Server 2008 Standard without Hyper-V',
+		'Windows Web Server 2008',
+		'Windows Server 2008 HPC',
+		'Windows 7 Professional',
+		'Windows 7 Professional N',
+		'Windows 7 Professional E',
+		'Windows 7 Enterprise',
+		'Windows 7 Enterprise N',
+		'Windows 7 Enterprise E',
+		'Windows Server 2008 R2 Web',
+		'Windows Server 2008 R2 HPC edition',
+		'Windows Server 2008 R2 Standard',
+		'Windows Server 2008 R2 Enterprise',
+		'Windows Server 2008 R2 Datacenter',
+		'Windows Server 2008 R2 for Itanium-based Systems',
+		'Other',
+	);
+	
+	my @operation_choices = (
+		'List Product Keys',
+		'Add Product Key',
+		'Delete Product Key',
+	);
+	
+	my @setup_path = @{$ENV{setup_path}};
+	OPERATION: while (1) {
+		@{$ENV{setup_path}} = @setup_path;
+		
+		print '-' x 76 . "\n";
+		
+		print "Choose an operation:\n";
+		my $operation_choice_index = setup_get_array_choice(@operation_choices);
+		last if (!defined($operation_choice_index));
+		my $operation_name = $operation_choices[$operation_choice_index];
+		print "\n";
+		
+		push @{$ENV{setup_path}}, $operation_name;
+		
+		if ($operation_name =~ /list/i) {
+			$self->setup_display_product_key_info();
+			print "\n";
+		}
+		
+		elsif ($operation_name =~ /add/i) {
+			print "Choose an affiliation:\n";
+			my $affiliation_id = setup_get_hash_choice($affiliation_info, 'name');
+			next if (!defined($affiliation_id));
+			my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+			print "Selected affiliation: $affiliation_name\n\n";
+			
+			$self->setup_display_product_key_info($affiliation_id);
+			print "\n";
+			
+			print "Choose a Windows product:\n";
+			my $product_choice_index = setup_get_array_choice(@product_names);
+			next OPERATION if (!defined($product_choice_index));
+			
+			my $product_name = $product_names[$product_choice_index];
+			if ($product_name eq 'Other') {
+				$product_name = setup_get_input_string("Enter a product name");
+				next OPERATION if (!defined($product_name));
+			}
+			print "Windows product: $product_name\n\n";
+			
+			my $product_key;
+			while (!$product_key) {
+				$product_key = setup_get_input_string("Enter the product key xxxxx-xxxxx-xxxxx-xxxxx-xxxxx");
+				next OPERATION if (!defined($product_key));
+				if ($product_key !~ /(\w{5}-?){5}/) {
+					print "Product key is not in the correct format: $product_key\n";
+					$product_key = 0;
+				}
+			}
+			$product_key = uc($product_key);
+			print "\n";
+			
+			# Attempt to set the product key in the database
+			if ($self->set_product_key($affiliation_id, $product_name, $product_key)) {
+				print "Product key has been saved to the database:\nAffiliation: $affiliation_name\nProduct name: $product_name\nProduct key: $product_key\n";
+			}
+			else {
+				print "ERROR: failed to save product key to the database:\nAffiliation: $affiliation_name\nProduct name: $product_name\nProduct key: $product_key\n";
+			}
+		}
+		
+		elsif ($operation_name =~ /delete/i) {
+			# Get the product key information from the database
+			my $product_key_info = $self->get_product_key_info();
+			if (!defined($product_key_info)) {
+				notify($ERRORS{'WARNING'}, 0, "failed to retrieve product key information from the database");
+				return;
+			}
+			
+			my %product_keys;
+			for my $affiliation_id (keys %$product_key_info) {
+				my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+				
+				for my $product_name (keys %{$product_key_info->{$affiliation_id}}) {
+					my $product_key = $product_key_info->{$affiliation_id}{$product_name};
+					
+					my $product_key_choice_name = "$affiliation_name: '$product_name' ($product_key)";
+					
+					$product_keys{$product_key_choice_name}{affiliation_id} = $affiliation_id;
+					$product_keys{$product_key_choice_name}{product_name} = $product_name;
+					$product_keys{$product_key_choice_name}{product_key} = $product_key;
+				}
+			}
+			
+			# Choose an affiliation with populated product keys
+			print "Choose a product key to delete:\n";
+			my $product_key_choice_name = setup_get_hash_choice(\%product_keys);
+			next if (!defined($product_key_choice_name));
+			print "\n";
+			
+			my $affiliation_id = $product_keys{$product_key_choice_name}{affiliation_id};
+			my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+			my $product_name = $product_keys{$product_key_choice_name}{product_name};
+			my $product_key = $product_keys{$product_key_choice_name}{product_key};
+			
+			# Attempt to delete the product key from the database
+			if ($self->delete_product_key($affiliation_id, $product_name, $product_key)) {
+				print "Product key for has been deleted from the database:\nAffiliation: $affiliation_name\nProduct name: $product_name\nProduct key: $product_key\n";
+			}
+			else {
+				print "ERROR: failed to delete product key from the database:\nAffiliation: $affiliation_name\nProduct name: $product_name\nProduct key: $product_key\n";
+			}
+		}
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_display_product_key_info
+
+ Parameters  : $affiliation_id (optional)
+ Returns     :
+ Description : Displays the product keys configured in the winProductKey table
+               in the database. If an affiliation ID argument is specified, only
+               the information for that affiliation is displayed.
+
+=cut
+
+sub setup_display_product_key_info {
+	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 a hash containing all of the information from the affiliation table
+	my $affiliation_info = get_affiliation_info();
+	if (!$affiliation_info) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve affiliation info");
+		return;
+	}
+	
+	# Get the affiliation ID argument if it was specified
+	my $affiliation_id_argument = shift;
+	if ($affiliation_id_argument && !defined($affiliation_info->{$affiliation_id_argument})) {
+		notify($ERRORS{'WARNING'}, 0, "affiliation does not exist for affiliation ID argument: $affiliation_id_argument");
+		return;
+	}
+	
+	# Get the product key information from the database
+	my $product_key_info = $self->get_product_key_info();
+	if (!defined($product_key_info)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve product key information from the database");
+		return;
+	}
+	
+	# Print the title
+	if ($affiliation_id_argument) {
+		my $affiliation_name = $affiliation_info->{$affiliation_id_argument}{name};
+		print "Product key configuration for $affiliation_name ($affiliation_id_argument):\n";
+	}
+	else {
+		print "Product key configuration for all affiliations:\n";
+	}
+	
+	
+	my $product_key_info_string;
+	for my $affiliation_id (sort { $a <=> $b } keys %$product_key_info) {
+		
+		if (defined($affiliation_id_argument) && $affiliation_id ne $affiliation_id_argument) {
+			next;
+		}
+		
+		my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+		
+		$product_key_info_string .= "$affiliation_name ($affiliation_id):\n";
+		for my $product_name (keys %{$product_key_info->{$affiliation_id}}) {
+			my $product_key = $product_key_info->{$affiliation_id}{$product_name};
+			$product_key_info_string .= "   $product_name: $product_key\n";
+		}
+	}
+	
+	$product_key_info_string = "<not configured>\n" if !$product_key_info_string;
+	print "$product_key_info_string";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_kms_servers
+
+ Parameters  : none
+ Returns     : nothing
+ Description : Configures KMS servers in the winKMS table in the database when
+               vcld is run in setup mode.
+
+=cut
+
+sub setup_kms_servers {
+	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 a hash containing all of the information from the affiliation table
+	my $affiliation_info = get_affiliation_info();
+	if (!$affiliation_info) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve affiliation info");
+		return;
+	}
+	
+	my @operation_choices = (
+		'List KMS Servers',
+		'Add KMS Server',
+		'Delete KMS Server',
+	);
+	
+	my @setup_path = @{$ENV{setup_path}};
+	OPERATION: while (1) {
+		@{$ENV{setup_path}} = @setup_path;
+		print '-' x 76 . "\n";
+		
+		print "Choose an operation:\n";
+		my $operation_choice_index = setup_get_array_choice(@operation_choices);
+		last if (!defined($operation_choice_index));
+		my $operation_name = $operation_choices[$operation_choice_index];
+		print "\n";
+		
+		push @{$ENV{setup_path}}, $operation_name;
+		
+		if ($operation_name =~ /list/i) {
+			$self->setup_display_kms_server_info();
+			print "\n";
+		}
+		
+		elsif ($operation_name =~ /add/i) {
+			print "Choose an affiliation:\n";
+			my $affiliation_id = setup_get_hash_choice($affiliation_info, 'name');
+			next if (!defined($affiliation_id));
+			my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+			print "Selected affiliation: $affiliation_name\n\n";
+			
+			$self->setup_display_kms_server_info($affiliation_id);
+			print "\n";
+			
+			my $address;
+			while (!$address) {
+				$address = setup_get_input_string("Enter the KMS server host name or address");
+				next OPERATION if (!defined($address));
+				if (!is_valid_dns_host_name($address) && !is_valid_ip_address($address)) {
+					print "Address is not a valid DNS host name or IP address: $address\n";
+					$address = '';
+				}
+			}
+			print "\n";
+			
+			my $port;
+			while (!$port) {
+				$port = setup_get_input_string("Enter the KMS server port", 1688);
+				next OPERATION if (!defined($port));
+				if ($port !~ /^\d+$/) {
+					print "Port must be an integer: $port\n";
+					$port = '';
+				}
+			}
+			
+			print "\n";
+			
+			# Attempt to set the KMS server in the database
+			if ($self->set_kms_server($affiliation_id, $address, $port)) {
+				print "KMS server added to the database:\nAffiliation: $affiliation_name\nAddress: $address\nPort: $port\n";
+			}
+			else {
+				print "ERROR: failed to save product key to the database:\nAffiliation: $affiliation_name\nAddress: $address\nPort: $port\n";
+			}
+		}
+		
+		elsif ($operation_name =~ /delete/i) {
+			# Get the KMS server information from the database
+			my $kms_server_info = $self->get_kms_server_info();
+			if (!defined($kms_server_info)) {
+				notify($ERRORS{'WARNING'}, 0, "failed to retrieve KMS server information from the database");
+				return;
+			}
+			
+			my %kms_servers;
+			for my $affiliation_id (keys %$kms_server_info) {
+				my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+				
+				for my $address (keys %{$kms_server_info->{$affiliation_id}}) {
+					my $port = $kms_server_info->{$affiliation_id}{$address};
+					
+					my $kms_server_choice_name = "$affiliation_name: $address:$port";
+					
+					$kms_servers{$kms_server_choice_name}{affiliation_id} = $affiliation_id;
+					$kms_servers{$kms_server_choice_name}{address} = $address;
+					$kms_servers{$kms_server_choice_name}{port} = $port;
+				}
+			}
+			
+			# Choose an affiliation populated with a KMS server
+			print "Choose a KMS server to delete:\n";
+			my $kms_server_choice_name = setup_get_hash_choice(\%kms_servers);
+			next if (!defined($kms_server_choice_name));
+			print "\n";
+			
+			my $affiliation_id = $kms_servers{$kms_server_choice_name}{affiliation_id};
+			my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+			my $address = $kms_servers{$kms_server_choice_name}{address};
+			my $port = $kms_servers{$kms_server_choice_name}{port};
+			
+			## Attempt to delete the product key from the database
+			if ($self->delete_kms_server($affiliation_id, $address)) {
+				print "KMS server has been deleted from the database:\nAffiliation: $affiliation_name\nAddress: $address\nPort: $port\n";
+			}
+			else {
+				print "ERROR: failed to delete product key from the database:\nAffiliation: $affiliation_name\nAddress: $address\nPort: $port\n";
+			}
+		}
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_display_kms_server_info
+
+ Parameters  : $affiliation_id (optional)
+ Returns     :
+ Description : Displays the KMS server configuration stored in the winKMS table
+               in the database to STDOUT.
+
+=cut
+
+sub setup_display_kms_server_info {
+	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 a hash containing all of the information from the affiliation table
+	my $affiliation_info = get_affiliation_info();
+	if (!$affiliation_info) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve affiliation info");
+		return;
+	}
+	
+	# Get the affiliation ID argument if it was specified
+	my $affiliation_id_argument = shift;
+	if ($affiliation_id_argument && !defined($affiliation_info->{$affiliation_id_argument})) {
+		notify($ERRORS{'WARNING'}, 0, "affiliation does not exist for affiliation ID argument: $affiliation_id_argument");
+		return;
+	}
+	
+	# Get the KMS server information from the database
+	my $kms_server_info = $self->get_kms_server_info();
+	if (!defined($kms_server_info)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve KMS server information from the database");
+		return;
+	}
+	
+	# Print the title
+	if ($affiliation_id_argument) {
+		my $affiliation_name = $affiliation_info->{$affiliation_id_argument}{name};
+		print "KMS server configuration for $affiliation_name ($affiliation_id_argument):\n";
+	}
+	else {
+		print "KMS server configuration for all affiliations:\n";
+	}
+	
+	# Print the KMS serer information
+	my $kms_server_info_string;
+	for my $affiliation_id (sort { $a <=> $b } keys %$kms_server_info) {
+		# Ignore non-matching affiliations if the affiliation ID argument was specified
+		if (defined($affiliation_id_argument) && $affiliation_id ne $affiliation_id_argument) {
+			next;
+		}
+		
+		my $affiliation_name = $affiliation_info->{$affiliation_id}{name};
+		
+		$kms_server_info_string .= "$affiliation_name ($affiliation_id):\n";
+		for my $address (keys %{$kms_server_info->{$affiliation_id}}) {
+			my $port = $kms_server_info->{$affiliation_id}{$address};
+			$kms_server_info_string .= "   $address:$port\n";
+		}
+	}
+	
+	$kms_server_info_string = "<not configured>\n" if !$kms_server_info_string;
+	print "$kms_server_info_string";
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 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=986121&r1=986120&r2=986121&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 Aug 16 20:11:44 2010
@@ -382,7 +382,7 @@ sub capture {
 	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;
-	my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+	my $vmhost_profile_datastore_path = normalize_file_path($self->data->get_vmhost_profile_datastore_path());
 	
 	# Check if VM is responding to SSH before proceeding
 	if (!$self->os->is_ssh_responding()) {
@@ -969,9 +969,7 @@ sub prepare_vmx {
 	my $vm_cpu_count             = $self->data->get_image_minprocnumber() || 1;
 	my $vm_ethernet_adapter_type = $self->get_vm_ethernet_adapter_type() || return;
 	my $vm_eth0_mac              = $self->data->get_computer_eth0_mac_address() || return;
-	my $vm_eth1_mac              = $self->data->get_computer_eth1_mac_address() || return;
-	my $vm_private_ip_address    = $self->data->get_computer_ip_address() || return;
-	my $vm_public_ip_address     = $self->data->get_computer_private_ip_address()  || return;
+	my $vm_eth1_mac              = $self->data->get_computer_eth1_mac_address() || return;	
 	my $virtual_switch_0         = $self->data->get_vmhost_profile_virtualswitch0() || return;
 	my $virtual_switch_1         = $self->data->get_vmhost_profile_virtualswitch1() || return;
 	my $vm_disk_adapter_type     = $self->get_vm_disk_adapter_type() || return;
@@ -1100,11 +1098,9 @@ sub prepare_vmx {
 			 
 			 virtual switch 0: $virtual_switch_0
 			 eth0 MAC address: $vm_eth0_mac
-			 private IP address: $vm_private_ip_address
 			 
 			 virtual switch 1: $virtual_switch_1
 			 eth1 MAC address: $vm_eth1_mac
-			 public IP address: $vm_public_ip_address
 			 
 			 disk adapter type: $vm_disk_adapter_type
 			 disk mode: $vm_disk_mode
@@ -1514,15 +1510,14 @@ sub get_vmx_base_directory_path {
 	
 	my $vmx_base_directory_path;
 	
-	my $vmhost_profile_vmpath = $self->data->get_vmhost_profile_vmpath();
+	my $vmhost_profile_vmpath = normalize_file_path($self->data->get_vmhost_profile_vmpath());
 	if ($vmhost_profile_vmpath) {
 		$vmhost_profile_vmpath =~ s/\\//g;
 		$vmx_base_directory_path = $vmhost_profile_vmpath;
 	}
 	else {
-		my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+		my $vmhost_profile_datastore_path = normalize_file_path($self->data->get_vmhost_profile_datastore_path());
 		if ($vmhost_profile_datastore_path) {
-			$vmhost_profile_datastore_path =~ s/\\//g;
 			$vmx_base_directory_path = $vmhost_profile_datastore_path;
 		}
 		else {
@@ -1707,7 +1702,7 @@ sub get_vmdk_base_directory_path {
 	}
 	else {
 		# Get the VM host profile datastore path
-		my $vmhost_profile_datastore_path = $self->data->get_vmhost_profile_datastore_path();
+		my $vmhost_profile_datastore_path = normalize_file_path($self->data->get_vmhost_profile_datastore_path());
 		if (!$vmhost_profile_datastore_path) {
 			notify($ERRORS{'WARNING'}, 0, "unable to retrieve VM host profile datastore path");
 			return;

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm?rev=986121&r1=986120&r2=986121&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm Mon Aug 16 20:11:44 2010
@@ -1744,6 +1744,19 @@ sub initialize {
 	my $vmhost_username = $self->data->get_vmhost_profile_username();
 	my $vmhost_password = $self->data->get_vmhost_profile_password();
 	
+	if (!$vmhost_hostname) {
+		notify($ERRORS{'WARNING'}, 0, "VM host name could not be retrieved");
+		return;
+	}
+	elsif (!$vmhost_hostname) {
+		notify($ERRORS{'WARNING'}, 0, "VM host username is not configured in the database for the VM profile");
+		return;
+	}
+	elsif (!$vmhost_password) {
+		notify($ERRORS{'WARNING'}, 0, "VM host password is not configured in the database for the VM profile");
+		return;
+	}
+	
 	Opts::set_option('username', $vmhost_username);
 	Opts::set_option('password', $vmhost_password);
 	
@@ -1756,6 +1769,13 @@ sub initialize {
 		"https://$vmhost_hostname:8333/sdk",
 	);
 	
+	# Also add URLs containing the short host name if the VM hostname is a full DNS name
+	if ($vmhost_hostname =~ /\./) {
+		my ($vmhost_short_name) = $vmhost_hostname =~ /^([^\.]+)/;
+		push @possible_vmhost_urls, "https://$vmhost_short_name/sdk";
+		push @possible_vmhost_urls, "https://$vmhost_short_name:8333/sdk";
+	}
+	
 	# Call HostConnect, check how long it takes to connect
 	for my $host_url (@possible_vmhost_urls) {
 		Opts::set_option('url', $host_url);
@@ -2196,7 +2216,7 @@ sub get_normal_path {
 	
 	# Check if path argument is a normal path - does not contain brackets
 	if ($path !~ /\[/) {
-		return $path;
+		return normalize_file_path($path);
 	}
 	
 	# Extract the datastore name from the path
@@ -2252,14 +2272,13 @@ sub get_datastore_name {
 		return;
 	}
 	
-	# Remove any spaces from the beginning and end of the path
-	$path =~ s/(^\s+|\s+$)//g;
-	
 	# Check if path is a datastore path, datastore name will be in brackets
 	if ($path =~ /^\[(.+)\]/) {
 		return $1;
 	}
 	
+	$path = $self->get_normal_path($path);
+	
 	# Get the datastore summary information
 	my $datastore_summaries = $self->get_datastore_summaries();
 	my @datastore_normal_paths;

Modified: incubator/vcl/trunk/managementnode/lib/VCL/utils.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=986121&r1=986120&r2=986121&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/utils.pm Mon Aug 16 20:11:44 2010
@@ -148,6 +148,7 @@ our @EXPORT = qw(
   is_public_ip_address
   is_request_deleted
   is_request_imaging
+  is_valid_dns_host_name
   is_valid_ip_address
   isconnected
   isfilelocked
@@ -180,6 +181,11 @@ our @EXPORT = qw(
   setimageid
   setnextimage
   setstaticaddress
+  setup_confirm
+  setup_get_array_choice
+  setup_get_hash_choice
+  setup_get_input_string
+  setup_print_wrap
   string_to_ascii
   switch_state
   switch_vmhost_id
@@ -8870,6 +8876,9 @@ sub run_command {
 		notify($ERRORS{'DEBUG'}, 0, "executed command: $command, pid: $pid, exit status: $exit_status, output:\n@output");
 	}
 	
+	# Remove newlines from output lines
+	map { chomp $_ } @output;
+	
 	return ($exit_status, \@output);
 }
 	
@@ -9094,22 +9103,75 @@ sub is_valid_ip_address {
 	
 	# Make sure 4 octets were found
 	if (scalar @octets != 4) {
-		notify($ERRORS{'WARNING'}, 0, "IP address does not contain 4 octets: $ip_address, octets:\n" . join("\n", @octets));
+		notify($ERRORS{'DEBUG'}, 0, "IP address does not contain 4 octets: $ip_address, octets:\n" . join("\n", @octets));
 		return 0;
 	}
 	
 	# Make sure address only contains digits
 	if (grep(/\D/, @octets)) {
-		notify($ERRORS{'WARNING'}, 0, "IP address contains a non-digit: $ip_address");
+		notify($ERRORS{'DEBUG'}, 0, "IP address contains a non-digit: $ip_address");
 		return 0;
 	}
 	
 	# Make sure none of the octets is > 255
 	if ($octets[0] > 255 || $octets[0] > 255 || $octets[0] > 255 || $octets[0] > 255) {
-		notify($ERRORS{'WARNING'}, 0, "IP address contains an octet > 255: $ip_address");
+		notify($ERRORS{'DEBUG'}, 0, "IP address contains an octet > 255: $ip_address");
 		return 0;
 	}
 	
+	notify($ERRORS{'DEBUG'}, 0, "IP address is valid: $ip_address");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_valid_dns_host_name
+
+ Parameters  : $dns_host_name
+ Returns     : If valid: true
+               If not valid: false
+ Description : Determines if the argument is a valid DNS host name.
+
+=cut
+
+sub is_valid_dns_host_name {
+	my $dns_host_name = shift;
+	if (!$dns_host_name) {
+		notify($ERRORS{'WARNING'}, 0, "DNS host name argument was not specified");
+		return;
+	}
+	
+	if (!$dns_host_name) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name argument was not specified");
+		return 0;
+	}
+	
+	if ((my $length = length($dns_host_name)) > 255) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name is too long ($length characters): $dns_host_name");
+		return 0;
+	}
+	
+	if (my @illegal_characters = $dns_host_name =~ /([^\da-z\.\-])/i) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name contains illegal characters: " . join(" ", @illegal_characters));
+		return 0;
+	}
+	
+	if ($dns_host_name =~ /\.\./i) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name contains contiguous periods: $dns_host_name");
+		return 0;
+	}
+	
+	if ($dns_host_name !~ /^[\da-z]/i) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name does not begin with a letter or digit: " . string_to_ascii($dns_host_name));
+		return 0;
+	}
+	
+	if (grep(/^\d+$/, split(/\./, $dns_host_name))) {
+		notify($ERRORS{'DEBUG'}, 0, "DNS host name contains a section comprised only of digits: $dns_host_name");
+		return 0;
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "DNS host name is valid: $dns_host_name");
 	return 1;
 }
 
@@ -9539,7 +9601,7 @@ sub get_affiliation_info {
 		}
 	}
 	
-	#notify($ERRORS{'DEBUG'}, 0, "retrieved affiliation info:\n" . format_data(\%affiliation_info_hash));
+	notify($ERRORS{'DEBUG'}, 0, "retrieved affiliation info:\n" . format_data(\%affiliation_info_hash));
 	return \%affiliation_info_hash;
 }
 
@@ -9700,6 +9762,176 @@ sub parent_directory_path {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 setup_get_array_choice
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub setup_get_array_choice {
+	my (@choices) = @_;
+	notify($ERRORS{'DEBUG'}, 0, "choices argument:\n" . join("\n", @choices));
+	
+	my $choice_count = scalar(@choices);
+	
+	while (1) {
+		for (my $i=1; $i<=$choice_count; $i++) {
+			print "$i. $choices[$i-1]\n";
+		}
+		print "\n[" . join("/", @{$ENV{setup_path}}) . "]\n";
+		print "Make a selection (1";
+		print "-$choice_count" if ($choice_count > 1);
+		print ", 'c' to cancel): ";
+		
+		my $choice = <STDIN>;
+		chomp $choice;
+		
+		if ($choice =~ /^c$/i) {
+			return;
+		}
+		if ($choice !~ /^\d+$/ || $choice < 1 || $choice > $choice_count) {
+			print "\n*** Choice must be an integer between 1 and $choice_count ***\n\n";
+		}
+		else {
+			return ($choice - 1);
+		}
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_get_input_string
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub setup_get_input_string {
+	my ($message, $default_value) = @_;
+	if ($default_value) {
+		print "$message [$default_value]: ";
+	}
+	else {
+		print "$message ('c' to cancel): ";
+	}
+	
+	my $input = <STDIN>;
+	chomp $input;
+	if ($input =~ /^c$/i) {
+		return;
+	}
+	elsif ($default_value && !length($input)) {
+		return $default_value;
+	}
+	else {
+		return $input;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_get_hash_choice
+
+ Parameters  : $hash_ref, $display_key ()
+ Returns     :
+ Description :
+
+=cut
+
+sub setup_get_hash_choice {
+	my ($hash_ref, $display_key) = @_;
+	
+	my $choice_count = scalar(keys %$hash_ref);
+	
+	my %choices;
+	for my $key (keys %$hash_ref) {
+		my $display_name;
+		if ($display_key) {
+			$display_name = $hash_ref->{$key}{$display_key};
+		}
+		else {
+			$display_name = $key;
+		}
+		
+		if ($choices{$display_name}) {
+			notify($ERRORS{'WARNING'}, 0, "duplicate hash keys containing the value '$display_key' = '$display_name', hash argument:\n" . format_data($hash_ref));
+		}
+		
+		$choices{$display_name} = $key;
+	}
+	
+	my $choice_index = setup_get_array_choice(sort keys %choices);
+	return if (!defined($choice_index));
+	
+	my $choice_name = (sort keys %choices)[$choice_index];
+	return $choices{$choice_name};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_confirm
+
+ Parameters  : $message
+ Returns     : boolean
+ Description : Displays the message to the user and loops until they enter Y or
+               N.
+
+=cut
+
+sub setup_confirm {
+	my ($message) = @_;
+	
+	while (1) {
+		print "$message (Y/N)? ";
+		my $input = <STDIN>;
+		if ($input =~ /^y(es)?$/i) {
+			return 1;
+		}
+		elsif ($input =~ /^n(o)?$/i) {
+			return 0;
+		}
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 setup_print_wrap
+
+ Parameters  : $message, $columns (optional)
+ Returns     :
+ Description : Prints a message to STDOUT formatted to the column width. 76 is
+               the default column value.
+
+=cut
+
+sub setup_print_wrap {
+	my ($message, $columns) = @_;
+	$columns = 76 if !defined($columns);
+	
+	return if !$message;
+	
+	# Save the leading and trailing lines then remove them from the string
+	# This is done so wrap doesn't lose them
+	my ($leading_newlines) = $message =~ /^(\n+)/;
+	$message =~ s/^\n+//g;
+	my ($trailing_newlines) = $message =~ /(\n+)$/;
+	$message =~ s/\n+$//g;
+	
+	# Wrap text for lines
+	local($Text::Wrap::columns) = $columns;
+	print $leading_newlines if $leading_newlines;
+	print wrap('', '', $message);
+	print $trailing_newlines if $trailing_newlines;
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 1;
 __END__