Reputation: 1241
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
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
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
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
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