Skip to content
juil 8 / David Regnier

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

Laisser un commentaire


× 4 = 16