Example #1
0
    def send_email(self, subject, body, to_address):
        """
        Send an email to the supplied body to the specified address, logging
        any errors that are hit.
        """
        with tempfile.NamedTemporaryFile() as tmpfile:
            tmpfile.write(body)
            tmpfile.seek(0)

            mail_cmd = ["mail",
                        "-s", "enbackup: " + subject,
                        to_address]
            (rc, output, err) = run_cmd_output(mail_cmd,
                                               self,
                                               cmd_stdin=tmpfile.fileno())

        if rc != 0:
            self.error("Failed to send email to {0}, return code {1}:\n"
                       "{1}\n{2}".format(to_address, rc, output, err))
Example #2
0
def get_backup_args(pid):
    """
    Get the arguments that an instance of 'enbackup backup' was invoked with.

    :return:
        A string containing the process arguments (or an error message,
        if attempting to get the arguments fails).

    :param pid:
        The PID of the process to search for.
    """
    cmd = ["ps", "-p", str(pid), "-o", "args", "h"]
    rc, output, err = run_cmd_output(cmd)
    if rc == 0:
        # The rc file should be the first argument after the command
        (_, all_args) = output.split("enbackup backup")
        args = all_args.strip().split()[0]
    else:
        args = "<Cannot find args for PID {}".format(pid)
    return args
Example #3
0
def get_matching_pids(pattern):
    """
    Get a list of Process IDs for processes matching some pattern.

    :return:
        A list of numerical PIDs

    :param pattern:
        The pattern to search for.  The search includes arguments for running
        processes, as well as process names.
    """
    cmd = ["pgrep", "-f", pattern]
    rc, output, err = run_cmd_output(cmd)
    if rc == 0:
        # One or more processes matched
        pids = [int(p) for p in output.split('\n') if p != ""]
    elif rc == 1:
        # No processes matched
        pids = []
    else:
        raise UserVisibleError("Failed to run {}".format(" ".join(cmd)))
    return pids
Example #4
0
def read_debug_msgs(debug_file=None,
                    whole_file=False):
    """
    Read a debug log, and return a list of `DebugStmt`s.

    The default behaviour is to only read the most recent portion of the
    file (the last `DEFAULT_DEBUG_ENTRIES_TO_SEARCH` lines) for speed,
    but if the `whole_file` flag is set then the full contents will be read.

    :param debug_file:
        Optional name of the debug file to read.  If not set, `DEBUG_LOG` is
        used.

    :param whole_file:
        Optional flag controlling whether to read the full file or not.
    """
    if debug_file is None:
        debug_file = DEBUG_LOG

    if whole_file:
        with open(debug_file, 'r') as f:
            debug_msgs = debug_msgs_from_lines(f)
    else:
        # Only read the last section of the file
        rc, output, err = run_cmd_output(["tail",
                                          "-n",
                                          str(DEFAULT_DEBUG_ENTRIES_TO_SEARCH),
                                          debug_file])
        if rc != 0:
            raise DebugFileAccessError("Failed to read {}: {}".format(
                                            debug_file, err))

        debug_lines = output.split('\n')
        debug_msgs = debug_msgs_from_lines(debug_lines)

    return debug_msgs
Example #5
0
def mirror_local(src, tgt_dir):
    #
    # Start recording details of this operation to a logfile:
    #
    logger = start_logger()

    try:
        config = enbackup.utils.DefaultConfigParser(
                        { "Paths" :
                            { "tgt_dir_restrict": tgt_dir_restrict_default },
                          "Logging" :
                            { "log_email": email_default,
                              "log_filter": "" }})
        config.read(cfg_file_path)
        tgt_dir_restrict = config.get("Paths", "tgt_dir_restrict")
        email_to = config.get("Logging", "log_email")
        log_filter_file = config.get("Logging", "log_filter")

        #
        # Check the target directory is valid on this server.
        #
        (path_is_valid, msg) = validate_local_dir(tgt_dir, tgt_dir_restrict)

        if not path_is_valid:
            logger.log(msg)
            raise MirrorCmdError(tgt_dir, msg)

        #
        # Get the source directory.  This is selected from the config file,
        # based on the incoming argument, so we trust it's a sane value:
        #
        try:
            src_server = config.get(src, "src_server")
            src_dir    = config.get(src, "src_dir")
        except ConfigParser.NoSectionError:
            logger.log("No source entry found in %s matching keyword '%s'" %
                       (cfg_file_path, src))
            raise

        #
        # Work out which directory we're going to be writing to so we can
        # lock it.  The target directory depends on the format of the
        # source directory passed to rsync:
        # - If the source ends in a '/' then the contents are mirrored;
        #   the target is just the target dir as passed by the caller.
        # - If the source does not end in a '/' then everything will
        #   be copied into a subdirectory -- we should append the source
        #   directory name to the passed target directory to get the
        #   path to lock.
        #
        # This behavior falls out nicely if we just use the 'basename'
        # function to extract the last element of the source directory:
        #
        lock_dir = os.path.join(tgt_dir, os.path.basename(src_dir))

        #
        # If all is well, spawn rsync to mirror the data across.
        # Any failures will result in an exception which will
        # get propagated up to the caller.  If currently running as root,
        # make sure we drop down to the enbackup user account for actually
        # running the rsync (we don't want to open any remote connections
        # as root).
        #
        if src_server != "":
            src = "{0}:{1}".format(src_server, src_dir)
        else:
            src = src_dir

        rsync_cmd_str = mirror_cmd_local + "{0} {1}".format(src, tgt_dir)

        if os.geteuid() == 0:
            rsync_cmd = ["su", get_username(), "--command", rsync_cmd_str]
        else:
            rsync_cmd = rsync_cmd_str.split()

        logger.debug("Local mirror command: {0}".format(rsync_cmd))

        logger.debug("About to lock '{0}' for local mirror".format(lock_dir))
        start_time = datetime.datetime.now()
        with DirLock("mirror-local (PID: {0})".format(os.getpid()),
                     lock_dir):
            (rc, output, err) = run_cmd_output(rsync_cmd, logger)
        logger.debug("Local mirror done")
        duration = datetime.datetime.now() - start_time

        #
        # Filter the results of the rsync to remove noise, then record them
        # in a log file, and send an email with the details.
        #
        if log_filter_file != "":
            log_filter = StringFilter(log_filter_file)
            filtered_output = log_filter.filter(output.split("\n"), True)
            filtered_output = "\n".join(filtered_output)
        else:
            filtered_output = output

        if rc == 0:
            subject = "Mirror {0} completed".format(src)
            result_msg =                                                          \
                "Mirroring has completed successfully!\n"                         \
                "    from: {0}\n"                                                 \
                "    to:   {1}\n"                                                 \
                "Total elapsed time: {2}\n\n"                                     \
                "############ rsync summary log ############\n\n"                 \
                "{3}".format(src, tgt_dir, duration, filtered_output)
        else:
            subject = "Mirror {0} FAILED".format(src)
            result_msg =                                                          \
                "Mirroring of {0} to {1} FAILED (code {2}), please investigate."  \
                "\n\n{3}\n\n{4}".format(src, tgt_dir, rc, filtered_output, err)

        logger.log(result_msg)
        logger.send_email(subject, result_msg, email_to)
    except Exception as e:
        logger.error("Failed local mirror: {}".format(e))
        raise