Files
test/keywords/files/file_keywords.py
2025-05-05 13:00:14 +00:00

335 lines
14 KiB
Python

import time
from framework.exceptions.keyword_exception import KeywordException
from framework.logging.automation_logger import get_logger
from framework.ssh.ssh_connection import SSHConnection
from keywords.base_keyword import BaseKeyword
class FileKeywords(BaseKeyword):
"""
Class for file keywords.
"""
def __init__(self, ssh_connection: SSHConnection):
self.ssh_connection = ssh_connection
def download_file(self, remote_file_path: str, local_file_path: str) -> bool:
"""
Method to download a file from the remote host on which the SSH connection is established.
Args:
remote_file_path (str): Absolute path of the file to download.
local_file_path (str): Absolute path (incl file name) to be copied to.
Returns:
bool: True if download is successful, False otherwise.
Raises:
KeywordException: if unable to copy file.
"""
try:
sftp_client = self.ssh_connection.get_sftp_client()
sftp_client.get(remote_file_path, local_file_path)
except Exception as e:
get_logger().log_error(f"Exception while downloading remote file [{remote_file_path}] to [{local_file_path}]. {e}")
raise KeywordException(f"Exception while downloading remote file [{remote_file_path}] to [{local_file_path}]. {e}")
return True
def upload_file(self, local_file_path: str, remote_file_path: str, overwrite: bool = True) -> bool:
"""
Method to upload a file.
It will upload from the local host to the remote host on which the SSH connection
is established.
Args:
local_file_path (str): Absolute path for the file to be uploaded.
remote_file_path (str): Absolute path (incl file name) to upload to.
overwrite (bool): Whether to overwrite if it already exists.
Returns:
bool: True if upload is successful, False otherwise.
Raises:
KeywordException: if unable to upload file.
"""
try:
if overwrite or not self.file_exists(remote_file_path):
sftp_client = self.ssh_connection.get_sftp_client()
sftp_client.put(local_file_path, remote_file_path)
except Exception as e:
get_logger().log_error(f"Exception while uploading local file [{local_file_path}] to [{remote_file_path}]. {e}")
raise KeywordException(f"Exception while uploading local file [{local_file_path}] to [{remote_file_path}]. {e}")
return True
def file_exists(self, file_name: str) -> bool:
"""
Checks if the file exists.
Args:
file_name (str): the filename.
Returns:
bool: True if exists, False otherwise.
"""
try:
sftp_client = self.ssh_connection.get_sftp_client()
sftp_client.stat(file_name)
get_logger().log_info(f"{file_name} exists.")
return True
except IOError:
get_logger().log_info(f"{file_name} does not exist.")
return False
def create_file_with_echo(self, file_name: str, content: str) -> bool:
"""
Creates a file based on its content with the echo command.
Args:
file_name (str): the file name.
content (str): content to be added in the file.
Returns:
bool: True if create successful, False otherwise.
"""
self.ssh_connection.send(f"echo '{content}' > {file_name}")
return self.file_exists(file_name)
def delete_file(self, file_name: str) -> bool:
"""
Deletes the file.
Args:
file_name (str): the file name.
Returns:
bool: True if delete successful, False otherwise.
"""
self.ssh_connection.send_as_sudo(f"rm {file_name}")
return self.file_exists(file_name)
def get_files_in_dir(self, file_dir: str) -> list[str]:
"""
Gets a list of filenames in the given dir.
Args:
file_dir (str): the directory.
Returns:
list[str]: list of filenames.
"""
sftp_client = self.ssh_connection.get_sftp_client()
return sftp_client.listdir(file_dir)
def read_large_file(self, file_name: str, grep_pattern: str = None) -> list[str]:
"""
Function to read large files and filter.
We are timing out when reading files over 10000 lines. This function will read the lines in batches.
The grep pattern will filter lines using grep. If none is specified, all lines are returned.
Args:
file_name (str): the full path and filename ex. /var/log/user.log.
grep_pattern (str): the pattern to use to filter lines ex. 'ptp4l\|phc2sys'.
Returns:
list[str]: The output of the file.
"""
total_output = []
start_line = 1 # start at line 1
end_line = 10000 # we can handle 10000 lines without issue
end_time = time.time() + 300
grep_arg = ""
if grep_pattern:
grep_arg = f"| grep {grep_pattern}"
while time.time() < end_time:
output = self.ssh_connection.send(f"sed -n '{start_line},{end_line}p' {file_name} {grep_arg}")
if not output: # if we get no more output we are at end of file
break
total_output.extend(output)
start_line = end_line + 1
end_line = end_line + 10000
return total_output
def validate_file_exists_with_sudo(self, path: str) -> bool:
"""
Validates whether a file or directory exists at the specified path using sudo.
Args:
path (str): The path to the file or directory.
Returns:
bool: True if the file/directory exists, False otherwise.
Raises:
KeywordException: If there is an error executing the SSH command.
"""
try:
cmd = f"find {path} -mtime 0"
output = self.ssh_connection.send_as_sudo(cmd)
# Handle encoding issues
output = "".join([line.replace("‘", "").replace("’", "") for line in output])
return "No such file or directory" not in output
except Exception as e:
get_logger().log_error(f"Failed to check file existence at {path}: {e}")
raise KeywordException(f"Failed to check file existence at {path}: {e}")
def create_directory(self, dir_path: str) -> bool:
"""
Create a directory if it does not already exist (non-sudo).
Args:
dir_path (str): Absolute path to the directory to create.
Returns:
bool: True if directory exists or was created successfully.
"""
if self.file_exists(dir_path):
get_logger().log_info(f"Directory already exists: {dir_path}")
return True
self.ssh_connection.send(f"mkdir -p {dir_path}")
return self.file_exists(dir_path)
def create_directory_with_sudo(self, dir_path: str) -> bool:
"""
Create a directory using sudo if it does not already exist.
Args:
dir_path (str): Absolute path to the directory to create.
Returns:
bool: True if directory exists or was created successfully.
"""
if self.validate_file_exists_with_sudo(dir_path):
get_logger().log_info(f"Directory already exists: {dir_path}")
return True
self.ssh_connection.send_as_sudo(f"mkdir -p {dir_path}")
return self.validate_file_exists_with_sudo(dir_path)
def delete_folder_with_sudo(self, folder_path: str) -> bool:
"""
Deletes the folder.
Args:
folder_path (str): path to the folder.
Returns:
bool: True if delete successful, False otherwise.
"""
self.ssh_connection.send_as_sudo(f"rm -r -f {folder_path}")
return self.validate_file_exists_with_sudo(folder_path)
def rename_file(self, old_file_name: str, new_file_name: str):
"""
Renames the file.
Args:
old_file_name (str): path to file to be renamed
new_file_name (str): path to be set for renamed file
"""
self.ssh_connection.send_as_sudo(f"mv {old_file_name} {new_file_name}")
def rsync_to_remote_server(self, local_dest_path: str, remote_server: str, remote_user: str, remote_password: str, remote_path: str, recursive: bool = False, rsync_options: str = "") -> None:
"""
Rsync a file or directory to a remote server from the target host.
This method runs rsync on the host associated with the current SSHConnection
(self.ssh_connection). It initiates an outbound connection to the remote server
using sshpass for authentication, allowing flexible copying of files or directories
to external sources from the target host.
Default rsync options are '-avz' (archive mode, verbose, compression). Additional options
can be appended if needed to support scenarios like progress display, bandwidth throttling, or cleanup.
Args:
local_dest_path (str): Absolute path on the target host where the file or directory should be copied.
remote_server (str): Remote server IP address or hostname.
remote_user (str): Username to authenticate with the remote server.
remote_password (str): Password to authenticate with the remote server.
remote_path (str): Absolute path to the file or directory on the remote server.
recursive (bool, optional): Whether to copy directories recursively by adding 'r' to options. Defaults to False.
rsync_options (str, optional): Additional rsync command-line options (e.g., "--progress", "--bwlimit=10000"). Defaults to "".
Raises:
KeywordException: If the rsync operation fails due to SSH, rsync, or connection issues.
"""
opts = "-avz"
if recursive:
opts += "r"
if rsync_options:
opts += f" {rsync_options}"
cmd = f"sshpass -p '{remote_password}' rsync {opts} -e 'ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10' {local_dest_path} {remote_user}@{remote_server}:{remote_path}"
get_logger().log_info(f"Executing rsync command: {cmd}")
try:
self.ssh_connection.send(cmd)
self.validate_success_return_code(self.ssh_connection)
except Exception as e:
get_logger().log_error(f"Failed to rsync file from {local_dest_path} to {remote_user}@{remote_server}:{remote_path}: {e}")
raise KeywordException(f"Failed to rsync file from {local_dest_path} to {remote_user}@{remote_server}:{remote_path}: {e}") from e
def rsync_from_remote_server(self, remote_server: str, remote_user: str, remote_password: str, remote_path: str, local_dest_path: str, recursive: bool = False, rsync_options: str = "") -> None:
"""
Rsync a file or directory from a remote server to the target host.
This method runs rsync on the host associated with the current SSHConnection
(self.ssh_connection). It initiates an outbound connection to the remote server
using sshpass for authentication, allowing flexible copying of files or directories
from external sources to the target host.
Default rsync options are '-avz' (archive mode, verbose, compression). Additional options
can be appended if needed to support scenarios like progress display, bandwidth throttling, or cleanup.
Args:
remote_server (str): Remote server IP address or hostname.
remote_user (str): Username to authenticate with the remote server.
remote_password (str): Password to authenticate with the remote server.
remote_path (str): Absolute path to the file or directory on the remote server.
local_dest_path (str): Absolute path on the target host where the file or directory should be copied.
recursive (bool, optional): Whether to copy directories recursively by adding 'r' to options. Defaults to False.
rsync_options (str, optional): Additional rsync command-line options (e.g., "--progress", "--bwlimit=10000"). Defaults to "".
Raises:
KeywordException: If the rsync operation fails due to SSH, rsync, or connection issues.
"""
opts = "-avz"
if recursive:
opts += "r"
if rsync_options:
opts += f" {rsync_options}"
cmd = f"sshpass -p '{remote_password}' rsync {opts} -e 'ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10' {remote_user}@{remote_server}:{remote_path} {local_dest_path}"
get_logger().log_info(f"Executing rsync command: {cmd}")
try:
self.ssh_connection.send(cmd)
self.validate_success_return_code(self.ssh_connection)
except Exception as e:
get_logger().log_error(f"Failed to rsync file from {remote_user}@{remote_server}:{remote_path} to {local_dest_path}: {e}")
raise KeywordException(f"Failed to rsync file from {remote_user}@{remote_server}:{remote_path} to {local_dest_path}: {e}") from e
return True
def copy_file(self, src_file: str, dest_file: str):
"""Copies a file from the source path to the destination path.
Args:
src_file (str): The source file path.
dest_file (str): The destination file path.
"""
self.ssh_connection.send(f"cp {src_file} {dest_file}")