virtual-deployment/virtualbox/pybox/utils/sftp.py

161 lines
5.5 KiB
Python

#!/usr/bin/python3
#
# SPDX-License-Identifier: Apache-2.0
#
"""
This module provides functionality to send files and directories to remote servers using the
rsync and paramiko libraries.
"""
import getpass
import os
import time
import paramiko
from helper.install_lab import exec_cmd
from utils.install_log import LOG
def sftp_send(source, destination, client_dict):
"""
Send files to remote server
"""
remote_host = client_dict["remote_host"]
username = client_dict["username"]
sftp_client = None
LOG.info("Connecting to server %s with username %s", remote_host, username)
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# TODO(WEI): need to make this timeout handling better
retry = 0
while retry < 8:
try:
ssh_client.connect(
remote_host,
port=client_dict["remote_port"],
username=username,
password=client_dict["password"],
look_for_keys=False,
allow_agent=False
)
sftp_client = ssh_client.open_sftp()
retry = 8
except Exception: # pylint: disable=W0703
LOG.info("******* try again")
retry += 1
LOG.info("Waiting 10s")
time.sleep(10)
LOG.info("Sending file from %s to %s", source, destination)
if sftp_client:
sftp_client.put(source, destination)
LOG.info("Done")
sftp_client.close()
ssh_client.close()
# pylint: disable=R0801
def send_dir(params_dict):
"""
Send directory `source` to remote host `remote_host` at port `remote_port` and destination
`destination` using `rsync` over `ssh`.
Args:
params_dict (dict): A dictionary containing the following keys:
- source (str): The local directory to be sent.
- remote_host (str): The IP address or domain name of the remote host.
- remote_port (int): The port number of the remote host to connect to.
- destination (str): The remote directory to copy `source` into.
- username (str): The username for the SSH connection.
- password (str): The password for the SSH connection.
- follow_links (bool, optional): Whether to follow symbolic links when
copying files. Default is True.
- clear_known_hosts (bool, optional): Whether to clear the known_hosts file
before making the SSH connection. Default is True.
Raises:
Exception: If there is an error in `rsync`, raises an exception with the return code.
Note:
This method only works from a Linux environment.
"""
source = params_dict['source']
remote_host = params_dict['remote_host']
remote_port = params_dict['remote_port']
destination = params_dict['destination']
username = params_dict['username']
password = params_dict['password']
follow_links = params_dict.get('follow_links', True)
clear_known_hosts = params_dict.get('clear_known_hosts', True)
# Only works from linux for now
if not source.endswith('/') or not source.endswith('\\'):
source = source + '/'
follow_links = "L" if follow_links else ""
if clear_known_hosts:
if remote_host == '127.0.0.1':
keygen_arg = f"[127.0.0.1]:{remote_port}"
else:
keygen_arg = remote_host
cmd = f'ssh-keygen -f \
"/home/{getpass.getuser()}/.ssh/known_hosts" -R {keygen_arg} 2>/dev/null'
exec_cmd(cmd)
LOG.info('#### Running rsync of dir: %s -> %s@%s:%s', source, username, remote_host, destination)
cmd = (f'rsync -av{follow_links} --rsh="/usr/bin/sshpass -p {password} '
f'ssh -p {remote_port} -o StrictHostKeyChecking=no -l {username}" '
f'{source}* {username}@{remote_host}:{destination}')
exec_cmd(cmd)
def send_dir_fallback(source, remote_host, destination, username, password):
"""
Send directory contents to remote server, usually controller-0
Note: does not send nested directories only files.
args:
- source: full path to directory
e.g. /localhost/loadbuild/jenkins/latest_build/
- Remote host: name of host to log into, controller-0 by default
e.g. myhost.com
- destination: where to store the file on host: /home/myuser/
"""
LOG.info("Connecting to server %s with username %s", remote_host, username)
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(
remote_host,
username=username,
password=password,
look_for_keys=False,
allow_agent=False
)
sftp_client = ssh_client.open_sftp()
send_img = False
for items in os.listdir(source):
path = source + items
if os.path.isfile(path):
if items.endswith('.img'):
remote_path = destination + 'images/' + items
LOG.info("Sending file from %s to %s", path, remote_path)
sftp_client.put(path, remote_path)
send_img = True
elif items.endswith('.iso'):
pass
else:
remote_path = destination + items
LOG.info("Sending file from %s to %s", path, remote_path)
sftp_client.put(path, remote_path)
LOG.info("Done")
sftp_client.close()
ssh_client.close()
if send_img:
LOG.info("Waiting 10s")
time.sleep(10)