plink - getting continuous output - python

I'm using plink.exe in my python script to send commands to a router. I get back the result in "output". One command triggers the router to run a series of commands itself. Is there any way to continously get the output until all commands are finished. I only get the first few rows back into output.
comm = "plink.exe -pw admin admin#172.16.0.1 COMMAND"
b = sub.Popen(comm,stdout=sub.PIPE,stderr=sub.PIPE)
output, errors = b.communicate()
print output
I hope this makes any sence to someone. =)

I found a solution to my question.
The Paramiko module worked for me. =)
import sys
import time
import paramiko
host = '192.168.1.1'
user = 'admin'
pwd = 'admin'
i = 1
#def interactive_shell():
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
transport = paramiko.Transport((host,22))
transport.connect(username=user, password=pwd)
time.sleep(0.2)
#chan = paramiko.transport.open_session()
chan = transport.open_session()
chan.setblocking(0)
chan.invoke_shell()
chan.send("command") # Send command to device
chan.send("\n") # Send Enter
time.sleep(1) #optional
while not chan.exit_status_ready():
time.sleep(0.1)
if chan.recv_ready() :
output = chan.recv(8192)
if len(output) > 0 :
outputLines = output.splitlines(True)
sys.stdout.write(output)
if "unit" in output:
sys.stdout.flush()
break
if chan.recv_stderr_ready() :
mystderr = chan.recv_stderr(8192)
if len(mystderr) > 0 :
print mystderr, ","
transport.close()

Related

Python - Trying to backup a network switch using Paramiko, not receiving the expected output [duplicate]

This question already has an answer here:
Executing command using Paramiko exec_command on device is not working
(1 answer)
Closed 3 months ago.
I've been trying to create a script that would back up our switches from a CSV file.
I'm using Paramiko to SSH into a switch and run "show run", but for some reason the only output I'm receiving is the name of the switch.
import pandas
import paramiko
# Connection info
USERNAME = "username"
PW = "password"
PORT = 22
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Backup \ import location
DEST = "C:\\temp\\test\\"
CSV = DEST+"Switches.csv"
device_list = pandas.read_csv(CSV)
for index, row in device_list.iterrows():
IP = row["IP"]
floor = row["Floor"]
side = row["Side"]
formatted_ip = IP.replace(".", "_")
filename = f"{formatted_ip}_{floor}{side}.txt"
ssh.connect(hostname=IP, username=USERNAME, password=PW, port=PORT)
stdin, stdout, stderr = ssh.exec_command('show running-config')
stdin.close()
output = stdout.readlines()
errors = stderr.read()
print(output)
print(stderr.readlines())
outfile = open(DEST + filename, "w")
for char in output:
outfile.write(char)
ssh.close()
outfile.close()
The output I'm receiving (and also writing into the created file) is SW-3A-48p-4>
I'm able to connect to the switch and run "show run".
I'm expecting to get the whole switch configuration but the output stops on the first line.
As #MarinPrikryl pointed, the devices I was trying to connect do not support the "exec" channel.
Here's how I managed to do it using the shell:
import pandas
import paramiko
from time import sleep
# Connection info
USERNAME = "username"
PW = "password"
PORT = 22
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # I know this isn't recommended, I'll find a better solution for it.
# Backup \ import location
DEST = "C:\\temp\\test\\"
CSV = DEST + "Switches.csv"
device_list = pandas.read_csv(CSV)
for index, row in device_list.iterrows():
IP = row["IP"]
floor = row["Floor"]
side = row["Side"]
formatted_ip = IP.replace(".", "_")
filename = f"{formatted_ip}_{floor}{side}.txt"
ssh.connect(hostname=IP, username=USERNAME, password=PW, port=PORT)
channel = ssh.invoke_shell()
stdout = channel.makefile('r')
channel.send('enable\r\n')
sleep(1)
channel.send('terminal length 0\r\n')
sleep(1)
channel.send('copy running-config startup-config\r\n')
sleep(1)
channel.send('y\r\n')
sleep(10)
channel.send('show running-config\r\n')
sleep(2)
data = channel.recv(5000)
data = data.decode("utf8", "ignore")
data = data.split('show running-config')
data = data[1][:len(data) - 15]
outfile = open(DEST + filename, "w")
for char in data:
outfile.write(char)
ssh.close()
outfile.close()
I'm sure there are better ways to do it, but this works for now.
Thanks a lot to everyone who helped.

Python SSH script to extract Cisco switches information in preparation for network migration

