pysftp copying remote directory using get_r() - python-3.x

I am trying to copy contents of a remote directory to my local directory using pysftp.
Here's the code:
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
p=pysftp.Connection("10.2.2.99",username= "user",
password="password", cnopts=cnopts)
remote_path = '/cradius-data/files/webapps/vcm/somedirectory'
local_path = 'E:\\New Folder\\FTP Download Folder'
p.get_r(remotedir=remote_path,localdir=local_path)
I get the following File Not Found error message,
No such file or directory: 'E:\\New Folder\\FTP Download Folder\\./cradius-data/files/webapps/vcm/somedirectory/SOME_File.ZIP'
It seems that both the paths are being concatenated for some reason which is leading to an incorrect FileNotFound exception.
Also ,I've verified that the file is present in the remote directory.
Any help is much appreciated.

It is because of the '\\' in path string from windows, if you are copying from windows to Linux then this problem occurs. I had the same problem, I rewrote the path strings removing the extra '\' and everything worked fine, you can use my code, include the DirectoryCopy.py in your script and use it as used in script.py below.
DirectoryCopy.py:
import paramiko
import os
import socket
from stat import S_ISDIR
class SSHSession(object):
# Usage:
# Detects DSA or RSA from key_file, either as a string filename or a
# file object. Password auth is possible, but I will judge you for
# using it. So:
# ssh=SSHSession('targetserver.com','root',key_file=open('mykey.pem','r'))
# ssh=SSHSession('targetserver.com','root',key_file='/home/me/mykey.pem')
# ssh=SSHSession('targetserver.com','root','mypassword')
# ssh.put('filename','/remote/file/destination/path')
# ssh.put_all('/path/to/local/source/dir','/path/to/remote/destination')
# ssh.get_all('/path/to/remote/source/dir','/path/to/local/destination')
# ssh.command('echo "Command to execute"')
def __init__(self,hostname,username='root',key_file=None,password=None):
#
# Accepts a file-like object (anything with a readlines() function)
# in either dss_key or rsa_key with a private key. Since I don't
# ever intend to leave a server open to a password auth.
#
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((hostname,22))
self.t = paramiko.Transport(self.sock)
self.t.start_client()
#keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
key = self.t.get_remote_server_key()
# supposed to check for key in keys, but I don't much care right now to find the right notation
key_file=None
if key_file is not None:
if isinstance(key,str):
key_file=open(key,'r')
key_head=key_file.readline()
key_file.seek(0)
if 'DSA' in key_head:
keytype=paramiko.DSSKey
elif 'RSA' in key_head:
keytype=paramiko.RSAKey
else:
raise Exception("Can't identify key type")
pkey=keytype.from_private_key(key_file)
self.t.auth_publickey(username, pkey)
else:
if password is not None:
self.t.auth_password(username,password,fallback=False)
else: raise Exception('Must supply either key_file or password')
self.sftp=paramiko.SFTPClient.from_transport(self.t)
def command(self,cmd):
# Breaks the command by lines, sends and receives
# each line and its output separately
#
# Returns the server response text as a string
chan = self.t.open_session()
chan.get_pty()
chan.invoke_shell()
chan.settimeout(20.0)
ret=''
try:
ret+=chan.recv(1024)
except:
chan.send('\n')
ret+=chan.recv(1024)
for line in cmd.split('\n'):
chan.send(line.strip() + '\n')
ret+=chan.recv(1024)
return ret
def put(self,localfile,remotefile):
# Copy localfile to remotefile, overwriting or creating as needed.
self.sftp.put(localfile,remotefile)
def put_all(self,localpath,remotepath):
# recursively upload a full directory
os.chdir(os.path.split(localpath)[0])
parent=os.path.split(localpath)[1]
#print('Parent dir:',parent)
for walker in os.walk(parent):
#print("walker:",walker)
try:
#print("Directory created with path:",os.path.join(remotepath,walker[0]) )
#temp path for directory for changin all the '\' to '/'
direcTemp = os.path.join(remotepath,walker[0])
direcTemp = direcTemp.replace('\x5c','/')
#self.sftp.mkdir(os.path.join(remotepath,walker[0]))
self.sftp.mkdir(direcTemp)
except:
pass
for file in walker[2]:
#in order to transfer the whole directory it is necesssary to change all the '\' to '/'
localTemp=os.path.join(walker[0],file)
localTemp=localTemp.replace('\x5c','/')
remoTemp =os.path.join(remotepath,walker[0],file)
remoTemp = remoTemp.replace('\x5c','/')
# print('Local windows path:',localTemp)
print('Remo Linux path:',remoTemp)
#self.put(localTemp,os.path.join(remotepath,walker[0],file))
self.put(localTemp,remoTemp)
def get(self,remotefile,localfile):
# Copy remotefile to localfile, overwriting or creating as needed.
self.sftp.get(remotefile,localfile)
def sftp_walk(self,remotepath):
# Kindof a stripped down version of os.walk, implemented for
# sftp. Tried running it flat without the yields, but it really
# chokes on big directories.
path=remotepath
files=[]
folders=[]
for f in self.sftp.listdir_attr(remotepath):
if S_ISDIR(f.st_mode):
folders.append(f.filename)
else:
files.append(f.filename)
print (path,folders,files)
yield path,folders,files
for folder in folders:
new_path=os.path.join(remotepath,folder)
for x in self.sftp_walk(new_path):
yield x
def get_all(self,remotepath,localpath):
# recursively download a full directory
# Harder than it sounded at first, since paramiko won't walk
#
# For the record, something like this would gennerally be faster:
# ssh user#host 'tar -cz /source/folder' | tar -xz
self.sftp.chdir(os.path.split(remotepath)[0])
parent=os.path.split(remotepath)[1]
try:
os.mkdir(localpath)
except:
pass
for walker in self.sftp_walk(parent):
try:
os.mkdir(os.path.join(localpath,walker[0]))
except:
pass
for file in walker[2]:
self.get(os.path.join(walker[0],file),os.path.join(localpath,walker[0],file))
def write_command(self,text,remotefile):
# Writes text to remotefile, and makes remotefile executable.
# This is perhaps a bit niche, but I was thinking I needed it.
# For the record, I was incorrect.
self.sftp.open(remotefile,'w').write(text)
self.sftp.chmod(remotefile,755)
script.py:
import paramiko
from DirectoryCopy import SSHSession
def creat_ssh_connection(host, port, username, password, keyfilepath, keyfiletype):
ssh = None
key = None
try:
if keyfilepath is not None:
# Get private key used to authenticate user.
if keyfiletype == 'DSA':
# The private key is a DSA type key.
key = paramiko.DSSKey.from_private_key_file(keyfilepath)
else:
# The private key is a RSA type key.
key = paramiko.RSAKey.from_private_key(keyfilepath)
# Create the SSH client.
ssh = paramiko.SSHClient()
# Setting the missing host key policy to AutoAddPolicy will silently add any missing host keys.
# Using WarningPolicy, a warning message will be logged if the host key is not previously known
# but all host keys will still be accepted.
# Finally, RejectPolicy will reject all hosts which key is not previously known.
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the host.
if key is not None:
# Authenticate with a username and a private key located in a file.
ssh.connect(host, port, username, None, key)
else:
# Authenticate with a username and a password.
ssh.connect(host, port, username, password)
#console = ssh.invoke_shell()
#console.keep_this = ssh
return ssh
except:
print("exception")
ssh=SSHSession('ipaddres','user_name',password='password_string')
#directory_win is copied inside the directory_lin folder.
ssh.put_all('../../windows/example/directory_win','/linux/example/directory_lin/')

