Paramiko capturing command output - python

I have an issue that has been giving me a headache for a few days. I am using the Paramiko module with Python 2.7.10 and I'd like to issue multiple commands to a Brocade router, but only return output from one of the given commands like so:
#!/usr/bin/env python
import paramiko, time
router = 'r1.test.example.com'
password = 'password'
username = 'testuser'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(router, username=username, password=password)
print('Successfully connected to %s' % router)
remote_conn = ssh.invoke_shell()
output = remote_conn.recv(1000)
# Disable paging on Brocade.
remote_conn.send('terminal length 0\n')
# Check interface status.
remote_conn.send('show interfaces ethernet 0/1\n') # I only want output from this command.
time.sleep(2)
output = remote_conn.recv(5000)
print(output)
If I were to print the full output it would contain everything issued to the router, but I only want to see output from the show interfaces ethernet 0/1\n command.
Can anyone help with this issue?
One final thing I would like to ask. I want to filter through the output variable and check for occurrences of strings like "up" or "down", but I can't seem to get it to work because everything in the output appears to be on new lines?
For example:
If I iterate over the output variable in a for loop I get all of the characters in the variable like so:
for line in output:
print(line)
I get an output like this:
t
e
r
m
i
n
a
l
l
e
n
g
t
h
0
Any way around this?
Again,
Thanks in advance for any help.
Best regards,
Aaron C.

After reading all of the comment I have made the following changes:
#!/usr/bin/env python
import paramiko, time
router = 'r2.test.example.com'
password = 'password'
username = 'testuser'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(router, username=username, password=password)
print('Successfully connected to %s' % router)
remote_conn = ssh.invoke_shell()
output = remote_conn.recv(1000)
# Disable paging on Brocade.
remote_conn.send('terminal length 0\n')
time.sleep(2)
# Clearing output.
if remote_conn.recv_ready():
output = remote_conn.recv(1000)
# Check interface status.
remote_conn.send('show interfaces ethernet 4/1\n') # I only want output from this command.
time.sleep(2)
# Getting output I want.
if remote_conn.recv_ready():
output = remote_conn.recv(5000)
print(output)
# Test: Check if interface is up.
for line in output.split('\n'):
if 'line protocol is up' in line:
print(line)
Everything works great now.
Thank you for all the help.
Best regards,
Aaron C.

For your second question: Though I am not specialist of paramiko, I see that function recv, according to the doc, returns a string. If you apply a for loop on a string, you will get characters (and not lines as one might perhaps expect). The newline is caused by your use of the print function as explained on this page, at paragraph 6.3.
I haven't studied what paramiko suggests to do. But why don't you treat the full string as a single entity? For example, you could check the presence of "up" as:
if "up" in output:
Or, if that suits your needs better, you could split the string into lines and then do whatever test you want to do:
for line in output.split('\n'):

If you can, the exec_command() call provides a simpler mechanism to invoke a command. I have seen Cisco switches abruptly drop connections that try exec_command(), so that may not be usable with Brocade devices.
If you must go the invoke_shell() route, be sure to clear all pending output after connecting and after send('terminal length 0\n'), checking recv_ready() before calling recv() to avoid blocking on reading data that might not ever arrive. Since you are controlling an interactive shell, sleep() calls might be needed to allow the server adequate time to process and send data, or it might be necessary to poll the output string to confirm that your last command completed by recognizing the shell prompt string.

Related

How to read each line in the user input and process the commnad

