Append output by Host - python

I wrote a script which executes commands (provided as arguments) on remote hosts (provided from a file) with paramiko module python module. However, I want the results of the execution grouped by hostname and command outputs separated by delimiter '#'
I wrote this script
#!/usr/bin/env python
from msilib.schema import File
import paramiko
import os
import subprocess
import getpass
import argparse
import itertools
username = getpass.getuser()
#password = getpass.getpass()
password = 'Password'
port = 22
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--command", help="Please add each command in double quotes Eg: script.py 'cmd_1' 'cmd_2' ", nargs='+')
parser.add_argument("-f", "--file", type=open)
args = parser.parse_args()
# hfile = open(args.file)
loc = args.file.read().splitlines()
for host in loc:
for vals in args.command:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host,port,username,password)
stdin, stdout, stderr = client.exec_command(vals)
output = stdout.read()
output_main = str(output)
o2 = output_main.split('\n')
o3 = host + ':' + str(o2)[4:-6] + '#'
print(o3)
# print(o3)
# print(host + '\n' + output_main)
When I use this script with following usage:
python multiple_cmd_execute.py -f tes2.txt -c "id" "cat /etc/redhat-release"
It gives the output as
lnyfxaggap2c:uid=1011308397(rm08397) gid=90015(helpdevelop) groups=90015(helpdevelop)#
lnyfxaggap2c:Red Hat Enterprise Linux Server release 6.10 (Santiago)#
lnyfxatrap02u:uid=1011308397(rm08397) gid=90015(helpdevelop) groups=90015(helpdevelop) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023#
lnyfxatrap02u:Red Hat Enterprise Linux Server release 7.9 (Maipo)#
I want the output to be
Host1:Result_of_command_1#Result_of_command_2
Host2:Result_of_command_1#Result_of_command_2

Related

Executing a shell script using python's paramiko library

