nightowl nightowl - 2 months ago 21
Python Question

Elegant way to test SSH availability

I need a Python program I'm using to poll a remote server for SSH connectivity and notify when it is available. I am currently doing this using paramiko; attempt to connect, if failure, wait and retry until success or max retries. This works, but it's a bit clunky. Also paramiko seems to either connect or throw an error, so the only way I could see to do this was with a try/except block which is bad, bad, bad. Here is the method:

def check_ssh(self, ip, user, key_file, initial_wait=0, interval=0, retries=1):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

sleep(initial_wait)

for x in range(retries):
try:
ssh.connect(ip, username=user, key_filename=key_file)
return True
except Exception, e:
print e
sleep(interval)
return False


There must be a more elegant solution than this. Paramiko is my SSH library of choice but am open to any suggestions here.

To clarify, I want to avoid using try / except as a means to control the normal flow of code execution - it should be used for catching actual errors such as bad host key, invalid user etc.

Answer

As mentioned in the comment by frb, a try ... except block is a good approach to test availability of a specific service. You shouldn't use a "catch-all" except block though, but limit it to the specific exceptions that occur if the service is unavailable.

According to documentation, paramiko.SSHClient.connect may throw different exceptions, depending on the problem that occured while connecting. If you want to catch all those, your try ... except block would look like this:

try:
    ssh.connect(ip, username=user, key_filename=key_file)
    return True
except (BadHostKeyException, AuthenticationException, 
        SSHException, socket.error) as e:
    print e
    sleep(interval)

If just a subset of these exceptions is relevant to your case, put only those into the tuple after except.