Search code examples
pythonubuntusshparamikoiperf

Using paramiko to check if an SSH can be done


I am trying to do a an automated Ethernet bandwidth test on all the ports of a router. My set up involves connecting from a Windows PC with 1 connection to a router to a Linux PC with 7 connections. I'm using the Paramiko module to SSH into the the Linux PC to start an Ethernet testm which works fine.

However, I wanted to see what would happen if I removed one of the Ethernet cables in the test. This causes the program to crash, saying "socket operation was attempted to an unreachable host". I want to be able to avoid this error. Is there a way to check if the connection is possible so that I don't get a crash? Ideally, I would like the code to skip over the bad connection and proceed to the next cable. Here is a snippet of the code used:

    ssh.connect( hostname = target_host[i] , username = un, password = pwd )
    stdin, stdout, stderr = ssh.exec_command('iperf -c 192.168.0.98')
    t_read=stdout.read()
    read[i]=t_read[360:375]
    raw_speed[i]=t_read[360:364]
    address[i]=t_read[221:233]
    print('Receieved data on cable %s from %s via IP: %s at %s \n'%(Cable[i],WMI_Port[i],address[i],read[i]))
    stdin, stdout, stderr = ssh.exec_command('sudo ifconfig %s down'%(target_eth[i]))
    print ('Disabling %s : \n\n'%(WMI_Port[i]))
    ssh.close()    

Solution

  • The easiest way to avoid this error is to not do what caused it--pulling the cable. However, you seem to want to be able to handle that in your program, so here are some options! (I'm assuming this snippet resides within some sort of loop based on a list of hosts.)

    Option 1: You can preemptively check if the host at that address is live before trying to connect. Wrapping the ssh connection code in a if statement with os.system("ping -c 1 " + target_host[i]) is 0 as the condition will allow you to skip the connection on a dead host.

    Option 2: Another approach is to try first and just handle the failure. If you wrap everything from ssh.connect(...) to ssh.close() in a try-except block that watches for paramiko.ssh_exception.NoValidConnectionsError, you can handle the failed connections however you like and continue with the program. An example of this:

    for i in len(target_host):
        try:
            ssh.connect( hostname = target_host[i] , username = un, password = pwd )
            stdin, stdout, stderr = ssh.exec_command('iperf -c 192.168.0.98')
            t_read=stdout.read()
            read[i]=t_read[360:375]
            raw_speed[i]=t_read[360:364]
            address[i]=t_read[221:233]
            print('Receieved data on cable %s from %s via IP: %s at %s \n'%(Cable[i],WMI_Port[i],address[i],read[i]))
            stdin, stdout, stderr = ssh.exec_command('sudo ifconfig %s down'%(target_eth[i]))
            print ('Disabling %s : \n\n'%(WMI_Port[i]))
            ssh.close()    
        except paramiko.ssh_exception.NoValidConnectionsError as error:
            print("Failed to connect to host '%s' with error: %s" % (target_host[i], error))
    

    That exception may not be exactly what you get when it dumps, so replace it with the one you got before. The more closely you match a handler to the expected exception, the better. Too broad of a handler can gobble up exceptions you NEED to see, resulting in silent failures.

    For more info on Paramiko exceptions, look here: http://docs.paramiko.org/en/2.4/api/ssh_exception.html

    For info on handling exceptions in Python 3: https://docs.python.org/3/tutorial/errors.html