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
Patrick Williams57318182022-12-08 06:18:26 -06003import paramiko
George Keishinge635ddc2022-12-08 07:38:02 -06004from paramiko.ssh_exception import AuthenticationException
5from paramiko.ssh_exception import NoValidConnectionsError
6from paramiko.ssh_exception import SSHException
7from paramiko.ssh_exception import BadHostKeyException
Patrick Williams57318182022-12-08 06:18:26 -06008from paramiko.buffered_pipe import PipeTimeout as PipeTimeout
Patrick Williams57318182022-12-08 06:18:26 -06009from scp import SCPClient, SCPException
George Keishinge635ddc2022-12-08 07:38:02 -060010import time
11import socket
12import logging
13from socket import timeout as SocketTimeout
Patrick Williams57318182022-12-08 06:18:26 -060014
Peter D Phan72ce6b82021-06-03 06:18:26 -050015
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):
George Keishinge635ddc2022-12-08 07:38:02 -060023
Peter D Phan72ce6b82021-06-03 06:18:26 -050024 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):
George Keishinge635ddc2022-12-08 07:38:02 -060041
Peter D Phan72ce6b82021-06-03 06:18:26 -050042 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
George Keishinge635ddc2022-12-08 07:38:02 -060051 self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Peter D Phan72ce6b82021-06-03 06:18:26 -050052 # Connect to the server
George Keishinge635ddc2022-12-08 07:38:02 -060053 self.sshclient.connect(hostname=self.hostname,
54 username=self.username,
55 password=self.password,
56 banner_timeout=120,
57 timeout=60,
58 look_for_keys=False)
Peter D Phan72ce6b82021-06-03 06:18:26 -050059
George Keishinge635ddc2022-12-08 07:38:02 -060060 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):
George Keishinge635ddc2022-12-08 07:38:02 -060067
Peter D Phan72ce6b82021-06-03 06:18:26 -050068 r"""
69 Clean up.
70 """
71
72 if self.sshclient:
73 self.sshclient.close()
74
75 if self.scpclient:
76 self.scpclient.close()
77
George Keishinge635ddc2022-12-08 07:38:02 -060078 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
George Keishinge635ddc2022-12-08 07:38:02 -060088 empty = ''
Peter D Phanbabf2962021-07-07 11:24:40 -050089 cmd_start = time.time()
Peter D Phan72ce6b82021-06-03 06:18:26 -050090 try:
George Keishinge635ddc2022-12-08 07:38:02 -060091 stdin, stdout, stderr = \
92 self.sshclient.exec_command(command, timeout=default_timeout)
Peter D Phanbabf2962021-07-07 11:24:40 -050093 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()
George Keishinge635ddc2022-12-08 07:38:02 -060098 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
George Keishinge635ddc2022-12-08 07:38:02 -0600105 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
George Keishinge635ddc2022-12-08 07:38:02 -0600114 except (paramiko.AuthenticationException, paramiko.SSHException,
115 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 Keishinge635ddc2022-12-08 07:38:02 -0600117 logging.error("\n\tERROR: Fail remote command %s %s" % (e.__class__, e))
118 logging.error("\tCommand '%s' Elapsed Time %s" %
119 (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):
George Keishinge635ddc2022-12-08 07:38:02 -0600123
Peter D Phan72ce6b82021-06-03 06:18:26 -0500124 r"""
125 Create a scp connection for file transfer.
126 """
Peter D Phan733df632021-06-17 13:13:36 -0500127 try:
George Keishinge635ddc2022-12-08 07:38:02 -0600128 self.scpclient = SCPClient(self.sshclient.get_transport(), sanitize=lambda x: x)
129 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 Keishinge635ddc2022-12-08 07:38:02 -0600132 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):
George Keishinge635ddc2022-12-08 07:38:02 -0600137
Peter D Phan72ce6b82021-06-03 06:18:26 -0500138 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 Keishinge635ddc2022-12-08 07:38:02 -0600154 "\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