| #!/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, port_ssh): |
| 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 |
| self.port_ssh = port_ssh |
| |
| 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, |
| port=self.port_ssh, |
| 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 |