SCP with paramiko, using different remote and local directories - python

I have a Python code that uses Paramiko.
#!/usr/bin/env python
import paramiko
username = ('user')
password = ('1234')
hostname = ('test-server.com')
ports = 22
localD = ('/var/tmp/testxxxxxxxx.tar.gz')
remoteD = ('/var/tmp/testxxxxxxxx.tar.gz')
paramiko.util.log_to_file('/tmp/paramiko.log')
transport = paramiko.Transport((hostname, ports))
transport.connect(username = username, password = password)
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put(remotepath=remoteD, localpath=localD)
sftp.close()
transport.close()
With this code, the local-dir and the remote-dir should be equals. if not "file not found"
How can I change or use another remote-dir different to local-dir?
Example:
localD = ('/var/tmp/testxxxxxxxx.tar.gz')
remoteD = ('/home/user/testxxxxxxxx.tar.gz')
Thank you

Since the error message says 'No such file or directory', I'd first check to make sure the directory exists on remote system and is writable by the credentials you're using. The SFTPClient class has all sorts of other methods you can call to verify the existence of target paths and create them if they don't exist yet.
For example, calling the stat() method passing in the destination path should give you back a tuple the same as that returned by os.stat. Try running this script (I just hacked up a little path check routine and dropped it into your script):
#!/usr/bin/env python
import paramiko
username = ('user')
password = ('1234')
hostname = ('test-server.com')
ports = 22
localD = ('/var/tmp/testxxxxxxxx.tar.gz')
remoteD = ('/var/tmp/testxxxxxxxx.tar.gz')
def check(sftp, path):
parts = path.split('/')
for n in range(2, len(parts) + 1):
path = '/'.join(parts[:n])
print 'Path:', path,
sys.stdout.flush()
try:
s = sftp.stat(path)
print 'mode =', oct(s.st_mode)
except IOError as e:
print e
paramiko.util.log_to_file('/tmp/paramiko.log')
transport = paramiko.Transport((hostname, ports))
transport.connect(username = username, password = password)
sftp = paramiko.SFTPClient.from_transport(transport)
check(sftp, remoteD)
sftp.close()
transport.close()
Output should be something like this:
Path: /var mode = 040755
Path: /var/tmp mode = 040700
Path: /var/tmp/testxxxxxxxx.tar.gz [Errno 2] No such file
The mode numbers will most likely differ, but you shouldn't get "No such file" error on any of the parts of the path other than the file name. If you do, then it probably means you need to construct the path down to the point where you want to put the file using sftp.mkdir()

this is sftp, it's not SCP. when connecting to devices which have dropbear (such as those on OpenWRT) there is no sftp sub-system, so sftp fails. instead i use a tar command (paramiko abstracted behind the "connect" function.
i am however looking for actual SCP over paramiko and unfortunately the answer here, which is at the top of a google search, doesn't actually answer the question.
# argh argh argh dropbear on openwrt not compiled with sftp
# have to use some horribleness...
tw = connect(ipaddr, "root", server_root_password, command="tar xvf -",
debug_level=debug_level)
tf = StringIO()
tar = tarfile.open(mode= "w:", fileobj = tf)
taradd(tar, "etc/tinc/....")
taradd(tar, ".....", ...)
taradd(tar, "....", ...)
taradd(tar, "etc/init.d/tinc", ...., ex=True)
tar.close()
tf.seek(0)
txt = tf.read()
tw.t.write(txt)
tw.t.write('^D')
tw.t.w.close()
tw.t.w = None
tw.close()

https://github.com/jbardin/scp.py
far from not actually answering the question which is asked, the above code provides an actual implementation of scp that is compatible with the openssh SCP protocol. personally i've not tested it with dropbear yet but it is code that actually answers the question that's in the subject line.
SCP != SFTP.

Related

multiple commands by logging into SSH

I am trying to log in to the server and give multiple commands from the input file. Here input file is web.txt (contains 'bash', 'df-g' as examples). I am sucessfully able to login to server, but not able to run the commands. I don't know what I am doing wrong here. Can anyone help me please.
import paramiko
web_list = []
def create_web_list():
file = open("web.txt", "r", encoding='utf-8')
for value in file.readlines():
web_list.append(value.strip( ))
return web_list
ip = 'x.x.x.x'
username = 'username'
password = 'password'
def web_device(web_list):
SESSION = paramiko.SSHClient()
SESSION.set_missing_host_key_policy(paramiko.AutoAddPolicy)
SESSION.connect(ip,port=22,username=username,password=password,look_for_keys=False,allow_agent=False)
print("Connection Sucessful")
for cmd in web_list:
stdin,stdout,stderr=SESSION.exec_command(cmd)
outlines=stdout.readlines()
resp=''.join(outlines)
print(resp)
SESSION.close()
if __name__ == "__main__":
web_device(create_web_list())
Please specify either you want output in one go or you want output for each command separately? if you want with one go then please find below:
Executing multiple commands on paramiko SSHClient, you can place all commands in one line with ; separator like:
client.exec_command('ls -l; cwd; whoiam') etc
so please read line from file accordingly and execute commands with one go instead using a loop.
For Individual command execution case use below:
with open("web.txt", "r", encoding='utf-8') as f:
return web_list.append(f.strip( ))
and print output in utf format like '\n'.join(outlines)

