I can do ssh from one server to another using this:
# ssh root#1.2.4.148
The following code is doing the same in pythonic way:
import paraminko
#paramiko.util.log_to_file('ssh.log') # sets up logging
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('1.2.4.148')
stdin, stdout, stderr = client.exec_command('ls -l')
But if I need to connect to third server from the second server, I can do this:
# ssh -t root#1.2.4.148 ssh root#1.2.4.149
How is this done in python?
My current server (250) has password less keys saved with 148 server for easy access. But connection to 149 from 148 will need password if that matters.
This python function will connect to middle_server first and then to last_server. It will execute the command "mycommand" on last_server and return it's output.
def myconnect():
middle_server='1.2.3.4'
middle_port=3232
middle_user='shantanu'
middle_key_filename='/root/.ssh/id_rsa.pub'
last_server='6.7.8.9'
last_port=1224
last_user='root'
last_password='xxxxx'
mycommand='pwd'
import paramiko
proxy_client = paramiko.SSHClient()
proxy_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy_client.connect(middle_server, port=middle_port, username=middle_user, key_filename=middle_key_filename)
transport = proxy_client.get_transport()
dest_addr = (last_server, last_port)
local_addr = ('127.0.0.1', 1234)
channel = transport.open_channel("direct-tcpip", dest_addr, local_addr)
remote_client = paramiko.SSHClient()
remote_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
remote_client.connect('localhost', port=last_port, username=last_user, password=last_password, sock=channel)
(sshin1, sshout1, ssherr1) = remote_client.exec_command(mycommand)
print sshout1.read()
except:
print "error"
return 0
I have the following script to connect to an custom ssh shell.
When I execute the script it just hangs. It doesnt execute the command. I suspect problems with the shell because it does not have any prompt. Do you have any idea?
import sys
import os
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.115.130.22', username='admin', password='xxx', timeout = 30)
stdin, stdout, stderr = ssh.exec_command('xconfiguration SystemUnit Name: devicename')
print stdout.readlines()
ssh.close()`
I spent way too much time on this problem. I found that I needed to use the invoke_shell() to be able to get anything past the greeting banner on the Tandberg C/E series video endpoints. Here's my working code, FWIW:
import time
import paramiko
command = 'help'
host = 'x.x.x.x'
port = 22
user = 'admin'
passwd = 'TANDBERG'
def tbgShell(host,port,username,password,cmd):
"""send an arbitrary command to a Cisco/TBG gizmo's ssh and
get the result"""
transport = paramiko.Transport((host, port))
transport.connect(username = user, password = passwd)
chan = transport.open_channel("session")
chan.setblocking(0)
chan.invoke_shell()
out = ''
chan.send(cmd+'\n')
tCheck = 0
while not chan.recv_ready():
time.sleep(1)
tCheck+=1
if tCheck >= 6:
print 'time out'#TODO: add exeption here
return False
out = chan.recv(1024)
return out
output = tbgShell(host, port, user, passwd, command)
print output
This is a custom shell. It is a cisco ex90 video conferencing system.
But I tried different commands like xconfig which show you the config.
Strange behaviour in Python & paramiko 1.7.7.1.
I have a function and a class that have effectively exactly the same code but the class breaks it across 2 methods. The behaviour of the paramiko library is different.
The function: opens an ssh connection, runs a command, waits for completion and gets the exit value.
The class: in setup() opens an ssh connection, runs a command and in finish() waits for completion and gets the exit value.
I note that if I read from stdout in setup() it then works (commented out).
I'm interested in two things,
how to get the class to work while storing the channel as a member, and
what exactly is different in terms of assignments etc. that is changing the behaviour?
Output:
[function]
exitVal: 0
stdout: hello
[class]
exitVal: -1
stdout:
Code:
MY_SERVER is a hostname with key-based login (no password required).
from paramiko import SSHClient
SSH_SERVER = "MY_SERVER"
SSH_CMD = "echo 'hello'"
def working_function():
client = SSHClient()
client.load_system_host_keys()
# connect with current user credentials
client.connect(SSH_SERVER)
sshChannel = client.get_transport().open_session()
sshChannel.exec_command(SSH_CMD)
stdout = sshChannel.makefile('rb')
exitVal = sshChannel.recv_exit_status()
print "exitVal: {0}\nstdout: {1}\n".format(exitVal, stdout.read())
class NotWorkingClass:
def setup(self):
client = SSHClient()
client.load_system_host_keys()
# connect with current user credentials
client.connect(SSH_SERVER)
self.sshChannel = client.get_transport().open_session()
self.sshChannel.exec_command(SSH_CMD)
self.stdout = self.sshChannel.makefile('rb')
# works
# self.stdout.read()
def finish(self):
exitVal = self.sshChannel.recv_exit_status()
print "exitVal: {0}\nstdout: {1}\n".format(exitVal, self.stdout.read())
working_function()
c = NotWorkingClass()
c.setup()
c.finish()
# sshpy v1 by s0urd
# simple ssh client
# irc.gonullyourself.org 6667 #code
import paramiko
import os
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
privatekey = os.path.expanduser('/home/rabia/private')
mkey = paramiko.RSAKey.from_private_key_file(privatekey)
ssh.connect('78.46.172.47', port=22, username='s0urd', password=None, pkey=mkey)
while True:
pick = raw_input("sshpy: ")
stdin, stdout, stderr = ssh.exec_command(pick)
print stdout.readlines()
ssh.close()
When I try to run more then 1 command I get this error:
AttributeError: 'NoneType' object has no attribute 'open_session'
Looks like it's because at the end of the while loop you do ssh.close() (thus closing the session).
I want to run a tail -f logfile command on a remote machine using python's paramiko module. I've been attempting it so far in the following fashion:
interface = paramiko.SSHClient()
#snip the connection setup portion
stdin, stdout, stderr = interface.exec_command("tail -f logfile")
#snip into threaded loop
print stdout.readline()
I'd like the command to run as long as necessary, but I have 2 problems:
How do I stop this cleanly? I thought of making a Channel and then using the shutdown() command on the channel when I'm through with it- but that seems messy. Is it possible to do something like sent Ctrl-C to the channel's stdin?
readline() blocks, and I could avoid threads if I had a non-blocking method of getting output- any thoughts?
Instead of calling exec_command on the client, get hold of the transport and generate your own channel. The channel can be used to execute a command, and you can use it in a select statement to find out when data can be read:
#!/usr/bin/env python
import paramiko
import select
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('host.example.com')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -f /var/log/everything/current")
while True:
rl, wl, xl = select.select([channel],[],[],0.0)
if len(rl) > 0:
# Must be stdout
print channel.recv(1024)
The channel object can be read from and written to, connecting with stdout and stdin of the remote command. You can get at stderr by calling channel.makefile_stderr(...).
I've set the timeout to 0.0 seconds because a non-blocking solution was requested. Depending on your needs, you might want to block with a non-zero timeout.
1) You can just close the client if you wish. The server on the other end will kill the tail process.
2) If you need to do this in a non-blocking way, you will have to use the channel object directly. You can then watch for both stdout and stderr with channel.recv_ready() and channel.recv_stderr_ready(), or use select.select.
Just a small update to the solution by Andrew Aylett. The following code actually breaks the loop and quits when the external process finishes:
import paramiko
import select
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('host.example.com')
channel = client.get_transport().open_session()
channel.exec_command("tail -f /var/log/everything/current")
while True:
if channel.exit_status_ready():
break
rl, wl, xl = select.select([channel], [], [], 0.0)
if len(rl) > 0:
print channel.recv(1024)
To close the process simply run:
interface.close()
In terms of nonblocking, you can't get a non-blocking read. The best you would be able to to would be to parse over it one "block" at a time, "stdout.read(1)" will only block when there are no characters left in the buffer.
Just for information, there is a solution to do this using channel.get_pty(). Fore more details have a look at: https://stackoverflow.com/a/11190727/1480181
The way I've solved this is with a context manager. This will make sure my long running commands are aborted. The key logic is to wrap to mimic SSHClient.exec_command but capture the created channel and use a Timer that will close that channel if the command runs for too long.
import paramiko
import threading
class TimeoutChannel:
def __init__(self, client: paramiko.SSHClient, timeout):
self.expired = False
self._channel: paramiko.channel = None
self.client = client
self.timeout = timeout
def __enter__(self):
self.timer = threading.Timer(self.timeout, self.kill_client)
self.timer.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exited Timeout. Timed out:", self.expired)
self.timer.cancel()
if exc_val:
return False # Make sure the exceptions are re-raised
if self.expired:
raise TimeoutError("Command timed out")
def kill_client(self):
self.expired = True
print("Should kill client")
if self._channel:
print("We have a channel")
self._channel.close()
def exec(self, command, bufsize=-1, timeout=None, get_pty=False, environment=None):
self._channel = self.client.get_transport().open_session(timeout=timeout)
if get_pty:
self._channel.get_pty()
self._channel.settimeout(timeout)
if environment:
self._channel.update_environment(environment)
self._channel.exec_command(command)
stdin = self._channel.makefile_stdin("wb", bufsize)
stdout = self._channel.makefile("r", bufsize)
stderr = self._channel.makefile_stderr("r", bufsize)
return stdin, stdout, stderr
To use the code it's pretty simple now, the first example will throw a TimeoutError
ssh = paramiko.SSHClient()
ssh.connect('hostname', username='user', password='pass')
with TimeoutChannel(ssh, 3) as c:
ssh_stdin, ssh_stdout, ssh_stderr = c.exec("cat") # non-blocking
exit_status = ssh_stdout.channel.recv_exit_status() # block til done, will never complete because cat wants input
This code will work fine (unless the host is under insane load!)
ssh = paramiko.SSHClient()
ssh.connect('hostname', username='user', password='pass')
with TimeoutChannel(ssh, 3) as c:
ssh_stdin, ssh_stdout, ssh_stderr = c.exec("uptime") # non-blocking
exit_status = ssh_stdout.channel.recv_exit_status() # block til done, will complete quickly
print(ssh_stdout.read().decode("utf8")) # Show results