blob: fb44121f9b87715c8ebe3e062454c9d326046094 [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
25 def __init__(self, hostname, username, password):
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
41
42 def ssh_remoteclient_login(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -050043 r"""
44 Method to create a ssh connection to remote host.
45 """
46
Peter D Phan5963d632021-07-12 09:58:55 -050047 is_ssh_login = True
Peter D Phan72ce6b82021-06-03 06:18:26 -050048 try:
49 # SSHClient to make connections to the remote server
50 self.sshclient = paramiko.SSHClient()
Peter D Phan8462faf2021-06-16 12:24:15 -050051 # setting set_missing_host_key_policy() to allow any host
Patrick Williams20f38712022-12-08 06:18:26 -060052 self.sshclient.set_missing_host_key_policy(
53 paramiko.AutoAddPolicy()
54 )
Peter D Phan72ce6b82021-06-03 06:18:26 -050055 # Connect to the server
Patrick Williams20f38712022-12-08 06:18:26 -060056 self.sshclient.connect(
57 hostname=self.hostname,
58 username=self.username,
59 password=self.password,
60 banner_timeout=120,
61 timeout=60,
62 look_for_keys=False,
63 )
Peter D Phan72ce6b82021-06-03 06:18:26 -050064
Patrick Williams20f38712022-12-08 06:18:26 -060065 except (
66 BadHostKeyException,
67 AuthenticationException,
68 SSHException,
69 NoValidConnectionsError,
70 socket.error,
71 ) as e:
Peter D Phan5963d632021-07-12 09:58:55 -050072 is_ssh_login = False
73
74 return is_ssh_login
Peter D Phan72ce6b82021-06-03 06:18:26 -050075
76 def ssh_remoteclient_disconnect(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -050077 r"""
78 Clean up.
79 """
80
81 if self.sshclient:
82 self.sshclient.close()
83
84 if self.scpclient:
85 self.scpclient.close()
86
Patrick Williams20f38712022-12-08 06:18:26 -060087 def execute_command(self, command, default_timeout=60):
Peter D Phan72ce6b82021-06-03 06:18:26 -050088 """
89 Execute command on the remote host.
90
91 Description of argument(s):
92 command Command string sent to remote host
93
94 """
95
Patrick Williams20f38712022-12-08 06:18:26 -060096 empty = ""
Peter D Phanbabf2962021-07-07 11:24:40 -050097 cmd_start = time.time()
Peter D Phan72ce6b82021-06-03 06:18:26 -050098 try:
Patrick Williams20f38712022-12-08 06:18:26 -060099 stdin, stdout, stderr = self.sshclient.exec_command(
100 command, timeout=default_timeout
101 )
Peter D Phanbabf2962021-07-07 11:24:40 -0500102 start = time.time()
103 while time.time() < start + default_timeout:
Peter D Phanba48e9b2021-08-12 11:35:50 -0500104 # Need to do read/write operation to trigger
105 # paramiko exec_command timeout mechanism.
Peter D Phanfd631a12021-08-12 12:58:08 -0500106 xresults = stderr.readlines()
Patrick Williams20f38712022-12-08 06:18:26 -0600107 results = "".join(xresults)
Peter D Phanfd631a12021-08-12 12:58:08 -0500108 time.sleep(1)
Peter D Phanbabf2962021-07-07 11:24:40 -0500109 if stdout.channel.exit_status_ready():
110 break
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500111 cmd_exit_code = stdout.channel.recv_exit_status()
Peter D Phan8a7ec172021-08-03 13:20:26 -0500112
113 # Convert list of string to one string
Patrick Williams20f38712022-12-08 06:18:26 -0600114 err = ""
115 out = ""
Peter D Phanba48e9b2021-08-12 11:35:50 -0500116 for item in results:
Peter D Phanfd631a12021-08-12 12:58:08 -0500117 err += item
118 for item in stdout.readlines():
Peter D Phan8a7ec172021-08-03 13:20:26 -0500119 out += item
120
121 return cmd_exit_code, err, out
Peter D Phanbabf2962021-07-07 11:24:40 -0500122
Patrick Williams20f38712022-12-08 06:18:26 -0600123 except (
124 paramiko.AuthenticationException,
125 paramiko.SSHException,
126 paramiko.ChannelException,
127 SocketTimeout,
128 ) as e:
Peter D Phan8462faf2021-06-16 12:24:15 -0500129 # Log command with error. Return to caller for next command, if any.
Patrick Williams20f38712022-12-08 06:18:26 -0600130 logging.error(
131 "\n\tERROR: Fail remote command %s %s" % (e.__class__, e)
132 )
133 logging.error(
134 "\tCommand '%s' Elapsed Time %s"
135 % (
136 command,
137 time.strftime(
138 "%H:%M:%S", time.gmtime(time.time() - cmd_start)
139 ),
140 )
141 )
Peter D Phan2b6cb3a2021-07-19 06:55:42 -0500142 return 0, empty, empty
Peter D Phan72ce6b82021-06-03 06:18:26 -0500143
144 def scp_connection(self):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500145 r"""
146 Create a scp connection for file transfer.
147 """
Peter D Phan733df632021-06-17 13:13:36 -0500148 try:
Patrick Williams20f38712022-12-08 06:18:26 -0600149 self.scpclient = SCPClient(
150 self.sshclient.get_transport(), sanitize=lambda x: x
151 )
152 logging.info(
153 "\n\t[Check] %s SCP transport established.\t [OK]"
154 % self.hostname
155 )
Peter D Phan733df632021-06-17 13:13:36 -0500156 except (SCPException, SocketTimeout, PipeTimeout) as e:
157 self.scpclient = None
Patrick Williams20f38712022-12-08 06:18:26 -0600158 logging.error(
159 "\n\tERROR: SCP get_transport has failed. %s %s"
160 % (e.__class__, e)
161 )
162 logging.info(
163 "\tScript continues generating FFDC on %s." % self.hostname
164 )
165 logging.info(
166 "\tCollected data will need to be manually offloaded."
167 )
Peter D Phan72ce6b82021-06-03 06:18:26 -0500168
169 def scp_file_from_remote(self, remote_file, local_file):
Peter D Phan72ce6b82021-06-03 06:18:26 -0500170 r"""
171 scp file in remote system to local with date-prefixed filename.
172
173 Description of argument(s):
174 remote_file Full path filename on the remote host
175
176 local_file Full path filename on the local host
177 local filename = date-time_remote filename
178
179 """
180
181 try:
Peter D Phan56429a62021-06-23 08:38:29 -0500182 self.scpclient.get(remote_file, local_file, recursive=True)
Peter D Phancf352e52022-02-14 13:18:01 -0600183 except (SCPException, SocketTimeout, PipeTimeout, SSHException) as e:
Peter D Phan8462faf2021-06-16 12:24:15 -0500184 # Log command with error. Return to caller for next file, if any.
Peter D Phane86d9a52021-07-15 10:42:25 -0500185 logging.error(
Patrick Williams20f38712022-12-08 06:18:26 -0600186 "\n\tERROR: Fail scp %s from remotehost %s %s\n\n"
187 % (remote_file, e.__class__, e)
188 )
Peter D Phancf352e52022-02-14 13:18:01 -0600189 # Pause for 2 seconds allowing Paramiko to finish error processing before next fetch.
190 # Without the delay after SCPException,
191 # next fetch will get 'paramiko.ssh_exception.SSHException'> Channel closed Error.
192 time.sleep(2)
Peter D Phan72ce6b82021-06-03 06:18:26 -0500193 return False
Peter D Phan72ce6b82021-06-03 06:18:26 -0500194 # Return True for file accounting
195 return True