I have a list of Cisco Nexus5548 IP addresses and FQDNs. Could you please help with a Python script to SSH to each and extract the following in order to import it into Excel column format:
IP Name Port Number Port Description Port Type Vlans Optic Type Medium Type
172.x.x.x hqcr1-swx-x E1/x The actual port description (Access or Trunk) 300-305,2276,… 1g-sr, 10g-sr, 1g-glct (copper fiber, or twinax)
This is what I have so far:
import paramiko, getpass, time
devices = {'device1': {'ip': 'xx.xx.xx.xx'}}
'device2': {'ip': 'xx.xx.xx.xx'}}
commands = ['show version\n', 'show run\n']
username = input('Username: ')
password = getpass.getpass('Password: ')
max_buffer = 65535
def clear_buffer(connection):
if connection.recv_ready():
return connection.recv(max_buffer)
# Starts the loop for devices
for device in devices.keys():
outputFileName = device + '_output.txt'
connection = paramiko.SSHClient()
connection.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connection.connect(devices[device]['ip'], username=username, password=password, look_for_keys=False, allow_agent=False)
new_connection = connection.invoke_shell()
output = clear_buffer(new_connection)
time.sleep(2)
new_connection.send("terminal length 0\n")
output = clear_buffer(new_connection)
with open(outputFileName, 'wb') as f:
for command in commands:
new_connection.send(command)
time.sleep(2)
output = new_connection.recv(max_buffer)
print(output)
f.write(output)
new_connection.close()
Thank you very much.
have you tried using exec_command on the SSHClient? not sure how Cisco boxes play with opening/closing multiple channels, but it seems as though it might help separate the output from each command up.
I'd do something like:
from paramiko import SSHClient, AutoAddPolicy
def process_devices(devices, connect_args, commands):
with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy())
for device in devices:
client.connect(device, **connect_args)
cmdout = []
for cmd in commands:
stdin, stdout, stderr = client.exec_command(cmd, timeout=10)
cmdout.append((stdout.read(), stderr.read()))
yield (device, cmdout)
which is useful for things like:
from getpass import getpass
devices = [
'127.0.0.1',
]
connect_args = dict(
username='smason',
password=getpass("Password: "),
)
commands = [
"echo hello world",
"date",
]
for dev, cmdout in process_devices(devices, connect_args, commands):
print(f"{dev}: {cmdout}")
you can of course put the output from process_devices directly into a dict if you want, it's an iterator that returns appropriate pairs

Paramiko recv_ready() returns false values

I am trying to execute a number of commands remotely using paramiko, however the recv_ready() does not return the correct value.
For example after a pwd \n command it will continuously report that the channel is not still ready (obviously false). For some commands it works properly e.g. ls.
Is there something wrong with what I am doing, or is there an issue with paramiko?
import paramiko
import re
import time
def sudo_ssh(hostname, usernameIn, passIn, cmd):
# Create an SSH client
client = paramiko.SSHClient()
# Make sure that we add the remote server's SSH key automatically
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the client
client.connect(hostname, username=usernameIn, password=passIn)
# Create a raw shell
channel = client.invoke_shell()
# Send the sudo command
for command in cmd:
print("CMD= " + command + "\n")
time.sleep(1)
# wait until channel is ready
while not channel.recv_ready() :
print("NOT READY " + str(channel.recv_ready()) + "\n \n")
time.sleep(1)
# Send the command
channel.send(command)
channel.send("\n")
# Wait a bit, if necessary
time.sleep(1)
# Flush the receive buffer
receive_buffer = channel.recv(4096)
# If promted send the sudo pass
if re.search(b".*\[sudo\].*", receive_buffer):
time.sleep(1)
print(" TYPING SUDO PASSWORD .... \n")
channel.send( "sudoPass" + "\n" )
receive_buffer = channel.recv(4096)
# Print the receive buffer, if necessary
print(receive_buffer)
print("Executed all of the commands. Now will exit \n")
client.close()
com = []
com.append("sudo ls")
com.append("cd /home/user/Downloads")
com.append("sleep 5")
com.append("ls")
com.append("pwd")
com.append("cd /opt/")
sudo_ssh("myhost.com", "user", "pass", com)
The recv_ready method is to check if the data of channel is ready to read or not i.e. data is buffered or not. It doesn't check if channel itself is ready, see - recv_ready().
So you should move the recv_ready() while loop just before the receive_buffer = channel.recv(4096) to make it work.

Paramiko - python SSH - multiple command under a single channel