Related

Paramiko SSH connection timeout after 3 hours

I develop a Python two scripts to transfer lot of data (~120Go) on my vm, with Paramiko.
My vm is on OVH server.
First script transfert ~ 40Go, and the second script ~ 80Go.
Stack :
Python 3.9.1
Paramiko 2.7.2
SCP 0.13.3
On my both scripts, I use this function to setup SSH connection.
def connect():
transport = paramiko.Transport((target_host, target_port))
transport.connect(None, target_username, target_pwd)
sftp_client = paramiko.SFTPClient.from_transport(transport)
green_print("SSH connected")
return sftp_client, transport
If I create one script which do the two transfer, I'm timeout after 3 hours.
With two distincts script which run in the same time, I'm timeout after 2h30 of transfer.
I already read many many many post on Paramiko, SSH connection, timeout parameter, ClientAliveInterval, etc... But nothing works.
After this time, I have this error
Connexion fermée par l'hôte distant / Connection closed by remote host
Three functions of my script :
def connect():
transport = paramiko.Transport((target_host, target_port))
transport.connect(None, target_username, target_pwd)
sftp_client = paramiko.SFTPClient.from_transport(transport)
green_print("SSH connected")
return sftp_client, transport
def transfert(sftp, vm, object_path):
os.chdir(os.path.split(object_path)[0])
parent = os.path.split(object_path)[1]
try:
sftp.mkdir(vm)
except:
pass
for path, _, files in os.walk(parent):
try:
sftp.mkdir(os.path.join(vm, path))
except:
pass
for filename in files:
sftp.put(os.path.join(object_path, filename),
os.path.join(vm, path, filename))
def job():
green_print("\nProcess start...")
check_folder()
folder = forfiles_method()
vm, lidar, pos = name_path(folder)
sftp, transport = connect()
transfert(sftp, vm, pos)
sftp.close()
transport.close()
minimal reproducible example :
from paramiko.sftp_client import SFTPClient
import paramiko
import os
target_host = 'xx.xx.x.xxx'
target_port = 22
target_username = "xxxxxxx"
target_pwd = 'xxxxxx'
remote_path = "e:/x/" # => on your vm
target_folder = '/folder1' # => on your computer
def connect():
transport = paramiko.Transport((target_host, target_port))
transport.connect(None, target_username, target_pwd)
sftp_client = paramiko.SFTPClient.from_transport(transport)
return sftp_client, transport
def transfert(sftp, remote_path, object_path):
os.chdir(os.path.split(object_path)[0])
parent = os.path.split(object_path)[1]
try:
sftp.mkdir(remote_path)
except:
pass
for path, _, files in os.walk(parent):
try:
sftp.mkdir(os.path.join(remote_path, path))
except:
pass
for filename in files:
sftp.put(os.path.join(object_path, filename),
os.path.join(remote_path, path, filename))
def job():
sftp, transport = connect()
transfert(sftp, remote_path, target_folder)
sftp.close()
transport.close()
The tree structure of my files, and I want to transfer only the "test" folder which contains more than 120GB.
folder / test
I'm new in Python dev.
If someone have a solution, I take it !
So the solution :
subprocess.run(["winscp.com", "/script=" + cmdFile], shell=True)
If winscp.com is not found like command, insert the path like : C:/Program Files (x86)/WinSCP/winscp.com
Write your commandes line in a txt file, here cmdFile.
Links, which can help you :
Running WinSCP command from Python
From Python run WinSCP commands in console
https://winscp.net/eng/docs/commandline

