blob: 73cdd9ce77d78fcfcf70949cbd141f16a3fb64ae [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Peter D Phan72ce6b82021-06-03 06:18:26 -05002
George Keishinge635ddc2022-12-08 07:38:02 -06003import logging
Patrick Williams20f38712022-12-08 06:18:26 -06004import socket
5import time
George Keishinge635ddc2022-12-08 07:38:02 -06006from socket import timeout as SocketTimeout
Patrick Williams57318182022-12-08 06:18:26 -06007
Patrick Williams20f38712022-12-08 06:18:26 -06008import 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
Peter D Phan72ce6b82021-06-03 06:18:26 -050018
19class SSHRemoteclient:
20 r"""
21 Class to create ssh connection to remote host
22 for remote host command execution and scp.
23 """
24
George Keishing7a61aa22023-06-26 13:18:37 +053025 def __init__(self, hostname, username, password, port_ssh):
Peter D Phan72ce6b82021-06-03 06:18:26 -050026 r"""
27 Description of argument(s):
28
Peter D Phan8462faf2021-06-16 12:24:15 -050029 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
Peter D Phan72ce6b82021-06-03 06:18:26 -050032 """
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
George Keishing7a61aa22023-06-26 13:18:37 +053041 self.port_ssh = port_ssh
Peter D Phan72ce6b82021-06-03 06:18:26 -050042
43 def ssh_remoteclient_login(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -050044 r"""
45 Method to create a ssh connection to remote host.
46 """
47
Peter D Phan5963d632021-07-12 09:58:55 -050048 is_ssh_login = True
Peter D Phan72ce6b82021-06-03 06:18:26 -050049 try:
50 # SSHClient to make connections to the remote server
51 self.sshclient = paramiko.SSHClient()
Peter D Phan8462faf2021-06-16 12:24:15 -050052 # setting set_missing_host_key_policy() to allow any host
Patrick Williams20f38712022-12-08 06:18:26 -060053 self.sshclient.set_missing_host_key_policy(
54 paramiko.AutoAddPolicy()
55 )
Peter D Phan72ce6b82021-06-03 06:18:26 -050056 # Connect to the server
Patrick Williams20f38712022-12-08 06:18:26 -060057 self.sshclient.connect(
58 hostname=self.hostname,
George Keishing7a61aa22023-06-26 13:18:37 +053059 port=self.port_ssh,
Patrick Williams20f38712022-12-08 06:18:26 -060060 username=self.username,
61 password=self.password,
62 banner_timeout=120,
63 timeout=60,
64 look_for_keys=False,
65 )
Peter D Phan72ce6b82021-06-03 06:18:26 -050066
Patrick Williams20f38712022-12-08 06:18:26 -060067 except (
68 BadHostKeyException,
69 AuthenticationException,
70 SSHException,
71 NoValidConnectionsError,
72 socket.error,
73 ) as e:
Peter D Phan5963d632021-07-12 09:58:55 -050074 is_ssh_login = False
George Keishing15352052025-04-24 18:55:47 +053075 print("SSH Login: Exception: %s" % e)
Peter D Phan5963d632021-07-12 09:58:55 -050076
77 return is_ssh_login
Peter D Phan72ce6b82021-06-03 06:18:26 -050078
79 def ssh_remoteclient_disconnect(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -050080 r"""
81 Clean up.
82 """
83
84 if self.sshclient:
85 self.sshclient.close()
86
87 if self.scpclient:
88 self.scpclient.close()
89
Patrick Williams20f38712022-12-08 06:18:26 -060090 def execute_command(self, command, default_timeout=60):
Peter D Phan72ce6b82021-06-03 06:18:26 -050091 """
92 Execute command on the remote host.
93
94 Description of argument(s):
95 command Command string sent to remote host
96
97 """
98
Patrick Williams20f38712022-12-08 06:18:26 -060099 empty = ""
Peter D Phanbabf2962021-07-07 11:24:40 -0500100 cmd_start = time.time()
Peter D Phan72ce6b82021-06-03 06:18:26 -0500101 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600102 stdin, stdout, stderr = self.sshclient.exec_command(
103 command, timeout=default_timeout
104 )
Peter D Phanbabf2962021-07-07 11:24:40 -0500105 start = time.time()
106 while time.time() < start + default_timeout:
Peter D Phanba48e9b2021-08-12 11:35:50 -0500107 # Need to do read/write operation to trigger
108 # paramiko exec_command timeout mechanism.
Peter D Phanfd631a12021-08-12 12:58:08 -0500109 xresults = stderr.readlines()
Patrick Williams20f38712022-12-08 06:18:26 -0600110 results = "".join(xresults)
Peter D Phanfd631a12021-08-12 12:58:08 -0500111 time.sleep(1)
Peter D Phanbabf2962021-07-07 11:24:40 -0500112 if stdout.channel.exit_status_ready():
113 break
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500114 cmd_exit_code = stdout.channel.recv_exit_status()
Peter D Phan8a7ec172021-08-03 13:20:26 -0500115
116 # Convert list of string to one string
Patrick Williams20f38712022-12-08 06:18:26 -0600117 err = ""
118 out = ""
Peter D Phanba48e9b2021-08-12 11:35:50 -0500119 for item in results:
Peter D Phanfd631a12021-08-12 12:58:08 -0500120 err += item
121 for item in stdout.readlines():
Peter D Phan8a7ec172021-08-03 13:20:26 -0500122 out += item
123
124 return cmd_exit_code, err, out
Peter D Phanbabf2962021-07-07 11:24:40 -0500125
Patrick Williams20f38712022-12-08 06:18:26 -0600126 except (
127 paramiko.AuthenticationException,
128 paramiko.SSHException,
129 paramiko.ChannelException,
130 SocketTimeout,
131 ) as e:
George Keishingc754b432025-04-24 14:27:14 +0530132 # Log command with error.
133 # Return to caller for next command, if any.
Patrick Williams20f38712022-12-08 06:18:26 -0600134 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 )
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500146 return 0, empty, empty
Peter D Phan72ce6b82021-06-03 06:18:26 -0500147
148 def scp_connection(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500149 r"""
150 Create a scp connection for file transfer.
151 """
Peter D Phan733df632021-06-17 13:13:36 -0500152 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600153 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 )
Peter D Phan733df632021-06-17 13:13:36 -0500160 except (SCPException, SocketTimeout, PipeTimeout) as e:
161 self.scpclient = None
Patrick Williams20f38712022-12-08 06:18:26 -0600162 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 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500172
173 def scp_file_from_remote(self, remote_file, local_file):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500174 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:
Peter D Phan56429a62021-06-23 08:38:29 -0500186 self.scpclient.get(remote_file, local_file, recursive=True)
Peter D Phancf352e52022-02-14 13:18:01 -0600187 except (SCPException, SocketTimeout, PipeTimeout, SSHException) as e:
Peter D Phan8462faf2021-06-16 12:24:15 -0500188 # Log command with error. Return to caller for next file, if any.
Peter D Phane86d9a52021-07-15 10:42:25 -0500189 logging.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600190 "\n\tERROR: Fail scp %s from remotehost %s %s\n\n"
191 % (remote_file, e.__class__, e)
192 )
George Keishingc754b432025-04-24 14:27:14 +0530193 # 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.
Peter D Phancf352e52022-02-14 13:18:01 -0600197 time.sleep(2)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500198 return False
Peter D Phan72ce6b82021-06-03 06:18:26 -0500199 # Return True for file accounting
200 return True