]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: Control cephadm.log messages based on a new mgr logging level flag
authorAshwin M. Joshi <ashjosh1@in.ibm.com>
Tue, 10 Feb 2026 06:29:49 +0000 (11:59 +0530)
committerAshwin M. Joshi <ashjosh1@in.ibm.com>
Fri, 12 Jun 2026 08:07:31 +0000 (13:37 +0530)
  Introduces a new 'cephadm_binary_logging_level' config option to control
  the verbosity of cephadm logging to persistent destinations (cephadm.log, syslog).

  - Adds --logging-level CLI flag (info, debug, error, warning)
  - Adds mgr/cephadm/cephadm_binary_logging_level config option
  - Applies logging level to file and syslog handlers
  - Console handlers maintain their defaults for terminal UX

Fixes: https://tracker.ceph.com/issues/74872
Signed-off-by: Ashwin M. Joshi <ashjosh1@in.ibm.com>
src/cephadm/cephadm.py
src/cephadm/cephadmlib/context.py
src/cephadm/cephadmlib/logging.py
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/serve.py
src/pybind/mgr/cephadm/tests/test_cephadm.py

index 45cb599e6481198a4f43bdeaad1dda8c303306b6..4bf0f2b1015104060fc7681001e6a2a37f024689 100755 (executable)
@@ -4977,6 +4977,11 @@ def _get_parser():
         action='store_true',
         default=False,
         help='Do not run containers with --cgroups=split (currently only relevant when using podman)')
+    parser.add_argument(
+        '--logging-level',
+        choices=['info', 'debug', 'error', 'warning'],
+        default='debug',
+        help='Tunable log level for cephadm binary: info, debug, error, warning (default: debug)')
 
     subparsers = parser.add_subparsers(help='sub-command')
 
index 3411c199ebb689a850c667704eabb2166be0ae1f..502d2c239c7038f9087d334adaa022065deb46c8 100644 (file)
@@ -31,6 +31,7 @@ class BaseConfig:
         self.memory_request: Optional[int] = None
         self.memory_limit: Optional[int] = None
         self.log_to_journald: Optional[bool] = None
+        self.logging_level: str = 'debug'
 
         self.container_init: bool = CONTAINER_INIT
         # FIXME(refactor) : should be Optional[ContainerEngine]
