-
Notifications
You must be signed in to change notification settings - Fork 278
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
652 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
# SUSE's openQA tests | ||
# | ||
# Copyright SUSE LLC | ||
# SPDX-License-Identifier: FSFAP | ||
# Maintainer: QE-SAP <[email protected]> | ||
|
||
package sles4sap::console_redirection; | ||
use strict; | ||
use warnings; | ||
use testapi; | ||
use Carp qw(croak); | ||
use Exporter qw(import); | ||
use serial_terminal qw(select_serial_terminal); | ||
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. | ||
=cut | ||
|
||
our @EXPORT = qw( | ||
connect_target_to_serial | ||
disconnect_target_from_serial | ||
verify_ip | ||
redirection_init | ||
check_serial_redirection | ||
); | ||
|
||
my $ssh_opt = '-o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=120'; | ||
|
||
=head2 handle_login_prompt | ||
handle_login_prompt(); | ||
B<ssh_user> Login user | ||
Detects if login prompt appears and types the password. | ||
=cut | ||
sub handle_login_prompt { | ||
my $pwd = get_var('_SECRET_SUT_PASSWORD', $testapi::password); | ||
set_serial_term_prompt(); | ||
my $serial_response = wait_serial(qr/Password:\s*$|:~/i); | ||
# Handle password prompt if it appears | ||
if (grep /Password:\s*$/, $serial_response) { | ||
type_password $pwd; | ||
send_key 'ret'; | ||
} | ||
croak 'Failed to setup serial console' unless grep /:~/, $serial_response; | ||
} | ||
=head2 redirection_init | ||
redirection_init(); | ||
Do preparation before redirecting console. Gets base VM id and initial setup. | ||
Needs to be done only once. | ||
=cut | ||
sub redirection_init { | ||
# This should get worker VM id before any redirection happening | ||
# ID serves as identification of the 'base' VM to return to. | ||
set_var('WORKER_VM_ID', script_output 'cat /etc/machine-id'); | ||
} | ||
=head2 set_serial_term_prompt | ||
set_serial_term_prompt(); | ||
Detects if login prompt appears and types the password. | ||
=cut | ||
sub set_serial_term_prompt { | ||
$testapi::distri->{serial_term_prompt} = ''; # reset previous prompt first | ||
my $current_user = script_output('whoami'); | ||
croak 'SSH user not defined and/or "whoami" script failed.' unless defined($current_user); | ||
$testapi::distri->{serial_term_prompt} = ($current_user eq 'root' ? '# ' : '> '); | ||
} | ||
|
||
=head2 connect_target_to_serial | ||
connect_target_to_serial([ssh_user=>ssh_user, target_ip=>$target_ip]); | ||
B<ssh_user> Login user - can be alternatively defined by OpenQA parameter REDIRECT_TARGET_USER | ||
B<target_ip> Target host IP - can be alternatively defined by OpenQA parameter REDIRECT_TARGET_IP | ||
Detects if login prompt appears and types the password. | ||
=cut | ||
sub connect_target_to_serial { | ||
my (%args) = @_; | ||
my $redirect_ip = $args{'target_ip'} // get_required_var('REDIRECT_TARGET_IP'); | ||
my $ssh_user = $args{'ssh_user'} // get_required_var('REDIRECT_TARGET_USER'); | ||
|
||
croak 'Missing "ssh_user" argument' unless $ssh_user; | ||
croak 'Missing "redirect_ip" argument' unless $redirect_ip; | ||
croak "OpenQA variable WORKER_VM_ID undefined. Run 'redirection_init()' first" unless get_var('WORKER_VM_ID'); | ||
croak "IP address '$redirect_ip' is not valid." unless verify_ip($redirect_ip); | ||
croak 'Global variable "$searialdev" undefined' unless $serialdev; | ||
croak "Console is already redirected to:" . script_output('hostname') if check_serial_redirection(); | ||
|
||
enter_cmd "ssh $ssh_opt $ssh_user\@$redirect_ip 2>&1 | tee -a /dev/$serialdev"; | ||
handle_login_prompt($ssh_user); | ||
record_info('Redirect ON', "Serial redirection established to: $redirect_ip"); | ||
} | ||
|
||
=head2 disconnect_target_from_serial | ||
disconnect_target_from_serial($worker_id); | ||
Disconnects target from serial console by typing 'exit' command until host IP matches IP addr of the worker VM. | ||
=cut | ||
sub disconnect_target_from_serial { | ||
my ($worker_id) = @_; | ||
my $worker_machine_id = $worker_id // get_required_var('WORKER_VM_ID'); | ||
set_serial_term_prompt(); | ||
my $serial_redirection_status = check_serial_redirection($worker_machine_id); | ||
while ($serial_redirection_status ne 0) { | ||
enter_cmd('exit'); # Enter command and wait for screen start changing | ||
$testapi::distri->{serial_term_prompt} = ''; # reset console prompt | ||
wait_serial(qr/Connection.*closed./, timeout => 10); # Wait for connection to close | ||
wait_serial(qr/# |> /, timeout => 10); # Wait for console prompt to appear | ||
set_serial_term_prompt(); # after logout user might change and prompt with it. | ||
$serial_redirection_status = check_serial_redirection($worker_machine_id); | ||
} | ||
record_info('Redirect OFF', "Serial redirection closed. Console set to: " . script_output('hostname')); | ||
} | ||
|
||
|
||
=head2 verify_ip | ||
verify_ip($ip_address); | ||
B<ip_address> IP address | ||
Validates IP address format. Returns IP address if format is valied, 0 if invalid. | ||
=cut | ||
sub verify_ip { | ||
my ($ip_address) = @_; | ||
my $result = grep(/^$RE{net}{IPv4}$/, $ip_address) ? $ip_address : 0; | ||
return $result; | ||
} | ||
|
||
=head2 check_serial_redirection | ||
check_serial_redirection([$source_machine_id]); | ||
B<$source_machine_id> /etc/machine-id of the original machine where the console is redirected from | ||
Compares current machine-id to the worker VM ID either defined by WORKER_VM_ID variable or positional argument. | ||
Machine ID is used instead of IP addr since cloud VM IP might not be visible from the inside (for example via 'ip a') | ||
=cut | ||
sub check_serial_redirection { | ||
my ($source_machine_id) = @_; | ||
my $expected_id = $source_machine_id // get_required_var('WORKER_VM_ID'); | ||
my $current_id = script_output 'cat /etc/machine-id'; | ||
|
||
my $redirection_status = $current_id eq $expected_id ? 0 : 1; | ||
record_info('Redir check', 'Console redirection is not active') unless $redirection_status; | ||
record_info('Redir check', "Console is redirected to: " . script_output('hostname')) if $redirection_status; | ||
return $redirection_status; | ||
} | ||
|
||
1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# SUSE's openQA tests | ||
# | ||
# Copyright SUSE LLC | ||
# SPDX-License-Identifier: FSFAP | ||
# Maintainer: QE-SAP <[email protected]> | ||
# | ||
# Library used for Microsoft SDAF deployment | ||
|
||
package sles4sap::microsoft_sdaf; | ||
|
||
use strict; | ||
use warnings; | ||
use testapi; | ||
use Exporter qw(import); | ||
use Carp qw(croak); | ||
use mmapi qw(get_current_job_id); | ||
use utils qw(write_sut_file file_content_replace); | ||
use qesapdeployment qw(qesap_az_get_resource_group); | ||
use File::Basename; | ||
use Regexp::Common qw(net); | ||
|
||
=head1 SYNOPSIS | ||
Library with common functions for Microsoft SDAF deployment automation: | ||
https://learn.microsoft.com/en-us/azure/sap/automation/get-started | ||
=cut | ||
|
||
our @EXPORT = qw( | ||
az_login | ||
sdaf_prepare_ssh_keys | ||
sdaf_get_deployer_ip | ||
); | ||
|
||
=head2 az_login | ||
az_login(); | ||
Azure login using SPN credentials defined by secret OpenQA parameters: | ||
B<_SECRET_AZURE_SPN_APPLICATION_ID> | ||
B<_SECRET_AZURE_SPN_APP_PASSWORD> | ||
B<_SECRET_AZURE_ARM_TENANT_ID> | ||
Returns 'subscription ID' on success. | ||
=cut | ||
|
||
sub az_login { | ||
# Note: For login I cannot use standard PC library, because it gives credentials which are missing permissions | ||
my $app_id = get_required_var('_SECRET_AZURE_SDAF_APP_ID'); | ||
my $app_secret = get_required_var('_SECRET_AZURE_SDAF_APP_PASSWORD'); | ||
my $tenant_id = get_required_var('_SECRET_AZURE_SDAF_TENANT_ID'); | ||
my @secret_variables = ( | ||
"export ARM_CLIENT_ID=$app_id", | ||
"export ARM_CLIENT_SECRET=$app_secret", | ||
"export ARM_TENANT_ID=$tenant_id" | ||
); | ||
|
||
write_bashrc_variables(@secret_variables); | ||
|
||
my $login_cmd = 'while ! az login --service-principal -u ${ARM_CLIENT_ID} -p ${ARM_CLIENT_SECRET} -t ${ARM_TENANT_ID}; do sleep 10; done'; | ||
assert_script_run($login_cmd, timeout => 120); | ||
|
||
my $subscription_id = script_output('az account show -o tsv --query id'); | ||
return ($subscription_id); | ||
} | ||
|
||
=head2 write_bashrc_variables | ||
write_bashrc_variables(@variable list); | ||
=cut | ||
|
||
sub write_bashrc_variables { | ||
my (@variables) = @_; | ||
|
||
write_sut_file('/root/az_variables', join("\n", "\n", @variables)); | ||
assert_script_run('cat /root/az_variables >> /root/.bashrc', quiet => 1); | ||
assert_script_run('source /root/.bashrc', quiet => 1); | ||
} | ||
|
||
=head2 az_get_ssh_key | ||
az_get_ssh_key(deployer_key_vault=$deployer_key_vault, ssh_key_name=$key_name, ssh_key_filename=$ssh_key_filename); | ||
B<deployer_key_vault> Deployer key vault name | ||
B<ssh_key_name> SSH key name residing on keyvault | ||
B<ssh_key_filename> Target filename for SSH key | ||
Retrieves SSH key from DEPLOYER keyvault. | ||
=cut | ||
|
||
sub az_get_ssh_key { | ||
my (%args) = @_; | ||
my $cmd = join(' ', | ||
'az', 'keyvault', 'secret', 'show', | ||
'--vault-name', $args{deployer_key_vault}, | ||
'--name', $args{ssh_key_name}, | ||
'--query', 'value', | ||
'--output', 'tsv', '>', "/root/.ssh/$args{ssh_key_filename}"); | ||
|
||
my $rc = 1; | ||
my $retry = 3; | ||
while ($rc) { | ||
$rc = script_run($cmd); | ||
last if $rc; | ||
croak 'Failed to retrieve ssh key from keyvault' unless $retry; | ||
$retry--; | ||
sleep 5; | ||
} | ||
} | ||
|
||
=head2 sdaf_prepare_ssh_keys | ||
sdaf_prepare_ssh_keys(deployer_key_vault=$deployer_key_vault, ssh_key_name=$key_name); | ||
B<deployer_key_vault> Deployer key vault name | ||
B<ssh_key_name> SSH key name residing on keyvault | ||
Retrieves public and private ssh key from DEPLOYER keyvault and sets up permissions. | ||
=cut | ||
|
||
sub sdaf_prepare_ssh_keys { | ||
my (%args) = @_; | ||
croak 'Missing mandatory argument $args{deployer_key_vault}' unless $args{deployer_key_vault}; | ||
|
||
my $az_cmd = "az keyvault secret list --vault-name $args{deployer_key_vault} --query [].name --output tsv"; | ||
my %ssh_keys = ( | ||
id_rsa => script_output("$az_cmd | grep sshkey\$"), | ||
'id_rsa.pub' => script_output("$az_cmd | grep sshkey-pub\$") | ||
); | ||
|
||
assert_script_run('mkdir -p /root/.ssh'); | ||
assert_script_run('chmod 700 /root/.ssh'); | ||
for my $key_file (keys %ssh_keys) { | ||
az_get_ssh_key( | ||
deployer_key_vault => $args{deployer_key_vault}, | ||
ssh_key_name => $ssh_keys{$key_file}, | ||
ssh_key_filename => $key_file | ||
); | ||
} | ||
assert_script_run("chmod 600 /root/.ssh/id_rsa"); | ||
assert_script_run("chmod 644 /root/.ssh/id_rsa.pub"); | ||
} | ||
|
||
=head2 sdaf_get_deployer_ip | ||
sdaf_get_deployer_ip(deployer_resource_group=>$deployer_resource_group); | ||
B<deployer_resource_group> Deployer key vault name | ||
Retrieves public IP of the deployer VM. | ||
=cut | ||
|
||
sub sdaf_get_deployer_ip { | ||
my (%args) = @_; | ||
croak 'Missing "deployer_resource_group" argument' unless $args{deployer_resource_group}; | ||
|
||
my $vm_name = script_output("az vm list --resource-group $args{deployer_resource_group} --query [].name --output tsv"); | ||
my $az_query_cmd = join(' ', 'az', 'vm', 'list-ip-addresses', '--resource-group', $args{deployer_resource_group}, | ||
'--name', $vm_name, '--query', '"[].virtualMachine.network.publicIpAddresses[0].ipAddress"', '-o', 'tsv'); | ||
|
||
my $ip_addr = script_output($az_query_cmd); | ||
croak "Not a valid ip addr: $ip_addr" unless grep /^$RE{net}{IPv4}$/, $ip_addr; | ||
return $ip_addr; | ||
} | ||
|
||
1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# SUSE's openQA tests | ||
# | ||
# Copyright SUSE LLC | ||
# SPDX-License-Identifier: FSFAP | ||
# Maintainer: QE-SAP <[email protected]> | ||
# | ||
# Basetest used for Microsoft SDAF deployment | ||
|
||
package sles4sap::microsoft_sdaf_basetest; | ||
use strict; | ||
use warnings; | ||
use testapi; | ||
use parent 'opensusebasetest'; | ||
use sles4sap::microsoft_sdaf; | ||
|
||
sub post_fail_hook { | ||
record_info('Post fail', 'Executing post fail hook'); | ||
} | ||
|
||
sub post_run_hook { | ||
record_info('Post run', 'Executing post run hook'); | ||
unless (get_var('SDAF_DO_CLEANUP')) { | ||
record_info('Skip cleanup', "Openqa variable 'SDAF_DO_CLEANUP' not set, skipping cleanup"); | ||
return; | ||
} | ||
} | ||
|
||
1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
name: microsoft_sdaf | ||
description: | | ||
microsoft sdaf based deployment | ||
schedule: | ||
- boot/boot_to_desktop | ||
- sles4sap/microsoft_sdaf/sdaf_prepare_jumphost |
Oops, something went wrong.