1#!/usr/bin/env python3 2 3import logging 4import socket 5import time 6from socket import timeout as SocketTimeout 7 8import paramiko 9from paramiko.buffered_pipe import PipeTimeout as PipeTimeout 10from paramiko.ssh_exception import ( 11 AuthenticationException, 12 BadHostKeyException, 13 NoValidConnectionsError, 14 SSHException, 15) 16from scp import SCPClient, SCPException 17 18 19class SSHRemoteclient: 20 r""" 21 Class to create ssh connection to remote host 22 for remote host command execution and scp. 23 """ 24 25 def __init__(self, hostname, username, password, port_ssh): 26 r""" 27 Description of argument(s): 28 29 hostname Name/IP of the remote (targeting) host 30 username User on the remote host with access to FFCD files 31 password Password for user on remote host 32 """ 33 34 self.ssh_output = None 35 self.ssh_error = None 36 self.sshclient = None 37 self.scpclient = None 38 self.hostname = hostname 39 self.username = username 40 self.password = password 41 self.port_ssh = port_ssh 42 43 def ssh_remoteclient_login(self): 44 r""" 45 Method to create a ssh connection to remote host. 46 """ 47 48 is_ssh_login = True 49 try: 50 # SSHClient to make connections to the remote server 51 self.sshclient = paramiko.SSHClient() 52 # setting set_missing_host_key_policy() to allow any host 53 self.sshclient.set_missing_host_key_policy( 54 paramiko.AutoAddPolicy() 55 ) 56 # Connect to the server 57 self.sshclient.connect( 58 hostname=self.hostname, 59 port=self.port_ssh, 60 username=self.username, 61 password=self.password, 62 banner_timeout=120, 63 timeout=60, 64 look_for_keys=False, 65 ) 66 67 except ( 68 BadHostKeyException, 69 AuthenticationException, 70 SSHException, 71 NoValidConnectionsError, 72 socket.error, 73 ) as e: 74 is_ssh_login = False 75 print("SSH Login: Exception: %s" % e) 76 77 return is_ssh_login 78 79 def ssh_remoteclient_disconnect(self): 80 r""" 81 Clean up. 82 """ 83 84 if self.sshclient: 85 self.sshclient.close() 86 87 if self.scpclient: 88 self.scpclient.close() 89 90 def execute_command(self, command, default_timeout=60): 91 """ 92 Execute command on the remote host. 93 94 Description of argument(s): 95 command Command string sent to remote host 96 97 """ 98 99 empty = "" 100 cmd_start = time.time() 101 try: 102 stdin, stdout, stderr = self.sshclient.exec_command( 103 command, timeout=default_timeout 104 ) 105 start = time.time() 106 while time.time() < start + default_timeout: 107 # Need to do read/write operation to trigger 108 # paramiko exec_command timeout mechanism. 109 xresults = stderr.readlines() 110 results = "".join(xresults) 111 time.sleep(1) 112 if stdout.channel.exit_status_ready(): 113 break 114 cmd_exit_code = stdout.channel.recv_exit_status() 115 116 # Convert list of string to one string 117 err = "" 118 out = "" 119 for item in results: 120 err += item 121 for item in stdout.readlines(): 122 out += item 123 124 return cmd_exit_code, err, out 125 126 except ( 127 paramiko.AuthenticationException, 128 paramiko.SSHException, 129 paramiko.ChannelException, 130 SocketTimeout, 131 ) as e: 132 # Log command with error. 133 # Return to caller for next command, if any. 134 logging.error( 135 "\n\tERROR: Fail remote command %s %s" % (e.__class__, e) 136 ) 137 logging.error( 138 "\tCommand '%s' Elapsed Time %s" 139 % ( 140 command, 141 time.strftime( 142 "%H:%M:%S", time.gmtime(time.time() - cmd_start) 143 ), 144 ) 145 ) 146 return 0, empty, empty 147 148 def scp_connection(self): 149 r""" 150 Create a scp connection for file transfer. 151 """ 152 try: 153 self.scpclient = SCPClient( 154 self.sshclient.get_transport(), sanitize=lambda x: x 155 ) 156 logging.info( 157 "\n\t[Check] %s SCP transport established.\t [OK]" 158 % self.hostname 159 ) 160 except (SCPException, SocketTimeout, PipeTimeout) as e: 161 self.scpclient = None 162 logging.error( 163 "\n\tERROR: SCP get_transport has failed. %s %s" 164 % (e.__class__, e) 165 ) 166 logging.info( 167 "\tScript continues generating FFDC on %s." % self.hostname 168 ) 169 logging.info( 170 "\tCollected data will need to be manually offloaded." 171 ) 172 173 def scp_file_from_remote(self, remote_file, local_file): 174 r""" 175 scp file in remote system to local with date-prefixed filename. 176 177 Description of argument(s): 178 remote_file Full path filename on the remote host 179 180 local_file Full path filename on the local host 181 local filename = date-time_remote filename 182 183 """ 184 185 try: 186 self.scpclient.get(remote_file, local_file, recursive=True) 187 except (SCPException, SocketTimeout, PipeTimeout, SSHException) as e: 188 # Log command with error. Return to caller for next file, if any. 189 logging.error( 190 "\n\tERROR: Fail scp %s from remotehost %s %s\n\n" 191 % (remote_file, e.__class__, e) 192 ) 193 # Pause for 2 seconds allowing Paramiko to finish error processing 194 # before next fetch. Without the delay after SCPException, next 195 # fetch will get 'paramiko.ssh_exception.SSHException'> Channel 196 # closed Error. 197 time.sleep(2) 198 return False 199 # Return True for file accounting 200 return True 201