tests: Embed test framework within OCL repository
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
4
test/functional/test-framework/connection/__init__.py
Normal file
4
test/functional/test-framework/connection/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
85
test/functional/test-framework/connection/base_executor.py
Normal file
85
test/functional/test-framework/connection/base_executor.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import time
|
||||
from datetime import timedelta
|
||||
|
||||
from core.test_run import TestRun
|
||||
from test_utils.output import CmdException
|
||||
|
||||
|
||||
class BaseExecutor:
|
||||
def _execute(self, command, timeout):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _rsync(self, src, dst, delete, symlinks, checksum, exclude_list, timeout,
|
||||
dut_to_controller):
|
||||
raise NotImplementedError()
|
||||
|
||||
def rsync_to(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[],
|
||||
timeout: timedelta = timedelta(seconds=90)):
|
||||
return self._rsync(src, dst, delete, symlinks, checksum, exclude_list, timeout, False)
|
||||
|
||||
def rsync_from(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[],
|
||||
timeout: timedelta = timedelta(seconds=90)):
|
||||
return self._rsync(src, dst, delete, symlinks, checksum, exclude_list, timeout, True)
|
||||
|
||||
def is_remote(self):
|
||||
return False
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
def wait_for_connection(self, timeout: timedelta = None):
|
||||
pass
|
||||
|
||||
def run(self, command, timeout: timedelta = timedelta(minutes=30)):
|
||||
if TestRun.dut and TestRun.dut.env:
|
||||
command = f"{TestRun.dut.env} && {command}"
|
||||
command_id = TestRun.LOGGER.get_new_command_id()
|
||||
ip_info = TestRun.dut.ip if len(TestRun.duts) > 1 else ""
|
||||
TestRun.LOGGER.write_command_to_command_log(command, command_id, info=ip_info)
|
||||
output = self._execute(command, timeout)
|
||||
TestRun.LOGGER.write_output_to_command_log(output, command_id)
|
||||
return output
|
||||
|
||||
def run_in_background(self,
|
||||
command,
|
||||
stdout_redirect_path="/dev/null",
|
||||
stderr_redirect_path="/dev/null"):
|
||||
command += f" > {stdout_redirect_path} 2> {stderr_redirect_path} &echo $!"
|
||||
output = self.run(command)
|
||||
|
||||
if output is not None:
|
||||
return int(output.stdout)
|
||||
|
||||
def wait_cmd_finish(self, pid: int, timeout: timedelta = timedelta(minutes=30)):
|
||||
self.run(f"tail --pid={pid} -f /dev/null", timeout)
|
||||
|
||||
def check_if_process_exists(self, pid: int):
|
||||
output = self.run(f"ps aux | awk '{{print $2 }}' | grep ^{pid}$", timedelta(seconds=10))
|
||||
return True if output.exit_code == 0 else False
|
||||
|
||||
def kill_process(self, pid: int):
|
||||
# TERM signal should be used in preference to the KILL signal, since a
|
||||
# process may install a handler for the TERM signal in order to perform
|
||||
# clean-up steps before terminating in an orderly fashion.
|
||||
self.run(f"kill -s SIGTERM {pid} &> /dev/null")
|
||||
time.sleep(3)
|
||||
self.run(f"kill -s SIGKILL {pid} &> /dev/null")
|
||||
|
||||
def run_expect_success(self, command, timeout: timedelta = timedelta(minutes=30)):
|
||||
output = self.run(command, timeout)
|
||||
if output.exit_code != 0:
|
||||
raise CmdException(f"Exception occurred while trying to execute '{command}' command.",
|
||||
output)
|
||||
return output
|
||||
|
||||
def run_expect_fail(self, command, timeout: timedelta = timedelta(minutes=30)):
|
||||
output = self.run(command, timeout)
|
||||
if output.exit_code == 0:
|
||||
raise CmdException(f"Command '{command}' executed properly but error was expected.",
|
||||
output)
|
||||
return output
|
15
test/functional/test-framework/connection/dummy_executor.py
Normal file
15
test/functional/test-framework/connection/dummy_executor.py
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
from connection.base_executor import BaseExecutor
|
||||
|
||||
|
||||
class DummyExecutor(BaseExecutor):
|
||||
def _execute(self, command, timeout=None):
|
||||
print(command)
|
||||
|
||||
def _rsync(self, src, dst, delete, symlinks, checksum, exclude_list, timeout,
|
||||
dut_to_controller):
|
||||
print(f'COPY FROM "{src}" TO "{dst}"')
|
48
test/functional/test-framework/connection/local_executor.py
Normal file
48
test/functional/test-framework/connection/local_executor.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import subprocess
|
||||
from datetime import timedelta
|
||||
|
||||
from connection.base_executor import BaseExecutor
|
||||
from test_utils.output import Output
|
||||
|
||||
|
||||
class LocalExecutor(BaseExecutor):
|
||||
def _execute(self, command, timeout):
|
||||
completed_process = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=timeout.total_seconds())
|
||||
|
||||
return Output(completed_process.stdout,
|
||||
completed_process.stderr,
|
||||
completed_process.returncode)
|
||||
|
||||
def _rsync(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[],
|
||||
timeout: timedelta = timedelta(seconds=90), dut_to_controller=False):
|
||||
options = []
|
||||
|
||||
if delete:
|
||||
options.append("--delete")
|
||||
if symlinks:
|
||||
options.append("--links")
|
||||
if checksum:
|
||||
options.append("--checksum")
|
||||
|
||||
for exclude in exclude_list:
|
||||
options.append(f"--exclude {exclude}")
|
||||
|
||||
completed_process = subprocess.run(
|
||||
f'rsync -r {src} {dst} {" ".join(options)}',
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=timeout.total_seconds())
|
||||
|
||||
if completed_process.returncode:
|
||||
raise Exception(f"rsync failed:\n{completed_process}")
|
142
test/functional/test-framework/connection/ssh_executor.py
Normal file
142
test/functional/test-framework/connection/ssh_executor.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#
|
||||
# Copyright(c) 2019-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
import paramiko
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from connection.base_executor import BaseExecutor
|
||||
from core.test_run import TestRun
|
||||
from test_utils.output import Output
|
||||
|
||||
|
||||
class SshExecutor(BaseExecutor):
|
||||
def __init__(self, ip, username, port=22):
|
||||
self.ip = ip
|
||||
self.user = username
|
||||
self.port = port
|
||||
self.ssh = paramiko.SSHClient()
|
||||
self._check_config_for_reboot_timeout()
|
||||
|
||||
def __del__(self):
|
||||
self.ssh.close()
|
||||
|
||||
def connect(self, user=None, port=None,
|
||||
timeout: timedelta = timedelta(seconds=30)):
|
||||
user = user or self.user
|
||||
port = port or self.port
|
||||
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
self.ssh.connect(self.ip, username=user,
|
||||
port=port, timeout=timeout.total_seconds(),
|
||||
banner_timeout=timeout.total_seconds())
|
||||
except paramiko.AuthenticationException as e:
|
||||
raise paramiko.AuthenticationException(
|
||||
f"Authentication exception occurred while trying to connect to DUT. "
|
||||
f"Please check your SSH key-based authentication.\n{e}")
|
||||
except (paramiko.SSHException, socket.timeout) as e:
|
||||
raise ConnectionError(f"An exception of type '{type(e)}' occurred while trying to "
|
||||
f"connect to {self.ip}.\n {e}")
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self.ssh.close()
|
||||
except Exception:
|
||||
raise Exception(f"An exception occurred while trying to disconnect from {self.ip}")
|
||||
|
||||
def _execute(self, command, timeout):
|
||||
try:
|
||||
(stdin, stdout, stderr) = self.ssh.exec_command(command,
|
||||
timeout=timeout.total_seconds())
|
||||
except paramiko.SSHException as e:
|
||||
raise ConnectionError(f"An exception occurred while executing command '{command}' on"
|
||||
f" {self.ip}\n{e}")
|
||||
|
||||
return Output(stdout.read(), stderr.read(), stdout.channel.recv_exit_status())
|
||||
|
||||
def _rsync(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[],
|
||||
timeout: timedelta = timedelta(seconds=90), dut_to_controller=False):
|
||||
options = []
|
||||
|
||||
if delete:
|
||||
options.append("--delete")
|
||||
if symlinks:
|
||||
options.append("--links")
|
||||
if checksum:
|
||||
options.append("--checksum")
|
||||
|
||||
for exclude in exclude_list:
|
||||
options.append(f"--exclude {exclude}")
|
||||
|
||||
src_to_dst = f"{self.user}@{self.ip}:{src} {dst} " if dut_to_controller else\
|
||||
f"{src} {self.user}@{self.ip}:{dst} "
|
||||
|
||||
try:
|
||||
completed_process = subprocess.run(
|
||||
f'rsync -r -e "ssh -p {self.port} '
|
||||
f'-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" '
|
||||
+ src_to_dst + f'{" ".join(options)}',
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=timeout.total_seconds())
|
||||
except Exception as e:
|
||||
TestRun.LOGGER.exception(f"Exception occurred during rsync process. "
|
||||
f"Please check your SSH key-based authentication.\n{e}")
|
||||
|
||||
if completed_process.returncode:
|
||||
raise Exception(f"rsync failed:\n{completed_process}")
|
||||
|
||||
def is_remote(self):
|
||||
return True
|
||||
|
||||
def _check_config_for_reboot_timeout(self):
|
||||
if "reboot_timeout" in TestRun.config.keys():
|
||||
self._parse_timeout_to_int()
|
||||
else:
|
||||
self.reboot_timeout = None
|
||||
|
||||
def _parse_timeout_to_int(self):
|
||||
self.reboot_timeout = int(TestRun.config["reboot_timeout"])
|
||||
if self.reboot_timeout < 0:
|
||||
raise ValueError("Reboot timeout cannot be negative.")
|
||||
|
||||
def reboot(self):
|
||||
self.run("reboot")
|
||||
self.wait_for_connection_loss()
|
||||
self.wait_for_connection(timedelta(seconds=self.reboot_timeout)) \
|
||||
if self.reboot_timeout is not None else self.wait_for_connection()
|
||||
|
||||
def is_active(self):
|
||||
try:
|
||||
self.ssh.exec_command('', timeout=5)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def wait_for_connection(self, timeout: timedelta = timedelta(minutes=10)):
|
||||
start_time = datetime.now()
|
||||
with TestRun.group("Waiting for DUT ssh connection"):
|
||||
while start_time + timeout > datetime.now():
|
||||
try:
|
||||
self.connect()
|
||||
return
|
||||
except paramiko.AuthenticationException:
|
||||
raise
|
||||
except Exception:
|
||||
continue
|
||||
raise ConnectionError("Timeout occurred while trying to establish ssh connection")
|
||||
|
||||
def wait_for_connection_loss(self, timeout: timedelta = timedelta(minutes=1)):
|
||||
with TestRun.group("Waiting for DUT ssh connection loss"):
|
||||
end_time = datetime.now() + timeout
|
||||
while end_time > datetime.now():
|
||||
self.disconnect()
|
||||
try:
|
||||
self.connect(timeout=timedelta(seconds=5))
|
||||
except Exception:
|
||||
return
|
||||
raise ConnectionError("Timeout occurred before ssh connection loss")
|
Reference in New Issue
Block a user