1expected bytes, str found when trying to connect to postresql in Google Functions

I have not been able to connect to postgresql from google functions. This is the code - I'm following these instructions https://cloud.google.com/sql/docs/postgres/connect-functions. The actual output of my program is 1expected bytes, str found, as it manages to do the first print before raising the Exception.
I haven't even been able to get a more explicit error than this. Any ideas would be very much welcome :-).
import os
import sqlalchemy
import pg8000
def hello_world(request):
output = ''
try:
# Remember - storing secrets in plaintext is potentially unsafe. Consider using
# something like https://cloud.google.com/secret-manager/docs/overview to help keep
# secrets secret.
db_user = $DB_USER
db_pass = $PASS
db_name = $DB_NAME
db_socket_dir = os.environ.get("DB_SOCKET_DIR", "/cloudsql")
cloud_sql_connection_name = $CLOUD_SQL_CONNECTION_NAME
pool = sqlalchemy.create_engine(
# Equivalent URL:
# postgres+pg8000://<db_user>:<db_pass>#/<db_name>
# ?unix_sock=<socket_path>/<cloud_sql_instance_name>/.s.PGSQL.5432
sqlalchemy.engine.url.URL(
drivername="postgres+pg8000",
username=db_user, # e.g. "my-database-user"
password=db_pass, # e.g. "my-database-password"
database=db_name, # e.g. "my-database-name"
query={
"unix_sock": "%s/%s/.s.PGSQL.5432" % (
db_socket_dir, # e.g. "/cloudsql"
cloud_sql_connection_name) # i.e "<PROJECT-NAME>:<INSTANCE-REGION>:<INSTANCE-NAME>"
}
),
# ... Specify additional properties here.
)
output += '1'
pool.connect()
output += '2'
return output
except Exception as e:
output += str(e)
return output
I think you might be running into this issue of compatibility between SQLAlchemy and the latest version of pg8000: https://github.com/tlocke/pg8000/issues/53
OK, I got it. The parameters need to be bytes, so if db_user = "user", for example, I need to change that to db_user = b"user"

