Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2023-12-11 21:51:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Mon Dec 11 21:51:03 2023 rev:314 rq:1132388 version:4.6.0+20231211.4b74412a

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2023-12-07 
19:12:55.088820500 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.25432/crmsh.changes   2023-12-11 
21:51:15.809111225 +0100
@@ -1,0 +2,17 @@
+Mon Dec 11 03:00:33 UTC 2023 - xli...@suse.com
+
+- Update to version 4.6.0+20231211.4b74412a:
+  * Fix: scripts.health: call `setup_logging()` before importing 
crmsh.reprot.utils
+  * Dev: unittest: adjuest unit tests for previous changes
+  * Dev: behave: adjust regression tests for previous changes
+  * Dev: log: save backtrace of ValueError in logfile and suppress it in 
console
+  * Refactor: log: use levelno instead levelname to filter logs
+  * Refactor: log: implement lineno in Logger instead of handler
+  * Refactor: log: remove FileCustomFormatter
+  * Refactor: log: remove ConsoleReportFormatter
+  * Refactor: log: add LeveledFormatter to use different formats for different 
log levels
+  * Refactor: log: unused code removal
+  * Refactor: log: refactor DEBUG2 into a standard Logger interface
+  * Refactor: log: refactor ConsoleCustomFormatter
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.6.0+20231207.89c74e6c.tar.bz2

New:
----
  crmsh-4.6.0+20231211.4b74412a.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.kVcgJQ/_old  2023-12-11 21:51:16.369132005 +0100
+++ /var/tmp/diff_new_pack.kVcgJQ/_new  2023-12-11 21:51:16.373132154 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.6.0+20231207.89c74e6c
+Version:        4.6.0+20231211.4b74412a
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.kVcgJQ/_old  2023-12-11 21:51:16.409133489 +0100
+++ /var/tmp/diff_new_pack.kVcgJQ/_new  2023-12-11 21:51:16.409133489 +0100
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">89aab78af02c02a04ef9bc99be3386825bda2ae5</param>
+  <param 
name="changesrevision">a23bea64548038b55b3650ae73cb3b88f025ad68</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-4.6.0+20231207.89c74e6c.tar.bz2 -> 
crmsh-4.6.0+20231211.4b74412a.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.6.0+20231207.89c74e6c/crmsh/log.py 
new/crmsh-4.6.0+20231211.4b74412a/crmsh/log.py
--- old/crmsh-4.6.0+20231207.89c74e6c/crmsh/log.py      2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/crmsh/log.py      2023-12-11 
03:36:33.000000000 +0100
@@ -6,15 +6,73 @@
 import shutil
 import logging
 import logging.config
+import typing
 from contextlib import contextmanager
 
 from . import options
 from . import constants
 
-
+DEBUG2 = logging.DEBUG + 5
 CRMSH_LOG_FILE = "/var/log/crmsh/crmsh.log"
 
 
