Skip to content

Commit

Permalink
Merge pull request os-autoinst#19791 from lpalovsky/console_redirecti…
Browse files Browse the repository at this point in the history
…on_fixes

Remove autossh dependency
  • Loading branch information
lpalovsky authored Jul 25, 2024
2 parents 623cc1c + 50b6c06 commit 63305bd
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 246 deletions.
158 changes: 26 additions & 132 deletions lib/sles4sap/console_redirection.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@ use Regexp::Common qw(net);
=head1 SYNOPSIS
Library that enables console redirection and file transfers from worker based VM to another host.
Can be used for cases where worker VM is not the target host for API calls and command execution, but serves only as a jumphost.
Can be used for cases where worker VM is not the target host for API calls and command execution, but serves only
as a jumphost. Console redirection is achieved by ssh remote port forwarding and redirecting remote VM ssh session
stdin and stdout outputs into serial terminal of the worker VM.
B<USAGE:>
1. Set mandatory OpenQA parameters (they can be as well provided directly as named arguments in functions):
- REDIRECT_DESTINATION_USER: SSH user for remote SUT
- REDIRECT_DESTINATION_IP: IP address for remote SUT
2. Establish console redirection to SUT host by calling function B<connect_target_to_serial>
- all test code is now transparently executed on SUT instead of worker VM
- from OpenQA perspective SUT is the worker VM
3. Disable console redirection from SUT host by calling function B<disconnect_target_from_serial>
=cut

our @EXPORT = qw(
connect_target_to_serial
disconnect_target_from_serial
redirection_init
check_serial_redirection
);

my $ssh_opt = '-o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=120';
# Create ssh key into /tmp so all users have access to it.
our $reverse_ssh_key_base_name = '/tmp/id_reverse_openqa';

=head2 handle_login_prompt
Expand Down Expand Up @@ -56,130 +65,6 @@ sub handle_login_prompt {
set_serial_term_prompt(); # set correct serial prompt
}

=head2 redirection_init
redirection_init( [, ssh_user=>$ssh_user, destination_ip=>$destination_ip, ssh_tunnel_port=>$ssh_tunnel_port]);
B<ssh_user>: SSH login user for B<destination_ip> - default value is defined by OpenQA parameter REDIRECT_DESTINATION_USER
B<destination_ip>: Destination host IP - default value is defined by OpenQA parameter REDIRECT_DESTINATION_IP
B<ssh_tunnel_port>: Port on the B<destination_ip> to forward ssh traffic to. Default: 22022
Does initial setup for console redirection which includes:
=over
=item * Takes machine id value from currently controlled VM and sets BASE_VM_ID. This is considered origin point of
console redirection and a point where function disconnect_target_from_serial() stops 'logging out'.
=item * SSH key exchange for reverse ssh connection.
=item * remote port forwarding for reverse SSH session
=item * remote port forwarding of incoming traffic from B<destination_ip> to OpenQA
server resources (upload_logs, download from 'data')
=back
=cut

sub redirection_init {
my (%args) = @_;
$args{destination_ip} //= get_required_var('REDIRECT_DESTINATION_IP');
$args{ssh_user} //= get_required_var('REDIRECT_DESTINATION_USER');
$args{ssh_tunnel_port} //= '22022';

croak 'Package autossh is not installed' if script_run('rpm -qi autossh');

record_info('Redirection init', "Preparing console redirection to: $args{destination_ip}");

# This should get base VM id before any redirection happening
# ID serves as identification for origin point where redirection is not anymore in place.
set_var('BASE_VM_ID', script_output 'cat /etc/machine-id');

# Prepare keyless access from remote host to worker VM
connect_target_to_serial(%args);
script_run("sudo rm $reverse_ssh_key_base_name*", quiet => 1);
# remove any existing entry in known_hosts file
script_run("ssh-keygen -R [localhost]:$args{ssh_tunnel_port} -f ~/.ssh/known_hosts", quiet => 1);
script_run("sudo ssh-keygen -R [localhost]:$args{ssh_tunnel_port} -f /root/.ssh/known_hosts", quiet => 1);
assert_script_run("ssh-keygen -f $reverse_ssh_key_base_name -t rsa -b 2048 -N ''");
my $public_key = script_output("cat $reverse_ssh_key_base_name.pub", quiet => 1);
disconnect_target_from_serial(%args);

# Add cloud VM key to worker VM authorized keys
assert_script_run("echo \"$public_key\" >> ~/.ssh/authorized_keys", quiet => 1);

# Forward common ports.
# Starts permanent reverse SSH connection from remote VM to worker VM.
remote_port_forward(destination_port => $args{ssh_tunnel_port},
ssh_user => $args{ssh_user},
destination_ip => 'localhost',
monitor_port => '20000');

# Port below is also required to access resources using generated port. Check QEMUPORT for details
# https://github.com/os-autoinst/os-autoinst/blob/master/doc/backend_vars.asciidoc
remote_port_forward(destination_port => get_var("QEMUPORT") + 1,
ssh_user => $args{ssh_user},
destination_ip => get_var('QEMU_HOST_IP', '10.0.2.2'),
monitor_port => '20001');
record_info('Redirected', 'Console redirection ready.');
}