ssh to remote machine with password in script via python

I am working with remote machine. I had to ssh everytime i need verification of file update time and there are multiple scripts which will do ssh to the remote machine.
I look over internet but couldn't find according to my requirements.
I am trying to find a python script which uses ssh and the password is also in the script because my python scripts will check every 5 minutes the file modification times and i cannot enter password everytime the script execute.
I tried these codes from SO and internet but couldn't fulfil my need.
Establish ssh session by giving password using script in python
How to execute a process remotely using python
Also I enter into one remote machine through ssh simply.then I am trying to ssh but via python script to another remote machine cox this python script also include code to check the modification time of different files.. mean i already ssh to one remote machine and then i want to run some python scripts from there which checks file modification time of files on another remote machine.
Is there a simple way to ssh remote machine along with password in python script. .
I would be grateful.
If you want to try paramiko module. Here is a sample working python script.
import paramiko
def start_connection():
u_name = 'root'
pswd = ''
port = 22
r_ip = '198.x.x.x'
sec_key = '/mycert.ppk'
myconn = paramiko.SSHClient()
myconn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
my_rsa_key = paramiko.RSAKey.from_private_key_file(sec_key)
session = myconn.connect(r_ip, username =u_name, password=pswd, port=port,pkey=my_rsa_key)
remote_cmd = 'ifconfig'
(stdin, stdout, stderr) = myconn.exec_command(remote_cmd)
print("{}".format(stdout.read()))
print("{}".format(type(myconn)))
print("Options available to deal with the connectios are many like\n{}".format(dir(myconn)))
myconn.close()
if __name__ == '__main__':
start_connection()
Adding my program as well here which relies on the user password and displays the status on different output files.
#!/bin/python3
import threading, time, paramiko, socket, getpass
from queue import Queue
locke1 = threading.Lock()
q = Queue()
#Check the login
def check_hostname(host_name, pw_r):
with locke1:
print ("Checking hostname :"+str(host_name)+" with " + threading.current_thread().name)
file_output = open('output_file','a')
file_success = open('success_file','a')
file_failed = open('failed_file','a')
file_error = open('error_file','a')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(host_name, username='root', password=pw_r, timeout=5)
#print ("Success")
file_success.write(str(host_name+"\n"))
file_success.close()
file_output.write("success: "+str(host_name+"\n"))
file_output.close()
# printing output if required from remote machine
#stdin,stdout,stderr = ssh.exec_command("hostname&&uptime")
#for line in stdout.readlines():
# print (line.strip())
except paramiko.SSHException:
# print ("error")
file_failed.write(str(host_name+"\n"))
file_failed.close()
file_output.write("failed: "+str(host_name+"\n"))
file_output.close()
#quit()
except paramiko.ssh_exception.NoValidConnectionsError:
#print ("might be windows------------")
file_output.write("failed: " + str(host_name + "\n"))
file_output.close()
file_failed.write(str(host_name+"\n"))
file_failed.close()
#quit()
except socket.gaierror:
#print ("wrong hostname/dns************")
file_output.write("error: "+str(host_name+"\n"))
file_output.close()
file_error.write(str(host_name + "\n"))
file_error.close()
except socket.timeout:
#print ("No Ping %%%%%%%%%%%%")
file_output.write("error: "+str(host_name+"\n"))
file_output.close()
file_error.write(str(host_name + "\n"))
file_error.close()
ssh.close()
def performer1():
while True:
hostname_value = q.get()
check_hostname(hostname_value,pw_sent)
q.task_done()
if __name__ == '__main__':
print ("This script checks all the hostnames in the input_file with your standard password and write the outputs in below files: \n1.file_output\n2.file_success \n3.file_failed \n4.file_error \n")
f = open('output_file', 'w')
f.write("-------Output of all hosts-------\n")
f.close()
f = open('success_file', 'w')
f.write("-------Success hosts-------\n")
f.close()
f = open('failed_file', 'w')
f.write("-------Failed hosts-------\n")
f.close()
f = open('error_file', 'w')
f.write("-------Hosts with error-------\n")
f.close()
with open("input_file") as f:
hostname1 = f.read().splitlines()
#Read the standard password from the user
pw_sent=getpass.getpass("Enter the Password:")
start_time1 = time.time()
for i in hostname1:
q.put(i)
#print ("all the hostname : "+str(list(q.queue)))
for no_of_threads in range(10):
t = threading.Thread(target=performer1)
t.daemon=True
t.start()
q.join()
print ("Check output files for results")
print ("completed task in" + str(time.time()-start_time1) + "seconds")

