Ouvrir une connexion SFTP à travers SSH en Python
Ouvrir une connexion SFTP à travers SSH en Python.
Prérequis:
- Python 2.5
- La librairie Python config: Librairie Python config
- Pour Windows: Microsoft Visual C++ 2005, 2008, 2010 Redistributable
- La librairie Python pycrypto
- La librairie Python paramiko
Voici un exemple de script Python qui récupère sur un serveur Linux via SSH/SFTP des fichiers (XML).
Par la suite le code va appliquer ses règles d’affaires, vous n’avez qu’à vous inspirer du script pour l’adapter selon vos besoins.
Tout d’abord il faut préparer les librairies à importer:
... # Import lib import os, sys, shutil, paramiko, traceback # Lib for helpers try: from lib.helper.helper_misc import ToolsBox, LogStats except ImportError: ToolsBox = None LogStats = None sys.stderr.write('ERROR: %s\n' % traceback.format_exc()) sys.exit() # Lib for config variable try: from lib.config.configobj import ConfigObj except ImportError: ConfigObj = None sys.stderr.write('ERROR: %s' % traceback.format_exc()) sys.exit() ...
Contenu de la classe « LogStats », cette classe va permettre les logs sur la console et un fichier plat en même temps:
... """Begin LogStats class""" class LogStats(object): def __init__(self, f = os.getcwd() + '\\log\\stats.log'): """ Description: Constructor, init stats log file @param f: Log filename @return void """ self.console = sys.stdout self.file = open(f, 'w') def write(self, flow): """ Description: Write flow to specific handle @param flow: String to write @return int """ try: if flow <> '': self.console.write(flow) # Write to console self.file.write(flow) # Write to stats log file return 0 except Exception: sys.stderr.write('ERROR: %s\n' % traceback.format_exc()) sys.exit() """End LogStats class""" ...
Voici le contenu de la classe ToolsBox utilisée dans ce script:
... """Begin ToolsBox class""" class ToolsBox(object): @staticmethod def init_error_log(): """ Description: Init error log file @return int """ try: if not os.path.exists('log'): os.makedirs('log') file_socket = open(os.getcwd() + '\\log\\error.log', 'w') sys.stderr = file_socket return 0 except Exception: sys.stderr.write('ERROR: %s\n' % traceback.format_exc()) sys.exit() @staticmethod def rm_files(folder = '', ext = '.xml'): """ Description: Delete files by extension by folder @param folder: Folder input @param ext: Default extension to delete @return void """ try: if folder: lists = [f for f in os.listdir(folder) if f.endswith(ext)] for f in lists: os.remove(folder + '\\' + f) else: print 'No input folder has been provided => KO' sys.exit() except Exception: sys.stderr.write('ERROR: %s\n' % traceback.format_exc()) sys.exit() """End ToolsBox class""" ...
Enfin, voici le script principal, le programme se connecte à un serveur via SSH/SFTP, récupère des fichiers dans un répertoire, dépose les fichiers en local dans le répertoire IN, copie les fichiers sur un partage réseau Windows puis stocke les fichiers en local dans le répertoire OUT. Ce script va également copier et supprimer les fichiers sur le serveur en vue d’une nouvelle exécution.
... # First, init log files ToolsBox.init_error_log() # Error log sys.stdout = LogStats() # Stats log # Set needed variables obj_config = ConfigObj('config/config.cfg') local_input_folder = os.getcwd() + '\\' + obj_config['file_section']['local_input_folder'] # Input local folder local_output_folder = os.getcwd() + '\\' + obj_config['file_section']['local_output_folder'] # Output local folder sftp_input_folder = obj_config['sftp_section']['sftp_input_folder'] # Input sftp folder sftp_output_folder = obj_config['sftp_section']['sftp_output_folder'] # Output sftp folder netbios_output_folder_path = obj_config['file_section']['netbios_output_folder_path'].encode('latin-1') # Output network drive through Windows """Begin main process""" try: print 'Starting program...' # Pre-process, we cleanup input_folder ToolsBox.rm_files(local_input_folder) print 'Cleanup input_folder directory => OK' i = 0 files = [] # Set needed variables for SFTP print 'Starting Upload process...' host, username, password = (obj_config['sftp_section']['sftp_host'] , obj_config['sftp_section']['sftp_username'] , obj_config['sftp_section']['sftp_password']) ssh = paramiko.SSHClient() print 'Create SSH client object => OK' ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) print 'Set missing SSH host key => OK' paramiko.util.log_to_file(os.getcwd() + '\\log\\ssh.log') print 'Set SSH log file => OK' ssh.connect(host, username=username, password=password) print 'Open SSH connection => OK' sftp = ssh.open_sftp() print 'Open SFTP connection => OK' print 'Iterate through remote dir: ' + sftp_input_folder for f in sftp.listdir(path=sftp_input_folder): # Filter xml files only if f.lower().endswith('xml'): # Set local file path local_file = os.path.join(local_input_folder, f) # Set remote file path remote_file = sftp_input_folder + '/' + os.path.basename(f) # Check if we already have this file if not os.path.exists(local_file): print 'Get ERSV3 XML message from remote server: ' + remote_file sftp.get(remote_file, local_file, None) files.append(local_file) # Feed list with local_file print 'Download done for file: ' + remote_file i += 1 else: print 'Local file already exist, we skip the upload for file: ' + remote_file print 'Download done => OK' # We have downloaded files, and network drive is accessible if i > 0 and os.path.exists(netbios_output_folder_path): print 'We have uploaded files, continue process => OK' print 'Network drive is accessible => OK' # Copy files to network drive for f in files: file_name = f.split('\\')[-1] if not os.path.exists(netbios_output_folder_path + '\\' + file_name): shutil.copy(f, netbios_output_folder_path) print 'Copy file to network drive: ' + file_name # Copy files from local to remote sftp_output_folder for f in files: file_name = f.split('\\')[-1] sftp.put(f, sftp_output_folder + '/' + file_name) print 'Upload done for file: ' + file_name # Delete files from remote sftp_input_folder for f in files: sftp.remove(sftp_input_folder + '/' + f.split('\\')[-1]) print 'Delete done for file: ' + file_name # Not used anymore, we close SFTP sftp.close() ssh.close() print 'Closing SSH, SFTP' # Copy done, we move file from input_folder to output_folder, last step for f in files: if not os.path.exists(local_output_folder + '\\' + f.split('\\')[-1]): shutil.move(f, local_output_folder) print 'Copy input_folder to output_folder => OK' # Post-process, we cleanup input_folder ToolsBox.rm_files(local_input_folder) print 'Cleanup input_folder post-process directory => OK' print 'Program ends => OK' except Exception: sys.stderr.write('ERROR: %s\n' % traceback.format_exc()) sys.exit() """End main process""" ...
Voici également mon fichier de configuration pour exemple (j’utilise la librairie: ConfigObj):
# Config file for Python
[file_section]
local_input_folder = IN
local_output_folder = OUT
netbios_output_folder_path = X:\votre_chemin_réseau\windows
[sftp_section]
sftp_host = 10.10.NN.NNN
sftp_username = votre_identifiant_sftp
sftp_password = votre_mot_de_passe_sftp
sftp_input_folder = /data/votre_chemin_vers_les_fichiers_xml
sftp_output_folder = /data/votre_chemin_vers_les_fichiers_xml_pour_le_stockage