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))
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
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
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
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