From 6a0df95bb82f456f7cc87a696e5d176333d79f10 Mon Sep 17 00:00:00 2001 From: Edward Hope-Morley Date: Sat, 11 Jan 2025 15:13:16 +0000 Subject: [PATCH] Protect against no date If for whatever reason hotsos cannot get the current date, perhaps because sosreport didnt collect it, we now print a warning log when we fail to create a search constraint as a result. --- hotsos/core/host_helpers/cli/cli.py | 15 ++++++++++++--- hotsos/core/host_helpers/exceptions.py | 11 ++++++++--- hotsos/core/host_helpers/uptime.py | 4 ++-- hotsos/core/plugins/kernel/kernlog/common.py | 12 ++++++++++-- hotsos/core/plugins/openvswitch/ovs.py | 6 +++++- hotsos/core/search.py | 17 +++++++++++------ hotsos/core/ycheck/common.py | 7 ++++++- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/hotsos/core/host_helpers/cli/cli.py b/hotsos/core/host_helpers/cli/cli.py index bee5c9e05..93f8536a5 100644 --- a/hotsos/core/host_helpers/cli/cli.py +++ b/hotsos/core/host_helpers/cli/cli.py @@ -41,6 +41,11 @@ def since_date(self): and has the format "YEAR-MONTH-DAY". It does not specify a time. """ current = CLIHelper().date(format="--iso-8601") + if not current: + log.warning("could not determine since date for journalctl " + "command") + return None + ts = datetime.datetime.strptime(current, "%Y-%m-%d") if HotSOSConfig.use_all_logs: days = HotSOSConfig.max_logrotate_depth @@ -64,12 +69,16 @@ def format_journalctl_cmd(self, **kwargs): if kwargs.get("date"): self.cmd = f"{self.cmd} --since {kwargs.get('date')}" - else: + elif self.since_date: self.cmd = f"{self.cmd} --since {self.since_date}" class JournalctlBinFileCmd(BinFileCmd, JournalctlBase): - """ Implements file-based journalctl command. """ + """ Implements file-based journalctl command. + + NOTE: this may suffer from incompatibility issues if the journal data was + created with a different version of systemd-journald. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register_hook("pre-exec", self.preformat_sos_journalctl) @@ -81,7 +90,7 @@ def preformat_sos_journalctl(self, **kwargs): if kwargs.get("date"): self.path = f"{self.path} --since {kwargs.get('date')}" - else: + elif self.since_date: self.path = f"{self.path} --since {self.since_date}" diff --git a/hotsos/core/host_helpers/exceptions.py b/hotsos/core/host_helpers/exceptions.py index d71cd56e7..d276b2df7 100644 --- a/hotsos/core/host_helpers/exceptions.py +++ b/hotsos/core/host_helpers/exceptions.py @@ -10,11 +10,13 @@ class CLIExecError(Exception): """ Exception raised when execution of a command files. """ - def __init__(self, return_value=None): + def __init__(self, message, return_value=None): """ + @param message: information message about the exception @param return_value: default return value that a command should return if execution fails. """ + self.message = message self.return_value = return_value @@ -31,9 +33,12 @@ def catch_exceptions_inner2(*args, **kwargs): log.debug(msg) if isinstance(exc, json.JSONDecodeError): - raise CLIExecError(return_value={}) from exc + raise CLIExecError(str(exc), return_value={}) from exc - raise CLIExecError(return_value=[]) from exc + if isinstance(exc, subprocess.CalledProcessError): + raise CLIExecError(exc.output, return_value={}) from exc + + raise CLIExecError(str(exc), return_value=[]) from exc return catch_exceptions_inner2 diff --git a/hotsos/core/host_helpers/uptime.py b/hotsos/core/host_helpers/uptime.py index 938b415b2..155586bf2 100644 --- a/hotsos/core/host_helpers/uptime.py +++ b/hotsos/core/host_helpers/uptime.py @@ -35,8 +35,8 @@ def __init__(self): def in_minutes(self): """ Total uptime in minutes. """ if not self.subgroups: - log.info("uptime not available") - return None + log.warning("uptime not available") + return 0 if self.subgroups['hour']['value']: expr = self.subgroups['hour']['expr'] diff --git a/hotsos/core/plugins/kernel/kernlog/common.py b/hotsos/core/plugins/kernel/kernlog/common.py index 0936cf985..36ed15d49 100644 --- a/hotsos/core/plugins/kernel/kernlog/common.py +++ b/hotsos/core/plugins/kernel/kernlog/common.py @@ -8,6 +8,7 @@ FileSearcher, SearchConstraintSearchSince, ) +from hotsos.core.log import log KERNLOG_TS = r'\[\s*\d+\.\d+\]' KERNLOG_PREFIX = rf'(?:\S+\s+\d+\s+[\d:]+\s+\S+\s+\S+:\s+)?{KERNLOG_TS}' @@ -85,8 +86,15 @@ def __iter__(self): class KernLogBase(): """ Base class for kernlog analysis implementations. """ def __init__(self): - c = SearchConstraintSearchSince(ts_matcher_cls=CommonTimestampMatcher) - self.searcher = FileSearcher(constraint=c) + try: + constraint = SearchConstraintSearchSince( + ts_matcher_cls=CommonTimestampMatcher) + except ValueError as exc: + log.warning("failed to create global search constraint for " + "calltrace checker: %s", exc) + constraint = None + + self.searcher = FileSearcher(constraint=constraint) self.hostnet_helper = HostNetworkingHelper() self.cli_helper = CLIHelper() diff --git a/hotsos/core/plugins/openvswitch/ovs.py b/hotsos/core/plugins/openvswitch/ovs.py index 133b97ebc..ed781d9fd 100644 --- a/hotsos/core/plugins/openvswitch/ovs.py +++ b/hotsos/core/plugins/openvswitch/ovs.py @@ -239,10 +239,14 @@ def simple_search(cls): """ # noqa pattern = (r'([\d-]+)T([\d:]+)\.\d+Z.+\|bfd\(\S+\)\|INFO\|' r'([a-z0-9-]+): BFD state change: (\S+)') + constraints = [] constraint = create_constraint(search_result_age_hours=24, min_hours_since_last_boot=0) + if constraint: + constraints.append(constraint) + return SearchDef(pattern, tag=cls.unique_search_tag, - constraints=[constraint]) + constraints=constraints) @classmethod def paths(cls): diff --git a/hotsos/core/search.py b/hotsos/core/search.py index ca2bd0779..4f5bee4ad 100644 --- a/hotsos/core/search.py +++ b/hotsos/core/search.py @@ -57,9 +57,9 @@ def __init__(self, *args, **kwargs): current_date = CLIHelper().date(format='+%Y-%m-%d %H:%M:%S') if not current_date or not isinstance(current_date, str): - log.error("current date '%s' being provided to search " - "constraints is not valid.", current_date) - return + msg = (f"current date '{current_date}' being provided to search " + "constraints is not valid.") + raise ValueError(msg) super().__init__(*args, current_date=current_date, **kwargs) @@ -241,6 +241,11 @@ def create_constraint(search_result_age_hours=None, hours = min(hours, max(uptime_etime_hours - min_hours_since_last_boot, 0)) - return SearchConstraintSearchSince( - ts_matcher_cls=CommonTimestampMatcher, - hours=hours) + try: + return SearchConstraintSearchSince( + ts_matcher_cls=CommonTimestampMatcher, + hours=hours) + except ValueError as exc: + log.warning("failed to create search constraint: %s", exc) + + return None diff --git a/hotsos/core/ycheck/common.py b/hotsos/core/ycheck/common.py index d8718a04f..e424ad6ea 100644 --- a/hotsos/core/ycheck/common.py +++ b/hotsos/core/ycheck/common.py @@ -68,8 +68,13 @@ class GlobalSearcher(contextlib.AbstractContextManager, UserDict): """ def __init__(self): - constraint = SearchConstraintSearchSince( + try: + constraint = SearchConstraintSearchSince( ts_matcher_cls=CommonTimestampMatcher) + except ValueError as exc: + log.warning("failed to create global search constraint: %s", exc) + constraint = None + self._loaded_searches = [] self._results = None self._searcher = FileSearcher(constraint=constraint)