Check if paramiko ssh connection is still alive
Question:
Is there a way to check if a paramiko
SSH connection is still alive?
In [1]: import paramiko
In [2]: ssh = paramiko.SSHClient()
In [3]: ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
In [4]: ssh.connect(hostname)
I want to mimic something like this (which doesn’t currently exist)
In [5]: ssh.isalive()
True
Answers:
if ssh.get_transport() is not None:
ssh.get_transport().is_active()
should do it …. assuming I read the docs right.
I had observed is_active()
returning false positives.
I would recommend using this piece:
# use the code below if is_active() returns True
try:
transport = client.get_transport()
transport.send_ignore()
except EOFError, e:
# connection is closed
For me the above answer doesn’t work so I’ve done it by sending an exec_command() with a timeout :
self.ssh.exec_command('ls', timeout=5)
for exemple the full method will be like :
def check_connection(self):
"""
This will check if the connection is still availlable.
Return (bool) : True if it's still alive, False otherwise.
"""
try:
self.ssh.exec_command('ls', timeout=5)
return True
except Exception as e:
print "Connection lost : %s" %e
return False
and I call it every 5 or so seconds.
I was having issues at the moment to download a file after 5 minutes or less of inactivity ( the exception that I was getting was paramiko.ssh_exception.SSHException
), the way I handled a possible fix was the following:
class ...:
def connect(self) -> paramiko.SFTPClient:
"""
Important information to take into consideration:
- with .ppk files, the following exception is being raised.
paramiko.ssh_exception.SSHException: not a valid RSA private key file
- a possible fix is to convert .ppk file to .pem file
brew install putty
puttygen <filename>.ppk -O private-openssh -o <filename>.pem
- then the content of the .pem file should be updated in GCP secret manager.
"""
transport = paramiko.Transport(self.hostname, int(self.port))
params: Dict[str, Any] = {
"username": self.username,
}
if self.password:
params.update(
{
"password": self.password,
}
)
if self.certificate:
private_key = paramiko.RSAKey.from_private_key(open(self.certificate))
params.update(
{
"pkey": private_key,
}
)
transport.connect(**params)
conn = paramiko.SFTPClient.from_transport(transport)
if conn:
if required_directory := self.directory:
conn.chdir(required_directory)
return conn
raise SFTPClientException(f"Unable to connect to sftp {self.hostname}")
def reconnect(self) -> None:
"""
Start a new connection to sftp host
"""
self.client = self.connect()
def download_file(self, path_file: str) -> str:
"""
Download a file from a specific path.
"""
from os import path
local_destination: str = f"/tmp/{uuid4()}_{path.basename(path_file)}"
try:
self.client.get(path_file, local_destination)
except paramiko.ssh_exception.SSHException:
self.reconnect()
self.client.get(path_file, local_destination)
return local_destination
I really hope the shared code helps anybody who is facing a similar incident.
Is there a way to check if a paramiko
SSH connection is still alive?
In [1]: import paramiko
In [2]: ssh = paramiko.SSHClient()
In [3]: ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
In [4]: ssh.connect(hostname)
I want to mimic something like this (which doesn’t currently exist)
In [5]: ssh.isalive()
True
if ssh.get_transport() is not None:
ssh.get_transport().is_active()
should do it …. assuming I read the docs right.
I had observed is_active()
returning false positives.
I would recommend using this piece:
# use the code below if is_active() returns True
try:
transport = client.get_transport()
transport.send_ignore()
except EOFError, e:
# connection is closed
For me the above answer doesn’t work so I’ve done it by sending an exec_command() with a timeout :
self.ssh.exec_command('ls', timeout=5)
for exemple the full method will be like :
def check_connection(self):
"""
This will check if the connection is still availlable.
Return (bool) : True if it's still alive, False otherwise.
"""
try:
self.ssh.exec_command('ls', timeout=5)
return True
except Exception as e:
print "Connection lost : %s" %e
return False
and I call it every 5 or so seconds.
I was having issues at the moment to download a file after 5 minutes or less of inactivity ( the exception that I was getting was paramiko.ssh_exception.SSHException
), the way I handled a possible fix was the following:
class ...:
def connect(self) -> paramiko.SFTPClient:
"""
Important information to take into consideration:
- with .ppk files, the following exception is being raised.
paramiko.ssh_exception.SSHException: not a valid RSA private key file
- a possible fix is to convert .ppk file to .pem file
brew install putty
puttygen <filename>.ppk -O private-openssh -o <filename>.pem
- then the content of the .pem file should be updated in GCP secret manager.
"""
transport = paramiko.Transport(self.hostname, int(self.port))
params: Dict[str, Any] = {
"username": self.username,
}
if self.password:
params.update(
{
"password": self.password,
}
)
if self.certificate:
private_key = paramiko.RSAKey.from_private_key(open(self.certificate))
params.update(
{
"pkey": private_key,
}
)
transport.connect(**params)
conn = paramiko.SFTPClient.from_transport(transport)
if conn:
if required_directory := self.directory:
conn.chdir(required_directory)
return conn
raise SFTPClientException(f"Unable to connect to sftp {self.hostname}")
def reconnect(self) -> None:
"""
Start a new connection to sftp host
"""
self.client = self.connect()
def download_file(self, path_file: str) -> str:
"""
Download a file from a specific path.
"""
from os import path
local_destination: str = f"/tmp/{uuid4()}_{path.basename(path_file)}"
try:
self.client.get(path_file, local_destination)
except paramiko.ssh_exception.SSHException:
self.reconnect()
self.client.get(path_file, local_destination)
return local_destination
I really hope the shared code helps anybody who is facing a similar incident.