I am trying to ping multiple host to check communication. Below is my script and it works fine. I am giving the server names as test226693 test226693 test226825 but would like to give the server name in the new line like below as I have more server list so cannot put everything in each line.
currently it is:
Enter the server name:
test226693 test226745 test226825
Wanted to give the server names like below and get the output and line to print "you are going to do ping test 5 clients.The number 5 will vary based on the input.
test226693
test226745
test226825
test226889
test227082
def ping():
server_names=input("Enter the server name: \n")
server_names=server_names.split(" ")
for servers in server_names:
command = f"ping {servers}"
output=subprocess.Popen((command),shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE).communicate()[0]
print(output)
Requiring interactive I/O makes your script less useful. You can run it like
tr '\n' ' ' <servernames.txt |
python yourscript
but a much better solution is to just have it read the file directly.
import sys
servers = [line.rstrip('\n') for line in sys.stdin]
print("will ping", len(servers), "hosts")
for server in servers:
subprocess.run(['ping', '-c', '1', server], check=True)
You would use this simply like
python tripleee.py <servernames.txt
The check=True keyword argument to subprocess.run will cause Python to raise an exception if the ping fails. If you want to continue the script in case of an error, you probably want to add a try / except handler around the subprocess; or simply take out the check=True (I don't generally recommend this, though).
Notice also how we always want to avoid shell=True when we can and prefer to avoid Popen when we can, like the subprocess documentation recommends.
There are a couple of ways to solve this:
You state how many server names you are going to input first, and then looping that amount of times to get all the inputs:
nr_server_names = int(input("How many server names are you going to input?"))
server_names = [input("Enter the server name: \n") for _ in range(nr_server_names)]
You could also have a stopping statement, that will break you out of a continuous loop:
server_name = input("Enter the server name: \n")
server_names.append(server_name)
while server_name != "STOP":
server_name = input("Enter the server name: \n")
server_names.append(server_name)
For convenience, if I have a large input, I would put it in a text file and read the text file out. This could be inconvenient in your case, if you specifically want to call some server (e.g. that you do not want to change a text file every time), but if it were the same server names you want to keep calling, this might help out.
with open("servernames.txt","r") as f:
server_names = [server_name.rstrip() for server_name in f.readlines()]
and your servernames.txt containing
test226693
test226745
test226825
test226889
test227082
I hope this helps you continue.
EDIT:
From the comments, you stated to prefer the second option. The code for that would then in full be:
def ping():
server_names=[]
server_name = input("Enter the server name: \n")
while server_name != "STOP":
server_names.append(server_name)
server_name = input()
for servers in server_names:
command = f"ping {servers}"
output=subprocess.Popen((command),shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE).communicate()[0]
print(output)
Then you can input your desired server names, and conclude with "STOP" in order to let the program know you that you are finished with the input.

Reading CTRL+... commands from a file and sending them with a remote Paramiko connection in Python

I want to read some CTRL+... commands from a file and send them to an open remote session through Paramiko, for instance, after parsing the file I have:
cmd1 = "CTRL+s" and cmd2 = CTRL+ALT+A
I know the ASCII codes from this page: ascii-chart
For instance I can use,
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username, password, port, look_for_keys=False, allow_agent=False)
chan = client.invoke_shell()
chan.send('\x13') #to send Ctrl+s
#chan.send(chr(19)) #same as above
The first issue here is that I have hardcoded the commands. I want to use chan.send(cmd1) to actually send it. One way is to write cmd1 = 19 in the file (let the user figure out the code based on the ASCII chart). Than I can use chan.send(chr(19)) to send the combination. But is there a way to actually convert what the user puts in cmd1 to the correct key to be sent? For instance, If I read CTRL then s separately, then I guess I can send them one after the other.
Since the ASCII chart does not include other combinations that start with CTRL, how can I send cmd2? Using + as a delimiter I can send each part separately, but what is the way to just send CTRL key not as part of a combination such as in '\x13'
If you know libraries that can facilitate this, let me know.

python telnet not pressing enter with "\n"

I am working on a script to pull configs from Cisco devices in GNS3. Currently it is using a hard coded IP & port due to another separate issue but this is the code I am currently using.
import getpass
import telnetlib
print "Getting running-config 127.0.0.1:5002"
tn = telnetlib.Telnet("127.0.0.1","5002")
tn.write("terminal length 0\n")
tn.write("show run\n")
tn.write("exit\n")
readoutput = tn.read_until("exit")
saveoutput = open("C:\Programdata\switch.txt","w")
saveoutput.write(readoutput)
saveoutput.write("\n")
saveoutput.close
print tn.read_all()
If I comment out the readouput and saveoutput blocks of code the script runs through as fast as I would expect it to. However once I leave that code in there it just sits and hangs. I have put print() commands in to verify and it hangs on the readoutput line. I have tried both tn.read_all & tn.read_until (without and without a string parameter) and neither works.
Even though the readoutput is not working I know something is occurring because if I telnet into the switch with telnet 127.0.0.1 5002 in a command prompt it comes up without issue as well as being able to progress through my terminal commands.
Edit:
I did more testing with this today and found out through the use of a lot of sleep timers and printing to the terminal what was going on. As it turns out for some reason the telnet session is not actually pressing enter when it should. I tried changing the tn.write("text\n") to tn.write("text" + "\n")and even splitting that into two write commands, but none of it works. Below is a telnet session I opened after some failed attempts at making the script work. Note all the commands are just sitting there in line? If I change the readoutput to look for "xit" which is the text on the first line of the ios console, then it will find it in telnet and print everything in the telnet session to that point.
If anyone knows why "\n" is not actually hitting enter I would greatly appreciate the help as this should be my last major hurdle to making the program this script is a part of work.
The reason for this behaviour is because \n isn't actually carriage return (CR) - it's line feed (LF) in string, and different systems may have different EOL implementation (the most obvious example being Linux's \n versus Windows's \r\n). In case of Cisco IOS, it seems that \r is enough, although I can't currently verify it.
Try adding \r\n as this is default Telnet behaviour according to the spec, that should work.
All of your code is True and also both of :
tn.write("text\n")
tn.write("text" + "\n")
Is true. Just one missing is b :
Your code should be like this :
tn.write(b"en" + b"\n")
tn.write(b"terminal length 0\n")
tn.write(b"show run\n")
tn.write(b"exit\n")

