blob: fb44121f9b87715c8ebe3e062454c9d326046094 [file] [log] [blame]
#!/usr/bin/env python3
import logging
import socket
import time
from socket import timeout as SocketTimeout
import paramiko
from paramiko.buffered_pipe import PipeTimeout as PipeTimeout
from paramiko.ssh_exception import (
AuthenticationException,
BadHostKeyException,
NoValidConnectionsError,
SSHException,
)
from scp import SCPClient, SCPException
class SSHRemoteclient:
r"""
Class to create ssh connection to remote host
for remote host command execution and scp.
"""
def __init__(self, hostname, username, password):
r"""
Description of argument(s):
hostname Name/IP of the remote (targeting) host
username User on the remote host with access to FFCD files
password Password for user on remote host
"""
self.ssh_output = None
self.ssh_error = None
self.sshclient = None
self.scpclient = None
self.hostname = hostname
self.username = username
self.password = password
def ssh_remoteclient_login(self):
r"""
Method to create a ssh connection to remote host.
"""
is_ssh_login = True
try:
# SSHClient to make connections to the remote server
self.sshclient = paramiko.SSHClient()
# setting set_missing_host_key_policy() to allow any host
self.sshclient.set_missing_host_key_policy(
paramiko.AutoAddPolicy()
)
# Connect to the server
self.sshclient.connect(
hostname=self.hostname,
username=self.username,
password=self.password,
banner_timeout=120,
timeout=60,
look_for_keys=False,
)
except (
BadHostKeyException,
AuthenticationException,
SSHException,
NoValidConnectionsError,
socket.error,
) as e:
is_ssh_login = False
return is_ssh_login
def ssh_remoteclient_disconnect(self):
r"""
Clean up.
"""
if self.sshclient:
self.sshclient.close()
if self.scpclient:
self.scpclient.close()
def execute_command(self, command, default_timeout=60):
"""
Execute command on the remote host.
Description of argument(s):
command Command string sent to remote host
"""
empty = ""
cmd_start = time.time()
try:
stdin, stdout, stderr = self.sshclient.exec_command(
command, timeout=default_timeout
)
start = time.time()
while time.time() < start + default_timeout:
# Need to do read/write operation to trigger
# paramiko exec_command timeout mechanism.
xresults = stderr.readlines()
results = "".join(xresults)
time.sleep(1)
if stdout.channel.exit_status_ready():
break
cmd_exit_code = stdout.channel.recv_exit_status()
# Convert list of string to one string
err = ""
out = ""
for item in results:
err += item
for item in stdout.readlines():
out += item
return cmd_exit_code, err, out
except (
paramiko.AuthenticationException,
paramiko.SSHException,
paramiko.ChannelException,
SocketTimeout,
) as e:
# Log command with error. Return to caller for next command, if any.
logging.error(
"\n\tERROR: Fail remote command %s %s" % (e.__class__, e)
)
logging.error(
"\tCommand '%s' Elapsed Time %s"
% (
command,
time.strftime(
"%H:%M:%S", time.gmtime(time.time() - cmd_start)
),
)
)
return 0, empty, empty
def scp_connection(self):
r"""
Create a scp connection for file transfer.
"""
try:
self.scpclient = SCPClient(
self.sshclient.get_transport(), sanitize=lambda x: x
)
logging.info(
"\n\t[Check] %s SCP transport established.\t [OK]"
% self.hostname
)
except (SCPException, SocketTimeout, PipeTimeout) as e:
self.scpclient = None
logging.error(
"\n\tERROR: SCP get_transport has failed. %s %s"
% (e.__class__, e)
)
logging.info(
"\tScript continues generating FFDC on %s." % self.hostname
)
logging.info(
"\tCollected data will need to be manually offloaded."
)
def scp_file_from_remote(self, remote_file, local_file):
r"""
scp file in remote system to local with date-prefixed filename.
Description of argument(s):
remote_file Full path filename on the remote host
local_file Full path filename on the local host
local filename = date-time_remote filename
"""
try:
self.scpclient.get(remote_file, local_file, recursive=True)
except (SCPException, SocketTimeout, PipeTimeout, SSHException) as e:
# Log command with error. Return to caller for next file, if any.
logging.error(
"\n\tERROR: Fail scp %s from remotehost %s %s\n\n"
% (remote_file, e.__class__, e)
)
# Pause for 2 seconds allowing Paramiko to finish error processing before next fetch.
# Without the delay after SCPException,
# next fetch will get 'paramiko.ssh_exception.SSHException'> Channel closed Error.
time.sleep(2)
return False
# Return True for file accounting
return True