I'm not a programmer, but would like to use Python for automation of some Administrative purposes.
The first application after "Hello world" I tried to create is interactive ssh client.
I've read some documentation and articles and decided it would be the easiest way to use paramiko module, but unfortunately I'm facing a problem:
My applications asks you to enter some neccessary information such as server ip, username, password. After this it establishes connection with defined server and provide you with cli on your screen. For emulating the process of entering command I use while loop.
Unfortunately my application works well only with the first command you enter. While trying to type the second command an error appears:
Traceback (most recent call last):
File "C:\Python27\Tests\ssh_client.py", line 53, in <module>
client.execute_command(command)
File "C:\Python27\Tests\ssh_client.py", line 26, in execute_command
stdin,stdout,stderr = self.connection.exec_command(command)
File "C:\Python27\lib\site-packages\paramiko\client.py", line 343, in exec_command
chan.exec_command(command)
AttributeError: 'NoneType' object has no attribute 'exec_command'
Code of the programm (Windows 7):
import paramiko
SERVER = raw_input('Please enter an ip address of remote host: ')
USER = raw_input('Please enter your username: ')
PASSWORD = raw_input('Please enter your password: ')
class MYSSHClient():
def __init__(self, server=SERVER, username=USER, password=PASSWORD):
self.server = server
self.username = username
self.password = password
self.connection = None
self.result = ''
self.is_error = False
def do_connect(self):
self.connection = paramiko.SSHClient()
self.connection.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.connection.connect(self.server, username=self.username, password=self.password)
def execute_command(self, command):
if command:
print command
stdin,stdout,stderr = self.connection.exec_command(command)
stdin.close()
error = str(stderr.read())
if error:
self.is_error = True
self.result = error
print 'error'
else:
self.is_error = False
self.result = str(stdout.read())
print 'no error'
print self.result
else:
print "no command was entered"
def do_close(self):
self.connection.close()
if __name__ == '__main__':
client = MYSSHClient()
client.do_connect()
while 1:
command = raw_input('cli: ')
if command == 'q': break
client.execute_command(command)
client.do_close()
I tried to delete while loop and just call commands one by one right in the code, but have the same problem (when typing the second command see the same error).
It looks like I don't understand fully how paramiko module works. I tried to find information on web but unfortunately didn't find any solution.
I'd be very appreciated if somebody could tell me what I do wrong or give me a link on the similar issue where I can find a solution.
Thanks in advance for any help.
Please use for pxssh module this is very useful for your application if work for windows
Python: How can remote from my local pc to remoteA to remoteb to remote c using Paramiko
this example very helpful for you Python - Pxssh - Getting an password refused error when trying to login to a remote server
i think u check for your server settings in remote host machine
Not a real answer to your problem but more of a suggestion.
I would recommend you to have a look at fabric, which does exactly what you want: Automate tasks on local or remote hosts. Might be a bit easier since you don't have to implement the logic for the connection and execution of commands.
Fabric documentation: http://docs.fabfile.org/en/1.6/
Unfortunately I didn't find the way to resolve my issue using paramiko module, but I've found such a module as Exscript.
The simple code is below:
from Exscript.util.interact import read_login
from Exscript.protocols import SSH2
account = read_login()
conn = SSH2()
conn.connect('192.168.1.1')
conn.login(account)
while True:
command = raw_input('cli: ')
if command == 'q': break
conn.execute(command)
print conn.response
conn.send('quit\r')
conn.close()
Related
Because this question seems to aim somewhere else I am going to point my problem here:
In my python script I am using multiple requests to a remote server using ssh:
def ssh(command):
command = 'ssh SERVER "command"'
output = subprocess.check_output(
command,
stderr=subprocess.STDOUT,
shell=True,
universal_newlines=True
)
return output
here I will get the content of file1 as output.
I have now multiple methods which use this function:
def show_one():
ssh('cat file1')
def show_two():
ssh('cat file2')
def run():
one = show_one()
print(one)
two = show_two()
print(two)
Executing run() will open and close the ssh connection for each show_* method which makes it pretty slow.
Solutions:
I can put:
Host SERVER
ControlMaster auto
ControlPersist yes
ControlPath ~/.ssh/socket-%r#%h:%p
into my .ssh/config but I would like to solve this within python.
There is the ssh flag -T to keep a connection open, and in the before mentioned Question one answer was to use this with Popen() and p.communicate() but it is not possible to get the output between the communicates because it throws an error ValueError: Cannot send input after starting communication
I could somehow change my functions to execute a single ssh command like echo "--show1--"; cat file1; echo "--show2--"; cat file2 but this looks hacky to me and I hope there is a better method to just keep the ssh connection open and use it like normal.
What I would like to have: For example a pythonic/bashic to do the same as I can configure in the .ssh/config (see 1.) to declare a specific socket for the connection and explicitly open, use, close it
Try to create ssh object from class and pass it to the functions:
import paramiko
from pythonping import ping
from scp import SCPClient
class SSH():
def __init__(self, ip='192.168.1.1', username='user', password='pass',connect=True,Timeout=10):
self.ip = ip
self.username = username
self.password = password
self.Timeout=Timeout
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if connect:
self.OpenConnection()
self.scp = SCPClient(self.ssh.get_transport())
def OpenConnection(self):
try:
skip_ping = False
ping_res=False
log.info('Sending ping to host (timeout=3,count=3) :'+self.ip)
try:
PingRes = ping(target=self.ip,timeout=3,count=3, verbose=True)
log.info('Ping to host result :' + str(PingRes.success()))
ping_res=PingRes.success()
except:
skip_ping=True
if ping_res or skip_ping:
log.info('Starting to open connection....')
self.ssh.connect(hostname=self.ip, username=self.username, password=self.password, timeout=self.Timeout, auth_timeout=self.Timeout,banner_timeout=self.Timeout)
self.scp = SCPClient(self.ssh.get_transport())
log.info('Connection open')
return True
else:
log.error('ssh OpenConnection failed: No Ping to host')
return False
myssh = SSH(ip='192.168.1.1',password='mypass',username='myusername')
the ping result is wrapped in try catch because sometimes my machine return an error you can remove it and just verify a ping to the host.
The self.scp is for file transfer.
I'm trying to write a mini script that read a credential file on a local pc, that call another script on a remote machine with the user/password as argument. I'm using Fabric2 to ssh on the remote machine. It all work, but I don't want to send the credential as clear text as it does now. I'd like it to be as secure as possible since I want to implement this in production. Any suggestion ?
The big probleme is on the remote server, if I run ps -ef | grep py I see both username/password clear as argument like this:
armkreuz 6780 6779 0 15:11 ? 00:00:00 /usr/bin/python3.6
/home/armkreuz/scripts/test_password.py fname.lname#exmaple.com
test123
#Local script
from os.path import expanduser,isfile
from getpass import getpass
import sys
home = expanduser('~')
from fabric import Connection
if not isfile(f"{home}\AppData\Roaming\comcast_credential"):
user = input('Enter your email adress: ')
password = getpass("Enter your password: ")
with open(f"{home}\AppData\Roaming\comcast_credential", 'w') as cred_file:
cred_file.write(f"{user}\n")
cred_file.write(password)
else:
with open(f"{home}\AppData\Roaming\comcast_credential") as cred_file:
user=cred_file.readline().rstrip('\n')
password=cred_file.readline()
conn = Connection('armkreuz#192.168.1.188')
if user == "" or password == "":
print("No user or password")
exit(0)
print(f"User = {user}")
print(f"Password = {password}")
conn.run(f"/home/armkreuz/scripts/test_password.py {user} {password}")
#Remote script
import sys
from time import sleep
user = sys.argv[1]
password = sys.argv[2]
with open('test.txt', 'w') as file:
file.write(f"User = {user}\n")
file.write(f"Password = {password}")
for x in range(1000):
print(x)
sleep(10)
You are right to be concerned about this. Passwords should not be given as arguments for this reason.
There's a few ways to pass data over SSH
read it in from stdin (your calling script will have to feed details into fabric2)
set environment variables from the SSH client. I have no idea if fabric2 supports this. But many servers limit environment variables accepted from a client anyway.
send details in a file ahead of time
send data in the command
You've gone for the fourth option which has the problem you identified. I've not tested this myself but the following should work:
conn.run(f"PASSWORD='{password}' /home/armkreuz/scripts/test_password.py {user}")
Instead of taking the second argument as password you would use an environment variable "PASSWORD"
This sends the password in the command (still option 4) but instead of the command passing the password as an argument it is passed it as an environment variable. Because you set this in the command the SSH server shouldn't block it.
This relays on the syntax of the servers shell (Linux/bash). Note that this may not work for Windows servers.
I am having a very specific problem. I use Putty for database management for my small business. We recently had an update and our normal command path to update our records is no longer in use.
We run Putty on all computers in store. Putty is used on a virtual machine with oracle. We have a main computer for the system in which the update occurred.
We normally input ~/Desktop/getdata.sh into putty using root user and it complies an updated list, creates a text file that we use. Unfortunately, the person who created this script no longer works with us.
I am trying to find a way to re execute this file again.
After the update, when we type in ~/Desktop/getdata.sh (after logging in as root) into Putty we get 'directory cannot be found.' I've searched everyday to find this file. However, I did find a getdata.py file and a getdata.bat files.
I can show both scripts if needed, I can update the question.
When I tried to run getdata.py I get
[root#RT_Store-01 /]# ~/Desktop/getdata.py
import: unable to open X server `'.
import: unable to open X server `'.
import: unable to open X server `'.
import: unable to open X server `'.
: command not foundta.py: line 5:
/root/Desktop/getdata.py: line 6: from: command not found
/root/Desktop/getdata.py: line 7: from: command not found
: command not foundta.py: line 8:
/root/Desktop/getdata.py: line 9: syntax error near unexpected token `('
/root/Desktop/getdata.py: line 9: `dir_path = os.path.dirname(os.path.realpath(_'file__))
Do I need to convert my files to .sh? How would I do that? Is this a bigger problem?
The script for getdata.py is
import os
import tempfile
import paramiko
import time
from pywinauto import Application
from subprocess import Popen, PIPE, STDOUT
dir_path = os.path.dirname(os.path.realpath(__file__))
class Connection(object):
"""Connects and logs into the specified hostname.
Arguments that are not given are guessed from the environment."""
def __init__(self,
host,
username=None,
private_key=None,
password=None,
port=22,
):
self._sftp_live = False
self._sftp = None
if not username:
username = os.environ['LOGNAME']
# Log to a temporary file.
templog = tempfile.mkstemp('.txt', 'ssh-')[1]
paramiko.util.log_to_file(templog)
# Begin the SSH transport.
self._transport = paramiko.Transport((host, port))
self._tranport_live = True
# Authenticate the transport.
if password:
# Using Password.
self._transport.connect(username=username, password=password)
else:
# Use Private Key.
if not private_key:
# Try to use default key.
if os.path.exists(os.path.expanduser('~/.ssh/id_rsa')):
private_key = '~/.ssh/id_rsa'
elif os.path.exists(os.path.expanduser('~/.ssh/id_dsa')):
private_key = '~/.ssh/id_dsa'
else:
raise TypeError(
"You have not specified a password or key.")
private_key_file = os.path.expanduser(private_key)
rsa_key = paramiko.RSAKey.from_private_key_file(private_key_file)
self._transport.connect(username=username, pkey=rsa_key)
def _sftp_connect(self):
"""Establish the SFTP connection."""
if not self._sftp_live:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self._sftp_live = True
def get(self, remotepath, localpath=None):
"""Copies a file between the remote host and the local host."""
if not localpath:
localpath = os.path.split(remotepath)[1]
self._sftp_connect()
self._sftp.get(remotepath, localpath)
def put(self, localpath, remotepath=None):
"""Copies a file between the local host and the remote host."""
if not remotepath:
remotepath = os.path.split(localpath)[1]
self._sftp_connect()
self._sftp.put(localpath, remotepath)
def execute(self, command):
"""Execute the given commands on a remote machine."""
channel = self._transport.open_session()
channel.exec_command(command)
output = channel.makefile('rb', -1).readlines()
if output:
return output
else:
return channel.makefile_stderr('rb', -1).readlines()
def update(self):
"""Execute the given commands on a remote machine."""
channel = self._transport.invoke_shell(term='xterm')
channel.exec_command('~/Desktop/update.sh')
output = channel.makefile('rb', -1).readlines()
if output:
return output
else:
return channel.makefile_stderr('rb', -1).readlines()
def close(self):
"""Closes the connection and cleans up."""
# Close SFTP Connection.
if self._sftp_live:
self._sftp.close()
self._sftp_live = False
# Close the SSH Transport.
if self._tranport_live:
self._transport.close()
self._tranport_live = False
def __del__(self):
"""Attempt to clean up if not explicitly closed."""
self.close()
def getData():
"""Create, get, and put delim file when called directly."""
app = Application().start(r"c:\putty.exe trak#10.1.10.70 -pw trak")
app.window_(
title_re=".*trak.*").TypeKeys("/home/trak/Desktop/getdata.sh && exit{ENTER}")
app.window_(title_re=".*trak.*").WaitNot("exists", timeout=120)
trakfile = dir_path + '/storage/trakdelim.txt'
shell = Connection('10.1.10.70', "trak", password="trak")
shell.get('/trak/data/trakdelim.txt', trakfile)
shell.close()
if __name__ == "__main__":
getData()
I appreciate anyone who can help and I can clarify when needed!
A bit of googling showed me what this is:
Someone copied this code and wrote a simple script with it that takes a file (/trak/data/trakdelim.txt) on a remote computer with ip address 10.1.10.70 username trak and password trak and copies it to the storage/trakdelim.txt file. if this is not working for you now then take a tool that allows you to do this manually with such as winSCP and use that instead.
Good luck.
You need to execute it as a python script.
python ~/Desktop/getdata.py
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.
I have read over this post extensively and have researched Exscript, paramiko, Fabric and pxssh and I am still lost Persistent ssh session to Cisco router . I am new to python scripting.
I am attempting to write a script in Python that will SSH into a Cisco device, run "show version", display the results in notepad, then end the script.
I can get this working with show commands that do not require the user to interact with the device. For example:
from Exscript.util.interact import read_login
from Exscript.protocols import SSH2
account = read_login()
conn = SSH2()
conn.connect('192.168.1.11')
conn.login(account)
conn.execute('show ip route')
print conn.response
conn.send('exit\r')
conn.close()
The above script will display the results of "show ip route".
If I try conn.execute('show version') the script times out because the Cisco device is expecting the user to press space bar to continue, press return to show the next line or any key to back out to the command line.
How can I execute the show version command, press space bar twice to display the entire output of the show version command, then print it in python?
Thank you!!!!
Try executing terminal length 0 before running show version. For example:
from Exscript.util.interact import read_login
from Exscript.protocols import SSH2
account = read_login()
conn = SSH2()
conn.connect('192.168.1.11')
conn.login(account)
conn.execute('terminal length 0')
conn.execute('show version')
print conn.response
conn.send('exit\r')
conn.close()
From the Cisco terminal docs: http://www.cisco.com/en/US/docs/ios/12_1/configfun/command/reference/frd1003.html#wp1019281
First execute
terminal length 0
to disable paging.
I just asked the same thing and the below code will run from a list and obtain the information you are asking for.
from __future__ import print_function
from netmiko import ConnectHandler
import sys
import time
import select
import paramiko
import re
fd = open(r'C:\NewdayTest.txt','w') # Where you want the file to save to.
old_stdout = sys.stdout
sys.stdout = fd
platform = 'cisco_ios'
username = 'username' # edit to reflect
password = 'password' # edit to reflect
ip_add_file = open(r'C:\IPAddressList.txt','r') # a simple list of IP addresses you want to connect to each one on a new line
for host in ip_add_file:
host = host.strip()
device = ConnectHandler(device_type=platform, ip=host, username=username, password=password)
output = device.send_command('terminal length 0')
output = device.send_command('enable') #Editable to be what ever is needed
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()