J.Wang
J.Wang

Reputation: 1241

How to keep ssh session not expired using paramiko?

I intend to run several commands on remote host using paramiko, but the ssh session closed after running a command.
The code listed below:

from paramiko import SSHClient  
import paramiko  
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, 22, user, passwd, timeout=3)
stdin, stdout, stderr = ssh.exec_command('uname -a')

So is there any way to stop the ssh session from closing? Or any alternatives for paramiko?

Update:
I was able to keeping calling exec_command on my Macbook when connected to a Linux server, but the ssh session closed automatically after exec_command once on a Linux server when connected to a switch and raised an
SSHException: paramiko.ssh_exception.SSHException: SSH session not active

>>> print ssh.get_transport()  
>>> <paramiko.Transport at 0xf00216d0L (unconnected)>  
>>> print ssh.get_transport().is_active()  
>>> False  
>>> print ssh.get_transport().is_authenticated()  
>>> False

Is there any way to keep paramiko ssh session active all the time?

The paramiko debug mode info returned as following:

starting thread (client mode): 0x2657e10L  
Connected (version 1.99, client Comware-5.20)  
kex algos:[u'diffie-hellman-group-exchange-sha1', u'diffie-hellman- group14-sha1', u'diffie-hellman-group1-sha1'] server key:[u'ssh-rsa'] client encrypt:[u'aes128-cbc', u'3des-cbc', u'des-cbc'] server encrypt:[u'aes128-cbc', u'3des-cbc', u'des-cbc'] client mac:[u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5', u'hmac-md5-96'] server mac:[u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5', u'hmac-md5-96'] client compress:[u'none'] server compress:[u'none'] client lang:[u''] server lang:[u''] kex follows?False  
Ciphers agreed: local=aes128-cbc, remote=aes128-cbc  
using kex diffie-hellman-group14-sha1; server key type ssh-rsa; cipher: local aes128-cbc, remote aes128-cbc; mac: local hmac-sha1, remote hmac-sha1; compression: local none, remote none  
Switch to new keys ...  
userauth is OK  
Authentication (password) successful!  
[chan 0] Max packet in: 32768 bytes  
[chan 1] Max packet in: 32768 bytes  
[chan 0] Max packet out: 32496 bytes  
Secsh channel 0 opened.  
Secsh channel 2 open FAILED:  
Resource shortage: Resource shortage  
[chan 0] Sesch channel 0 request ok  
[chan 0] EOF sent (0)

Upvotes: 12

Views: 33865

Answers (4)

permi
permi

Reputation: 1

From the docs:

exec_command(command)

Execute a command on the server. If the server allows it, the channel will then be directly connected to the stdin, stdout, and stderr of the command being executed.

When the command finishes executing, the channel will be closed and can’t be reused. You must open a new channel if you wish to execute another command.

Parameters command (str) – a shell command to execute.

Raises SSHException – if the request was rejected or the channel was closed

Upvotes: -1

amkawai
amkawai

Reputation: 382

I found an answer on this link. You can use the command send instead of exec_command:

from paramiko import SSHClient  
import paramiko  

ssh = SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, 22, user, passwd, timeout=3)

commands = ssh.invoke_shell()

commands.send("uname -a")
time.sleep(.5)
output = commands.recv(65535)
output = output.decode("utf-8")
print (output)

commands.send("ls -la")
time.sleep(.5)
output = commands.recv(65535)
output = output.decode("utf-8")
print (output)

Upvotes: 0

misha
misha

Reputation: 837

You can implement an interactive shell using paramiko, that way the channel is not closed after a command is executed on the remote shell.

import paramiko
import re


class ShellHandler:

    def __init__(self, host, user, psw):
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(host, username=user, password=psw, port=22)

        channel = self.ssh.invoke_shell()
        self.stdin = channel.makefile('wb')
        self.stdout = channel.makefile('r')

    def __del__(self):
        self.ssh.close()

    @staticmethod
    def _print_exec_out(cmd, out_buf, err_buf, exit_status):
        print('command executed: {}'.format(cmd))
        print('STDOUT:')
        for line in out_buf:
            print(line, end="")
        print('end of STDOUT')
        print('STDERR:')
        for line in err_buf:
            print(line, end="")
        print('end of STDERR')
        print('finished with exit status: {}'.format(exit_status))
        print('------------------------------------')
        pass

    def execute(self, cmd):
        """

        :param cmd: the command to be executed on the remote computer
        :examples:  execute('ls')
                    execute('finger')
                    execute('cd folder_name')
        """
        cmd = cmd.strip('\n')
        self.stdin.write(cmd + '\n')
        finish = 'end of stdOUT buffer. finished with exit status'
        echo_cmd = 'echo {} $?'.format(finish)
        self.stdin.write(echo_cmd + '\n')
        shin = self.stdin
        self.stdin.flush()

        shout = []
        sherr = []
        exit_status = 0
        for line in self.stdout:
            if str(line).startswith(cmd) or str(line).startswith(echo_cmd):
                # up for now filled with shell junk from stdin
                shout = []
            elif str(line).startswith(finish):
                # our finish command ends with the exit status
                exit_status = int(str(line).rsplit(maxsplit=1)[1])
                if exit_status:
                    # stderr is combined with stdout.
                    # thus, swap sherr with shout in a case of failure.
                    sherr = shout
                    shout = []
                break
            else:
                # get rid of 'coloring and formatting' special characters
                shout.append(re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]').sub('', line).
                             replace('\b', '').replace('\r', ''))

        # first and last lines of shout/sherr contain a prompt
        if shout and echo_cmd in shout[-1]:
            shout.pop()
        if shout and cmd in shout[0]:
            shout.pop(0)
        if sherr and echo_cmd in sherr[-1]:
            sherr.pop()
        if sherr and cmd in sherr[0]:
            sherr.pop(0)

        self._print_exec_out(cmd=cmd, out_buf=shout, err_buf=sherr, exit_status=exit_status)
        return shin, shout, sherr

Upvotes: 4

LarsVegas
LarsVegas

Reputation: 6812

I see you are using the timeout parameter in your connect call:

ssh.connect(host, 22, user, passwd, timeout=3)

From the documentation:

timeout (float) – an optional timeout (in seconds) for the TCP connect

In one of my scripts I simply do:

ssh = paramiko.SSHClient()
ssh.connect(host, username=settings.user)

which keeps the connection open until I call

ssh.close()

Upvotes: 1

Related Questions