From bfb14d0b7fbb3dd027699367dc75f5af74a9fac3 Mon Sep 17 00:00:00 2001 From: liangxin1300 Date: Mon, 1 Nov 2021 08:29:15 +0800 Subject: [PATCH 1/5] Dev: crm_report: Move hb_report directory to crmsh/report --- Makefile.am | 5 --- configure.ac | 1 - crmsh/config.py | 5 +-- crmsh/history.py | 19 ++++------- {hb_report => crmsh/report}/__init__.py | 0 {hb_report => crmsh/report}/collect.py | 0 {hb_report => crmsh/report}/constants.py | 0 .../hb_report.in => crmsh/report/core.py | 4 +-- {hb_report => crmsh/report}/utillib.py | 8 ++--- crmsh/ui_report.py | 32 ------------------- crmsh/ui_root.py | 7 ++-- etc/crm.conf.in | 6 ++-- setup.py | 4 +-- 13 files changed, 25 insertions(+), 66 deletions(-) rename {hb_report => crmsh/report}/__init__.py (100%) rename {hb_report => crmsh/report}/collect.py (100%) rename {hb_report => crmsh/report}/constants.py (100%) rename hb_report/hb_report.in => crmsh/report/core.py (99%) mode change 100755 => 100644 rename {hb_report => crmsh/report}/utillib.py (99%) delete mode 100644 crmsh/ui_report.py diff --git a/Makefile.am b/Makefile.am index 063053ce6b..c9debab417 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,11 +55,6 @@ install-data-hook: mv $(DESTDIR)$(datadir)/@PACKAGE@/test $(DESTDIR)$(datadir)/@PACKAGE@/tests; \ cp test/testcases/xmlonly.sh $(DESTDIR)$(datadir)/@PACKAGE@/tests/testcases/configbasic-xml.filter -hanoarchdir = $(datadir)/@PACKAGE@/hb_report -hanoarch_DATA = hb_report/constants.py hb_report/utillib.py hb_report/collect.py -hanoarch_SCRIPTS = hb_report/hb_report -EXTRA_DIST = $(hanoarch_DATA) - # Python module installation all-local: (cd $(srcdir); $(PYTHON) setup.py build \ diff --git a/configure.ac b/configure.ac index da57bf1e2f..59bd44ee47 100644 --- a/configure.ac +++ b/configure.ac @@ -54,7 +54,6 @@ AC_PATH_PROGS(ASCIIDOC, asciidoc) AM_CONDITIONAL(BUILD_ASCIIDOC, test x"${ASCIIDOC}" != x"") AC_CONFIG_FILES(Makefile \ -hb_report/hb_report \ etc/crm.conf \ version \ crmsh.spec \ diff --git a/crmsh/config.py b/crmsh/config.py index 60d7911463..50da5ae246 100644 --- a/crmsh/config.py +++ b/crmsh/config.py @@ -294,7 +294,8 @@ def get(self, value): 'collect_extra_logs': opt_string('/var/log/messages /var/log/pacemaker/pacemaker.log /var/log/pacemaker.log /var/log/crmsh/crmsh.log'), 'remove_exist_dest': opt_boolean('no'), 'single_node': opt_boolean('no'), - 'sanitize_rule': opt_string('passw.*') + 'sanitize_rule': opt_string('passw.*'), + 'verbosity': opt_string('0') } } @@ -370,7 +371,7 @@ def get(self, section, name, raw=False): return DEFAULTS[section][name].get(self.get_impl(section, name)) def set(self, section, name, value): - if section not in ('core', 'path', 'color'): + if section not in ('core', 'path', 'color', 'report'): raise ValueError("Setting invalid section " + str(section)) if not self._defaults.has_option(section, name): raise ValueError("Setting invalid option %s.%s" % (section, name)) diff --git a/crmsh/history.py b/crmsh/history.py index 0ecec8d06c..74817be1d2 100644 --- a/crmsh/history.py +++ b/crmsh/history.py @@ -24,9 +24,9 @@ # -# hb_report interface +# crm report interface # -# read hb_report generated report, show interesting stuff, search +# read crm report generated report, show interesting stuff, search # through logs, get PE input files, get log slices (perhaps even # coloured nicely!) # @@ -122,9 +122,9 @@ def mkarchive(idir): class Report(object): ''' - A hb_report class. + A crm report class. ''' - live_recent = 6*60*60 # recreate live hb_report once every 6 hours + live_recent = 6*60*60 # recreate live crm report once every 6 hours short_live_recent = 60 # update once a minute nodecolors = ("NORMAL", "GREEN", @@ -191,7 +191,7 @@ def session_list(self): def unpack_report(self, tarball): ''' - Unpack hb_report tarball. + Unpack crm report tarball. Don't unpack if the directory already exists! ''' bfname = os.path.basename(tarball) @@ -458,12 +458,7 @@ def new_live_report(self): ''' Run the report command to get logs now. ''' - from . import ui_report - - extcmd = ui_report.report_tool() - if extcmd is None: - self.error("No reporting tool found") - return None + extcmd = "crm report" d = self._live_loc() if not utils.is_path_sane(d): @@ -559,7 +554,7 @@ def set_node_colors(self): def _report_setup_source(self): constants.pcmk_version = None - # is this an hb_report or a crm_report? + # is this an crm report or a crm_report? for descname in ("description.txt", "report.summary"): self.desc = os.path.join(self.loc, descname) if os.path.isfile(self.desc): diff --git a/hb_report/__init__.py b/crmsh/report/__init__.py similarity index 100% rename from hb_report/__init__.py rename to crmsh/report/__init__.py diff --git a/hb_report/collect.py b/crmsh/report/collect.py similarity index 100% rename from hb_report/collect.py rename to crmsh/report/collect.py diff --git a/hb_report/constants.py b/crmsh/report/constants.py similarity index 100% rename from hb_report/constants.py rename to crmsh/report/constants.py diff --git a/hb_report/hb_report.in b/crmsh/report/core.py old mode 100755 new mode 100644 similarity index 99% rename from hb_report/hb_report.in rename to crmsh/report/core.py index 25f5733724..c68d0eca17 --- a/hb_report/hb_report.in +++ b/crmsh/report/core.py @@ -99,7 +99,7 @@ def get_log(): def is_collector(): """ - the instance where user runs hb_report is the master + the instance where user runs crm report is the master the others are slaves """ if len(sys.argv) > 1 and sys.argv[1] == "__slave": @@ -142,7 +142,7 @@ def parse_argument(argv): if len(arg) == 0: constants.DESTDIR = "." - constants.DEST = "hb_report-%s" % datetime.datetime.now().strftime('%a-%d-%b-%Y') + constants.DEST = "crm_report-%s" % datetime.datetime.now().strftime('%a-%d-%b-%Y') elif len(arg) == 1: constants.TMP = arg[0] else: diff --git a/hb_report/utillib.py b/crmsh/report/utillib.py similarity index 99% rename from hb_report/utillib.py rename to crmsh/report/utillib.py index 4dc3166794..d0381ca5d7 100644 --- a/hb_report/utillib.py +++ b/crmsh/report/utillib.py @@ -1107,7 +1107,7 @@ def log_warning(msg): def make_temp_dir(): - dir_path = r"/tmp/.hb_report.workdir.%s" % random_string(6) + dir_path = r"/tmp/.crm_report.workdir.%s" % random_string(6) _mkdir(dir_path) return dir_path @@ -1362,12 +1362,12 @@ def stdchannel_redirected(stdchannel, dest_filename): def start_slave_collector(node, arg_str): if node == constants.WE: - cmd = r"/usr/sbin/hb_report __slave".format(os.getcwd()) + cmd = r"crm report __slave".format(os.getcwd()) for item in arg_str.split(): cmd += " {}".format(str(item)) _, out = crmutils.get_stdout(cmd) else: - cmd = r'ssh {} {} "/usr/sbin/hb_report __slave"'.format(constants.SSH_OPTS, node, os.getcwd()) + cmd = r'ssh {} {} "crm report __slave"'.format(constants.SSH_OPTS, node, os.getcwd()) for item in arg_str.split(): cmd += " {}".format(str(item)) code, out, err = crmutils.get_stdout_stderr(cmd) @@ -1384,7 +1384,7 @@ def start_slave_collector(node, arg_str): compress_data = "" for data in out.split('\n'): if data.startswith(constants.COMPRESS_DATA_FLAG): - # hb_report data from collector + # crm report data from collector compress_data = data.lstrip(constants.COMPRESS_DATA_FLAG) else: # log data from collector diff --git a/crmsh/ui_report.py b/crmsh/ui_report.py deleted file mode 100644 index c59b22b192..0000000000 --- a/crmsh/ui_report.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2008-2011 Dejan Muhamedagic -# Copyright (C) 2013 Kristoffer Gronlund -# See COPYING for license information. - -import os -import subprocess -from signal import signal, SIGPIPE, SIG_DFL - -from . import utils -from . import config -from . import options - - -def report_tool(): - toolopts = [os.path.join(config.path.sharedir, 'hb_report', 'hb_report'), - 'hb_report', - 'crm_report'] - for tool in toolopts: - if utils.is_program(tool): - return tool - return None - - -def create_report(context, args): - extcmd = report_tool() - if not extcmd: - context.fatal_error("No reporting tool found") - extraopts = str(config.core.report_tool_options).strip().split() - cmd = [extcmd] + extraopts + list(args) - if options.regression_tests: - print(".EXT", cmd) - return subprocess.call(cmd, shell=False, preexec_fn=lambda: signal(SIGPIPE, SIG_DFL)) diff --git a/crmsh/ui_root.py b/crmsh/ui_root.py index 2b0ade7d43..12d0f2e1ff 100644 --- a/crmsh/ui_root.py +++ b/crmsh/ui_root.py @@ -30,7 +30,6 @@ from . import ui_node from . import ui_options from . import ui_ra -from . import ui_report from . import ui_resource from . import ui_script from . import ui_site @@ -137,8 +136,10 @@ def do_ra(self): crmsh over the given period of time. ''') def do_report(self, context, *args): - rc = ui_report.create_report(context, args) - return rc == 0 + import sys + from crmsh.report import core + sys.argv[1:] = args + core.run() @command.level(ui_resource.RscMgmt) @command.help('''resources management diff --git a/etc/crm.conf.in b/etc/crm.conf.in index 9833a1ad80..2d4423e094 100644 --- a/etc/crm.conf.in +++ b/etc/crm.conf.in @@ -79,7 +79,7 @@ ocf_root = @OCF_ROOT_DIR@ ; ; sanitize_rule = sanitize_pattern[:options] ... ; -; This defines the way to hide sensitive data generated by hb_report. +; This defines the way to hide sensitive data generated by crm report. ; ; 'sanitize_pattern' is a RegEx string, which is used to matches 'name' ; field of CIB params. The sanitize process will hide 'value' of those @@ -88,13 +88,13 @@ ocf_root = @OCF_ROOT_DIR@ ; 'options' is the predefined, and 'raw' is the only one defined ; currently. With ':raw" option, the sanitize process will fetch ; 'value' results out of CIB 'name:value' pairs, and use them to -; hide all clear text occurence from all files hb_report collected. +; hide all clear text occurence from all files crm report collected. ; ; Example 1: ; sanitize_rule = passw.* ; ; This is the default. It will hide password nam:value pairs. -; The result of hb_report clould be like +; The result of crm report clould be like ; name="password", value=****** ; @name=password @value=****** ; passwd=****** diff --git a/setup.py b/setup.py index b8d043b074..850480335d 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ from setuptools import setup setup(name='crmsh', - version='4.3.0', + version='4.3.1', description='Command-line interface for High-Availability cluster management', author='Kristoffer Gronlund, Xin Liang', author_email='XLiang@suse.com', url='http://crmsh.github.io/', - packages=['crmsh', 'crmsh.crash_test'], + packages=['crmsh', 'crmsh.crash_test', 'crmsh.report'], install_requires=['parallax', 'lxml', 'PyYAML', 'py-dateutil'], scripts=['bin/crm'], data_files=[('/usr/share/crmsh', ['doc/crm.8.adoc'])], From 6e8e1eeeed24f134e2d2b2cdbc6582ff1ef1967e Mon Sep 17 00:00:00 2001 From: liangxin1300 Date: Mon, 1 Nov 2021 08:34:14 +0800 Subject: [PATCH 2/5] Dev: crm_report: Integrate report log into crmsh logging --- crmsh/config.py | 3 +- crmsh/log.py | 54 +++++++++++++++--- crmsh/report/collect.py | 21 +++---- crmsh/report/core.py | 30 +++++----- crmsh/report/utillib.py | 122 +++++++++++++++++----------------------- 5 files changed, 128 insertions(+), 102 deletions(-) diff --git a/crmsh/config.py b/crmsh/config.py index 50da5ae246..1722f0d384 100644 --- a/crmsh/config.py +++ b/crmsh/config.py @@ -291,7 +291,8 @@ def get(self, value): 'from_time': opt_string('-12H'), 'compress': opt_boolean('yes'), 'speed_up': opt_boolean('no'), - 'collect_extra_logs': opt_string('/var/log/messages /var/log/pacemaker/pacemaker.log /var/log/pacemaker.log /var/log/crmsh/crmsh.log'), + 'collect_extra_logs': opt_string('/var/log/messages /var/log/pacemaker/pacemaker.log \ + /var/log/pacemaker.log /var/log/crmsh/crmsh.log /etc/crm/profiles.yml /etc/crm/crm.conf'), 'remove_exist_dest': opt_boolean('no'), 'single_node': opt_boolean('no'), 'sanitize_rule': opt_string('passw.*'), diff --git a/crmsh/log.py b/crmsh/log.py index 7994472bdb..f245387079 100644 --- a/crmsh/log.py +++ b/crmsh/log.py @@ -47,9 +47,11 @@ class ConsoleCustomFormatter(logging.Formatter): FORMAT = "%(levelname)s: %(message)s" FORMAT_RAW = "%(message)s" - def __init__(self, lineno=-1, raw_msg=False): + def __init__(self, lineno=-1, raw_msg=False, fmt=None): self.lineno = lineno - if raw_msg: + if fmt: + super().__init__(fmt=fmt) + elif raw_msg: super().__init__(fmt=self.FORMAT_RAW) else: super().__init__(fmt=self.FORMAT) @@ -67,6 +69,16 @@ def format(self, record): return super().format(record) +class ConsoleReportFormatter(ConsoleCustomFormatter): + """ + Custom formatter for crm report + """ + FORMAT_REPORT = "{}: %(levelname)s: %(message)s".format(socket.gethostname()) + + def __init__(self): + super().__init__(fmt=self.FORMAT_REPORT) + + class FileCustomFormatter(logging.Formatter): """ A custom formatter for file @@ -83,9 +95,11 @@ class DebugCustomFilter(logging.Filter): A custom filter for debug message """ def filter(self, record): + from .config import core, report if record.levelname == "DEBUG": - from .config import core - return core.debug + return core.debug or int(report.verbosity) >= 1 + if record.levelname == "DEBUG2": + return int(report.verbosity) > 1 else: return True @@ -94,6 +108,9 @@ def filter(self, record): "version": 1, "disable_existing_loggers": "False", "formatters": { + "console_report": { + "()": ConsoleReportFormatter + }, "console": { "()": ConsoleCustomFormatter }, @@ -110,6 +127,11 @@ def filter(self, record): 'null': { 'class': 'logging.NullHandler' }, + "console_report": { + "()": ConsoleCustomHandler, + "formatter": "console_report", + "filters": ["filter"] + }, "console": { "()": ConsoleCustomHandler, "formatter": "console", @@ -139,8 +161,8 @@ def filter(self, record): "propagate": False, "level": "DEBUG" }, - "hb_report": { - "handlers": ["null", "file", "console"], + "crmsh.report": { + "handlers": ["null", "file", "console_report"], "propagate": False, "level": "DEBUG" } @@ -163,6 +185,14 @@ def __init__(self, logger): self.lineno = 0 self.__save_lineno = 0 + def set_debug2_level(self): + """ + Create DEBUG2 level for verbosity + """ + logging.DEBUG2 = logging.DEBUG + 5 + logging.addLevelName(logging.DEBUG2, "DEBUG2") + self.logger.debug2 = lambda msg, *args: self.logger._log(logging.DEBUG2, msg, args) + def get_handler(self, _type): """ Get logger specific handler @@ -200,7 +230,7 @@ def incr_lineno(self): """ self.lineno += 1 self.set_console_formatter(self.lineno) - + @contextmanager def suppress_new_line(self): """ @@ -421,3 +451,13 @@ def setup_logger(name): logger.handlers = logger.parent.handlers logger.propagate = False return logger + + +def setup_report_logger(name): + """ + Get the logger for crm report + """ + logger = setup_logger(name) + logger_utils = LoggerUtils(logger) + logger_utils.set_debug2_level() + return logger diff --git a/crmsh/report/collect.py b/crmsh/report/collect.py index c171debf05..e363785189 100644 --- a/crmsh/report/collect.py +++ b/crmsh/report/collect.py @@ -10,11 +10,12 @@ import pwd import datetime -sys.path.append(os.path.dirname(os.path.realpath(__file__))) - -import constants -import utillib +from crmsh import log from crmsh import utils as crmutils +from crmsh.report import constants, utillib + + +logger = log.setup_report_logger(__name__) def collect_ocfs2_info(): @@ -52,7 +53,7 @@ def collect_ratraces(): trace_dir = os.path.join(constants.HA_VARLIB, "trace_ra") if not os.path.isdir(trace_dir): return - utillib.log_debug("looking for RA trace files in %s" % trace_dir) + logger.debug("looking for RA trace files in %s", trace_dir) flist = [] for f in utillib.find_files(trace_dir, constants.FROM_TIME, constants.TO_TIME): dest_dir = os.path.join(constants.WORKDIR, '/'.join(f.split('/')[-3:-1])) @@ -140,7 +141,7 @@ def collect_backtraces(): flist = [f for f in cores if "core" in os.path.basename(f)] if flist: utillib.print_core_backtraces(flist) - utillib.log_debug("found backtraces: %s" % ' '.join(flist)) + logger.debug("found backtraces: %s", ' '.join(flist)) def collect_config(): @@ -178,7 +179,7 @@ def collect_pe_inputs(): to_time = constants.TO_TIME work_dir = constants.WORKDIR pe_dir = constants.PE_STATE_DIR - utillib.log_debug("looking for PE files in %s in %s" % (pe_dir, constants.WE)) + logger.debug("looking for PE files in %s in %s", pe_dir, constants.WE) flist = [] for f in utillib.find_files(pe_dir, from_time, to_time): @@ -191,16 +192,16 @@ def collect_pe_inputs(): utillib._mkdir(flist_dir) for f in flist: os.symlink(f, os.path.join(flist_dir, os.path.basename(f))) - utillib.log_debug("found %d pengine input files in %s" % (len(flist), pe_dir)) + logger.debug("found %d pengine input files in %s", len(flist), pe_dir) if len(flist) <= 20: if not constants.SKIP_LVL: for f in flist: utillib.pe_to_dot(os.path.join(flist_dir, os.path.basename(f))) else: - utillib.log_debug("too many PE inputs to create dot files") + logger.debug("too many PE inputs to create dot files") else: - utillib.log_debug("Nothing found for the giving time") + logger.debug("Nothing found for the giving time") def collect_sbd_info(): diff --git a/crmsh/report/core.py b/crmsh/report/core.py index c68d0eca17..83810561d1 100644 --- a/crmsh/report/core.py +++ b/crmsh/report/core.py @@ -10,11 +10,13 @@ import datetime import shutil -sys.path.append(os.path.dirname(os.path.realpath(__file__))) -import constants -import utillib from crmsh import utils as crmutils -from crmsh import config +from crmsh import config, log +from crmsh.report import constants, utillib + + +logger = log.setup_report_logger(__name__) + def collect_for_nodes(nodes, arg_str): """ @@ -23,8 +25,8 @@ def collect_for_nodes(nodes, arg_str): process_list = [] for node in nodes.split(): if utillib.node_needs_pwd(node): - utillib.log_info("Please provide password for %s at %s" % (utillib.say_ssh_user(), node)) - utillib.log_info("Note that collecting data will take a while.") + logger.info("Please provide password for %s at %s", utillib.say_ssh_user(), node) + logger.info("Note that collecting data will take a while.") utillib.start_slave_collector(node, arg_str) else: p = multiprocessing.Process(target=utillib.start_slave_collector, args=(node, arg_str)) @@ -71,7 +73,7 @@ def get_log(): if constants.HA_LOG and not os.path.isfile(constants.HA_LOG): if not is_collector(): # warning if not on slave - utillib.log_warning("%s not found; we will try to find log ourselves" % constants.HA_LOG) + logger.warning("%s not found; we will try to find log ourselves", constants.HA_LOG) constants.HA_LOG = "" if not constants.HA_LOG: constants.HA_LOG = utillib.find_log() @@ -79,7 +81,7 @@ def get_log(): if constants.CTS: pass # TODO else: - utillib.log_warning("not log at %s" % constants.WE) + logger.warning("not log at %s", constants.WE) return if constants.CTS: @@ -94,7 +96,7 @@ def get_log(): if utillib.dump_logset(constants.HA_LOG, constants.FROM_TIME, constants.TO_TIME, outf): utillib.log_size(constants.HA_LOG, outf+'.info') else: - utillib.log_warning("could not figure out the log format of %s" % constants.HA_LOG) + logger.warning("could not figure out the log format of %s", constants.HA_LOG) def is_collector(): @@ -132,6 +134,7 @@ def load_env(env_str): constants.EXTRA_LOGS = env_dict["EXTRA_LOGS"] constants.PCMK_LOG = env_dict["PCMK_LOG"] constants.VERBOSITY = int(env_dict["VERBOSITY"]) + config.report.verbosity = constants.VERBOSITY def parse_argument(argv): @@ -189,6 +192,7 @@ def parse_argument(argv): constants.EXTRA_LOGS += " %s" % option if args == "-v": constants.VERBOSITY += 1 + config.report.verbosity = constants.VERBOSITY if args == '-d': constants.COMPRESS = False @@ -246,7 +250,7 @@ def run(): if not is_collector(): constants.NODES = ' '.join(utillib.get_nodes()) - utillib.log_debug("nodes: %s" % constants.NODES) + logger.debug("nodes: %s", constants.NODES) if constants.NODES == "": utillib.log_fatal("could not figure out a list of nodes; is this a cluster node?") if constants.WE in constants.NODES.split(): @@ -254,7 +258,7 @@ def run(): if not is_collector(): if constants.THIS_IS_NODE != 1: - utillib.log_warning("this is not a node and you didn't specify a list of nodes using -n") + logger.warning("this is not a node and you didn't specify a list of nodes using -n") # # ssh business # @@ -267,10 +271,10 @@ def run(): # assume that only root can collect data if ((not constants.SSH_USER) and (os.getuid() != 0)) or \ constants.SSH_USER and constants.SSH_USER != "root": - utillib.log_debug("ssh user other than root, use sudo") + logger.debug("ssh user other than root, use sudo") constants.SUDO = "sudo -u root" if os.getuid() != 0: - utillib.log_debug("local user other than root, use sudo") + logger.debug("local user other than root, use sudo") constants.LOCAL_SUDO = "sudo -u root" # diff --git a/crmsh/report/utillib.py b/crmsh/report/utillib.py index d0381ca5d7..d6197c876a 100644 --- a/crmsh/report/utillib.py +++ b/crmsh/report/utillib.py @@ -21,30 +21,25 @@ from threading import Timer from inspect import getmembers, isfunction -sys.path.append(os.path.dirname(os.path.realpath(__file__))) -import constants -import collect import crmsh.config from crmsh import utils as crmutils -from crmsh import corosync -from crmsh import log -import logging +from crmsh import corosync, log +from crmsh.report import constants, collect -log.setup_logging() -logger = logging.getLogger("hb_report") +logger = log.setup_report_logger(__name__) class Tempfile(object): def __init__(self): self.file = create_tempfile() - log_debug("create tempfile \"{}\"".format(self.file)) + logger.debug("create tempfile \"%s\"", self.file) def add(self, filename): with open(self.file, 'a') as f: f.write(filename + '\n') - log_debug("add tempfile \"{}\" to \"{}\"".format(filename, self.file)) + logger.debug("add tempfile \"%s\" to \"%s\"", filename, self.file) def drop(self): with open(self.file, 'r') as f: @@ -54,7 +49,7 @@ def drop(self): if os.path.isfile(line): os.remove(line) os.remove(self.file) - log_debug("remove tempfile \"{}\"".format(self.file)) + logger.debug("remove tempfile \"%s\"", self.file) def add_tempfiles(filename): @@ -93,12 +88,12 @@ def arch_logs(logf, from_time, to_time): continue elif res == 1: # include log and continue ret.append(f) - log_debug("found log %s" % f) + logger.debug("found log %s", f) elif res == 2: # don't go through older logs! break elif res == 3: # include log and continue ret.append(f) - log_debug("found log %s" % f) + logger.debug("found log %s", f) break return ret @@ -196,7 +191,7 @@ def check_env(): def check_if_log_is_empty(): for f in find_files_all(constants.HALOG_F, constants.WORKDIR): if os.stat(f).st_size == 0: - log_warning("Report contains no logs; did you get the right timeframe?") + logger.warning("Report contains no logs; did you get the right timeframe?") def check_logs(workdir): @@ -253,7 +248,7 @@ def cib_diff(file1, file2): out_string += tmp_string else: code = 1 - log_warning("crm_diff(8) not found, cannot diff CIBs") + logger.warning("crm_diff(8) not found, cannot diff CIBs") else: code = 1 out_string += "can't compare cibs from running and stopped systems\n" @@ -309,7 +304,7 @@ def collect_info(): os.symlink(constants.HALOG_F, os.path.join(constants.WORKDIR, os.path.basename(l))) continue if is_our_log(l, constants.FROM_TIME, constants.TO_TIME) == 4: - log_debug("found irregular log file %s" % l) + logger.debug("found irregular log file %s", l) outf = os.path.join(constants.WORKDIR, os.path.basename(l)) shutil.copy2(l, constants.WORKDIR) log_size(l, outf+'.info') @@ -321,12 +316,12 @@ def collect_info(): if dump_logset(l, constants.FROM_TIME, constants.TO_TIME, outf): log_size(l, outf+'.info') else: - log_warning("could not figure out the log format of %s" % l) + logger.warning("could not figure out the log format of %s", l) def collect_journal(from_t, to_t, outf): if not which("journalctl"): - log_warning("Command journalctl not found") + logger.warning("Command journalctl not found") return if crmutils.is_int(from_t) and from_t == 0: @@ -338,10 +333,10 @@ def collect_journal(from_t, to_t, outf): elif crmutils.is_int(to_t): to_time = ts_to_dt(to_t).strftime("%Y-%m-%d %H:%M") if os.path.isfile(outf): - log_warning("%s already exists" % outf) + logger.warning("%s already exists", outf) - log_debug("journalctl from: '%d' until: '%d' from_time: '%s' to_time: '%s' > %s" % - (from_t, to_t, from_time, to_time, outf)) + logger.debug("journalctl from: '%d' until: '%d' from_time: '%s' to_time: '%s' > %s", + from_t, to_t, from_time, to_time, outf) cmd = 'journalctl -o short-iso --since "%s" --until "%s" --no-pager | tail -n +2' % \ (from_time, to_time) crmutils.str2file(get_command_info(cmd)[1], outf) @@ -359,7 +354,7 @@ def compatibility_pcmk(): log_fatal("cannot find cib daemon directory!") constants.PCMK_LIB = os.path.dirname(constants.CIB_DIR) - log_debug("setting PCMK_LIB to %s" % constants.PCMK_LIB) + logger.debug("setting PCMK_LIB to %s", constants.PCMK_LIB) constants.CORES_DIRS = os.path.join(constants.PCMK_LIB, "cores") constants.CONF = "/etc/corosync/corosync.conf" if os.path.isfile(constants.CONF): @@ -411,7 +406,7 @@ def distro(): """ ret = "" if which("lsb_release"): - log_debug("using lsb_release for distribution info") + logger.debug("using lsb_release for distribution info") res = get_command_info("lsb_release -d")[1] if re.search("Description:", res): ret = ' '.join(res.split()[1:]) @@ -448,7 +443,7 @@ def dump_logset(logf, from_time, to_time, outf): out_string += print_logseg(oldest, from_time, 0) for f in mid_logfiles: out_string += print_logseg(f, 0, 0) - log_debug("including complete %s logfile" % f) + logger.debug("including complete %s logfile", f) out_string += print_logseg(newest, 0, to_time) crmutils.str2file(out_string, outf) @@ -501,7 +496,7 @@ def find_files(dirs, from_time, to_time): res = [] if (not crmutils.is_int(from_time)) or (from_time <= 0): - log_warning("sorry, can't find files based on time if you don't supply time") + logger.warning("sorry, can't find files based on time if you don't supply time") return file_with_stamp = create_tempfile(from_time) @@ -551,17 +546,15 @@ def filter_lines(data, from_line, to_line): def finalword(): if constants.COMPRESS == 1: - log_info("The report is saved in %s/%s.tar%s" % (constants.DESTDIR, constants.DEST, constants.COMPRESS_EXT)) + logger.info("The report is saved in %s/%s.tar%s", constants.DESTDIR, constants.DEST, constants.COMPRESS_EXT) else: - log_info("The report is saved in %s/%s" % (constants.DESTDIR, constants.DEST)) + logger.info("The report is saved in %s/%s", constants.DESTDIR, constants.DEST) if constants.TO_TIME == 0: to_time = datetime.datetime.now().strftime("%x %X") else: to_time = ts_to_dt(constants.TO_TIME).strftime("%x %X") - log_info("Report timespan: %s - %s" % - (ts_to_dt(constants.FROM_TIME).strftime("%x %X"), - to_time)) - log_info("Thank you for taking time to create this report.") + logger.info("Report timespan: %s - %s", ts_to_dt(constants.FROM_TIME).strftime("%x %X"), to_time) + logger.info("Thank you for taking time to create this report.") def find_getstampproc(log_file): @@ -585,17 +578,17 @@ def find_getstampproc_raw(line): res = get_stamp_syslog(line) if res: func = "syslog" - log_debug("the log file is in the syslog format") + logger.debug("the log file is in the syslog format") return func res = get_stamp_rfc5424(line) if res: func = "rfc5424" - log_debug("the log file is in the rfc5424 format") + logger.debug("the log file is in the rfc5424 format") return func res = get_stamp_legacy(line) if res: func = "legacy" - log_debug("the log file is in the legacy format (please consider switching to syslog format)") + logger.debug("the log file is in the legacy format (please consider switching to syslog format)") return func return func @@ -619,7 +612,7 @@ def find_log(): return l if constants.HA_DEBUGFILE: - log_debug("will try with %s" % constants.HA_DEBUGFILE) + logger.debug("will try with %s", constants.HA_DEBUGFILE) return constants.HA_DEBUGFILE @@ -641,18 +634,18 @@ def find_ssh_user(): ssh_s = n if test_ssh_conn(ssh_s): - log_debug("ssh %s OK" % ssh_s) + logger.debug("ssh %s OK", ssh_s) ssh_user = u try_user_list = u rc = 0 break else: - log_debug("ssh %s failed" % ssh_s) + logger.debug("ssh %s failed", ssh_s) if rc == 1: constants.SSH_PASSWORD_NODES += " %s" % n if constants.SSH_PASSWORD_NODES: - log_warning("passwordless ssh to node(s) %s does not work" % constants.SSH_PASSWORD_NODES) + logger.warning("passwordless ssh to node(s) %s does not work", constants.SSH_PASSWORD_NODES) if ssh_user == "__undef": return if ssh_user != "__default": @@ -732,9 +725,9 @@ def isexec(filename): fname = m.group(1) binname = findbin(fname) if binname is not None: - log_debug("found the program at {} for core {}".format(testpath, corefile)) + logger.debug("found the program at %s for core %s", testpath, corefile) else: - log_warning("Could not find the program path for core {}".format(corefile)) + logger.warning("Could not find the program path for core %s", corefile) return binname @@ -744,7 +737,7 @@ def print_core_backtraces(flist): flist: names of core files to check """ if not which("gdb"): - log_warning("Please install gdb to get backtraces") + logger.warning("Please install gdb to get backtraces") return for corefile in flist: absbinpath = find_binary_for_core(corefile) @@ -839,8 +832,8 @@ def get_log_vars(): elif is_conf_set("to_syslog"): constants.HA_LOGFACILITY = get_conf_var("syslog_facility", default="daemon") - log_debug("log settings: facility=%s logfile=%s debugfile=%s" % - (constants.HA_LOGFACILITY, constants.HA_LOGFILE, constants.HA_DEBUGFILE)) + logger.debug("log settings: facility=%s logfile=%s debugfile=%s", + constants.HA_LOGFACILITY, constants.HA_LOGFILE, constants.HA_DEBUGFILE) def get_nodes(): @@ -906,7 +899,7 @@ def get_pkg_mgr(): elif which("pkginfo"): pkg_mgr = "pkginfo" else: - log_warning("Unknown package manager!") + logger.warning("Unknown package manager!") return pkg_mgr @@ -1046,7 +1039,7 @@ def is_our_log(logf, from_time, to_time): """ data = read_from_file(logf) if not data: - log_debug("Found empty file \"{}\"; exclude".format(logf)) + logger.debug("Found empty file \"%s\"; exclude", logf) return 0 first_time = find_first_ts(head(10, data)) last_time = find_first_ts(tail(10, data)) @@ -1082,15 +1075,6 @@ def load_ocf_dirs(): constants.HA_BIN = grep("HA_BIN:=", infile=inf)[0].split(':=')[1].strip('}') -def log_debug(msg): - if constants.VERBOSITY > 0 or crmsh.config.core.debug: - logger.info(msg) - - -def log_info(msg): - logger.info(msg) - - def log_fatal(msg): logger.error(msg) sys.exit(1) @@ -1102,10 +1086,6 @@ def log_size(logf, outf): crmutils.str2file(out_string, outf) -def log_warning(msg): - logger.warning(msg) - - def make_temp_dir(): dir_path = r"/tmp/.crm_report.workdir.%s" % random_string(6) _mkdir(dir_path) @@ -1145,7 +1125,7 @@ def pe_to_dot(pe_file): cmd = "%s -D %s -x %s" % (constants.PTEST, dotf, pe_file) code, _ = crmutils.get_stdout(cmd) if code != 0: - log_warning("pe_to_dot: %s -> %s failed" % (pe_file, dotf)) + logger.warning("pe_to_dot: %s -> %s failed", pe_file, dotf) def pick_compress(): @@ -1158,7 +1138,7 @@ def pick_compress(): else: constants.COMPRESS_EXT = ".gz" else: - log_warning("could not find a compression program; \ + logger.warning("could not find a compression program; \ the resulting tarball may be huge") constants.COMPRESS_PROG = "cat" @@ -1208,7 +1188,7 @@ def pkg_versions(packages): pkg_mgr = get_pkg_mgr() if not pkg_mgr: return "" - log_debug("the package manager is %s" % pkg_mgr) + logger.debug("the package manager is %s", pkg_mgr) if pkg_mgr == "deb": return pkg_ver_deb(packages) if pkg_mgr == "rpm": @@ -1246,7 +1226,7 @@ def print_logseg(logf, from_time, to_time): if to_line is None: return "" - log_debug("Including segment [{}-{}] from {}".format(from_line, to_line, logf)) + logger.debug("Including segment [%d-%d] from %s", from_line, to_line, logf) return filter_lines(data, from_line, to_line) @@ -1271,7 +1251,7 @@ def sanitize(): """ replace sensitive info with '****' """ - log_debug("Check or replace sensitive info from cib, pe and log files") + logger.debug("Check or replace sensitive info from cib, pe and log files") get_sensitive_key_value_list() @@ -1284,8 +1264,8 @@ def sanitize(): for f in [item for item in file_list if os.path.isfile(item)]: rc = sanitize_one(f) if rc == 1: - log_warning("Some PE/CIB/log files contain possibly sensitive data") - log_warning("Using \"-s\" option can replace sensitive data") + logger.warning("Some PE/CIB/log files contain possibly sensitive data") + logger.warning("Using \"-s\" option can replace sensitive data") break @@ -1300,7 +1280,7 @@ def sanitize_one(in_file): return if not constants.DO_SANITIZE: return 1 - log_debug("Replace sensitive info for {}".format(in_file)) + logger.debug("Replace sensitive info for %s", in_file) write_to_file(in_file, sub_sensitive_string(data)) @@ -1372,13 +1352,13 @@ def start_slave_collector(node, arg_str): cmd += " {}".format(str(item)) code, out, err = crmutils.get_stdout_stderr(cmd) if code != 0: - log_warning(err) + logger.warning(err) for ip in get_peer_ip(): - log_info("Trying connect by %s" % ip) + logger.info("Trying connect by %s", ip) cmd = cmd.replace(node, ip, 1) code, out, err = crmutils.get_stdout_stderr(cmd) if code != 0: - log_warning(err) + logger.warning(err) break compress_data = "" @@ -1451,7 +1431,7 @@ def touch_r(src, dst): like shell command "touch -r src dst" """ if not os.path.exists(src): - log_warning("In touch_r function, %s not exists" % src) + logger.warning("In touch_r function, %s not exists", src) return stat_info = os.stat(src) os.utime(dst, (stat_info.st_atime, stat_info.st_mtime)) @@ -1557,7 +1537,7 @@ def get_sensitive_key_value_list(): constants.SANITIZE_VALUE_CIB += extract_sensitive_value_list(key) constants.SANITIZE_KEY_CIB.append(key.strip('.*?')+'.*?') except (FileNotFoundError, EOFError) as e: - log_warning(e) + logger.warning(e) def extract_sensitive_value_list(rule): From d5b56b362d4fcd4ef5950c272f5fd63eb4f21f51 Mon Sep 17 00:00:00 2001 From: liangxin1300 Date: Mon, 1 Nov 2021 14:40:10 +0800 Subject: [PATCH 3/5] Dev: behave: Change in functional test for previous crm report changes --- .github/workflows/crmsh-ci.yml | 10 +++++----- data-manifest | 2 +- test/docker_scripts.sh | 4 ++-- ...t_bugs.feature => crm_report_bugs.feature} | 20 +++++++++---------- test/features/steps/step_implementation.py | 8 ++++---- test/run-in-travis.sh | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) rename test/features/{hb_report_bugs.feature => crm_report_bugs.feature} (89%) diff --git a/.github/workflows/crmsh-ci.yml b/.github/workflows/crmsh-ci.yml index 3bf7a57325..8a2844c902 100644 --- a/.github/workflows/crmsh-ci.yml +++ b/.github/workflows/crmsh-ci.yml @@ -37,15 +37,15 @@ jobs: run: | tox -v - functional_test_hb_report_bugs: + functional_test_crm_report_bugs: runs-on: ubuntu-latest timeout-minutes: 40 steps: - uses: actions/checkout@v2 - - name: functional test for hb_report + - name: functional test for crm_report run: | - $DOCKER_SCRIPT hb_report before_install - $DOCKER_SCRIPT hb_report run bugs + $DOCKER_SCRIPT crm_report before_install + $DOCKER_SCRIPT crm_report run bugs functional_test_bootstrap_bugs: runs-on: ubuntu-latest @@ -169,7 +169,7 @@ jobs: delivery: needs: [unit_test, - functional_test_hb_report_bugs, + functional_test_crm_report_bugs, functional_test_bootstrap_bugs, functional_test_bootstrap_common, functional_test_bootstrap_options, diff --git a/data-manifest b/data-manifest index 60c085965b..427cc812c9 100644 --- a/data-manifest +++ b/data-manifest @@ -72,9 +72,9 @@ test/features/bootstrap_sbd_delay.feature test/features/bootstrap_sbd_normal.feature test/features/configure_bugs.feature test/features/constraints_bugs.feature +test/features/crm_report_bugs.feature test/features/environment.py test/features/geo_setup.feature -test/features/hb_report_bugs.feature test/features/ocfs2.feature test/features/qdevice_options.feature test/features/qdevice_setup_remove.feature diff --git a/test/docker_scripts.sh b/test/docker_scripts.sh index 967c550451..c8ec02ab03 100755 --- a/test/docker_scripts.sh +++ b/test/docker_scripts.sh @@ -1,7 +1,7 @@ #!/bin/bash Docker_image='liangxin1300/hatbw' HA_packages='pacemaker corosync corosync-qdevice' -TEST_TYPE='bootstrap qdevice hb_report geo' +TEST_TYPE='bootstrap qdevice crm_report geo' etc_hosts_content=`cat < Date: Tue, 2 Nov 2021 16:58:01 +0800 Subject: [PATCH 4/5] Dev: crm report: Get distribution info correctly and reuse it --- crmsh/report/collect.py | 2 +- crmsh/report/constants.py | 2 +- crmsh/report/utillib.py | 21 ++++++++++++--------- scripts/health/collect.py | 5 ++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/crmsh/report/collect.py b/crmsh/report/collect.py index e363785189..7593739d7c 100644 --- a/crmsh/report/collect.py +++ b/crmsh/report/collect.py @@ -257,7 +257,7 @@ def collect_sys_info(): out_string += "Kernel release: %s\n" % os.uname()[2] out_string += "Architecture: %s\n" % os.uname()[-1] if os.uname()[0] == "Linux": - out_string += "Distribution: %s\n" % utillib.distro() + out_string += "Distribution: %s\n" % utillib.get_distro_info() sys_info_f = os.path.join(constants.WORKDIR, constants.SYSINFO_F) crmutils.str2file(out_string, sys_info_f) diff --git a/crmsh/report/constants.py b/crmsh/report/constants.py index fe44c2db04..3767e703e7 100644 --- a/crmsh/report/constants.py +++ b/crmsh/report/constants.py @@ -144,5 +144,5 @@ TIME_F = "time.txt" OCFS2_F = "ocfs2.txt" SBD_F = "sbd.txt" - +OSRELEASE = "/etc/os-release" # vim:ts=4:sw=4:et: diff --git a/crmsh/report/utillib.py b/crmsh/report/utillib.py index d6197c876a..7382120e93 100644 --- a/crmsh/report/utillib.py +++ b/crmsh/report/utillib.py @@ -400,17 +400,20 @@ def diff_check(file1, file2): else: return (0, txt_diff(file1, file2)) -def distro(): + +def get_distro_info(): """ - get some system info + get distribution information """ - ret = "" - if which("lsb_release"): - logger.debug("using lsb_release for distribution info") - res = get_command_info("lsb_release -d")[1] - if re.search("Description:", res): - ret = ' '.join(res.split()[1:]) - return ret + res = None + if os.path.exists(constants.OSRELEASE): + logger.debug("Using {} to get distribution info".format(constants.OSRELEASE)) + res = re.search("PRETTY_NAME=\"(.*)\"", read_from_file(constants.OSRELEASE)) + elif which("lsb_release"): + logger.debug("Using lsb_release to get distribution info") + out = crmutils.get_stdout_or_raise_error("lsb_release -d") + res = re.search("Description:\s+(.*)", out) + return res.group(1) if res else "Unknown" def dump_log(logf, from_line, to_line): diff --git a/scripts/health/collect.py b/scripts/health/collect.py index fb3284f2e4..9b777be693 100755 --- a/scripts/health/collect.py +++ b/scripts/health/collect.py @@ -6,6 +6,7 @@ import hashlib import platform import crm_script +from crmsh.report import utillib data = crm_script.get_input() PACKAGES = ['booth', 'cluster-glue', 'corosync', 'crmsh', 'csync2', 'drbd', @@ -33,7 +34,7 @@ def sys_info(): # the number of currently running processes and the total number of # processes. The last column displays the last process ID used. system, node, release, version, machine, processor = platform.uname() - distname, distver, distid = platform.linux_distribution() + distname = utillib.get_distro_info() hostname = os.uname()[1] uptime = open('/proc/uptime').read().split() @@ -46,8 +47,6 @@ def sys_info(): 'machine': machine, 'processor': processor, 'distname': distname, - 'distver': distver, - 'distid': distid, 'user': get_user(), 'hostname': hostname, 'uptime': uptime[0], From e653956bf49333225eae90a8d89d049b9da3f4fa Mon Sep 17 00:00:00 2001 From: liangxin1300 Date: Wed, 22 Dec 2021 10:01:39 +0800 Subject: [PATCH 5/5] Dev: doc: Rename hb_report as crm report --- Makefile.am | 2 +- README.md | 2 +- crmsh.spec.in | 2 +- doc/crm.8.adoc | 8 ++++---- ...rmsh_hb_report.8.adoc => crmsh_crm_report.8.adoc} | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) rename doc/{crmsh_hb_report.8.adoc => crmsh_crm_report.8.adoc} (98%) diff --git a/Makefile.am b/Makefile.am index c9debab417..0af5a6cde4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,7 +29,7 @@ crmconf_DATA = etc/crm.conf etc/profiles.yml contribdir = $(docdir)/contrib contrib_DATA = contrib/pcmk.vim contrib/README.vimsyntax helpdir = $(datadir)/$(PACKAGE) -asciiman = doc/crm.8.adoc doc/crmsh_hb_report.8.adoc doc/profiles.adoc +asciiman = doc/crm.8.adoc doc/crmsh_crm_report.8.adoc doc/profiles.adoc help_DATA = doc/crm.8.adoc generated_docs = diff --git a/README.md b/README.md index 5e8c526be9..6ca02b6a6f 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ included in the repository. ./test: unit tests and regression tests ./contrib: vim highlighting scripts and other semi-related contributions -./hb_report: log file collection and analysis tool +./crm report: log file collection and analysis tool ``` ## Development diff --git a/crmsh.spec.in b/crmsh.spec.in index 4a66282e59..fa011adff9 100644 --- a/crmsh.spec.in +++ b/crmsh.spec.in @@ -216,7 +216,7 @@ result2=$? %{crmsh_docdir}/COPYING %{crmsh_docdir}/AUTHORS %{crmsh_docdir}/crm.8.html -%{crmsh_docdir}/crmsh_hb_report.8.html +%{crmsh_docdir}/crmsh_crm_report.8.html %{crmsh_docdir}/profiles.html %{crmsh_docdir}/ChangeLog %{crmsh_docdir}/README.md diff --git a/doc/crm.8.adoc b/doc/crm.8.adoc index a8bd2ea0d0..b90d263aa5 100644 --- a/doc/crm.8.adoc +++ b/doc/crm.8.adoc @@ -4697,7 +4697,7 @@ history commands look at events within a certain time span. For the `live` source, the default time span is the _last hour_. -There is no time span limit for the `hb_report` source. +There is no time span limit for the `crm report` source. The time period is parsed by the `dateutil` python module. It covers a wide range of date formats. For instance: @@ -4833,7 +4833,7 @@ current settings can be saved and later retrieved. If the current history being examined is coming from a live cluster the logs, PE inputs, and other files are saved too, because they may disappear from nodes. For the existing reports -coming from `hb_report`, only the directory location is saved +coming from `crm report`, only the directory location is saved (not to waste space). A history session may also be packed into a tarball which can @@ -4890,7 +4890,7 @@ show pe-input-2080.bz2 status ==== `source` Events to be examined can come from the current cluster or from a -`hb_report` report. This command sets the source. `source live` +`crm report` report. This command sets the source. `source live` sets source to the running cluster and system logs. If no source is specified, the current source information is printed. @@ -5024,7 +5024,7 @@ and other relevant data for a given time period. This is a useful tool for collecting data to attach to bug reports, or for detecting the root cause of errors resulting in resource failover, for example. -See `crmsh_hb_report(8)` for more details on arguments, +See `crmsh_crm_report(8)` for more details on arguments, or call `crm report -h` Usage: diff --git a/doc/crmsh_hb_report.8.adoc b/doc/crmsh_crm_report.8.adoc similarity index 98% rename from doc/crmsh_hb_report.8.adoc rename to doc/crmsh_crm_report.8.adoc index df9bdbfc16..98ef0a77d0 100644 --- a/doc/crmsh_hb_report.8.adoc +++ b/doc/crmsh_crm_report.8.adoc @@ -1,13 +1,13 @@ -:man source: crmsh_hb_report +:man source: crmsh_crm_report :man version: 1.2 :man manual: Pacemaker documentation -crmsh_hb_report(8) +crmsh_crm_report(8) ================== NAME ---- -crmsh_hb_report - create report for CRM based clusters (Pacemaker) +crmsh_crm_report - create report for CRM based clusters (Pacemaker) SYNOPSIS @@ -19,7 +19,7 @@ SYNOPSIS DESCRIPTION ----------- -The crmsh_hb_report(8) is a utility to collect all information (logs, +The crmsh_crm_report(8) is a utility to collect all information (logs, configuration files, system information, etc) relevant to Pacemaker (CRM) over the given period of time. @@ -29,8 +29,8 @@ OPTIONS dest:: The report name. It can also contain a path where to put the report tarball. If left out, the tarball is created in the - current directory named "hb_report-current_date", for instance - hb_report-Wed-03-Mar-2010. + current directory named "crm_report-current_date", for instance + crm_report-Wed-03-Mar-2010. *-d*:: Don't create the compressed tar, but leave the result in a