Enter an ssh password using the standard python library (not pexpect)

Related questions that are essentially asking the same thing, but have answers that don't work for me:
Make python enter password when running a csh script
How to interact with ssh using subprocess module
How to execute a process remotely using python
I want to ssh into a remote machine and run one command. For example:
ssh <user>#<ipv6-link-local-addr>%eth0 sudo service fooService status
The problem is that I'm trying to do this through a python script with only the standard libraries (no pexpect). I've been trying to get this to work using the subprocess module, but calling communicate always blocks when requesting a password, even though I supplied the password as an argument to communicate. For example:
proc = subprocess.Popen(
[
"ssh",
"{testUser1}#{testHost1}%eth0".format(**locals()),
"sudo service cassandra status"],
shell=False,
stdin=subprocess.PIPE)
a, b = proc.communicate(input=testPasswd1)
print "a:", a, "b:", b
print "return code: ", proc.returncode
I've tried a number of variants of the above, as well (e.g., removing "input=", adding/removing subprocess.PIPE assignments to stdout and sterr). However, the result is always the same prompt:
ubuntu#<ipv6-link-local-addr>%eth0's password:
Am I missing something? Or is there another way to achieve this using the python standard libraries?
This answer is just an adaptation of this answer by Torxed, which I recommend you go upvote. It simply adds the ability to capture the output of the command you execute on the remote server.
import pty
from os import waitpid, execv, read, write
class ssh():
def __init__(self, host, execute='echo "done" > /root/testing.txt',
askpass=False, user='root', password=b'SuperSecurePassword'):
self.exec_ = execute
self.host = host
self.user = user
self.password = password
self.askpass = askpass
self.run()
def run(self):
command = [
'/usr/bin/ssh',
self.user+'#'+self.host,
'-o', 'NumberOfPasswordPrompts=1',
self.exec_,
]
# PID = 0 for child, and the PID of the child for the parent
pid, child_fd = pty.fork()
if not pid: # Child process
# Replace child process with our SSH process
execv(command[0], command)
## if we havn't setup pub-key authentication
## we can loop for a password promt and "insert" the password.
while self.askpass:
try:
output = read(child_fd, 1024).strip()
except:
break
lower = output.lower()
# Write the password
if b'password:' in lower:
write(child_fd, self.password + b'\n')
break
elif b'are you sure you want to continue connecting' in lower:
# Adding key to known_hosts
write(child_fd, b'yes\n')
else:
print('Error:',output)
# See if there's more output to read after the password has been sent,
# And capture it in a list.
output = []
while True:
try:
output.append(read(child_fd, 1024).strip())
except:
break
waitpid(pid, 0)
return ''.join(output)
if __name__ == "__main__":
s = ssh("some ip", execute="ls -R /etc", askpass=True)
print s.run()
Output:
/etc:
adduser.conf
adjtime
aliases
alternatives
apm
apt
bash.bashrc
bash_completion.d
<and so on>

How to redirect output of ssh.exec_command to a file in Python paramiko module?

I want to run a python script say test.py on a Linux target machine (which has a python interpreter) and capture the output of the command in a text file as the part of another python script invoke.py using paramiko module.
The statement in the script
stdin, stdout, sterr = ssh.exec_command("python invoke.py > log.txt")
generates a blank file log.txt.
Please suggest corrections / alternate way to do this. to write the output to the file correctly.
test.py when run locally outputs sane text (which is expected to be logged in log.txt).
There are some relevant posts here and here, but no one deals with output of python script
instead of calling client.exec_command() you can use client.open_channel() and use channel session's recv() and recv_stderr() streams to read write stdout/std err:
def sshExecute(hostname, username, password, command, logpath = None):
buffSize = 2048
port = 22
client = paramiko.Transport((hostname, port))
client.connect(username=username, password=password)
if logpath == None:
logpath = "./"
timestamp = int(time.time())
hostname_prefix = "host-%s-%s"%(hostname.replace(".","-"),timestamp)
stdout_data_filename = os.path.join(logpath,"%s.out"%(hostname_prefix))
stderr_data_filename = os.path.join(logpath,"%s.err"%(hostname_prefix))
stdout_data = open(stdout_data_filename,"w")
stderr_data = open(stderr_data_filename,"w")
sshSession = client.open_channel(kind='session')
sshSession.exec_command(command)
while True:
if sshSession.recv_ready():
stdout_data.write(sshSession.recv(buffSize))
if sshSession.recv_stderr_ready():
stderr_data.write(sshSession.recv_stderr(buffSize))
if sshSession.exit_status_ready():
break
stdout_data.close()
stderr_data.close()
sshSession.close()
client.close()
return sshSession.recv_exit_status()
Hope this fully working function helps you

