Aaron
Aaron

Reputation: 11

SSH programming with Python 3 paramiko, EOFError when trying to execute commands

Wizards of stackoverflow. I'm trying to read in commands from a .txt file to SSH using paramiko. Currently it will read in the first line from the file and execute it. When I get to the second line though, it will throw an EOFError and quit. I put a check in to see if the second command makes it to print and it does, however, it does not execute. I was hoping someone would be able to help and solve this issue with me. Here is my code:

from paramiko import client
import logging
import os

#Clear the screen before starting the script
os.system('cls')

# Prompting for the user input config file
filename = input('Input configuration filename, the file extension must be attached: ')

# Creating the LOG file for the execution of the config file
logFileName = "LOG" + filename[0:]
try:
    logging.basicConfig(filename= logFileName ,format='%(asctime)s %(message)s', level= logging.DEBUG)
    print ("The file was created!")
except IOError:
    print ("File failed to create")

logging.info("---------------------------------------------------------------------------")
logging.info("NEW CONFIGURATION LOG ENTRY")
logging.info("---------------------------------------------------------------------------")


# Class for creating an SSH client, logging in, letting us write out commands, and close the client.
class ssh:
    client = None
    def __init__(self, address, username, password):


        print ("Connecting to server...")
        logging.info('Connecting to server...')

        self.client = client.SSHClient()
        self.client.set_missing_host_key_policy(client.AutoAddPolicy())
        self.client.connect(address, username= username, password= password, look_for_keys= False)

        logging.info("Address: " + address)
        logging.info("Username: " + username)
        print ("Connection successful!")
        logging.info('Connection successful!')

    def sendCommand(self, command):
        if (self.client):
            stdin, stdout, stderr = self.client.exec_command(command)
            receiveData = b""
            while not stdout.channel.exit_status_ready():
                receiveData += stdout.channel.recv(1024)

            if stdout.channel.recv_ready():
                received = stdout.channel.recv(1024)
                while received:
                    receiveData += received
                    received = stdout.channel.recv(1024)

            if receiveData:
                print (str(receiveData, "utf8"))

            else:
                print ("stdout is empty")
        else:
            print ("Connection failed, check credentials and try again..")
            logging.warning('Connection failed, check credentials and try again..')

connection = ssh('0.0.0.0', 'test', 'test')
with open(filename) as f:
    for line in f:
        print(line)
        connection.sendCommand(line)

The .txt file would read something like this:

configure terminal

Interface Gi0/9

description Test_Interface

Any help is much appreciated, thank you.

Upvotes: 1

Views: 2675

Answers (1)

Arnial
Arnial

Reputation: 1441

Possible bug. Current implementation of sendCommand may not receive output (or full output).

Reason exit_status_ready is a non blocking way to find that exit status is received. It is possible that lastpart of output still not readed by script. You need to call recv after while if recv_ready is True.

Also, I don't think that checking recv_ready in while loop is good idea. It's non-blocking method. Because of it while loop will uselessly run multiple times, just wasting your CPU power.

This version work for me:

receiveData = b""
while not stdout.channel.exit_status_ready():
    receiveData += stdout.channel.recv( 2048 )

if stdout.channel.recv_ready():
    received = stdout.channel.recv( 2048 )
    while received: #received will be empty only when all data received
        receiveData += received
        received = stdout.channel.recv( 2048 )

if receiveData:
    print( str( receiveData, "utf8" ) )
else:
    print( "stdout is empty" )

Also I should mention that there is easer way to build string from output. You can use the fact that stdin, stdout, and stderr are file like objects.

Here simpler example for stderr (it may be good idea to read it too):

data = ""
for line in stderr:
    #line in string already (probably will work only if locale is utf8)
    data += line 

if data:
    print( data ) 
else:
    print( "stderr is empty" )

Update: if you don't have multiple commands on one line then

filename = input('Input configuration filename, the file extension must be attached: ')
# define ssh class here (i'll skip it to make it shorter)

connection = ssh('0.0.0.0', 'test', 'test')
with open(filename) as f:
   for line in f:
       connection.sendCommand( line )

If you have several commands per line just split them to array of different commands.

Upvotes: 1

Related Questions