PwnTools recv() on output that expects input directly after

Hi I have a problem that I cannot seem to find any solution for.
(Maybe i'm just horrible at phrasing searches correctly in english)
I'm trying to execute a binary from python using pwntools and reading its output completely before sending some input myself.
The output from my binary is as follows:
Testmessage1
Testmessage2
Enter input: <binary expects me to input stuff here>
Where I would like to read the first line, the second line and the output part of the third line (with ':' being the last character).
The third line of the output does not contain a newline at the end and expects the user to make an input directly. However, I'm not able to read the output contents that the third line starts with, no matter what I try.
My current way of trying to achieve this:
from pwn import *
io = process("./testbin")
print io.recvline()
print io.recvline()
print io.recvuntil(":", timeout=1) # this get's stuck if I dont use a timeout
...
# maybe sending data here
# io.send(....)
io.close()
Do I missunderstand something about stdin and stdout? Is "Enter input:" of the third line not part of the output that I should be able to receive before making an input?
Thanks in advance
I finally figured it out.
I got the hint I needed from
https://github.com/zachriggle/pwntools-glibc-buffering/blob/master/demo.py
It seems that Ubuntu is doing lots of buffering on its own.
When manually making sure that pwnTools uses a pseudoterminal for stdin and stdout it works!
import * from pwn
pty = process.PTY
p = process(stdin=pty, stdout=pty)
You can use the clean function which is more reliable and which can be used for remote connections: https://docs.pwntools.com/en/dev/tubes.html#pwnlib.tubes.tube.tube.clean
For example:
def start():
p = remote("0.0.0.0", 4000)
return p
io = start()
io.send(b"YYYY")
io.clean()
io.send(b"ZZZ")

Reading data using tn.read_all() in python

read_all()" to read data from a cisco device.
some time it reads the data and sometime it won't read and gives empty string.
I tried below 2 commands but still it's not consitently reading data.
data=tn.read_until("exit")
data= tn.read_all()
please give some inputs i am new to python.
code i am using:
_command2='show chassis'
print 'Commands issued............'
#ISSUE COMMANDS VIA TELNET
tn.write("term len 0\r")
#tn.read_until(" ")
#tn.write(_command1+"\r")
tn.write(_command2+"\r")
tn.write("exit\r" )
print 'Read telnet data............'
#READ TELNET DATA
#data=tn.read_eager()
data=tn.read_until("exit")
#data= tn.read_all()
#print data
print 'Telnet data read successfully............'
I too faced the same problem..This would help:
tn = telnetlib.Telnet('64.0.0.1')
tn.write('ls \r\n')
data = ''
while data.find('#') == -1:
data = tn.read_very_eager()
print data
This snippet reads the info after a command being executed.
And reads till '#' prompt is shown.
Short answer:
Use time.sleep(1) in between write commands
Long answer:
When you enter a command on a Cisco IOS console, it blocks until the command completes. Any input you enter into the console while the command was running is piped into the running command, much like STDIN works on a bash shell. However in bash, if a commands doesn't explicitly read the input, upon the exit of the program bash takes the unconsumed input and interprets it as a shell command. So if you want to run two commands one after another, where the first command does not read from STDIN, you can enter the second command while first command is running, i.e. you don't have to wait for the first command to finish before you enter another command. This sort of a buffering mechanism makes telnet scripting easy and we have grown to expect this from mature shells. Apparently Cisco IOS lacks this feature, and so you have to make sure you don't enter your commands too soon. There are two ways I can think of, to go about it:
Wait for a fixed amount of time between commands. Usually 1 second is a safe bet for most commands.
Parse the output after each command until you find the prompt, then enter the next command, then parse again, and so on.
Use:
tn.read_very_eager()
instead of tn.read_all() - read_all waits until the connection is closed
#yogi 's answer is almost right.
but some details to be explained
data = ''
finish = '' #m1
while data.find(finish) == -1:
data += tn.read_very_eager() # m2
print data
m1: there is what your telnet server returns when command finished
m2: there should be +=, because some command results has several lines

Categories