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:\...@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__
 


Reply via email to