blob: 01b39dd1fc3bd8d31d2e93b5afc5b6a8d954f3cb [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Peter D Phan72ce6b82021-06-03 06:18:26 -05002
3import paramiko
Peter D Phan8462faf2021-06-16 12:24:15 -05004from paramiko.ssh_exception import AuthenticationException
5from paramiko.ssh_exception import NoValidConnectionsError
6from paramiko.ssh_exception import SSHException
7from paramiko.ssh_exception import BadHostKeyException
Peter D Phan72ce6b82021-06-03 06:18:26 -05008from paramiko.buffered_pipe import PipeTimeout as PipeTimeout
Peter D Phan733df632021-06-17 13:13:36 -05009from scp import SCPClient, SCPException
Peter D Phanbabf2962021-07-07 11:24:40 -050010import time
Peter D Phan72ce6b82021-06-03 06:18:26 -050011import socket
Peter D Phane86d9a52021-07-15 10:42:25 -050012import logging
Peter D Phan72ce6b82021-06-03 06:18:26 -050013from socket import timeout as SocketTimeout
14
15
16class SSHRemoteclient:
17 r"""
18 Class to create ssh connection to remote host
19 for remote host command execution and scp.
20 """
21
22 def __init__(self, hostname, username, password):
23
24 r"""
25 Description of argument(s):
26
Peter D Phan8462faf2021-06-16 12:24:15 -050027 hostname Name/IP of the remote (targeting) host
28 username User on the remote host with access to FFCD files
29 password Password for user on remote host
Peter D Phan72ce6b82021-06-03 06:18:26 -050030 """
31
32 self.ssh_output = None
33 self.ssh_error = None
34 self.sshclient = None
35 self.scpclient = None
36 self.hostname = hostname
37 self.username = username
38 self.password = password
39
40 def ssh_remoteclient_login(self):
41
42 r"""
43 Method to create a ssh connection to remote host.
44 """
45
Peter D Phan5963d632021-07-12 09:58:55 -050046 is_ssh_login = True
Peter D Phan72ce6b82021-06-03 06:18:26 -050047 try:
48 # SSHClient to make connections to the remote server
49 self.sshclient = paramiko.SSHClient()
Peter D Phan8462faf2021-06-16 12:24:15 -050050 # setting set_missing_host_key_policy() to allow any host
Peter D Phan72ce6b82021-06-03 06:18:26 -050051 self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
52 # Connect to the server
53 self.sshclient.connect(hostname=self.hostname,
54 username=self.username,
George Keishing1c3d86b2021-06-16 22:29:30 -050055 password=self.password,
Peter D Phane0472f82021-08-26 12:14:03 -050056 banner_timeout=120,
57 timeout=60,
George Keishing1c3d86b2021-06-16 22:29:30 -050058 look_for_keys=False)
Peter D Phan72ce6b82021-06-03 06:18:26 -050059
Peter D Phan8462faf2021-06-16 12:24:15 -050060 except (BadHostKeyException, AuthenticationException,
61 SSHException, NoValidConnectionsError, socket.error) as e:
Peter D Phan5963d632021-07-12 09:58:55 -050062 is_ssh_login = False
63
64 return is_ssh_login
Peter D Phan72ce6b82021-06-03 06:18:26 -050065
66 def ssh_remoteclient_disconnect(self):
67
68 r"""
69 Clean up.
70 """
71
72 if self.sshclient:
73 self.sshclient.close()
74
75 if self.scpclient:
76 self.scpclient.close()
77
Peter D Phan2b6cb3a2021-07-19 06:55:42 -050078 def execute_command(self, command,
79 default_timeout=60):
Peter D Phan72ce6b82021-06-03 06:18:26 -050080 """
81 Execute command on the remote host.
82
83 Description of argument(s):
84 command Command string sent to remote host
85
86 """
87
Peter D Phanbabf2962021-07-07 11:24:40 -050088 empty = ''
89 cmd_start = time.time()
Peter D Phan72ce6b82021-06-03 06:18:26 -050090 try:
Peter D Phanbabf2962021-07-07 11:24:40 -050091 stdin, stdout, stderr = \
92 self.sshclient.exec_command(command, timeout=default_timeout)
93 start = time.time()
94 while time.time() < start + default_timeout:
Peter D Phanba48e9b2021-08-12 11:35:50 -050095 # Need to do read/write operation to trigger
96 # paramiko exec_command timeout mechanism.
Peter D Phanfd631a12021-08-12 12:58:08 -050097 xresults = stderr.readlines()
Peter D Phanba48e9b2021-08-12 11:35:50 -050098 results = ''.join(xresults)
Peter D Phanfd631a12021-08-12 12:58:08 -050099 time.sleep(1)
Peter D Phanbabf2962021-07-07 11:24:40 -0500100 if stdout.channel.exit_status_ready():
101 break
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500102 cmd_exit_code = stdout.channel.recv_exit_status()
Peter D Phan8a7ec172021-08-03 13:20:26 -0500103
104 # Convert list of string to one string
105 err = ''
106 out = ''
Peter D Phanba48e9b2021-08-12 11:35:50 -0500107 for item in results:
Peter D Phanfd631a12021-08-12 12:58:08 -0500108 err += item
109 for item in stdout.readlines():
Peter D Phan8a7ec172021-08-03 13:20:26 -0500110 out += item
111
112 return cmd_exit_code, err, out
Peter D Phanbabf2962021-07-07 11:24:40 -0500113
Peter D Phan72ce6b82021-06-03 06:18:26 -0500114 except (paramiko.AuthenticationException, paramiko.SSHException,
Peter D Phanbabf2962021-07-07 11:24:40 -0500115 paramiko.ChannelException, SocketTimeout) as e:
Peter D Phan8462faf2021-06-16 12:24:15 -0500116 # Log command with error. Return to caller for next command, if any.
George Keishing7bf55092021-07-22 12:33:34 -0500117 logging.error("\n\tERROR: Fail remote command %s %s" % (e.__class__, e))
118 logging.error("\tCommand '%s' Elapsed Time %s" %
Peter D Phane86d9a52021-07-15 10:42:25 -0500119 (command, time.strftime("%H:%M:%S", time.gmtime(time.time() - cmd_start))))
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500120 return 0, empty, empty
Peter D Phan72ce6b82021-06-03 06:18:26 -0500121
122 def scp_connection(self):
123
124 r"""
125 Create a scp connection for file transfer.
126 """
Peter D Phan733df632021-06-17 13:13:36 -0500127 try:
Peter D Phan56429a62021-06-23 08:38:29 -0500128 self.scpclient = SCPClient(self.sshclient.get_transport(), sanitize=lambda x: x)
Peter D Phane86d9a52021-07-15 10:42:25 -0500129 logging.info("\n\t[Check] %s SCP transport established.\t [OK]" % self.hostname)
Peter D Phan733df632021-06-17 13:13:36 -0500130 except (SCPException, SocketTimeout, PipeTimeout) as e:
131 self.scpclient = None
George Keishing7bf55092021-07-22 12:33:34 -0500132 logging.error("\n\tERROR: SCP get_transport has failed. %s %s" % (e.__class__, e))
133 logging.info("\tScript continues generating FFDC on %s." % self.hostname)
134 logging.info("\tCollected data will need to be manually offloaded.")
Peter D Phan72ce6b82021-06-03 06:18:26 -0500135
136 def scp_file_from_remote(self, remote_file, local_file):
137
138 r"""
139 scp file in remote system to local with date-prefixed filename.
140
141 Description of argument(s):
142 remote_file Full path filename on the remote host
143
144 local_file Full path filename on the local host
145 local filename = date-time_remote filename
146
147 """
148
149 try:
Peter D Phan56429a62021-06-23 08:38:29 -0500150 self.scpclient.get(remote_file, local_file, recursive=True)
Peter D Phancf352e52022-02-14 13:18:01 -0600151 except (SCPException, SocketTimeout, PipeTimeout, SSHException) as e:
Peter D Phan8462faf2021-06-16 12:24:15 -0500152 # Log command with error. Return to caller for next file, if any.
Peter D Phane86d9a52021-07-15 10:42:25 -0500153 logging.error(
George Keishing7bf55092021-07-22 12:33:34 -0500154 "\n\tERROR: Fail scp %s from remotehost %s %s\n\n" % (remote_file, e.__class__, e))
Peter D Phancf352e52022-02-14 13:18:01 -0600155 # Pause for 2 seconds allowing Paramiko to finish error processing before next fetch.
156 # Without the delay after SCPException,
157 # next fetch will get 'paramiko.ssh_exception.SSHException'> Channel closed Error.
158 time.sleep(2)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500159 return False
Peter D Phan72ce6b82021-06-03 06:18:26 -0500160 # Return True for file accounting
161 return True