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__