index f722a33e78dff950c313ce2d94c842cad5e4b05c..b343e0d79dca2f1dcc2760037a39c053f6bac9b6 100644 (file)
@@ -173,7 +173,9 @@ def _copy(obj: Any) -> Any:
 
 
 def _complete_logging_config(
-    interactive: bool, destinations: Optional[List[str]]
+    interactive: bool,
+    destinations: Optional[List[str]],
+    logging_level: str = 'debug',
 ) -> Dict[str, Any]:
     """Return a logging configuration dict, based on the runtime parameters
     cephadm was invoked with.
@@ -183,6 +185,12 @@ def _complete_logging_config(
     if interactive:
         lc = _copy(_interactive_logging_config)
 
+    # Apply logging level to persistent destinations only (cephadm.log, syslog).
+    # Console handlers keep their template defaults for terminal UX.
+    level_upper = logging_level.upper()
+    lc['handlers']['log_file']['level'] = level_upper
+    lc['handlers']['syslog']['level'] = level_upper
+
     handlers = lc['loggers']['']['handlers']
     if not destinations:
         handlers.append(LogDestination.file.value)
@@ -206,9 +214,11 @@ def cephadm_init_logging(
     if not os.path.exists(LOG_DIR):
         os.makedirs(LOG_DIR)
 
+    logging_level = getattr(ctx, 'logging_level', 'debug').lower()
     lc = _complete_logging_config(
         any(op in args for op in _INTERACTIVE_CMDS),
         getattr(ctx, 'log_dest', None),
+        logging_level=logging_level,
     )
     logging.config.dictConfig(lc)
 
@@ -228,6 +238,7 @@ def cephadm_init_logging(
         # option is set
         if ctx.verbose and handler.name in _VERBOSE_HANDLERS:
             handler.setLevel(QUIET_LOG_LEVEL)
+
     logger.debug('%s\ncephadm %s' % ('-' * 80, args))
 
 
index ef4699838d1054e5fb8c9f7a6e4ac8f40c947643..555b70657613651b3052da42639265cbee61c09a 100644 (file)
@@ -509,6 +509,13 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule):
             desc="Destination for cephadm command persistent logging",
             enum_allowed=['file', 'syslog', 'file,syslog'],
         ),
+        Option(
+            'cephadm_binary_logging_level',
+            type='str',
+            default='debug',
+            desc='Logging verbosity for the cephadm binary when invoked by the mgr (e.g. check-host, gather-facts).',
+            enum_allowed=['info', 'debug', 'error', 'warning']
+        ),
         Option(
             'oob_default_addr',
             type='str',
@@ -622,6 +629,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule):
             self.certificate_automated_rotation_enabled = False
             self.certificate_check_debug_mode = False
             self.certificate_check_period = 0
+            self.cephadm_binary_logging_level = 'debug'
 
         self.notify(NotifyType.mon_map, None)
         self.config_notify()
index 1f1b5c2fd6f8258dfb469bb65d0c9cecfc488045..e4f57d8a7eee58d0be06f27653611355f2825787 100644 (file)
@@ -1738,6 +1738,9 @@ class CephadmServe:
         if image:
             final_args.extend(['--image', image])
 
+        cephadm_log_level = self.mgr.cephadm_binary_logging_level or 'debug'
+        final_args.extend(['--logging-level', cephadm_log_level])
+
         if not self.mgr.container_init:
             final_args += ['--no-container-init']
 
index e462d17e7614a505752e1d85ea5b17b19fc7e865..269a1664e68d85c4d199b52021318d6c8d1d9a2f 100644 (file)
@@ -3117,3 +3117,112 @@ Traceback (most recent call last):
                 assert wait(cephadm_module, c) == ['Scheduled osd.foo update...']
 
                 cephadm_module.set_osd_spec('osd.foo', ['1'])
+
+
+class TestCephadmBinaryLoggingLevel:
+    """Test that host-status / cephadm binary logs are suppressed based on
+    mgr/cephadm/cephadm_binary_logging_level.
+    """
+    @pytest.mark.parametrize("logging_level", ['info', 'debug', 'error', 'warning'])
+    @mock.patch("cephadm.ssh.SSHManager._remote_connection")
+    @mock.patch("cephadm.ssh.SSHManager._execute_command")
+    @mock.patch("cephadm.ssh.SSHManager._check_execute_command")
+    def test_check_host_invokes_cephadm_with_logging_level(
+        self, check_execute_command, execute_command, remote_connection, cephadm_module, logging_level
+    ):
+        """Cephadm binary must be invoked with --logging-level matching
+        mgr/cephadm/cephadm_binary_logging_level (info, debug, error, warning).
+        Check that mgr builds the cephadm command with appropriate --logging-level flag
+        Use check-host as a sample command although all cephadm commands receive the flag.
+        """
+        remote_connection.side_effect = async_side_effect(mock.Mock())
+        check_execute_command.side_effect = async_side_effect('/usr/bin/python3')
+        captured_commands = []
+
+        async def capture_execute(host, cmd, *args, **kwargs):
+            if hasattr(cmd, 'args'):
+                if 'check-host' in cmd.args:
+                    captured_commands.append(cmd)
+                # Return valid JSON for commands that use _run_cephadm_json
+                if 'ls' in cmd.args:
+                    return ('[]', '', 0)
+                if 'gather-facts' in cmd.args:
+                    return ('{}', '', 0)
+                if 'list-networks' in cmd.args:
+                    return ('[]', '', 0)
+            return ('', '', 0)
+
+        execute_command.side_effect = capture_execute
+
+        cephadm_module.cephadm_binary_logging_level = logging_level
+        with with_host(cephadm_module, 'test'):
+            pass
+
+        check_host_cmds = [c for c in captured_commands if 'check-host' in c.args]
+        assert len(check_host_cmds) >= 1, (
+            f'expected at least one check-host invocation for level {logging_level!r}'
+        )
+        cmd = check_host_cmds[0]
+        assert '--logging-level' in cmd.args, (
+            f'cephadm should be called with --logging-level when level is {logging_level!r}'
+        )
+        idx = cmd.args.index('--logging-level')
+        assert cmd.args[idx + 1] == logging_level, (
+            f'cephadm should be called with --logging-level {logging_level!r}'
+        )
+
+
+class TestCephadmLogDestination:
+    """Test that cephadm is invoked with --log-dest matching
+    mgr/cephadm/cephadm_log_destination.
+    """
+    @pytest.mark.parametrize(
+        "log_destination,expected_log_dests",
+        [
+            ('file', ['file']),
+            ('syslog', ['syslog']),
+            ('file,syslog', ['file', 'syslog']),
+        ],
+    )
+    @mock.patch("cephadm.ssh.SSHManager._remote_connection")
+    @mock.patch("cephadm.ssh.SSHManager._execute_command")
+    @mock.patch("cephadm.ssh.SSHManager._check_execute_command")
+    def test_check_host_invokes_cephadm_with_log_dest(
+        self, check_execute_command, execute_command, remote_connection,
+        cephadm_module, log_destination, expected_log_dests,
+    ):
+        """check-host must be invoked with --log-dest matching
+        mgr/cephadm/cephadm_log_destination (file, syslog, or both).
+        """
+        remote_connection.side_effect = async_side_effect(mock.Mock())
+        check_execute_command.side_effect = async_side_effect('/usr/bin/python3')
+        captured_commands = []
+
+        async def capture_execute(host, cmd, *args, **kwargs):
+            if hasattr(cmd, 'args'):
+                if 'check-host' in cmd.args:
+                    captured_commands.append(cmd)
+                if 'ls' in cmd.args:
+                    return ('[]', '', 0)
+                if 'gather-facts' in cmd.args:
+                    return ('{}', '', 0)
+                if 'list-networks' in cmd.args:
+                    return ('[]', '', 0)
+            return ('', '', 0)
+
+        execute_command.side_effect = capture_execute
+
+        cephadm_module.cephadm_log_destination = log_destination
+        with with_host(cephadm_module, 'test'):
+            pass
+
+        check_host_cmds = [c for c in captured_commands if 'check-host' in c.args]
+        assert len(check_host_cmds) >= 1, (
+            f'expected at least one check-host invocation for dest {log_destination!r}'
+        )
+        cmd = check_host_cmds[0]
+        for dest in expected_log_dests:
+            assert f'--log-dest={dest}' in cmd.args, (
+                f'cephadm should be called with --log-dest={dest!r} '
+                f'when cephadm_log_destination is {log_destination!r}'
+            )