5

I have a class that creates the connection. I can connect and execute 1 command before the channel is closed. On another system i have i can execute multiple commands and the channel does not close. Obviously its a config issue with the systems i am trying to connect to.

class connect:

    newconnection = ''

    def __init__(self,username,password): 
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh.connect('somehost', username=username,password=password,port=2222,timeout=5)
        except:
            print "Count not connect"
            sys.exit()
        self.newconnection = ssh

    def con(self):
        return self.newconnection

Then i use 'ls' command just to print some output

sshconnection = connect('someuser','somepassword').con()


stdin, stdout, stderr = sshconnection.exec_command("ls -lsa")

print stdout.readlines() 
print stdout 

stdin, stdout, stderr = sshconnection.exec_command("ls -lsa")

print stdout.readlines() 
print stdout 

sshconnection.close()
sys.exit()

After the first exec_command runs it prints the expected output of the dir list. When i print stdout after the first exec_command it looks like the channel is closed

<paramiko.ChannelFile from <paramiko.Channel 1 (closed) -> <paramiko.Transport at 0x2400f10L (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))>>> 

Like i said on another system i am able to keep running commands and the connection doesn't close. Is there a way i can keep this open? or a better way i can see the reason why it closes?

edit: So it looks like you can only run 1 command per SSHClient.exec_command... so i decided to get_transport().open_session() and then run a command. The first one always works. The second one always fails and the scripts just hangs

rubio
  • 936
  • 5
  • 16
  • 36
  • Did you found good solution for this? I am getting open channels msg in output for more than one exec_command. First command works fine. The issue is with subsequent that follows. –  Jan 07 '14 at 14:42
  • @Drt unfortunately it looks like exec_command will only run one command before closing the channel. Even if i was trying to run the next command it would tell me my channel was open. But the command would not execute. I tried fabric out and it worked on a different system than i was trying to use it on. If you are using it for a basic ssh connection fabric should work better. Or check out puppet – rubio Jan 08 '14 at 00:05
  • I found solution for this. I was using only `stdout` while printing, forgot that i need to use `stdout.read`. Very minor mistake. You can refer here [this is the question I raised](http://stackoverflow.com/questions/20951690/paramiko-ssh-exec-command-to-collect-output-says-open-channel-in-response.) –  Jan 08 '14 at 08:20
  • 1
    Possible duplicate of [Implement an interactive shell over ssh in Python using Paramiko?](https://stackoverflow.com/questions/35821184/implement-an-interactive-shell-over-ssh-in-python-using-paramiko) – misha May 15 '18 at 20:56

3 Answers3

5

With just paramiko after the exec_command executes the channel is closed and the ssh returns an auth prompt.

Seems its not possible with just paramiko, try fabric or another tool.

** fabric did not work out too.

Community
  • 1
  • 1
rubio
  • 936
  • 5
  • 16
  • 36
  • 1
    No it did not. It has been a long time since i worked on that project though. Maybe paramiko or fabric has changed since but at the time it did not work – rubio Oct 29 '15 at 17:07
2

Please see the following referece as it provides a way to do this in Paramiko:

How do you execute multiple commands in a single session in Paramiko? (Python)

Community
  • 1
  • 1
Michael Flyger
  • 321
  • 3
  • 4
1

it's possible with netmiko (tested on windows). this example is written for connecting to cisco devices but the principle is adaptable for others as well.

import netmiko
from netmiko import ConnectHandler
import json

def connect_enable_silent(ip_address,ios_command):
    with open ("credentials.txt") as line:
        line_1 = json.load(line)
        for k,v in line_1.items():
            router=(k,v)
            try:
                ssh = ConnectHandler(**router[1],device_type="cisco_ios",ip=ip_address)
                ssh.enable()
            except netmiko.ssh_exception.NetMikoAuthenticationException:
                #incorrect credentials
                continue
            except netmiko.ssh_exception.NetMikoTimeoutException:
                #oddly enough if it can log in but not able to authenticate to enable mode the ssh.enable() command does not give an authentication error
                #but a time-out error instead
                try:
                    ssh = ConnectHandler(username = router[1]['username'],password = router[1]['password'],device_type="cisco_ios", ip=ip_address)
                except netmiko.ssh_exception.NetMikoTimeoutException:
                    # connection timed out (ssh not enabled on device, try telnet)
                    continue
                except Exception:
                    continue
                else:
                    output = ssh.send_command(ios_command)
                    ssh.disconnect()
                    if "at '^' marker." in output:
                        #trying to run a command that requires enble mode but not authenticated to enable mode
                        continue
                    return output
            except Exception:
                continue
            else:
                output = ssh.send_command(ios_command)
                ssh.disconnect()
                return output

output = connect_enable_silent(ip_address,ios_command)
for line in output.split('\n'):
    print(line)

Credentials text is meant to store different credentials in case you are planning to call this function to access multiple devices and not all of them using the same credentials. It is in the format:

{"credentials_1":{"username":"username_1","password":"password_1","secret":"secret_1"},
"credentials_2":{"username":"username_2","password":"password_2","secret":"secret_2"},
"credentials_3": {"username": "username_3", "password": "password_3"}
}

The exceptions can be changed to do different things, in my case i just needed it to not return an error and continue trying the next set, which is why most exceptions are silenced.

  • I've switched to something like this as well -- thanks for posting this! For quick-n-dirty ssh sessions netmiko is a good choice. – Michael Flyger Sep 24 '16 at 02:49