i have read other Stackoverflow threads on this. Those are older posts, i would like to get the latest update.
Is it possible to send multiple commands over single channel in Paramiko ? or is it still not possible ?
If so, is there any other library which can do the same.
Example scenario, automating the Cisco router confi. : User need to first enter "Config t" before entering the other other commands. Its currently not possible in paramiko.
THanks.
if you are planning to use the exec_command() method provided within the paramiko API , you would be limited to send only a single command at a time , as soon as the command has been executed the channel is closed.
The below excerpt from Paramiko API docs .
exec_command(self, command) source code 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.
but since transport is also a form of socket , you can send commands without using the exec_command() method, using barebone socket programming.
Incase you have a defined set of commands then both pexpect and exscript can be used , where you read a set of commands form a file and send them across the channel.
See my answer here or this page
import threading, paramiko
strdata=''
fulldata=''
class ssh:
shell = None
client = None
transport = None
def __init__(self, address, username, password):
print("Connecting to server on ip", str(address) + ".")
self.client = paramiko.client.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.client.connect(address, username=username, password=password, look_for_keys=False)
self.transport = paramiko.Transport((address, 22))
self.transport.connect(username=username, password=password)
thread = threading.Thread(target=self.process)
thread.daemon = True
thread.start()
def closeConnection(self):
if(self.client != None):
self.client.close()
self.transport.close()
def openShell(self):
self.shell = self.client.invoke_shell()
def sendShell(self, command):
if(self.shell):
self.shell.send(command + "\n")
else:
print("Shell not opened.")
def process(self):
global strdata, fulldata
while True:
# Print data when available
if self.shell is not None and self.shell.recv_ready():
alldata = self.shell.recv(1024)
while self.shell.recv_ready():
alldata += self.shell.recv(1024)
strdata = strdata + str(alldata)
fulldata = fulldata + str(alldata)
strdata = self.print_lines(strdata) # print all received data except last line
def print_lines(self, data):
last_line = data
if '\n' in data:
lines = data.splitlines()
for i in range(0, len(lines)-1):
print(lines[i])
last_line = lines[len(lines) - 1]
if data.endswith('\n'):
print(last_line)
last_line = ''
return last_line
sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"
connection = ssh(sshServer, sshUsername, sshPassword)
connection.openShell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata) # print the last line of received data
print('==========================')
print(fulldata) # This contains the complete data received.
print('==========================')
connection.close_connection()
Have a look at parallel-ssh:
from pssh.pssh2_client import ParallelSSHClient
cmds = ['my cmd1', 'my cmd2']
hosts = ['myhost']
client = ParallelSSHClient(hosts)
for cmd in cmds:
output = client.run_command(cmd)
# Wait for completion
client.join(output)
Single client, multiple commands over same SSH session and optionally multiple hosts in parallel - also non-blocking.
I find this simple to understand and use. Code provides 2 examples with singlehost and multihost. Also added example where you can login to a second user and continue your commands with that user.
More info can be found in here: https://parallel-ssh.readthedocs.io/en/latest/advanced.html?highlight=channel#interactive-shells
from pssh.clients import SSHClient
from pssh.exceptions import Timeout
from pssh.clients import ParallelSSHClient
from pssh.config import HostConfig
def singleHost():
host_ = "10.3.0.10"
pwd_ = "<pwd>"
pwd_root = "<root pwd>"
user_ = "<user>"
client = SSHClient(host_, user=user_, password=pwd_, timeout=4, num_retries=1)
#####
shell = client.open_shell(read_timeout=2)
shell.run("whoami")
# login as new user example
shell.run("su - root")
shell.stdin.write(pwd_root + "\n")
shell.stdin.flush()
shell.run("pwd")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.run("whoami")
shell.run("cd ..")
print(".......")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.close()
def multiHost():
pwd_ = "<pwd>"
user_ = "<user>"
workingIP_list = ["10.3.0.10", "10.3.0.10"]
host_config_ = []
# HostConfig is needed one per each 'workingIP_list'
host_config_.append(HostConfig(user=user_, password=pwd_))
host_config_.append(HostConfig(user=user_, password=pwd_))
client_ = ParallelSSHClient(workingIP_list, host_config=host_config_, num_retries=1, timeout=3)
# now you have an open shell
shells = client_.open_shell(read_timeout=2)
command = "pwd"
client_.run_shell_commands(shells, command)
try:
# Reading Partial Shell Output, with 'timeout' > client_.open_shell(read_timeout=2)
for line in shells[0].stdout:
print(line)
except Timeout:
pass
print(".......")
command = "cd repo/"
client_.run_shell_commands(shells, command)
command = "pwd"
client_.run_shell_commands(shells, command)
#Joined on shells are closed and may not run any further commands.
client_.join_shells(shells)
for shell in shells:
for line in shell.stdout:
print(line)
print(shell.exit_code)
if __name__ == '__main__':
print("singleHost example:")
singleHost()
print("multiHost example:")
multiHost()

Paramiko hangs after connecting to custom shell

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.

Categories