ftp.py 4.57 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
import ftplib
import codecs
import re

import wimcollect.common.logger as logger


def ftp_connect(ftp_config: dict, log: logger.Logger, object_id: str) -> ftplib.FTP:
    """Connect to FTP server according to parameters read from settings.ini file.

    Parameters
    ----------
    ftp_config: dict
        Dict containing FTP connection information: `host`, `user` and `password`.
    log: wimcollect.logger
        Logger to record debug/info/error messages.
    object_id: str
        Object identifier, used as marker for log messages.

    Returns
    -------
    ftplib.FTP
        FTP session.
    """
    host = ftp_config["host"]
    user = ftp_config["user"]
    passwd = codecs.decode(ftp_config["password"], "rot_13")

    try:
        session = ftplib.FTP(host=host, user=user, passwd=passwd)
    except ftplib.error_perm as e:
        log.write(object_id, "Failed to connect to " + host + " as " + user + ": " + str(e))
        raise
    except BlockingIOError as e:
        log.write(object_id, "Host [" + host + "] seems to be unreachable.")
        raise
    else:
        log.write(object_id, "Connected to [" + host + "] as user: " + user)

    return session


43
def ftp_list_files(ftp_session: ftplib.FTP, distant_directory_path: str, log: logger.Logger, object_id: str) -> list:
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    """List the files in the distant FTP directory.

    Parameters
    ----------
    ftp_session: ftplib.FTP
        FTP session.
    distant_directory_path: str
        Full path of the directory whose content should be listed.
    log: wimcollect.logger
        Logger to record debug/info/error messages.
    object_id: str
        Object identifier, used as marker for log messages.

    Returns
    -------
    list
        List of files in the FTP directory given in parameter.
    """
    filepaths = []

    try:
        filepaths = ftp_session.nlst(distant_directory_path)
    except ftplib.error_perm as resp:
        if str(resp) == "550 No files found":
            log.write(object_id, "No files in directory " + distant_directory_path)
        else:
            raise

    return filepaths


75 76 77 78
def ftp_download_file(source_filepath: str, dest_filepath: str,
                      log: logger.Logger, object_id: str,
                      ftp_config: dict = None, ftp_session: ftplib.FTP = None,
                      delete_if_success: bool = False) -> bool:
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    """Download a file from the FTP server.

    Parameters
    ----------
    source_filepath: str
        Full path of the distant to-be_downloaded file.
    dest_filepath: str
        Full path where the downloaded file should be stored.
    log: wimcollect.logger
        Logger to record debug/info/error messages.
    object_id: str
        Object identifier, used as marker for log messages.
    ftp_config: dict, optional
        Dict containing FTP connection information: `host`, `user` and `password`. If not set, `ftp_session` must be
        provided.
    ftp_session: ftplib.FTP, optional
        FTP session. If not set, a session will be created using `ftp_config` parameters. Specifying a
        custom ftp_session is useful when multiple tasks should be performed before/after downloading a single file.
    delete_if_success: bool, optional
        If `True`, the distant FTP file will be deleted from the server, once the transfer is successfully done.

    Returns
    -------
    bool
        `True` if everything went well, otherwise raises Exception.
    """
    # Open FTP session if missing
    if ftp_session is None:
        close_session = True
        ftp_session = ftp_connect(ftp_config, log, object_id)
    else:
        close_session = False

    # Try to download file
    log.write(object_id, "Downloading " + source_filepath + " ...")
    with open(dest_filepath, 'wb') as file:
        try:
            response = ftp_session.retrbinary('RETR %s' % source_filepath, file.write)
        except (ftplib.error_perm, ftplib.error_temp) as e:
            response = str(e)

    # Analyse FTP's response and emit messages accordingly
    response_code = int(re.sub("[^0-9]", "", response))
    if response_code == 226:
        if delete_if_success:
            ftp_session.delete(source_filepath)
        log.write(object_id, "Done. Destination file: " + dest_filepath)
    else:
        log.write(object_id, "FAILED. Destination file: " + dest_filepath)
        raise Exception("Failed to download [" + source_filepath + "] in [" + dest_filepath + "]."
                        " FTP response: [" + response + "]")

    # Close the FTP session if it has been opened at the beginning of this function.
    if close_session:
        ftp_session.quit()

    return True