Author: arkurth
Date: Mon Dec 15 20:53:50 2014
New Revision: 1645748

URL: http://svn.apache.org/r1645748
Log:
VCL-767
Added utils.pm::determine_remote_connection_target. This get passed an IP 
address or hostname of a target to connect to. It attempts to determine which 
to use to connect. The preference is now to use the private IP address. If this 
is not populated in the database, it attempts to resolve the hostname and use 
the resolved IP address. If all else fails, the original argument is returned.

Added utils.pm::get_computer_current_private_ip_address. This retrieves the 
current privateIPaddress from the database.

Updated utils.pm::nmap_port, run_ssh_command, and run_scp_command to use the 
value returned from determine_remote_connection_target.

Updated DataStructure.pm::get_computer_private_ip_address to check the value in 
the database. The database value is authoritative. DataStructure.pm will get 
the correct value if updated outside of DataStructure.pm.

Updated OS.pm::execute_new to call determine_remote_connection_target and use 
the result to make the remote connection.

Modified:
    vcl/trunk/managementnode/lib/VCL/DataStructure.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS.pm
    vcl/trunk/managementnode/lib/VCL/utils.pm

Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1645748&r1=1645747&r2=1645748&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Mon Dec 15 20:53:50 2014
@@ -1505,35 +1505,55 @@ sub get_computer_private_ip_address {
        
        my $suppress_warning = shift;
        
+       my $computer_id = $self->get_computer_id();
        my $computer_hostname = $self->get_computer_hostname();
-       if (!$computer_hostname) {
-               notify($ERRORS{'WARNING'}, 0, "computer hostname is not stored 
in this DataStructure object");
+       if (!defined($computer_id) || !defined($computer_hostname)) {
+               notify($ERRORS{'WARNING'}, 0, "computer ID and hostname are not 
stored in this DataStructure object");
                return;
        }
        
-       # Check if this is being called by set_computer_private_ip_address
-       # Don't display log messages if this is the case to avoid confusion
-       my $display_output = 1;
-       if (get_calling_subroutine() =~ /set_computer_private_ip_address/) {
-               $display_output = 0;
-       }
-       
        # Check if the IP address is already stored
-       my $private_ip_address = 
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress};
-       if ($private_ip_address) {
-               notify($ERRORS{'DEBUG'}, 0, "returning private IP address of 
$computer_hostname already stored in this DataStructure object: 
$private_ip_address") if $display_output;
-               return $private_ip_address;
-       }
+       my $data_structure_private_ip_address = 
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress};
+       my $env_private_ip_address = 
$ENV{computer_private_ip_address}{$computer_id};
        
-       if ($display_output) {
-               if ($suppress_warning) {
-                       notify($ERRORS{'DEBUG'}, 0, "private IP address of 
$computer_hostname is not set in DataStructure object")
+       # Check if private IP adddress is stored in %ENV and differs from this 
object's data
+       if ($data_structure_private_ip_address) {
+               if ($env_private_ip_address) {
+                       if ($env_private_ip_address =~ /null/i) {
+                               notify($ERRORS{'DEBUG'}, 0, "private IP address 
for $computer_hostname ($computer_id) is stored in this object 
$data_structure_private_ip_address, \%ENV is set to null, deleting private IP 
address from this object");
+                               delete 
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress};
+                               return;
+                       }
+                       elsif ($data_structure_private_ip_address eq 
$env_private_ip_address) {
+                               notify($ERRORS{'DEBUG'}, 0, "private IP address 
for $computer_hostname ($computer_id) stored in this object matches IP address 
stored in \%ENV: $data_structure_private_ip_address");
+                       }
+                       else {
+                               notify($ERRORS{'DEBUG'}, 0, "private IP address 
for $computer_hostname ($computer_id) stored in this object 
$data_structure_private_ip_address does not match IP address stored in \%ENV, 
updating private IP address stored in this object: $env_private_ip_address");
+                               
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress}
 = $env_private_ip_address;
+                       }
+                       return $env_private_ip_address;
                }
                else {
-                       notify($ERRORS{'WARNING'}, 0, "private IP address of 
$computer_hostname is not set in DataStructure object")
+                       notify($ERRORS{'DEBUG'}, 0, "returning private IP 
address of $computer_hostname ($computer_id) already stored in this 
DataStructure object: $data_structure_private_ip_address");
+                       return $data_structure_private_ip_address;
+               }
+       }
+       else {
+               if ($env_private_ip_address && $env_private_ip_address !~ 
/null/i) {
+                       notify($ERRORS{'DEBUG'}, 0, "private IP address for 
$computer_hostname ($computer_id) is not stored in this object but is stored in 
\%ENV, setting private IP address in this object: $env_private_ip_address");
+                       
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress}
 = $env_private_ip_address;
+                       return $env_private_ip_address;
+               }
+               else {
+                       if ($suppress_warning) {
+                               notify($ERRORS{'DEBUG'}, 0, "private IP address 
of $computer_hostname ($computer_id) is not set in this object");
+                       }
+                       else {
+                               notify($ERRORS{'WARNING'}, 0, "private IP 
address of $computer_hostname ($computer_id) is not set in this object")
+                       }
+                       return;
                }
        }
-       return;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -1579,30 +1599,18 @@ sub set_computer_private_ip_address {
                return;
        }
        
-       my $existing_private_ip_address = 
$self->get_computer_private_ip_address();
-       
-       if (!$existing_private_ip_address && $private_ip_address_argument =~ 
/null/i) {
-               notify($ERRORS{'DEBUG'}, 0, "setting private IP address of 
$computer_hostname to '$private_ip_address_argument' not necessary, it is not 
set in this DataStructure object");
-               return 1;
-       }
-       elsif ($existing_private_ip_address && $existing_private_ip_address eq 
$private_ip_address_argument) {
-               notify($ERRORS{'DEBUG'}, 0, "private IP address of 
$computer_hostname is already set to $private_ip_address_argument");
-               return 1;
+       # Update this DataStructure object
+       if ($private_ip_address_argument =~ /null/i) {
+               delete 
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress};
        }
        else {
-               # Update this DataStructure object
-               if ($private_ip_address_argument =~ /null/i) {
-                       delete 
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress};
-               }
-               else {
-                       
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress}
 = $private_ip_address_argument;
-               }
-               
-               # Update the database
-               if (!update_computer_private_ip_address($computer_id, 
$private_ip_address_argument)) {
-                       notify($ERRORS{'WARNING'}, 0, "failed to update private 
IP address of $computer_hostname to $private_ip_address_argument, unable to 
update the database");
-                       return;
-               }
+               
$self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress}
 = $private_ip_address_argument;
+       }
+       
+       # Update the database
+       if (!update_computer_private_ip_address($computer_id, 
$private_ip_address_argument)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to update private IP 
address of $computer_hostname to $private_ip_address_argument, unable to update 
the database");
+               return;
        }
        
        notify($ERRORS{'DEBUG'}, 0, "private IP address of $computer_hostname 
set to $private_ip_address_argument");

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1645748&r1=1645747&r2=1645748&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS.pm Mon Dec 15 20:53:50 2014
@@ -2167,7 +2167,7 @@ sub execute {
        # Check the argument type
        if (ref($argument)) {
                if (ref($argument) eq 'HASH') {
-                       notify($ERRORS{'DEBUG'}, 0, "first argument is a hash 
reference:\n" . format_data($argument));
+                       #notify($ERRORS{'DEBUG'}, 0, "first argument is a hash 
reference:\n" . format_data($argument));
                        
                        $computer_name = $argument->{node} if (!$computer_name);
                        $command = $argument->{command};
@@ -2315,13 +2315,18 @@ sub execute_new {
                return;
        }
        
+       # Determine which string to use as the connection target
+       my $remote_connection_target = 
determine_remote_connection_target($computer_name);
+       my $computer_string = $computer_name;
+       $computer_string .= " ($remote_connection_target)" if 
($remote_connection_target ne $computer_name);
+       
        $display_output = 0 unless $display_output;
        $timeout_seconds = 60 unless $timeout_seconds;
        $max_attempts = 3 unless $max_attempts;
        $port = 22 unless $port;
        $user = 'root' unless $user;
        
-       my $ssh_options = '-o StrictHostKeyChecking=no -o ConnectTimeout=30';
+       my $ssh_options = '-o StrictHostKeyChecking=no -o ConnectTimeout=30 -x';
        
        # Figure out which identity key to use
        # If identity key argument was supplied, it may be a single path or a 
comma-separated list
@@ -2349,7 +2354,7 @@ sub execute_new {
                if ($attempt > 0) {
                        $attempt_string = "attempt $attempt/$max_attempts: ";
                        $ssh->close() if $ssh;
-                       delete $ENV{net_ssh_expect}{$computer_name};
+                       delete $ENV{net_ssh_expect}{$remote_connection_target};
                        
                        notify($ERRORS{'DEBUG'}, 0, $attempt_string . "sleeping 
for $attempt_delay seconds before making next attempt");
                        sleep $attempt_delay;
@@ -2362,10 +2367,10 @@ sub execute_new {
                # Use a flag to determine if null should be returned without 
making another attempt
                my $return_null;
                
-               if (!$ENV{net_ssh_expect}{$computer_name}) {
+               if (!$ENV{net_ssh_expect}{$remote_connection_target}) {
                        eval {
                                $ssh = Net::SSH::Expect->new(
-                                       host => $computer_name,
+                                       host => $remote_connection_target,
                                        user => $user,
                                        port => $port,
                                        raw_pty => 1,
@@ -2376,18 +2381,20 @@ sub execute_new {
                                
                                if ($ssh) {
                                        
-                                       notify($ERRORS{'DEBUG'}, 0, "created " 
. ref($ssh) . " object to control $computer_name, SSH options: $ssh_options");
+                                       notify($ERRORS{'DEBUG'}, 0, "created " 
. ref($ssh) . " object to control $computer_string, SSH options: $ssh_options");
                                }
                                else {
-                                       notify($ERRORS{'WARNING'}, 0, "failed 
to create Net::SSH::Expect object to control $computer_name, $!");
+                                       notify($ERRORS{'WARNING'}, 0, "failed 
to create Net::SSH::Expect object to control $computer_string, $!");
                                        next ATTEMPT;
                                }
                                
                                if (!$ssh->run_ssh()) {
-                                       notify($ERRORS{'WARNING'}, 0, ref($ssh) 
. " object failed to fork SSH process to control $computer_name, $!");
+                                       notify($ERRORS{'WARNING'}, 0, ref($ssh) 
. " object failed to fork SSH process to control $computer_string, $!");
                                        next ATTEMPT;
                                }
                                
+                               sleep_uninterrupted(1);
+                               
                                #$ssh->exec("stty -echo");
                                #$ssh->exec("stty raw -echo");
                                
@@ -2400,14 +2407,14 @@ sub execute_new {
                                        notify($ERRORS{'DEBUG'}, 0, "SSH 
initialization output:\n$initialization_output") if ($display_output);
                                        if ($initialization_output =~ 
/password:/i) {
                                                if (defined($password)) {
-                                                       
notify($ERRORS{'WARNING'}, 0, "$attempt_string unable to connect to 
$computer_name, SSH is requesting a password but password authentication is not 
implemented, password is configured, output:\n$initialization_output");
+                                                       
notify($ERRORS{'WARNING'}, 0, "$attempt_string unable to connect to 
$computer_string, SSH is requesting a password but password authentication is 
not implemented, password is configured, output:\n$initialization_output");
                                                        
                                                        # In EVAL block here, 
'return' won't return from entire subroutine, set flag
                                                        $return_null = 1;
                                                        return;
                                                }
                                                else {
-                                                       
notify($ERRORS{'WARNING'}, 0, "$attempt_string unable to connect to 
$computer_name, SSH is requesting a password but password authentication is not 
implemented, password is not configured, output:\n$initialization_output");
+                                                       
notify($ERRORS{'WARNING'}, 0, "$attempt_string unable to connect to 
$computer_string, SSH is requesting a password but password authentication is 
not implemented, password is not configured, output:\n$initialization_output");
                                                        $return_null = 1;
                                                        return;
                                                }
@@ -2421,27 +2428,27 @@ sub execute_new {
                        return if ($return_null);
                        if ($EVAL_ERROR) {
                                if ($EVAL_ERROR =~ /^(\w+) at \//) {
-                                       notify($ERRORS{'DEBUG'}, 0, 
$attempt_string . "$1 error occurred initializing Net::SSH::Expect object for 
$computer_name") if ($display_output);
+                                       notify($ERRORS{'DEBUG'}, 0, 
$attempt_string . "$1 error occurred initializing Net::SSH::Expect object for 
$computer_string") if ($display_output);
                                }
                                else {
-                                       notify($ERRORS{'DEBUG'}, 0, 
$attempt_string . "$EVAL_ERROR error occurred initializing Net::SSH::Expect 
object for $computer_name") if ($display_output);
+                                       notify($ERRORS{'DEBUG'}, 0, 
$attempt_string . "$EVAL_ERROR error occurred initializing Net::SSH::Expect 
object for $computer_string") if ($display_output);
                                }
                                next ATTEMPT;
                        }
                }
                else {
-                       $ssh = $ENV{net_ssh_expect}{$computer_name};
+                       $ssh = $ENV{net_ssh_expect}{$remote_connection_target};
                        
                        # Delete the stored SSH object to make sure it isn't 
saved if the command fails
                        # The SSH object will be added back to %ENV if the 
command completes successfully
-                       delete $ENV{net_ssh_expect}{$computer_name};
+                       delete $ENV{net_ssh_expect}{$remote_connection_target};
                }
                
                # Set the timeout
                $ssh->timeout($timeout_seconds);
                
                (my $command_formatted = $command) =~ s/\s+(;|&|&&)\s+/\n$1 /g;
-               notify($ERRORS{'DEBUG'}, 0, $attempt_string . "executing 
command on $computer_name (timeout: $timeout_seconds 
seconds):\n$command_formatted") if ($display_output);
+               notify($ERRORS{'DEBUG'}, 0, $attempt_string . "executing 
command on $computer_string (timeout: $timeout_seconds 
seconds):\n$command_formatted") if ($display_output);
                my $command_start_time = time;
                $ssh->send($command . ' 2>&1 ; echo exitstatus:$?');
                
@@ -2452,19 +2459,19 @@ sub execute_new {
                
                if ($EVAL_ERROR) {
                        if ($ignore_error) {
-                               notify($ERRORS{'DEBUG'}, 0, "executed command 
on $computer_name: '$command', ignoring error, returning null") if 
($display_output);
+                               notify($ERRORS{'DEBUG'}, 0, "executed command 
on $computer_string: '$command', ignoring error, returning null") if 
($display_output);
                                return;
                        }
                        elsif ($EVAL_ERROR =~ /^(\w+) at \//) {
-                               notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"$1 error occurred executing command on $computer_name: '$command'") if 
($display_output);
+                               notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"$1 error occurred executing command on $computer_string: '$command'") if 
($display_output);
                        }
                        else {
-                               notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"error occurred executing command on $computer_name: '$command'\nerror: 
$EVAL_ERROR") if ($display_output);
+                               notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"error occurred executing command on $computer_string: '$command'\nerror: 
$EVAL_ERROR") if ($display_output);
                        }
                        next ATTEMPT;
                }
                elsif (!$ssh_wait_status) {
-                       notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"command timed out after $timeout_seconds seconds on $computer_name: 
'$command'") if ($display_output);
+                       notify($ERRORS{'WARNING'}, 0, $attempt_string . 
"command timed out after $timeout_seconds seconds on $computer_string: 
'$command'") if ($display_output);
                        next ATTEMPT;
                }
                
@@ -2487,15 +2494,15 @@ sub execute_new {
                my @output_lines = split(/\n/, $output);
                map { s/[\r]+//g; } (@output_lines);
                
-               notify($ERRORS{'OK'}, 0, "executed command on $computer_name: 
'$command', exit status: $exit_status, output:\n$output") if ($display_output);
+               notify($ERRORS{'OK'}, 0, "executed command on $computer_string: 
'$command', exit status: $exit_status, output:\n$output") if ($display_output);
                
                # Save the SSH object for later use
-               $ENV{net_ssh_expect}{$computer_name} = $ssh;
+               $ENV{net_ssh_expect}{$remote_connection_target} = $ssh;
                
                return ($exit_status, \@output_lines);
        }
        
-       notify($ERRORS{'WARNING'}, 0, $attempt_string . "failed to execute 
command on $computer_name: '$command'") if ($display_output);
+       notify($ERRORS{'WARNING'}, 0, $attempt_string . "failed to execute 
command on $computer_string: '$command'") if ($display_output);
        return;
 }
 

Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1645748&r1=1645747&r2=1645748&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Mon Dec 15 20:53:50 2014
@@ -107,6 +107,7 @@ our @EXPORT = qw(
        delete_computerloadlog_reservation
        delete_request
        delete_variable
+       determine_remote_connection_target
        escape_file_path
        format_data
        format_hash_keys
@@ -123,6 +124,7 @@ our @EXPORT = qw(
        get_database_table_names
        get_code_ref_package_name
        get_code_ref_subroutine_name
+       get_computer_current_private_ip_address
        get_computer_current_state_name
        get_computer_grp_members
        get_computer_ids
@@ -2134,8 +2136,8 @@ sub check_ssh {
 
 =head2 nmap_port
 
- Parameters  : $hostname,n $port
- Returns     : 1 open 0 closed
+ Parameters  : $hostname, $port
+ Returns     : boolean
  Description : use nmap port scanning tool to determine if port is open
 
 =cut
@@ -2152,16 +2154,19 @@ sub nmap_port {
                return;
        }
        
-       my $command = "/usr/bin/nmap $hostname -P0 -p $port -T Aggressive";
-       my ($exit_status, $output) = run_command($command, 1);
+       # Determine which string to use as the connection target
+       my $remote_connection_target = 
determine_remote_connection_target($hostname);
+       my $hostname_string = $remote_connection_target;
+       $hostname_string .= " ($hostname)" if ($hostname ne 
$remote_connection_target);
        
+       my $command = "/usr/bin/nmap $remote_connection_target -P0 -p $port -T 
Aggressive";
+       my ($exit_status, $output) = run_command($command, 1);
        if (!defined($output)) {
                notify($ERRORS{'WARNING'}, 0, "failed to run nmap command on 
management node: '$command'");
                return;
        }
-       
-       if (grep(/(open|filtered)/i, @$output)) {
-               #notify($ERRORS{'DEBUG'}, 0, "port $port is open on $hostname");
+       elsif (grep(/(open|filtered)/i, @$output)) {
+               notify($ERRORS{'DEBUG'}, 0, "port $port is open on 
$hostname_string");
                return 1;
        }
        elsif (grep(/(nmap:|warning)/i, @$output)) {
@@ -2169,7 +2174,7 @@ sub nmap_port {
                return;
        }
        else {
-               #notify($ERRORS{'DEBUG'}, 0, "port $port is closed on 
$hostname");
+               notify($ERRORS{'DEBUG'}, 0, "port $port is closed on 
$hostname_string");
                return 0;
        }
 } ## end sub nmap_port
@@ -3999,7 +4004,6 @@ sub run_ssh_command {
                return VCL::Module::OS::execute_new($node, $command, 
$output_level, $timeout_seconds, $max_attempts, $port, $user, '', 
$identity_paths);
        }
        
-       # TODO: Add ssh path to config file and set global variable
        # Locate the path to the ssh binary
        my $ssh_path;
        if (-f '/usr/bin/ssh') {
@@ -4018,7 +4022,7 @@ sub run_ssh_command {
                notify($ERRORS{'WARNING'}, 0, "unable to locate the SSH 
executable in the usual places");
                return 0;
        }
-
+       
        # Format the identity path string
        if (defined $identity_paths && length($identity_paths) > 0) {
                # Add -i to beginning of string
@@ -4044,6 +4048,11 @@ sub run_ssh_command {
        #       $command = "echo -e \"$octal_string\" | \$SHELL";
        #       notify($ERRORS{'DEBUG'}, 0, "octal command:\n$command");
        #}
+       
+       # Determine which string to use as the connection target
+       my $remote_connection_target = 
determine_remote_connection_target($node);
+       my $node_string = $remote_connection_target;
+       $node_string .= " ($node)" if ($node ne $remote_connection_target);
 
        # Assemble the SSH command
        # -i <identity_file>, Selects the file from which the identity (private 
key) for RSA authentication is read.
@@ -4061,7 +4070,7 @@ sub run_ssh_command {
        $ssh_command .= "-l $user ";
        $ssh_command .= "-p $port ";
        $ssh_command .= "-x ";
-       $ssh_command .= "$node '$command' 2>&1";
+       $ssh_command .= "$remote_connection_target '$command' 2>&1";
        
        # Execute the command
        my $ssh_output = '';
@@ -4088,10 +4097,10 @@ sub run_ssh_command {
                
                # Print the SSH command, only display the attempt # if > 1
                if ($attempts == 1) {
-                       notify($ERRORS{'DEBUG'}, 0, "executing SSH command on 
$node: '$command'") if $output_level;
+                       notify($ERRORS{'DEBUG'}, 0, "executing SSH command on 
$node_string: '$command'") if $output_level;
                }
                else {
-                       notify($ERRORS{'DEBUG'}, 0, "attempt 
$attempts/$max_attempts: executing SSH command on $node: '$ssh_command'") if 
$output_level;
+                       notify($ERRORS{'DEBUG'}, 0, "attempt 
$attempts/$max_attempts: executing SSH command on $node_string: 
'$ssh_command'") if $output_level;
                }
                
                # Enclose SSH command in an eval block and use alarm to 
eventually timeout the SSH command if it hangs
@@ -4134,16 +4143,16 @@ sub run_ssh_command {
                        kill_child_processes($PID);
                        
                        if ($max_attempts == 1 || $attempts < $max_attempts) {
-                               notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: SSH command timed out after $duration seconds, timeout 
threshold: $timeout_seconds seconds, command: $node:\n$ssh_command");
+                               notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: SSH command timed out after $duration seconds, timeout 
threshold: $timeout_seconds seconds, command: $node_string:\n$ssh_command");
                        }
                        else {
-                               notify($ERRORS{'CRITICAL'}, 0, "attempt 
$attempts/$max_attempts: SSH command timed out after $duration seconds, timeout 
threshold: $timeout_seconds seconds, command: $node:\n$ssh_command");
+                               notify($ERRORS{'CRITICAL'}, 0, "attempt 
$attempts/$max_attempts: SSH command timed out after $duration seconds, timeout 
threshold: $timeout_seconds seconds, command: $node_string:\n$ssh_command");
                                return;
                        }
                        next;
                }
                elsif ($EVAL_ERROR) {
-                       notify($ERRORS{'CRITICAL'}, 0, "attempt 
$attempts/$max_attempts: eval error was generated attempting to run SSH 
command: $node:\n$ssh_command, error: $EVAL_ERROR");
+                       notify($ERRORS{'CRITICAL'}, 0, "attempt 
$attempts/$max_attempts: eval error was generated attempting to run SSH 
command: $node_string:\n$ssh_command, error: $EVAL_ERROR");
                        next;
                }
                
@@ -4193,26 +4202,26 @@ sub run_ssh_command {
                # ssh exits with the exit status of the remote command or with 
255 if an error occurred.
                # Check for vmware-cmd usage message, it returns 255 if the 
vmware-cmd usage output is returned
                if ($ssh_output_formatted =~ /ssh:.*(lost connection|reset by 
peer|no route to host|connection refused|connection timed out|resource 
temporarily unavailable|connection reset)/i) {
-                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: failed to execute SSH command on $node: '$command', 
exit status: $exit_status, output:\n$ssh_output_formatted") if $output_level;
+                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: failed to execute SSH command on $node_string: 
'$command', exit status: $exit_status, output:\n$ssh_output_formatted") if 
$output_level;
                        next;
                }
                elsif ($ssh_output_formatted =~ /(Connection timed out during 
banner exchange)/i) {
                        $banner_exchange_error_count++;
                        if ($banner_exchange_error_count >= 
$banner_exchange_error_limit) {
-                               notify($ERRORS{'WARNING'}, 0, "failed to 
execute SSH command on $node, encountered $banner_exchange_error_count banner 
exchange errors");
+                               notify($ERRORS{'WARNING'}, 0, "failed to 
execute SSH command on $node_string, encountered $banner_exchange_error_count 
banner exchange errors");
                                return ();
                        }
                        else {
                                # Don't count against attempt limit
                                $attempts--;
                                my $banner_exchange_delay_seconds = 
($banner_exchange_error_count * 2);
-                               notify($ERRORS{'DEBUG'}, 0, "encountered banner 
exchange error on $node, sleeping for $banner_exchange_delay_seconds seconds, 
command:\n$command\noutput:\n$ssh_output") if $output_level;
+                               notify($ERRORS{'DEBUG'}, 0, "encountered banner 
exchange error on $node_string, sleeping for $banner_exchange_delay_seconds 
seconds, command:\n$command\noutput:\n$ssh_output") if $output_level;
                                sleep $banner_exchange_delay_seconds;
                                next;
                        }
                }
                elsif ($exit_status == 255 && $ssh_command !~ 
/(vmware-cmd|vim-cmd|vmkfstools|vmrun)/i) {
-                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: failed to execute SSH command on $node: '$command', 
exit status: $exit_status, SSH exits with the exit status of the remote command 
or with 255 if an error occurred, output:\n$ssh_output_formatted") if 
$output_level;
+                       notify($ERRORS{'WARNING'}, 0, "attempt 
$attempts/$max_attempts: failed to execute SSH command on $node_string: 
'$command', exit status: $exit_status, SSH exits with the exit status of the 
remote command or with 255 if an error occurred, 
output:\n$ssh_output_formatted") if $output_level;
                        next;
                }
                else {
@@ -4269,6 +4278,24 @@ sub run_scp_command {
        $path1 =~ s/([^\\]) /$1\\ /g;
        $path2 =~ s/([^\\]) /$1\\ /g;
        
+       # Determine which IP address or hostname should be used to connect to 
the target
+       if ($path1 =~ /^([^:]+):/) {
+               my $node = $1;
+               my $connection_target = 
determine_remote_connection_target($node);
+               if ($connection_target ne $node) {
+                       $path1 =~ s/^$node:/$connection_target:/;
+               }
+       }
+       
+       if ($path2 =~ /^([^:]+):/) {
+               my $node = $1;
+               my $connection_target = 
determine_remote_connection_target($node);
+               if ($connection_target ne $node) {
+                       $path2 =~ s/^$node:/$connection_target:/;
+               }
+       }
+       
+       
        # Format the identity path string
        if ($identity_paths) {
                $identity_paths =~ s/^\s*/-i /;
@@ -4810,10 +4837,10 @@ sub update_lastcheckin {
  Parameters  : $computer_identifier, $private_ip_address
  Returns     : boolean
  Description : Updates the computer.privateIPaddress value of the computer
-               specified by the argument. The value can be set to null by
-               passing 'null' as the argument. The $computer_identifier 
argument
-               may either be the numeric computer.id value or the
-               computer.hostname value.
+                                       specified by the argument. The value 
can be set to null by
+                                       passing 'null' as the argument. The 
$computer_identifier argument
+                                       may either be the numeric computer.id 
value or the
+                                       computer.hostname value.
 
 =cut
 
@@ -4829,6 +4856,25 @@ sub update_computer_private_ip_address {
                return;
        }
        
+       # Figure out the computer ID based on the $computer_identifier argument
+       my $computer_id;
+       if ($computer_identifier =~ /^\d+$/) {
+               $computer_id = $computer_identifier;
+       }
+       else {
+               $computer_id = get_computer_ids($computer_identifier);
+               if (!$computer_id) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to update 
computer private IP address, computer ID could not be determed from argument: 
$computer_identifier");
+                       return;
+               }
+       }
+       
+       my $computer_string = $computer_identifier;
+       $computer_string .= " ($computer_id)" if ($computer_identifier ne 
$computer_id);
+       
+       # Delete cached data if previously set
+       delete $ENV{computer_private_ip_address}{$computer_id};
+       
        my $private_ip_address_text;
        if ($private_ip_address =~ /null/i) {
                $private_ip_address_text = 'NULL';
@@ -4845,25 +4891,19 @@ SET
 privateIPaddress = $private_ip_address_text
 WHERE
 computer.deleted != '1'
-AND 
+AND computer.id = '$computer_id'
 EOF
 
-       # If the computer identifier is all digits match it to computer.id
-       # Otherwise, match computer.hostname
-       if ($computer_identifier =~ /^\d+$/) {
-               $update_statement .= "computer.id = \'$computer_identifier\'";
-       }
-       else {
-               $update_statement .= "computer.hostname REGEXP 
'$computer_identifier(\\\\.|\$)'";
-       }
-
        # Call the database execute subroutine
        if (database_execute($update_statement)) {
-               notify($ERRORS{'OK'}, 0, "updated private IP address of 
computer $computer_identifier in database: $private_ip_address");
+               if ($private_ip_address !~ /null/i) {
+                       $ENV{computer_private_ip_address}{$computer_id} = 
$private_ip_address;
+               }
+               notify($ERRORS{'OK'}, 0, "updated private IP address of 
computer $computer_string in database: $private_ip_address");
                return 1;
        }
        else {
-               notify($ERRORS{'WARNING'}, 0, "failed to update private IP 
address of computer $computer_identifier in database: $private_ip_address");
+               notify($ERRORS{'WARNING'}, 0, "failed to update private IP 
address of computer $computer_string in database: $private_ip_address");
                return;
        }
 }
@@ -7555,17 +7595,27 @@ sub get_reservation_request_info {
 =head2 get_computer_ids
 
  Parameters  : $computer_identifier
- Returns     : array
+ Returns     : If called in scalar context: integer
+               If called in array context: array
  Description : Queries the computer table for computers matching the
                $computer_identifier argument. The argument may contain either
-               the computer's hostname or IP address. An array containing the
-               computer IDs is returned.
+               the computer's hostname or IP address. Deleted computers are not
+               considered.
+               
+               If called in scalar context:
+               - returns an integer if a single computer matched the argument
+               - returns null if no computers or multiple computers match the
+                 argument
+               
+               If called in array context:
+               - returns an array containing matching computer IDs
+               - returns an empty array if no computers match the argument
 
 =cut
 
 sub get_computer_ids {
        my ($computer_identifier) = @_;
-
+       
        if (!defined($computer_identifier)) {
                notify($ERRORS{'WARNING'}, $LOGFILE, "computer identifier 
argument was not supplied");
                return;
@@ -7573,7 +7623,8 @@ sub get_computer_ids {
 
        my $select_statement = <<EOF;
 SELECT
-*
+id,
+hostname
 FROM
 computer
 WHERE
@@ -7586,28 +7637,43 @@ AND (
 EOF
 
        my @selected_rows = database_select($select_statement);
-       if (!@selected_rows) {
-               notify($ERRORS{'DEBUG'}, 0, "no computers were found matching 
identifier: $computer_identifier");
-               return ();
-       }
-
        my @computer_ids = map { $_->{id} } @selected_rows;
-       notify($ERRORS{'DEBUG'}, 0, "found computers matching identifier: 
$computer_identifier, IDs: @computer_ids");
-       return sort @computer_ids;
+       my $computer_id_count = scalar(@computer_ids);
+       
+       if (!$computer_id_count) {
+               notify($ERRORS{'WARNING'}, 0, "no computers were found matching 
argument: $computer_identifier");
+               return;
+       }
+       
+       if (wantarray) {
+               notify($ERRORS{'DEBUG'}, 0, $computer_id_count . " computer" . 
($computer_id_count == 1 ? ' was' : 's were') . " found matching argument: 
$computer_identifier, returning computer ID" . ($computer_id_count == 1 ? '' : 
's') . ": " . join(", ", @computer_ids));
+               return @computer_ids;
+       }
+       else {
+               if ($computer_id_count > 1) {
+                       notify($ERRORS{'WARNING'}, 0, "multiple computers were 
found matching argument: $computer_identifier\n" . 
format_data(\@selected_rows));
+                       return;
+               }
+               else {
+                       my $computer_id = $computer_ids[0];
+                       #notify($ERRORS{'DEBUG'}, 0, "computer was found 
matching argument $computer_identifier, returning computer ID: $computer_id");
+                       return $computer_id;
+               }
+       }
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
 =head2 insert_request
 
- Parameters  : $managementnode_id, $request_state_name, 
$request_laststate_name, $end_minutes_in_future, $user_unityid, $computer_id, 
$image_id, $imagerevision_id
+ Parameters  : $managementnode_id, $request_state_name, 
$request_laststate_name, $end_minutes_in_future, $user_unityid, 
$computer_identifier, $image_id, $imagerevision_id
  Returns     : 1 if successful, 0 if failed
  Description :
 
 =cut
 
 sub insert_request {
-       my ($managementnode_id, $request_state_name, $request_laststate_name, 
$request_logid, $user_unityid, $computer_id, $image_id, $imagerevision_id, 
$start_minutes_in_future, $end_minutes_in_future) = @_;
+       my ($managementnode_id, $request_state_name, $request_laststate_name, 
$request_logid, $user_unityid, $computer_identifier, $image_id, 
$imagerevision_id, $start_minutes_in_future, $end_minutes_in_future) = @_;
 
 
        if (!$request_state_name) {
@@ -7623,8 +7689,8 @@ sub insert_request {
                return 0;
        }
 
-       if (!$computer_id) {
-               notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation 
key: computer_id");
+       if (!$computer_identifier) {
+               notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation 
key: computer_identifier");
                return 0;
        }
        if (!$image_id) {
@@ -7640,15 +7706,18 @@ sub insert_request {
                return 0;
        }
        
-       if ($computer_id !~ /^\d+$/) {
-               my @computer_ids = get_computer_ids($computer_id);
-               if (scalar(@computer_ids) != 1) {
-                       notify($ERRORS{'WARNING'}, 0, "computer ID argument is 
not numeric and computer ID could not be determined");
+       my $computer_id;
+       if ($computer_identifier =~ /^\d+$/) {
+               $computer_id = $computer_identifier;
+       }
+       else {
+               $computer_id = get_computer_ids($computer_identifier);
+               if (!$computer_id) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to insert 
request, computer ID could not be determined from argument: 
$computer_identifier");
                        return;
                }
-               $computer_id = $computer_ids[0];
        }
-
+       
        my $insert_request_statment = "
        INSERT INTO
        request
@@ -8762,7 +8831,7 @@ sub read_file_to_array {
 
 =head2 is_valid_ip_address
 
- Parameters  : IP address string
+ Parameters  : $ip_address, $display_output (optional)
  Returns     : If valid: true
                If not valid: false
  Description : Determines if the argument is a valid IP address.
@@ -8770,34 +8839,35 @@ sub read_file_to_array {
 =cut
 
 sub is_valid_ip_address {
-       my $ip_address = shift;
+       my ($ip_address, $display_output) = @_;
        if (!$ip_address) {
                notify($ERRORS{'WARNING'}, 0, "IP address argument was not 
specified");
                return;
        }
+       $display_output = 1 unless defined($display_output);
        
        # Split up the IP address being checked into its octets
        my @octets = split(/\./, $ip_address);
        
        # Make sure 4 octets were found
        if (scalar @octets != 4) {
-               notify($ERRORS{'DEBUG'}, 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)) if $display_output;
                return 0;
        }
        
        # Make sure address only contains digits
        if (grep(/\D/, @octets)) {
-               notify($ERRORS{'DEBUG'}, 0, "IP address contains a non-digit: 
$ip_address");
+               notify($ERRORS{'DEBUG'}, 0, "IP address contains a non-digit: 
$ip_address") if $display_output;
                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{'DEBUG'}, 0, "IP address contains an octet > 
255: $ip_address");
+               notify($ERRORS{'DEBUG'}, 0, "IP address contains an octet > 
255: $ip_address") if $display_output;
                return 0;
        }
        
-       #notify($ERRORS{'DEBUG'}, 0, "IP address is valid: $ip_address");
+       #notify($ERRORS{'DEBUG'}, 0, "IP address is valid: $ip_address") if 
$display_output;
        return 1;
 }
 
@@ -12379,6 +12449,84 @@ EOF
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_computer_current_private_ip_address
+
+ Parameters  : $computer_identifier, $no_cache (optional)
+ Returns     : string
+ Description : Retrieves the privateIPaddress value from the computer table for
+               a single computer.
+               
+               Returns the private IP address if:
+               - a single non-deleted or deleted computer matches the argument
+                 (a warning is displayed if the computer is deleted)
+               
+               Returns null if:
+               - no computers or multiple computers match the argument
+
+=cut
+
+sub get_computer_current_private_ip_address {
+       my ($computer_identifier, $no_cache) = @_;
+       if (!$computer_identifier) {
+               notify($ERRORS{'WARNING'}, 0, "computer identifier argument was 
not supplied");
+               return;
+       }
+       $no_cache = 0 unless defined($no_cache);
+       
+       # Figure out the computer ID based on the $computer_identifier argument
+       my $computer_id;
+       if ($computer_identifier =~ /^\d+$/) {
+               $computer_id = $computer_identifier;
+       }
+       else {
+               $computer_id = get_computer_ids($computer_identifier);
+               if (!$computer_id) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to retrieve 
current private IP address from database, computer ID could not be determed 
from argument: $computer_identifier");
+                       return;
+               }
+       }
+       
+       my $computer_string = $computer_identifier;
+       $computer_string .= " ($computer_id)" if ($computer_identifier ne 
$computer_id);
+       
+       if (!$no_cache && 
defined($ENV{computer_private_ip_address}{$computer_id})) {
+               notify($ERRORS{'DEBUG'}, 0, "returning cached private IP 
address for computer $computer_string: 
$ENV{computer_private_ip_address}{$computer_id}");
+               return $ENV{computer_private_ip_address}{$computer_id};
+       }
+       
+       my $select_statement = <<EOF;
+SELECT
+computer.hostname,
+computer.privateIPaddress
+FROM
+computer
+WHERE
+computer.id = '$computer_id'
+AND computer.deleted = 0
+EOF
+       
+       my @rows = database_select($select_statement);
+       if (!@rows) {
+               notify($ERRORS{'WARNING'}, 0, "failed to retrieve private IP 
address from database for computer $computer_string");
+               return;
+       }
+       
+       my $row = $rows[0];
+       my $private_ip_address = $row->{privateIPaddress};
+       my $hostname = $row->{hostname};
+       if ($private_ip_address) {
+               $ENV{computer_private_ip_address}{$computer_id} = 
$private_ip_address;
+               notify($ERRORS{'DEBUG'}, 0, "retrieved private IP address for 
computer $computer_string from database: $private_ip_address");
+               return $private_ip_address;
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "private IP address for computer 
$computer_string is set to null in database");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 hostname_to_ip_address
 
  Parameters  : $hostname
@@ -12396,11 +12544,11 @@ sub hostname_to_ip_address {
        my $packed_ip_address = gethostbyname($hostname);
        if (defined $packed_ip_address) {
                my $ip_address = inet_ntoa($packed_ip_address);
-               #notify($ERRORS{'DEBUG'}, 0, "determined IP address hostname 
$hostname resolves to: $ip_address");
+               notify($ERRORS{'DEBUG'}, 0, "resolved IP address from hostname 
$hostname --> $ip_address");
                return $ip_address;
        }
        else {
-               notify($ERRORS{'DEBUG'}, 0, "unable to determined IP address 
hostname $hostname resolves to");
+               notify($ERRORS{'DEBUG'}, 0, "unable to resolve IP address from 
hostname: $hostname");
                return;
        }
 }
@@ -12423,11 +12571,11 @@ sub ip_address_to_hostname {
        
        my $hostname = gethostbyaddr(inet_aton($ip_address), AF_INET);
        if (defined $hostname) {
-               notify($ERRORS{'DEBUG'}, 0, "determined hostname from IP 
address $ip_address: $hostname");
+               notify($ERRORS{'DEBUG'}, 0, "resolved hostname from IP address 
$ip_address --> $hostname");
                return $hostname;
        }
        else {
-               notify($ERRORS{'DEBUG'}, 0, "unable to determine hostname from 
IP address: $ip_address");
+               notify($ERRORS{'DEBUG'}, 0, "unable to resolve hostname from IP 
address: $ip_address");
                return;
        }
 }
@@ -12611,6 +12759,80 @@ sub ip_address_to_network_address {
        }
 }
 
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 determine_remote_connection_target
+
+ Parameters  : $argument, $no_cache (optional)
+ Returns     : string
+ Description : Attempts to determine the string to use when connecting to a
+               remote node. If an IP address is passed, the same IP address is
+               always returned. If a hostname is passed, an attempt is made to
+               retrieve the private IP address of the host. If successful, the
+               private IP address is returned. Otherwise, the hostname argument
+               is returned.
+
+=cut
+
+sub determine_remote_connection_target {
+       my ($argument, $no_cache) = @_;
+       $no_cache = 0 unless defined($no_cache);
+       
+       if (!$no_cache && defined($ENV{remote_connection_target}{$argument})) {
+               return $ENV{remote_connection_target}{$argument};
+       }
+       
+       # If an IP address was passed, use it
+       if (is_valid_ip_address($argument, 0)) {
+               $ENV{remote_connection_target}{$argument} = $argument;
+               notify($ERRORS{'DEBUG'}, 0, "argument is a valid IP address, it 
will be used as the remote connection target: 
$ENV{remote_connection_target}{$argument}");
+               return $ENV{remote_connection_target}{$argument};
+       }
+       
+       # Attempt to retrieve the private IP address from the database
+       my $database_private_ip_address = 
get_computer_current_private_ip_address($argument);
+       if ($database_private_ip_address) {
+               $ENV{remote_connection_target}{$argument} = 
$database_private_ip_address;
+               notify($ERRORS{'DEBUG'}, 0, "private IP address is set in 
database for $argument, it will be used as the remote connection target: 
$ENV{remote_connection_target}{$argument}");
+               return $ENV{remote_connection_target}{$argument};
+       }
+       
+       # Private IP address could not be retrieved from the database or is set 
to NULL
+       # Try to resolve the argument to an IP address
+       # First make sure it is a valid hostname
+       if (!is_valid_dns_host_name($argument)) {
+               $ENV{remote_connection_target}{$argument} = $argument;
+               notify($ERRORS{'WARNING'}, 0, "failed to reliably determine the 
remote connection target to use for '$argument', it is not a valid IP address 
or DNS hostname");
+               return $ENV{remote_connection_target}{$argument};
+       }
+       
+       # Check if the hostname includes a DNS suffix
+       # If so, extract the short hostname and try to resolve that
+       my $resolved_ip_address;
+       if ($argument =~ /^([^\.]+)\./) {
+               my $short_hostname = $1;
+               notify($ERRORS{'DEBUG'}, 0, "hostname $argument contains a DNS 
suffix, attempting to resolve the hostname without its DNS suffix: 
$short_hostname");
+               $resolved_ip_address = hostname_to_ip_address($short_hostname);
+       }
+       if (!$resolved_ip_address) {
+               $resolved_ip_address = hostname_to_ip_address($argument);
+       }
+       
+       if ($resolved_ip_address) {
+               # Attempt to set the private IP address in the database
+               update_computer_private_ip_address($argument, 
$resolved_ip_address);
+               
+               $ENV{remote_connection_target}{$argument} = 
$resolved_ip_address;
+               notify($ERRORS{'DEBUG'}, 0, "$argument resolves to IP address 
$resolved_ip_address, it will be used as the remote connection target");
+               return $ENV{remote_connection_target}{$argument};
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to reliably determine the 
remote connection target to use for '$argument', it is not a valid IP address, 
a private IP address is not set in the database, and '$argument' does not 
resolve to an IP address on this management node");
+               $ENV{remote_connection_target}{$argument} = $argument;
+               return $ENV{remote_connection_target}{$argument};
+       }
+}
+
 #/////////////////////////////////////////////////////////////////////////////
 
 1;


Reply via email to