execute local python script over sshClient() with Paramiko in remote machine

This is my first post in StackOverflow, so I hope to do it the right way! :)
I have this task to do for my new job that needs to connect to several servers and execute a python script in all of them. I'm not very familiar with servers (and just started using paramiko), so I apologize for any big mistakes!
The script I want to run on them modifies the authorized_keys file but to start, I'm trying it with only one server and not yet using the aforementioned script (I don't want to make a mistake and block the server in my first task!).
I'm just trying to list the directory in the remote machine with a very simple function called getDir(). So far, I've been able to connect to the server with paramiko using the basics (I'm using pdb to debug the script by the way):
try_paramiko.py
#!/usr/bin/python
import paramiko
from getDir import get_dir
import pdb
def try_this(server):
pdb.set_trace()
ssh = paramiko.SSHClient()
ssh.load_host_keys("pth/to/known_hosts")
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
my_key = paramiko.RSAKey.from_private_key_file("pth/to/id_rsa")
ssh.connect(server, username = "root", pkey = my_key)
i, o, e = ssh.exec_command(getDir())
This is the function to get the directory list:
getDir.py
#!/usr/bin/python
import os
import pdb
def get_dir():
pdb.set_trace()
print "Current dir list is:"
for item in os.listdir(os.getcwd()):
print item
While debugging I got the directory list of my local machine instead of the one from the remote machine... is there a way to pass a python function as a parameter through paramiko? I would like to just have the script locally and run it remotely like when you do it with a bash file from ssh with:
ssh -i pth/to/key username#domain.com 'bash -s' < script.sh
so to actually avoid to copy the python script to every machine and then run it from them (I guess with the above command the script would also be copied to the remote machine and then deleted, right?) Is there a way to do that with paramiko.sshClient()?
I have also tried to modify the code and use the standard output of the channel that creates exec_command to list the directory leaving the scripts like:
try_paramiko.py
#!/usr/bin/python
import paramiko
from getDir import get_dir
import pdb
def try_this(server):
pdb.set_trace()
ssh = paramiko.SSHClient()
ssh.load_host_keys("pth/to/known_hosts")
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
my_key = paramiko.RSAKey.from_private_key_file("pth/to/id_rsa")
ssh.connect(server, username = "root", pkey = my_key)
i, o, e = ssh.exec_command(getDir())
for line in o.readlines():
print line
for line in e.readlines():
print line
getDir.py
def get_dir():
return ', '.join(os.listdir(os.getcwd()))
But with this, it actually tries to run the local directory list as commands (which actually makes sense they way I have it). I had to convert the list to a string because I was having a TypeError saying that it expects a string or a read-only character buffer, not a list... I know this was a desperate attempt to pass the function... Does anyone know how I could do such thing (pass a local function through paramiko to execute it on a remote machine)?
If you have any corrections or tips on the code, they are very much welcome (actually, any kind of help would be very much appreciated!).
Thanks a lot in advance! :)
You cannot just execute python function through ssh. ssh is just a tunnel with your code on one side (client) and shell on another (server). You should execute shell commands on remote side.
If using raw ssh code is not critical, i suggest fabric as library for writing administration tools. It contains tools for easy ssh handling, file transferring, sudo, parallel execution and other.
I think you might want change the paramaters you're passing into ssh.exec_command Here's an idea:
Instead of doing:
def get_dir():
return ', '.join(os.listdir(os.getcwd()))
i, o, e = ssh.exec_command(getDir())
You might want to try:
i, o, e = ssh.exec_command('pwd')
o.printlines()
And other things to explore:
Writing a bash script or a Python that lives on your servers. You can use Paramiko to log onto the server and executing the script with ssh.exec_command(some_script.sh) or ssh.exec_command(some_script.py)
Paramiko has some FTP/SFTP utilities so you can actually use it to put the script on the server and then execute it.
It is possible to do this by using a here document to feed a module into the remote server's python interpreter.
remotepypath = "/usr/bin/"
# open the module as a text file
with open("getDir.py", "r") as f:
mymodule = f.read()
# setup from OP code
ssh = paramiko.SSHClient()
ssh.load_host_keys("pth/to/known_hosts")
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
my_key = paramiko.RSAKey.from_private_key_file("pth/to/id_rsa")
ssh.connect(server, username = "root", pkey = my_key)
# use here document to feed module into python interpreter
stdin, stdout, stderr = ssh.exec_command("{p}python - <<EOF\n{s}\nEOF".format(p=remotepypath, s=mymodule))
print("stderr: ", stderr.readlines())
print("stdout: ", stdout.readlines())

Categories