How to execute commands in a remote server using python?

This question is related to this other one: How to use sockets to send user and password to a devboard using ssh
I want to connect to the devboard in order to execute a script. All the outputs of that script I want to send to a Elasticsearch machine.
I can connect to the devboard (see IMAGE below) using my laptop which happens to have Elasticsearch installed. But, when I want to send data to the devboard, the script shows nothing.
What I am doing is:
As soon as you find mendel#undefined-eft:~$ , send the command: cd coral/tflite/python/examples/classification/Auto_benchmark\n
What am I doing wrong?
import paramiko
import os
#Server's data
IP = '172.16.2.47'
PORT = 22
USER = 'mendel'
PASSWORD = 'mendel'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname = IP, port=PORT, username = USER, password = PASSWORD)
channel = ssh.invoke_shell() #to get a dedicated channel
channel_data = str()
host = str()
while True:
if channel.recv_ready(): #is there data to be read?
channel_data += channel.recv(9999).decode("utf-8")
os.system('clear')
print(channel_data)
#ONLY WORKS UNTIL HERE!!!
else:
continue
if channel_data.endswith('mendel#undefined-eft:~$'):
channel.send('cd coral/tflite/python/examples/classification/Auto_benchmark\n')
channel_data += channel.recv(9999).decode("utf-8")
print(channel_data)
IMAGE
EDIT
channel = ssh.invoke_shell() #to get a dedicated channel
channel_data = str()
host = str()
while True:
if channel.recv_ready(): #is there data to be read?
channel_data += channel.recv(9999).decode("utf-8")
os.system('clear')
print(channel_data)
else:
continue
if channel_data.endswith('mendel#undefined-eft:~$ '):#it is good to send commands
channel.send('cd coral/tflite/python/examples/classification/Auto_benchmark\n')
#channel_data += channel.recv(9999).decode("utf-8")
#print(channel_data)
elif channel_data.endswith('mendel#undefined-eft:~/coral/tflite/python/examples/classification/Auto_benchmark$ '):
channel.send('ls -l\n') #python3 auto_benchmark.py')
channel_data += channel.recv(9999).decode("utf-8")
print(channel_data)
I guess you have to change the
if channel_data.endswith('mendel#undefined-eft:~$'):
to
if channel_data.endswith('mendel#undefined-eft:~$ '):
according to your prompt. Please note the space after :~$

Python code for telnetting DUT needs further optimization