I am trying to execute a shell script with command line arguments using python's paramiko library and the code is as shown below.
import paramiko
ip = input("Enter the ip address of the machine: ")
mac = input("Enter the mac address of the machine: ")
model = input("Enter the model of the box(moto/wb): ")
spec = input("Enter the spec of the box(A/B/C/CI/D/E): ")
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect('hostname', username='xxxx', password='yyyy')
stdin, stdout, stderr = ssh_client.exec_command('ls -l')
for line in iter(stdout.readline, ""):
print(line, end = "")
stdin, stdout, stderr = ssh_client.exec_command('./name.sh'+ ip+ model + spec+ mac)
for line in iter(stdout.readline, ""):
print(line, end = "")
print('finished.')
I am not getting the output of second execute command. Instead directly it is jumping to finished. May i know how to get the output of the command execution?
You are not getting output because there is none. Your command is not valid, so the shell fails. The only output is sent to stderr, which you do not print.
Details:
stdin, stdout, stderr = ssh_client.exec_command('./name.sh'+ ip+ model + spec+ mac)
Assuming the user put these values:
ip: 111.222.333.444
model: wb
spec: C
mac: 28:d2:33:e6:4e:73
Then your command is:
./name.sh111.222.333.444wbC28:d2:33:e6:4e:73
Everything is appended with no spaces in between. Simple fix:
stdin, stdout, stderr = ssh_client.exec_command('./name.sh "' + ip + '" "' + model + '" "' + spec + '" "' + mac + '"')
Your command will now be:
./name.sh "111.222.333.444" "wb" "C" "28:d2:33:e6:4e:73"
I put the " around the values to ensure it will work even if the user puts spaces in the values.
Or store your 4 variables into a list and the join() function (https://www.geeksforgeeks.org/join-function-python/) to build your command.

Switch user before executing a python script

I am trying to use su linux command in a python script along with getpass and subprocess python standard libraries to switch to a new user before executing my script. I am using this function:
import subprocess
import getpass
def ask_passwd():
pw = getpass.getpass(prompt='Please insert the passwd to use the auto api:') # prompt the user for a passwd
print pw
command = "su new_user"
p = subprocess.Popen(command.split(), stdin=subprocess.PIPE)
p.communicate(pw) # communicate the passwd
def main():
# code here
if __name__ == '__main__':
ask_passwd()
main()
When executing the script the main code works but not the su command. Here what I get back:
Please insert the passwd to use the auto api:
pass
su: must be run from a terminal
Any help? thanks
you may use subprocess.popen. There is an example
import subprocess
sudo_password = 'yourpassword'
command = '-i -u youruser \n id'
command = command.split()
cmd1 = subprocess.Popen(['echo', sudo_password], stdout=subprocess.PIPE)
cmd2 = subprocess.Popen(['sudo', '-S'] + command, stdin=cmd1.stdout, stdout=subprocess.PIPE)
output = cmd2.stdout.read()
print output

Unable to fake terminal input with termios.TIOCSTI

Most of the code samples I've seen are trying to read from stdin without local echo. To do this they modify the "local modes" flag to remove the setting to "Echo input characters". I thought I could just modify the "input modes" flag to TIOCSTI which is for "Insert the given byte in the input queue.". However, even though I run the script as root, it has no effect. anything I write to the fd seems to go to the terminal output, rather than the terminal input. Basically what I want to do is this exact thing, but in pure python.
"""
termfake.py
Usage: sudo python termfake.py /dev/ttys002
Get the tty device path of a different local termimal by running `tty`
in that terminal.
"""
import sys
import termios
fd = open(sys.argv[1], 'w')
fdno = fd.fileno()
# Returns [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
tatters = termios.tcgetattr(fdno)
print('original', tatters)
tatters[0] = termios.TIOCSTI
print('TIOCSTI', termios.TIOCSTI)
# Set iflag
termios.tcsetattr(fdno, termios.TCSANOW, tatters)
# Verify setting change
with open('/dev/ttys002', 'w') as fd2:
print('modified', termios.tcgetattr(fd2.fileno()))
fd.write('This is test\n')
fd.close()
TIOCSTI is an ioctl (documented in tty_ioctl(4)), not a terminal setting, so you can't use tcsetattr() -- you need to feed each character of the fake input to ioctl() instead. Never had to do ioctl's from Python before, but the following seems to work for running an ls in a different terminal (specified as the argument, e.g. /dev/pts/13) that's running Bash:
import fcntl
import sys
import termios
with open(sys.argv[1], 'w') as fd:
for c in "ls\n":
fcntl.ioctl(fd, termios.TIOCSTI, c)
TIOCSTI requires root privileges (or CAP_SYS_ADMIN to be more specific, but that's usually the same in practice) by the way -- see capabilities(7).
I took the answer from #Ulfalizer and expanded it a bit to be a complete and usable app.
import sys
import fcntl
import termios
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('tty', type=argparse.FileType('w'),
help='full tty path as given by the tty command')
group = parser.add_mutually_exclusive_group()
group.add_argument('-n', action='store_true',
help='prevent sending a trailing newline character')
group.add_argument('--stdin', action='store_true',
help='read input from stdin')
group = parser.add_argument_group()
group.add_argument('cmd', nargs='?',
help='command to run (required if not using --stdin)')
group.add_argument('args', nargs='*',
help='arguments to command')
args = parser.parse_known_args()
if args.stdin:
data = sys.stdin.read()
else:
data = ' '.join([args.cmd] + args.args)
for c in data:
fcntl.ioctl(args.tty, termios.TIOCSTI, c)
if not args.n and data[-1][-1] != '\n':
fcntl.ioctl(args.tty, termios.TIOCSTI, '\n')
Here is how you use it:
Terminal #1: do...
$ tty > /tmp/t1
Terminal #2: do...
$ sudo python termfake.py $(cat /tmp/t1) date +%s
Terminal #1: observe...
$ tty > /tmp/t1
$ date +%s
1487276400

How to pass the inputs of the argparser args to arguments to another function?

I am writing a program which basically logs into the remote server(Linux) and execute few commands. For that I need to get the inputs like "Server IP ", "Username", and "password" from the command line. For which am using the argparse module. My code looks something like this:
import sys
import argparse
import getpass
import re
from time import gmtime, strftime
import paramiko
try:
import interactive
except ImportError:
from . import interactive
def do_connect(esxi):
"""Connect to host in the esxi host"""
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='DEV Installer App')
parser.add_argument("--esxiIP", help="Mention the Esxi Host IP", required=True)
parser.add_argument("--userName", help="Enter the UserName of the Host", required=True)
parser.add_argument("--password", help="Enter the Password", required=True)
args = parser.parse_args()
What I am trying to do here is , I have a function for connecting to the remote server and am getting the inputs using argparse in the main() function. I need to pass these input arguments to the do_connect function. How can I accomplish this?
args is a Namespace instance object with attributes for each command-line argument. The arguments are based on the option switch, unless you give it a different name explicitly.
In this case, you'll have args.esxiIP, args.userName and args.password attributes, which you can then pass on to another function. If your function takes just the esxiIP argument, pass that on:
do_connect(args.esxiIP)
If it needs more arguments, explicitly pass these on too.
Demo:
>>> args = parser.parse_args('--esxiIP 10.20.30.40 --userName foo --password bar'.split())
>>> args
Namespace(esxiIP='10.20.30.40', password='bar', userName='foo')
>>> args.esxiIP
'10.20.30.40'
>>> args.userName
'foo'
>>> args.password
'bar'

Print SSH output from Cisco Router to a text file

I am a newbie to Python as well as the programming world. After a bit of research for the past 2 days am now able to successfully SSH into the Cisco router and execute set of commands. However my original goal is to print the resultant output to a text file. Checked lots of posts by forum members which helped me in constructing the code, but I couldn't get the result printed on the text file. Please help.
Here is my code:
import paramiko
import sys
import os
dssh = paramiko.SSHClient()
dssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
dssh.connect('10.0.0.1', username='cisco', password='cisco')
stdin, stdout, stderr = dssh.exec_command('sh ip ssh')
print stdout.read()
f = open('output.txt', 'a')
f.write(stdout.read())
f.close()
dssh.close()
stdout.read() will read the content and move the file pointer forward. As such, subsequent calls will not be able to read the content again. So if you want to print the content and write it to a file, you should store it in a variable first and then print and write that.
Instead of mentioning the IP address directly on the code, is it possible for me to fetch it from list of IP addresses (mentioned line by line) in a text file?
You can read lines from a file like this:
with open('filename') as f:
for line in f:
# Each line will be iterated; so you could call a function here
# that does the connection via SSH
print(line)
I know this is very late but the code below is what I'm using to do exactly what is being asked.
from __future__ import print_function
from netmiko import ConnectHandler
import sys
import time
import select
import paramiko
import re
fd = open(r'C:\Users\NewdayTest.txt','w')
old_stdout = sys.stdout
sys.stdout = fd
platform = 'cisco_ios'
username = 'Username'
password = 'Password'
ip_add_file = open(r'C:\Users\\IPAddressList.txt','r')
for host in ip_add_file:
device = ConnectHandler(device_type=platform, ip=host, username=username, password=password)
output = device.send_command('terminal length 0')
output = device.send_command('enable')
print('##############################################################\n')
print('...................CISCO COMMAND SHOW RUN OUTPUT......................\n')
output = device.send_command('sh run')
print(output)
print('##############################################################\n')
print('...................CISCO COMMAND SHOW IP INT BR OUTPUT......................\n')
output = device.send_command('sh ip int br')
print(output)
print('##############################################################\n')
fd.close()
Or, if you wanted to print from a single host, use this slight edit. That simply removes looking for a list to get the IP address:
from __future__ import print_function
from netmiko import ConnectHandler
import sys
import time
import select
import paramiko
import re
fd = open(r'C:\Users\NewdayTest.txt','w')
old_stdout = sys.stdout
sys.stdout = fd
host = '10.10.10.10'
platform = 'cisco_ios'
username = 'Username'
password = 'Password'
device = ConnectHandler(device_type=platform, ip=host, username=username, password=password)
output = device.send_command('terminal length 0')
output = device.send_command('enable')
print('##############################################################\n')
print('...................CISCO COMMAND SHOW RUN OUTPUT......................\n')
output = device.send_command('sh run')
print(output)
print('##############################################################\n')
print('...................CISCO COMMAND SHOW IP INT BR OUTPUT......................\n')
output = device.send_command('sh ip int br')
print(output)
print('##############################################################\n')
fd.close()

Categories

Resources