+class DEBUG2Logger(logging.Logger):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+    def debug2(self, msg, *args, **kwargs):
+        if self.isEnabledFor(DEBUG2):
+            self._log(DEBUG2, msg, args, **kwargs)
+
+
+class NumberedLoggerInterface(DEBUG2Logger):
+    """
+    Interface to prepend a number to the message, used for regression test. 
When this class is used directly, no numbers are prepend.
+    """
+    lineno = -1
+
+    @classmethod
+    def reset_lineno(cls, to=0):
+        pass
+
+    @classmethod
+    def incr_lineno(cls):
+        pass
+
+
+class NumberedLogger(NumberedLoggerInterface):
+    """
+    Prepend a number to the message, used for regression test
+    """
+    lineno = -1
+
+    def _log( self, level, msg, args, **kwargs):
+        if NumberedLogger.lineno > 0:
+            msg = f'{self.lineno}: {msg}'
+        super()._log(level, msg, args, **kwargs)
+
+    @classmethod
+    def reset_lineno(cls, to=0):
+        cls.lineno = to
+
+    @classmethod
+    def incr_lineno(cls):
+        cls.lineno += 1
+
+    if (sys.version_info.major, sys.version_info.minor) > (3, 6):
+        def findCaller(self, stack_info=False, stacklevel=1):
+            return super().findCaller(stack_info, stacklevel+1)
+    else:
+        def findCaller(self, stack_info=False):
+            if stack_info:
+                return super().findCaller(stack_info)
+            else:
+                f = sys._getframe(4)
+                co = f.f_code
+                sinfo = None
+                return co.co_filename, f.f_lineno, co.co_name, sinfo
+
+
 class ConsoleCustomHandler(logging.StreamHandler):
     """
     A custom handler for console
@@ -33,62 +91,75 @@
         stream.write(self.terminator)
 
 
-class ConsoleCustomFormatter(logging.Formatter):
-    """
-    A custom formatter for console
+class NoBacktraceFormatter(logging.Formatter):
+    """Suppress backtrace unless option debug is set."""
+    def format(self, record):
+        """
+        Format the specified record as text.
 
-    Wrap levelname with colors
-    Wrap message with line number which is used for regression test
-    """
+        The record's attribute dictionary is used as the operand to a
+        string formatting operation which yields the returned string.
+        Before formatting the dictionary, a couple of preparatory steps
+        are carried out. The message attribute of the record is computed
+        using LogRecord.getMessage(). If the formatting string uses the
+        time (as determined by a call to usesTime(), formatTime() is
+        called to format the event time. If there is exception information,
+        it is formatted using formatException() and appended to the message.
+        """
+        if record.exc_info or record.stack_info:
+            from crmsh import config
+            if config.core.debug:
+                return super().format(record)
+            else:
+                record.message = record.getMessage()
+                if self.usesTime():
+                    record.asctime = self.formatTime(record, self.datefmt)
+            return self.formatMessage(record)
+        else:
+            return super().format(record)
+
+
+class ConsoleColoredFormatter(NoBacktraceFormatter):
+    """Print levelname with colors and suppress backtrace."""
     COLORS = {
-        "WARNING": constants.YELLOW,
-        "INFO": constants.GREEN,
-        "ERROR": constants.RED
+        logging.WARNING: constants.YELLOW,
+        logging.INFO: constants.GREEN,
+        logging.ERROR: constants.RED
     }
     FORMAT = "%(levelname)s: %(message)s"
 
-    def __init__(self, lineno=-1, fmt=None):
-        self.lineno = lineno
-        if fmt:
-            super().__init__(fmt=fmt)
-        else:
-            super().__init__(fmt=self.FORMAT)
+    def __init__(self, fmt=None):
+        super().__init__(fmt)
+        if not fmt:
+            fmt = self.FORMAT
+        self._colored_formatter: typing.Mapping[int, logging.Formatter] = {
+            level: NoBacktraceFormatter(fmt.replace('%(levelname)s', 
f'{color}%(levelname)s{constants.END}'))
+            for level, color in self.COLORS.items()
+        }
 
     def format(self, record):
-        levelname = record.levelname
-        # wrap with colors
-        if levelname in self.COLORS and not options.regression_tests:
-            record.levelname = self.COLORS[levelname] + levelname + 
constants.END
-        # wrap with line number
-        if self.lineno > 0:
-            msg = record.msg
-            record.msg = "{}: {}".format(self.lineno, msg)
-            record.levelname = levelname
-        if record.levelname == "DEBUG2":
-            msg = record.msg
-            record.msg = f"{record.funcName}: {msg}"
-        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)
+        colored_formatter = self._colored_formatter.get(record.levelno)
+        if colored_formatter is not None:
+            return colored_formatter.format(record)
+        else:
+            return super().format(record)
 
 
-class FileCustomFormatter(logging.Formatter):
-    """
-    A custom formatter for file
-    """
-    FORMAT = "%(asctime)s {} %(name)s: %(levelname)s: 
%(message)s".format(socket.gethostname())
-    DATEFMT = "%b %d %H:%M:%S"
+class LeveledFormatter(logging.Formatter):
+    """Format log according to log level."""
+    def __init__(self, base_formatter_factory, default_fmt: str = None, 
level_fmt: typing.Mapping[int, str] = None):
+        super().__init__()
+        self.default_formatter = base_formatter_factory(default_fmt)
+        self.level_formatter = {
+            level: base_formatter_factory(fmt)
+            for level, fmt in level_fmt.items()
+        }
 
-    def __init__(self):
-        super().__init__(fmt=self.FORMAT, datefmt=self.DATEFMT)
+    def format(self, record):
+        formatter = self.level_formatter.get(record.levelno)
+        if formatter is None:
+            formatter = self.default_formatter
+        return formatter.format(record)
 
 
 class DebugCustomFilter(logging.Filter):
@@ -97,7 +168,7 @@
     """
     def filter(self, record):
         from .config import core
-        if record.levelname == "DEBUG":
+        if record.levelno == logging.DEBUG:
             return core.debug
         else:
             return True
@@ -109,9 +180,9 @@
     """
     def filter(self, record):
         from .config import report
-        if record.levelname == "DEBUG":
+        if record.levelno == logging.DEBUG:
             return int(report.verbosity) >= 1
-        if record.levelname == "DEBUG2":
+        if record.levelno == DEBUG2:
             return int(report.verbosity) > 1
         else:
             return True
@@ -140,13 +211,24 @@
     "disable_existing_loggers": "False",
     "formatters": {
         "console_report": {
-            "()": ConsoleReportFormatter
+            "()": LeveledFormatter,
+            "base_formatter_factory": ConsoleColoredFormatter,
+            "default_fmt": "{}: %(levelname)s: 
%(message)s".format(socket.gethostname()),
+            "level_fmt": {
+                DEBUG2: "{}: %(levelname)s: %(funcName)s: 
%(message)s".format(socket.gethostname()),
+            },
         },
         "console": {
-            "()": ConsoleCustomFormatter
+            "()": LeveledFormatter,
+            "base_formatter_factory": ConsoleColoredFormatter,
+            "default_fmt": "%(levelname)s: %(message)s",
+            "level_fmt": {
+                DEBUG2: "%(levelname)s: %(funcName)s %(message)s",
+            },
         },
         "file": {
-            "()": FileCustomFormatter
+            "format": "%(asctime)s {} %(name)s: %(levelname)s: 
%(message)s".format(socket.gethostname()),
+            "datefmt": "%b %d %H:%M:%S",
         }
     },
     "filters": {
@@ -204,29 +286,44 @@
 }
 
 
+NO_COLOR_FORMATTERS = {
+    "console_report": {
+        "()": LeveledFormatter,
+        "base_formatter_factory": logging.Formatter,
+        "default_fmt": "{}: %(levelname)s: 
%(message)s".format(socket.gethostname()),
+        "level_fmt": {
+            DEBUG2: "{}: %(levelname)s: %(funcName)s: 
%(message)s".format(socket.gethostname()),
+        },
+    },
+    "console": {
+        "()": LeveledFormatter,
+        "base_formatter_factory": logging.Formatter,
+        "default_fmt": "%(levelname)s: %(message)s",
+        "level_fmt": {
+            DEBUG2: "%(levelname)s: %(funcName)s %(message)s",
+        },
+    },
+    "file": {
+        "format": "%(asctime)s {} %(name)s: %(levelname)s: 
%(message)s".format(socket.gethostname()),
+        "datefmt": "%b %d %H:%M:%S",
+    }
+}
+
+
 class LoggerUtils(object):
     """
     A class to keep/update some attributes related with logger
     Also has methods related with handler and formatter
     And a set of wrapped log message for specific scenarios
     """
-    def __init__(self, logger):
+    def __init__(self, logger: NumberedLogger):
         """
         Init function
         """
         self.logger = logger
         # used in regression test
-        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
@@ -244,39 +341,17 @@
         console_handler = self.get_handler("console")
         console_handler.setLevel(logging.WARNING)
 
-    def set_console_formatter(self, lineno):
-        """
-        Pass line number to ConsoleCustomFormatter
-        """
-        console_handler = self.get_handler("console")
-        console_handler.setFormatter(ConsoleCustomFormatter(lineno=lineno))
-
     def reset_lineno(self, to=0):
         """
         Reset line number
         """
-        self.lineno = to
-        self.set_console_formatter(to)
+        self.logger.reset_lineno(to)
 
     def incr_lineno(self):
         """
         Increase line number
         """
-        self.lineno += 1
-        self.set_console_formatter(self.lineno)
-
-    @contextmanager
-    def suppress_new_line(self):
-        """
-        Supress new line in console
-        """
-        console_handler = self.get_handler("console")
-        try:
-            console_handler.terminator = ""
-            yield
-        finally:
-            sys.stdout.flush()
-            console_handler.terminator = "\n"
+        self.logger.incr_lineno()
 
     @contextmanager
     def only_file(self):
@@ -330,11 +405,11 @@
         Mark the line number in the log record
         """
         try:
-            self.__save_lineno = self.lineno
+            self.__save_lineno = self.logger.lineno
             self.reset_lineno()
             yield
         finally:
-            self.reset_lineno(self.__save_lineno)
+            self.logger.reset_lineno(self.__save_lineno)
 
     @contextmanager
     def status_long(self, msg):
@@ -469,7 +544,14 @@
     except (PermissionError, FileNotFoundError) as e:
         print('{}WARNING:{} Failed to open log file: 
{}'.format(constants.YELLOW, constants.END, e), file=sys.stderr)
         LOGGING_CFG["handlers"]["file"] = {'class': 'logging.NullHandler'}
-    logging.config.dictConfig(LOGGING_CFG)
+    logging.addLevelName(DEBUG2, "DEBUG2")
+    if os.environ.get('CRMSH_REGRESSION_TEST'):
+        logging.setLoggerClass(NumberedLogger)
+        LOGGING_CFG['formatters'] = NO_COLOR_FORMATTERS
+        logging.config.dictConfig(LOGGING_CFG)
+    else:
+        logging.setLoggerClass(NumberedLoggerInterface)
+        logging.config.dictConfig(LOGGING_CFG)
 
 
 def setup_logger(name):
@@ -489,6 +571,4 @@
     Get the logger for crm report
     """
     logger = setup_logger(name)
-    logger_utils = LoggerUtils(logger)
-    logger_utils.set_debug2_level()
     return logger
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.6.0+20231207.89c74e6c/crmsh/main.py 
new/crmsh-4.6.0+20231211.4b74412a/crmsh/main.py
--- old/crmsh-4.6.0+20231207.89c74e6c/crmsh/main.py     2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/crmsh/main.py     2023-12-11 
03:36:33.000000000 +0100
@@ -45,8 +45,8 @@
         try:
             if not context.run(inp):
                 raise ValueError("Error in RC file: " + rcfile)
-        except ValueError as msg:
-            logger.error(msg)
+        except ValueError as e:
+            logger.error(e, exc_info=e)
     f.close()
     sys.stdin = save_stdin
 
@@ -261,14 +261,16 @@
             try:
                 if not context.run(inp):
                     rc = 1
-            except ValueError as msg:
+            except ValueError as e:
                 rc = 1
-                logger.error(msg)
+                logger.error(e, exc_info=e)
         except KeyboardInterrupt:
             if options.interactive and not options.batch:
                 print("Ctrl-C, leaving")
             context.quit(1)
-    return rc
+        except Exception as e:
+            logger.error(e, exc_info=e)
+            context.quit(1)
 
 
 def compgen():
@@ -374,11 +376,10 @@
             print("Ctrl-C, leaving")
             sys.exit(1)
     except ValueError as e:
-        if config.core.debug:
-            import traceback
-            traceback.print_exc()
-            sys.stdout.flush()
-        logger.error(str(e))
+        logger.error(e, exc_info=e)
         sys.exit(1)
+    except Exception as e:
+        logger.error(e, exc_info=e)
+        raise
 
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.6.0+20231207.89c74e6c/crmsh/ui_context.py 
new/crmsh-4.6.0+20231211.4b74412a/crmsh/ui_context.py
--- old/crmsh-4.6.0+20231207.89c74e6c/crmsh/ui_context.py       2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/crmsh/ui_context.py       2023-12-11 
03:36:33.000000000 +0100
@@ -84,12 +84,8 @@
             if cmd:
                 utils.check_user_access(self.current_level().name)
                 rv = self.execute_command() is not False
-        except (ValueError, IOError) as msg:
-            if config.core.debug or options.regression_tests:
-                import traceback
-                traceback.print_exc()
-                sys.stdout.flush()
-            logger.error("%s: %s", self.get_qualified_name(), msg)
+        except (ValueError, IOError) as e:
+            logger.error("%s: %s", self.get_qualified_name(), e, exc_info=e)
             rv = False
         except utils.TerminateSubCommand:
             return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/scripts/health/collect.py 
new/crmsh-4.6.0+20231211.4b74412a/scripts/health/collect.py
--- old/crmsh-4.6.0+20231207.89c74e6c/scripts/health/collect.py 2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/scripts/health/collect.py 2023-12-11 
03:36:33.000000000 +0100
@@ -6,7 +6,11 @@
 import hashlib
 import platform
 import crm_script
+
+import crmsh.log
+crmsh.log.setup_logging()
 from crmsh.report import utils
+
 data = crm_script.get_input()
 
 PACKAGES = ['booth', 'cluster-glue', 'corosync', 'crmsh', 'csync2', 'drbd',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.6.0+20231207.89c74e6c/test/evaltest.sh 
new/crmsh-4.6.0+20231211.4b74412a/test/evaltest.sh
--- old/crmsh-4.6.0+20231207.89c74e6c/test/evaltest.sh  2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/evaltest.sh  2023-12-11 
03:36:33.000000000 +0100
@@ -7,6 +7,7 @@
 CRM_NO_REG="$CRM"
 CRM="$CRM -R"
 export PYTHONUNBUFFERED=1
+export CRMSH_REGRESSION_TEST=1
 
 if [ "$1" = prof ]; then
        CRM="$CRM -X regtest.profile"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/edit.exp 
new/crmsh-4.6.0+20231211.4b74412a/test/testcases/edit.exp
--- old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/edit.exp   2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/testcases/edit.exp   2023-12-11 
03:36:33.000000000 +0100
@@ -68,7 +68,7 @@
 .INP: primitive d3 ocf:heartbeat:Dummy
 .INP: group g2 d1 d2
 .INP: filter "sed '/g2/s/d1/p1/;/g1/s/p1/d1/'"
-ERROR: Cannot create group:g1: Child primitive:d1 already in group:g2
+ERROR: 29: Cannot create group:g1: Child primitive:d1 already in group:g2
 .INP: filter "sed '/g1/s/d1/p1/;/g2/s/p1/d1/'"
 .INP: filter "sed '$alocation loc-d1 d1 rule $id=r1 -inf: not_defined 
webserver rule $id=r2 webserver: defined webserver'"
 .INP: filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1
@@ -83,33 +83,33 @@
 .INP: modgroup g1 add p1
 ERROR: 1: syntax in group: child p1 listed more than once in group g1 parsing 
'group g1 p1 p2 d3 p1'
 .INP: modgroup g1 remove st
+ERROR: 42: configure.modgroup: st is not member of g1
 Traceback (most recent call last):
     rv = self.execute_command() is not False
     rv = self.command_info.function(*arglist)
     context.fatal_error("%s is not member of %s" % (prim_id, group_id))
     raise ValueError(msg)
 ValueError: st is not member of g1
-ERROR: 42: configure.modgroup: st is not member of g1
 .INP: modgroup g1 remove c1
+ERROR: 43: configure.modgroup: c1 is not member of g1
 Traceback (most recent call last):
     rv = self.execute_command() is not False
     rv = self.command_info.function(*arglist)
     context.fatal_error("%s is not member of %s" % (prim_id, group_id))
     raise ValueError(msg)
 ValueError: c1 is not member of g1
-ERROR: 43: configure.modgroup: c1 is not member of g1
 .INP: modgroup g1 remove nosuch
+ERROR: 44: configure.modgroup: nosuch is not member of g1
 Traceback (most recent call last):
     rv = self.execute_command() is not False
     rv = self.command_info.function(*arglist)
     context.fatal_error("%s is not member of %s" % (prim_id, group_id))
     raise ValueError(msg)
 ValueError: nosuch is not member of g1
-ERROR: 44: configure.modgroup: nosuch is not member of g1
 .INP: modgroup g1 add c1
-ERROR: a group may contain only primitives; c1 is clone
+ERROR: 45: a group may contain only primitives; c1 is clone
 .INP: modgroup g1 add nosuch
-ERROR: g1 refers to missing object nosuch
+ERROR: 46: g1 refers to missing object nosuch
 .INP: filter "sed 's/^/# this is a comment\n/'" loc-d1
 .INP: rsc_defaults $id="rsc_options" failure-timeout=10m
 .INP: filter "sed 's/2m/60s/'" op-options
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/scripts.exp 
new/crmsh-4.6.0+20231211.4b74412a/test/testcases/scripts.exp
--- old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/scripts.exp        
2023-12-07 10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/testcases/scripts.exp        
2023-12-11 03:36:33.000000000 +0100
@@ -240,13 +240,13 @@
 virtual-ip
 vmware
 .INP: list bogus
+ERROR: 7: script.list: Unexpected argument 'bogus': expected  [all|names]
 Traceback (most recent call last):
     rv = self.execute_command() is not False
     rv = self.command_info.function(*arglist)
     context.fatal_error("Unexpected argument '%s': expected  [all|names]" % 
(arg))
     raise ValueError(msg)
 ValueError: Unexpected argument 'bogus': expected  [all|names]
-ERROR: 7: script.list: Unexpected argument 'bogus': expected  [all|names]
 .INP: show mailto
 mailto (Basic)
 E-Mail
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/shadow.exp 
new/crmsh-4.6.0+20231211.4b74412a/test/testcases/shadow.exp
--- old/crmsh-4.6.0+20231207.89c74e6c/test/testcases/shadow.exp 2023-12-07 
10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/testcases/shadow.exp 2023-12-11 
03:36:33.000000000 +0100
@@ -11,13 +11,13 @@
 .EXT >/dev/null </dev/null crm_shadow -b -C 'regtest' --force
 INFO: 5: cib.commit: committed 'regtest' shadow CIB to the cluster
 .INP: delete regtest
+ERROR: 6: cib.delete: regtest shadow CIB is in use
 Traceback (most recent call last):
     rv = self.execute_command() is not False
     rv = self.command_info.function(*arglist)
     context.fatal_error("%s shadow CIB is in use" % name)
     raise ValueError(msg)
 ValueError: regtest shadow CIB is in use
-ERROR: 6: cib.delete: regtest shadow CIB is in use
 .INP: use
 .INP: delete regtest
 .EXT >/dev/null </dev/null crm_shadow -b -D 'regtest' --force
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_collect.py 
new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_collect.py
--- old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_collect.py     
2023-12-07 10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_collect.py     
2023-12-11 03:36:33.000000000 +0100
@@ -1,5 +1,6 @@
 from subprocess import TimeoutExpired
 from crmsh.report import collect, constants
+import crmsh.log
 
 import unittest
 from unittest import mock
@@ -80,13 +81,12 @@
         self.assertEqual(collect.get_corosync_log(), 
mock_get_value.return_value)
 
     @mock.patch('crmsh.report.utils.real_path')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.utils.get_cmd_output')
-    @mock.patch('logging.Logger.debug')
     @mock.patch('crmsh.report.utils.ts_to_str')
-    def test_collect_journal_logs(self, mock_ts_to_str, mock_debug, 
mock_get_cmd_output,
-                                  mock_str2file, mock_debug2, mock_real_path):
+    def test_collect_journal_logs(self, mock_ts_to_str, mock_get_cmd_output,
+                                  mock_str2file, mock_logger, mock_real_path):
         mock_real_path.side_effect = [
                 constants.JOURNAL_F,
                 constants.JOURNAL_PCMK_F,
@@ -113,14 +113,14 @@
             mock.call(cmd_list[2]),
             mock.call(cmd_list[3]),
             ])
-        mock_debug2.assert_has_calls([
+        mock_logger.debug2.assert_has_calls([
             mock.call("Collect journal logs since: 10.10 until: 10.12"),
             mock.call(f"Running command: {cmd_list[0]}"),
             mock.call(f"Running command: {cmd_list[1]}"),
             mock.call(f"Running command: {cmd_list[2]}"),
             mock.call(f"Running command: {cmd_list[3]}"),
             ])
-        mock_debug.assert_has_calls([
+        mock_logger.debug.assert_has_calls([
             mock.call(f"Dump jounal log for default into 
{constants.JOURNAL_F}"),
             mock.call(f"Dump jounal log for pacemaker into 
{constants.JOURNAL_PCMK_F}"),
             mock.call(f"Dump jounal log for corosync into 
{constants.JOURNAL_COROSYNC_F}"),
@@ -214,13 +214,13 @@
         mock_warning.assert_called_once_with('pe_to_dot: %s -> %s failed', 
'/opt/pe-input-0.bz2', '/opt/pe-input-0.dot')
 
     @mock.patch('crmsh.report.utils.find_files_in_timespan')
-    @mock.patch('crmsh.report.collect.logger.debug2')
-    def test_collect_pe_inputs_no_found(self, mock_debug, mock_find_files):
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
+    def test_collect_pe_inputs_no_found(self, mock_logger, mock_find_files):
         mock_ctx_inst = mock.Mock(pe_dir="/opt/pe_dir")
         mock_find_files.return_value = []
         collect.collect_pe_inputs(mock_ctx_inst)
         mock_find_files.assert_called_once_with(mock_ctx_inst, 
[mock_ctx_inst.pe_dir])
-        mock_debug.assert_has_calls([
+        mock_logger.debug2.assert_has_calls([
             mock.call(f"Looking for PE files in {mock_ctx_inst.pe_dir}"),
             mock.call("No PE file found for the giving time")
             ])
@@ -230,9 +230,8 @@
     @mock.patch('os.symlink')
     @mock.patch('crmsh.utils.mkdirp')
     @mock.patch('crmsh.report.utils.find_files_in_timespan')
-    @mock.patch('crmsh.report.collect.logger.debug2')
-    @mock.patch('logging.Logger.debug')
-    def test_collect_pe_inputs(self, mock_debug, mock_debug2, mock_find_files, 
mock_mkdir, mock_symlink, mock_to_dot, mock_real_path):
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
+    def test_collect_pe_inputs(self, mock_logger, mock_find_files, mock_mkdir, 
mock_symlink, mock_to_dot, mock_real_path):
         mock_real_path.return_value = "pe_dir"
         mock_ctx_inst = mock.Mock(pe_dir="/opt/pe_dir", 
work_dir="/opt/work_dir", speed_up=False)
         mock_find_files.return_value = ["/opt/pe_dir/pe_input1", 
"/opt/pe_dir/pe_input2"]
@@ -240,18 +239,17 @@
         collect.collect_pe_inputs(mock_ctx_inst)
 
         mock_find_files.assert_called_once_with(mock_ctx_inst, 
[mock_ctx_inst.pe_dir])
-        mock_debug2.assert_has_calls([
+        mock_logger.debug2.assert_has_calls([
             mock.call(f"Looking for PE files in {mock_ctx_inst.pe_dir}"),
             mock.call(f"Found 2 PE files in {mock_ctx_inst.pe_dir}"),
             ])
-        mock_debug.assert_called_once_with(f"Dump PE files into pe_dir")
+        mock_logger.debug.assert_called_once_with(f"Dump PE files into pe_dir")
 
     @mock.patch('crmsh.report.utils.real_path')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
-    @mock.patch('logging.Logger.warning')
     @mock.patch('crmsh.report.utils.get_cmd_output')
-    def test_collect_sys_stats(self, mock_run, mock_warning, mock_str2file, 
mock_debug2, mock_real_path):
+    def test_collect_sys_stats(self, mock_run, mock_str2file, mock_logger, 
mock_real_path):
         mock_real_path.return_value = constants.SYSSTATS_F
         mock_run.side_effect = [
                 "data_hostname", "data_uptime", "data_ps_axf", "data_ps_auxw",
@@ -260,7 +258,7 @@
                 ]
         mock_ctx_inst = mock.Mock(work_dir="/opt")
         collect.collect_sys_stats(mock_ctx_inst)
-        mock_warning.assert_called_once_with(f"Timeout while running command: 
df")
+        mock_logger.warning.assert_called_once_with(f"Timeout while running 
command: df")
         mock_run.assert_has_calls([
             mock.call("hostname", timeout=5),
             mock.call("uptime", timeout=5),
@@ -302,7 +300,7 @@
     @mock.patch('crmsh.report.utils.real_path')
     @mock.patch('crmsh.report.collect.dump_configurations')
     @mock.patch('crmsh.report.collect.consume_cib_in_workdir')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.collect.dump_runtime_state')
     @mock.patch('crmsh.report.collect.ServiceManager')
@@ -317,7 +315,7 @@
     @mock.patch('crmsh.report.utils.real_path')
     @mock.patch('crmsh.report.collect.dump_configurations')
     @mock.patch('crmsh.report.collect.consume_cib_in_workdir')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('shutil.copy2')
     @mock.patch('crmsh.report.collect.ServiceManager')
@@ -348,24 +346,23 @@
             mock.call("data2", f"/workdir/{constants.CRM_VERIFY_F}")
         ])
 
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.report.collect.sh.cluster_shell')
-    def test_collect_ratraces_return(self, mock_run, mock_debug):
+    def test_collect_ratraces_return(self, mock_run, mock_logger):
         mock_run_inst = mock.Mock()
         mock_run.return_value = mock_run_inst
         mock_run_inst.get_rc_stdout_stderr_without_input.return_value = (0, 
"data", None)
         mock_ctx_inst = mock.Mock(node_list=["node1"])
         collect.collect_ratraces(mock_ctx_inst)
-        mock_debug.assert_not_called()
+        mock_logger.debug2.assert_not_called()
 
     @mock.patch('crmsh.report.utils.real_path')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('shutil.copy2')
     @mock.patch('crmsh.utils.mkdirp')
     @mock.patch('crmsh.report.utils.find_files_in_timespan')
-    @mock.patch('logging.Logger.debug')
     @mock.patch('crmsh.report.collect.sh.cluster_shell')
-    def test_collect_ratraces(self, mock_run, mock_debug, mock_find, 
mock_mkdirp, mock_copy, mock_debug2, mock_real_path):
+    def test_collect_ratraces(self, mock_run, mock_find, mock_mkdirp, 
mock_copy, mock_logger, mock_real_path):
         mock_real_path.return_value = "/var/log"
         mock_run_inst = mock.Mock()
         mock_run.return_value = mock_run_inst
@@ -376,8 +373,8 @@
 
         collect.collect_ratraces(mock_ctx_inst)
 
-        mock_debug2.assert_called_once_with('Looking for RA trace files in 
"%s"', '/var/log/cluster')
-        mock_debug.assert_called_once_with(f'Dump RA trace files into 
{mock_real_path.return_value}')
+        mock_logger.debug2.assert_called_once_with('Looking for RA trace files 
in "%s"', '/var/log/cluster')
+        mock_logger.debug.assert_called_once_with(f'Dump RA trace files into 
{mock_real_path.return_value}')
 
     @mock.patch('crmsh.report.collect.ShellUtils')
     def test_lsof_ocfs2_device(self, mock_run):
@@ -406,7 +403,7 @@
         res = collect.ocfs2_commands_output()
         self.assertEqual(res, "\n\n#===== [ Command ] 
==========================#\n# mount\ndata")
 
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.collect.ShellUtils')
     def test_collect_ocfs2_info_error(self, mock_run, mock_str2file, 
mock_debug2):
@@ -417,7 +414,7 @@
         collect.collect_ocfs2_info(mock_ctx_inst)
         mock_str2file.assert_called_once_with('Failed to run "mounted.ocfs2 
-d": error', '/opt/workdir/ocfs2.txt')
 
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.collect.ShellUtils')
     def test_collect_ocfs2_info_no_found(self, mock_run, mock_str2file, 
mock_debug2):
@@ -432,7 +429,7 @@
     @mock.patch('crmsh.report.collect.ocfs2_commands_output')
     @mock.patch('crmsh.report.collect.lsof_ocfs2_device')
     @mock.patch('crmsh.report.collect.dump_D_process')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.collect.ShellUtils')
     def test_collect_ocfs2_info(self, mock_run, mock_str2file, mock_debug2, 
mock_D, mock_lsof, mock_output, mock_real_path):
@@ -492,19 +489,17 @@
         self.assertEqual("Core core.1 was generated by /usr/sbin/crm_mon", res)
 
     @mock.patch('crmsh.report.utils.real_path')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
-    @mock.patch('logging.Logger.warning')
     @mock.patch('shutil.which')
-    def test_dump_core_info_no_gdb(self, mock_which, mock_warning, 
mock_str2file, mock_debug2, mock_real_path):
+    def test_dump_core_info_no_gdb(self, mock_which, mock_str2file, 
mock_logger, mock_real_path):
         mock_real_path.return_value = constants.COREDUMP_F
         mock_which.return_value = False
         collect.dump_core_info("/opt/workdir", ["core.1"])
-        mock_warning.assert_called_once_with("Please install gdb to get more 
info for coredump files")
-        mock_debug2(f"Dump coredump info into {constants.COREDUMP_F}")
+        mock_logger.warning.assert_called_once_with("Please install gdb to get 
more info for coredump files")
 
     @mock.patch('crmsh.report.utils.real_path')
-    @mock.patch('crmsh.report.collect.logger.debug2')
+    @mock.patch('crmsh.report.collect.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.str2file')
     @mock.patch('crmsh.report.collect.find_binary_path_for_core')
     @mock.patch('shutil.which')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_core.py 
new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_core.py
--- old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_core.py        
2023-12-07 10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_core.py        
2023-12-11 03:36:33.000000000 +0100
@@ -1,5 +1,6 @@
 from crmsh import config
 from crmsh.report import core, constants, utils, collect
+import crmsh.log
 
 import sys
 import argparse
@@ -164,30 +165,30 @@
             ])
 
     @mock.patch('os.path.basename')
-    @mock.patch('crmsh.report.core.logger.debug2')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.mkdirp')
     @mock.patch('crmsh.report.core.is_collector')
     @mock.patch('crmsh.report.core.tmpfiles.create_dir')
-    def test_setup_workdir_collector(self, mock_create_dir, mock_collector, 
mock_mkdirp, mock_debug, mock_basename):
+    def test_setup_workdir_collector(self, mock_create_dir, mock_collector, 
mock_mkdirp, mock_logger, mock_basename):
         mock_create_dir.return_value = "/tmp/tmp_dir"
         mock_ctx_inst = mock.Mock(dest="/opt/report", 
work_dir="/opt/work_dir", me="node1")
         mock_collector.return_value = True
         mock_basename.return_value = "report"
         core.setup_workdir(mock_ctx_inst)
-        mock_debug.assert_called_once_with(f"Setup work directory in 
{mock_ctx_inst.work_dir}")
+        mock_logger.debug2.assert_called_once_with(f"Setup work directory in 
{mock_ctx_inst.work_dir}")
 
     @mock.patch('os.path.basename')
-    @mock.patch('crmsh.report.core.logger.debug2')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.mkdirp')
     @mock.patch('crmsh.report.core.is_collector')
     @mock.patch('crmsh.report.core.tmpfiles.create_dir')
-    def test_setup_workdir(self, mock_create_dir, mock_collector, mock_mkdirp, 
mock_debug, mock_basename):
+    def test_setup_workdir(self, mock_create_dir, mock_collector, mock_mkdirp, 
mock_logger, mock_basename):
         mock_create_dir.return_value = "/tmp/tmp_dir"
         mock_ctx_inst = mock.Mock(dest="/opt/report", work_dir="/opt/work_dir")
         mock_collector.return_value = False
         mock_basename.return_value = "report"
         core.setup_workdir(mock_ctx_inst)
-        mock_debug.assert_called_once_with(f"Setup work directory in 
{mock_ctx_inst.work_dir}")
+        mock_logger.debug2.assert_called_once_with(f"Setup work directory in 
{mock_ctx_inst.work_dir}")
 
     @mock.patch('os.path.isdir')
     @mock.patch('crmsh.report.core.load_from_crmsh_config')
@@ -242,8 +243,8 @@
     @mock.patch('crmsh.report.core.adjust_verbosity')
     @mock.patch('crmsh.report.core.config')
     @mock.patch('json.loads')
-    @mock.patch('crmsh.report.core.logger.debug2')
-    def test_load_context(self, mock_debug2, mock_json_loads, mock_config, 
mock_verbosity):
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
+    def test_load_context(self, mock_logger, mock_json_loads, mock_config, 
mock_verbosity):
         class Context:
             def __str__(self):
                 return "data"
@@ -255,7 +256,7 @@
         mock_json_loads.return_value = {"key": "value", "debug": "true"}
         mock_ctx_inst = Context()
         core.load_context(mock_ctx_inst)
-        mock_debug2.assert_called_once_with("Loading context from collector: 
data")
+        mock_logger.debug2.assert_called_once_with("Loading context from 
collector: data")
 
     @mock.patch('crmsh.report.core.adjust_verbosity')
     @mock.patch('crmsh.report.core.process_arguments')
@@ -361,10 +362,10 @@
 
         core.add_arguments()
 
-    @mock.patch('crmsh.report.core.logger.debug2')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.to_ascii')
     @mock.patch('crmsh.report.core.ShellUtils')
-    def test_push_data(self, mock_sh_utils, mock_to_ascii, mock_debug):
+    def test_push_data(self, mock_sh_utils, mock_to_ascii, mock_logger):
         mock_sh_utils_inst = mock.Mock()
         mock_sh_utils.return_value = mock_sh_utils_inst
         mock_sh_utils_inst.get_stdout_stderr.return_value = (0, "data", 
"error")
@@ -375,7 +376,7 @@
             core.push_data(mock_ctx_inst)
         self.assertEqual("error", str(err.exception))
 
-        mock_debug.assert_called_once_with("Pushing data from 
node1:/opt/work_dir to node1")
+        mock_logger.debug2.assert_called_once_with("Pushing data from 
node1:/opt/work_dir to node1")
         mock_sh_utils_inst.get_stdout_stderr.assert_called_once_with("cd 
/opt/work_dir/.. && tar -h -c node1", raw=True)
 
     @mock.patch('crmsh.report.core.finalword')
@@ -392,7 +393,7 @@
 
     @mock.patch('crmsh.report.core.finalword')
     @mock.patch('crmsh.report.core.sh.cluster_shell')
-    @mock.patch('crmsh.report.core.logger.debug2')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.report.utils.create_description_template')
     @mock.patch('crmsh.report.utils.analyze')
     @mock.patch('crmsh.report.utils.do_sanitize')
@@ -478,21 +479,19 @@
         mock_ctx_inst = mock.Mock(from_time=123, to_time=150)
         core.process_arguments(mock_ctx_inst)
 
-    @mock.patch('crmsh.report.core.logger.debug2')
-    @mock.patch('logging.Logger.warning')
-    @mock.patch('logging.Logger.debug')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.utils.check_ssh_passwd_need')
     @mock.patch('crmsh.report.core.userdir.getuser')
     @mock.patch('crmsh.report.core.userdir.get_sudoer')
-    def test_find_ssh_user_not_found(self, mock_get_sudoer, mock_getuser, 
mock_check_ssh, mock_debug, mock_warn, mock_debug2):
+    def test_find_ssh_user_not_found(self, mock_get_sudoer, mock_getuser, 
mock_check_ssh, mock_logger):
         mock_get_sudoer.return_value = ""
         mock_getuser.return_value = "user2"
         mock_check_ssh.return_value = True
         mock_ctx_inst = mock.Mock(ssh_user="", ssh_askpw_node_list=[], 
node_list=["node1", "node2"], me="node1")
         core.find_ssh_user(mock_ctx_inst)
-        mock_warn.assert_called_once_with(f"passwordless ssh to node(s) 
['node2'] does not work")
+        mock_logger.warning.assert_called_once_with(f"passwordless ssh to 
node(s) ['node2'] does not work")
 
-    @mock.patch('crmsh.report.core.logger.debug2')
+    @mock.patch('crmsh.report.core.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('logging.Logger.warning')
     @mock.patch('logging.Logger.debug')
     @mock.patch('crmsh.utils.check_ssh_passwd_need')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_utils.py 
new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_utils.py
--- old/crmsh-4.6.0+20231207.89c74e6c/test/unittests/test_report_utils.py       
2023-12-07 10:20:33.000000000 +0100
+++ new/crmsh-4.6.0+20231211.4b74412a/test/unittests/test_report_utils.py       
2023-12-11 03:36:33.000000000 +0100
@@ -3,6 +3,7 @@
 from crmsh import config
 from crmsh import utils as crmutils
 from crmsh.report import utils, constants
+import crmsh.log
 
 import unittest
 from unittest import mock
@@ -160,19 +161,19 @@
         self.assertEqual(self.s_inst.cib_data, "data")
         mock_read.assert_called_once_with(f"/opt/node1/{constants.CIB_F}")
 
-    @mock.patch('crmsh.report.utils.logger.debug2')
-    def test_parse_sensitive_set_no_set(self, mock_debug2):
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
+    def test_parse_sensitive_set_no_set(self, mock_logger):
         config.report.sanitize_rule = ""
         self.s_inst_no_sanitize_set._parse_sensitive_set()
         self.assertEqual(self.s_inst_no_sanitize_set.sensitive_regex_set, 
set(utils.Sanitizer.DEFAULT_RULE_LIST))
-        mock_debug2.assert_called_once_with(f"Regex set to match sensitive 
data: {set(utils.Sanitizer.DEFAULT_RULE_LIST)}")
+        mock_logger.debug2.assert_called_once_with(f"Regex set to match 
sensitive data: {set(utils.Sanitizer.DEFAULT_RULE_LIST)}")
 
-    @mock.patch('crmsh.report.utils.logger.debug2')
-    def test_parse_sensitive_set(self, mock_debug2):
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
+    def test_parse_sensitive_set(self, mock_logger):
         config.report.sanitize_rule = "passw.*"
         self.s_inst._parse_sensitive_set()
         self.assertEqual(self.s_inst.sensitive_regex_set, set(['test_patt', 
'passw.*']))
-        mock_debug2.assert_called_once_with(f"Regex set to match sensitive 
data: {set(['test_patt', 'passw.*'])}")
+        mock_logger.debug2.assert_called_once_with(f"Regex set to match 
sensitive data: {set(['test_patt', 'passw.*'])}")
 
     def test_sanitize_return(self):
         self.s_inst_no_sanitize.sanitize()
@@ -234,10 +235,10 @@
 
     @mock.patch('builtins.sorted', side_effect=lambda x, *args, **kwargs: 
x[::-1])
     @mock.patch('crmsh.report.utils.get_timespan_str')
-    @mock.patch('crmsh.report.utils.logger.debug2')
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('glob.glob')
     @mock.patch('crmsh.report.utils.is_our_log')
-    def test_arch_logs(self, mock_is_our_log, mock_glob, mock_debug2, 
mock_timespan, mock_sorted):
+    def test_arch_logs(self, mock_is_our_log, mock_glob, mock_logger, 
mock_timespan, mock_sorted):
         mock_is_our_log.return_value = utils.LogType.GOOD
         mock_glob.return_value = []
         mock_ctx_inst = mock.Mock()
@@ -247,7 +248,7 @@
 
         self.assertEqual(return_list, ["file1"])
         self.assertEqual(log_type, utils.LogType.GOOD)
-        mock_debug2.assert_called_once_with("Found logs ['file1'] in 
0101-0202")
+        mock_logger.debug2.assert_called_once_with("Found logs ['file1'] in 
0101-0202")
 
     @mock.patch('sys.stdout.flush')
     @mock.patch('traceback.print_exc')
@@ -360,7 +361,7 @@
         self.assertEqual(res, expected_result)
 
     @mock.patch('crmsh.report.utils.get_timespan_str')
-    @mock.patch('crmsh.report.utils.logger.debug2')
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.report.utils.arch_logs')
     def test_dump_logset_return(self, mock_arch, mock_debug, mock_timespan):
         mock_arch.return_value = [[], ""]
@@ -428,7 +429,7 @@
 
     @mock.patch('crmsh.utils.read_from_file')
     @mock.patch('os.path.exists')
-    @mock.patch('crmsh.report.utils.logger.debug2')
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
     def test_get_distro_info(self, mock_debug2, mock_exists, mock_read):
         mock_exists.return_value = True
         mock_read.return_value = """
@@ -442,7 +443,7 @@
     @mock.patch('shutil.which')
     @mock.patch('crmsh.report.utils.sh.LocalShell')
     @mock.patch('os.path.exists')
-    @mock.patch('crmsh.report.utils.logger.debug2')
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
     def test_get_distro_info_lsb(self, mock_debug2, mock_exists, mock_sh, 
mock_which):
         mock_which.return_value = True
         mock_exists.return_value = False
@@ -643,15 +644,15 @@
         self.assertEqual(res, "")
 
     @mock.patch('crmsh.report.utils.filter_lines')
-    @mock.patch('crmsh.report.utils.logger.debug2')
+    @mock.patch('crmsh.report.utils.logger', spec=crmsh.log.DEBUG2Logger)
     @mock.patch('crmsh.report.utils.findln_by_timestamp')
     @mock.patch('crmsh.utils.read_from_file')
-    def test_print_logseg(self, mock_read, mock_findln, mock_debug, 
mock_filter):
+    def test_print_logseg(self, mock_read, mock_findln, mock_logger, 
mock_filter):
         mock_read.return_value = "line1\nline2\nline3"
         mock_filter.return_value = "line1\nline2\nline3"
         res = utils.print_logseg("log1", 0, 0)
         self.assertEqual(res, mock_filter.return_value)
-        mock_debug.assert_called_once_with("Including segment [%d-%d] from 
%s", 1, 3, "log1")
+        mock_logger.debug2.assert_called_once_with("Including segment [%d-%d] 
from %s", 1, 3, "log1")
 
     def test_head(self):
         data = "line1\nline2\nline3"

Reply via email to