I need to further optimize my code in Python.
I was earlier executing commands on the Device Under Test step by step which was a lot as I also required sleep timers. However I was able to minimize it through a list and calling elements of the list in a for loop:
I need your inputs to further optimize this code:
ConfigListBFD = ['conf t' , 'int Fa1/0' , 'ip address 10.10.10.1 255.255.255.0', 'no shut']
for i in ConfigListBFD:
tn.write(i.encode('ascii') + b"\n")
print (i, "command entered successfully")
time.sleep(2)
Please note: I am telnetting the DUT as ssh is not supported.
i am using this optimized common code for telnet. we can create a common file where you can add this method
import telnetlib
import time
def telnet(host):
user = <username>
password = <password>
try :
tn = telnetlib.Telnet(host)
except :
print("Unable to connect")
sys.exit()
tn.read_until(b"Username:") # read until username prompt
tn.write(user.encode('ascii') + b"\n")
if password:
tn.read_until(b"password:") #read until password prompt
tn.write(password.encode('ascii') + b"\n")
tn.read_until(b"#")
return tn #return telnetlib handle
than import this method to another file, where we write our script

Python Script Creates Directories In /tmp/, Taking Up System Space

I am running a script that acts as a server, allows two clients to connect to it, and for one specific client to send a message to the server, the server modifies it, then sends it to the other client.
This appears to work, as the receiving client acknowledges that the input was received and is valid. This is a script that I intend to run continuously.
However, a big issue is that my /tmp/ directory is filling up with directories named _M... (The ellipses representing a random string), that contains python modules (such as cryptography, which, as far as I'm aware, I'm not using), and timezone information (quite literally every timezone that python supports). It seems to be creating them very frequently, but I can't identify what in the process exactly is doing this.
I have created a working cleanup bash script that removes files older than 5 minutes from the directory every 5 minutes, however, I cannot guarantee that when I am duplicating this process for other devices, that the directories will have the same name formatting. Rather than create a unique bash script for each process that I create, I'd rather be able to clean up the directories from within the python script, or even better, to prevent the directories from being created at all.
The problem is, I'm not certain of how this is accomplished, and I do not see anything on SO regarding what is creating these directories, nor how to delete them.
The following is my script
import time, socket, os, sys, re, select
IP = '192.168.109.8'
PORT = [3000, 3001]
PID = str(os.getpid())
PIDFILE = "/path/to/pidfile.pid"
client_counter = 0
sockets_list = []
def runCheck():
if os.path.isfile(PIDFILE):
return False
else:
with open(PIDFILE, 'w') as pidfile:
pidfile.write(PID)
return True
def openSockets():
for i in PORT:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((IP, i))
s.listen(1)
sockets_list.append(s)
def receiveMessage(client_socket):
try:
message = client_socket.recv(2048).decode('utf-8')
if not message:
return False
message = str(message)
return message
except:
return False
def fixString(local_string):
#processes
return local_string
def main():
try:
openSockets()
clients = {}
print(f'Listening for connections on {IP}:{PORT[0]} and {PORT[1]}...')
client_count = 0
while True:
read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
for notified_socket in read_sockets:
if notified_socket == sockets_list[0] or notified_socket == sockets_list[1]:
client_socket, client_address = sockets_list[client_count].accept()
client_count = (client_count + 1) % 2
sockets_list.append(client_socket)
clients[client_socket] = client_socket
print('Accepted new connection from: {}'.format(*client_address))
else:
message = receiveMessage(notified_socket)
if message is False:
continue
message = fixString(message)
for client_socket in clients:
if client_socket != notified_socket:
if message != "N/A":
client_socket.send(bytes(message, "utf-8"))
for notified_socket in exception_sockets:
sockets_list.remove(notified_socket)
del clients[notified_socket]
time.sleep(1)
except socket.timeout:
for i in sockets_list:
i.close()
os.remove(PIDFILE)
sys.exit()
except Exception as e:
for i in sockets_list:
i.close()
err_details = str('Error in line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
os.remove(PIDFILE)
print("Exception: {}".format(err_details))
sys.exit()
if __name__ == "__main__":
if runCheck():
main()
else:
pass
How might I set it up so that the python script will delete the directories it creates in the /tmp/ directory, or better, to not create them in the first place? Any help would be greatly appreciated.
As it would turn out, it is PyInstaller that was generating these files. In the documentation, it states that pyinstaller generates this _MEI directory when creating the executable in single-file mode, and it is supposed to delete it as well, but for some reason it didn't.

Resources