=head2 remote_port_forward
remote_port_forward(destination_port=>$destination_port, destination_ip=>$destination_ip
[, source_port=>$source_port, monitor_port=>$monitor_port, source_ip=>$source_ip]);
B<source_ip>: Source IP address or hostname to forward incoming traffic from. Default: REDIRECT_DESTINATION_IP
B<destination_ip>: Destination IP or hostname where will the traffic be forwarded. Can be localhost as well.
B<monitor_port>: Port for autossh to monitor tunnel status. Needs to be unique for each autossh instance. Default: off
B<source_port>: B<source_ip> port to forward traffic from. Default: source_port=destination_port
B<destination_port>: port which B<source_port> traffic should be forwarded to.
B<ssh_user>: Login user for B<source_ip> host.
Forwards traffic from B<source_ip>:B<source_port> to B<destination_ip>:B<destination_port>.
For example: It allows Cloud based SUT uploading logs directly to openQA instance without being able to resolve it directly.
Default finction behavior is:
- redirecting same port
- not using autossh monitoring port
=cut

sub remote_port_forward {
my (%args) = @_;
foreach ('destination_port', 'destination_ip') {
croak "Missing $args{$_} argument" unless $args{$_};
}

# source IP means source of the incomming traffic.
# REDIRECT_DESTINATION_IP and destination_ip are different things!
$args{source_ip} //= get_required_var('REDIRECT_DESTINATION_IP');
$args{ssh_user} //= get_required_var('REDIRECT_DESTINATION_USER');
$args{monitor_port} //= '0';
$args{source_port} //= $args{destination_port};

my $current_user = script_output('whoami', quiet => 1);
my $autossh_cmd = join(' ', 'autossh',
"-M $args{monitor_port}",
'-f', '-N',
"-R $args{source_port}:$args{destination_ip}:$args{destination_port}",
"$args{ssh_user}\@$args{source_ip}"
);
$autossh_cmd = 'sudo ' . $autossh_cmd if $current_user ne 'root';

assert_script_run($autossh_cmd);
record_info('Port FWD',
"Forwarding set from '$args{source_ip}:$args{source_port}' to '$args{destination_ip}:$args{destination_port}'");
}

=head2 set_serial_term_prompt
set_serial_term_prompt();
Expand All @@ -203,7 +88,9 @@ B<ssh_user>: SSH login user for B<destination_ip> - default value is defined by
B<destination_ip>: Destination host IP - default value is defined by OpenQA parameter REDIRECT_DESTINATION_IP
Establishes ssh connection to destination host and redirects serial output to serial console on worker VM.
This allows OpenQA access to command return codes and output for evaluation by standard API call.
Connection activates remote port forwarding of OpenQA QEMUPORT+1.
This allows running standard OpenQA modules directly on a remote host accessed from worker VM via SSH.
=cut

Expand All @@ -212,10 +99,14 @@ sub connect_target_to_serial {
$args{destination_ip} //= get_required_var('REDIRECT_DESTINATION_IP');
$args{ssh_user} //= get_required_var('REDIRECT_DESTINATION_USER');

croak "OpenQA variable BASE_VM_ID undefined. Run 'redirection_init()' first" unless get_var('BASE_VM_ID');
croak "IP address '$args{destination_ip}' is not valid." unless grep(/^$RE{net}{IPv4}$/, $args{destination_ip});
croak 'Global variable "$serialdev" undefined' unless $serialdev;

# This captures initial machine id which serves as a baseline host.
# disconnect_target_from_serial() types 'exit + enter' in a loop until /etc/machine-id matches machine id defined
# by BASE_VM_ID. This means original host before redirection was reached.
set_var('BASE_VM_ID', script_output 'cat /etc/machine-id');

if (check_serial_redirection()) {
record_info('Redirect ON', "Console is already redirected to:" . script_output('hostname', quiet => 1));
return;
Expand All @@ -227,9 +118,12 @@ sub connect_target_to_serial {
set_var('AUTOINST_URL_HOSTNAME_ORIGINAL', get_var('AUTOINST_URL_HOSTNAME'));
set_var('AUTOINST_URL_HOSTNAME', 'localhost');

enter_cmd "ssh $ssh_opt $args{ssh_user}\@$args{destination_ip} 2>&1 | tee -a /dev/$serialdev";
my $redirect_port = get_required_var("QEMUPORT") + 1;
my $redirect_ip = get_var('QEMU_HOST_IP', '10.0.2.2');
my $redirect_opts = "-R $redirect_port:$redirect_ip:$redirect_port";
enter_cmd "ssh $ssh_opt $redirect_opts $args{ssh_user}\@$args{destination_ip} 2>&1 | tee -a /dev/$serialdev";
handle_login_prompt($args{ssh_user});
check_serial_redirection();
die 'Failed redirecting console' unless check_serial_redirection();
record_info('Redirect ON', "Serial redirection established to: $args{destination_ip}");
}

Expand Down
Loading

0 comments on commit 63305bd

Please sign in to comment.