From cb721341d8ada9bfb0be02de69193177f626f52c Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox Date: Fri, 7 Jun 2024 09:23:44 +0300 Subject: [PATCH] Add a command to update log level and refresh configuration --- config/syslog.py | 49 ++++++++++++++++++++++++++ doc/Command-Reference.md | 29 +++++++++++++++ tests/syslog_test.py | 76 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/config/syslog.py b/config/syslog.py index a5d520d9cf..81b0d08eed 100644 --- a/config/syslog.py +++ b/config/syslog.py @@ -642,3 +642,52 @@ def disable_rate_limit_feature(db, service_name, namespace): if not failed: click.echo(f'Disabled syslog rate limit feature for {feature_name}') + + +@syslog.command('level') +@click.option("-c", "--component", + required=True, + help="Component name in DB for which loglevel is applied (provided with -l)") +@click.option("-l", "--level", + required=True, + help="Loglevel value", + type=click.Choice(['DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERROR'])) +@click.option("--service", + help="Container name to which the SIGHUP is sent (provided with --pid or --program)") +@click.option("--program", + help="Program name to which the SIGHUP is sent (provided with --service)") +@click.option("--pid", + help="Process ID to which the SIGHUP is sent (provided with --service if PID is from container)") +@clicommon.pass_db +def level(db, component, level, service, program, pid): + """ Configure log level """ + if program and not service: + raise click.UsageError('--program must be specified with --service') + + if service and not program and not pid: + raise click.UsageError('--service must be specified with --pid or --program') + + if component and level: + output, ret = clicommon.run_command(['swssloglevel', '-c', component, '-l', level], return_cmd=True) + if ret != 0: + raise click.ClickException(f'Failed: {output}') + + if not service and not program and not pid: + return + + log_config = db.cfgdb.get_entry('LOGGER', component) + require_manual_refresh = log_config.get('require_manual_refresh') + if not require_manual_refresh: + click.echo(f'Log {component} does not need manual refresh') + return + + if service: + if program: + command = ['docker', 'exec', '-i', service, 'supervisorctl', 'signal', 'HUP', program] + else: + command = ['docker', 'exec', '-i', service, 'kill', '-s', 'SIGHUP', pid] + else: + command = ['kill', '-s', 'SIGHUP', pid] + output, ret = clicommon.run_command(command, return_cmd=True) + if ret != 0: + raise click.ClickException(f'Failed: {output}') diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 757438dad0..d4fdea2b5f 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -10713,6 +10713,35 @@ This command is used to disable syslog rate limit feature. config syslog rate-limit-feature disable database -n asci0 ``` +**config syslog level** + +This command is used to configure log level for a given log identifier. + +- Usage: + ``` + config syslog level -c -l --service [] --program [] + + config syslog level -c -l --service [] --pid [] + + config syslog level -c -l ---pid [] + ``` + +- Example: + + ``` + # Update the log level without refresh the configuration + config syslog level -c xcvrd -l DEBUG + + # Update the log level and send SIGHUP to xcvrd running in PMON + config syslog level -c xcvrd -l DEBUG --service pmon --program xcvrd + + # Update the log level and send SIGHUP to PID 20 running in PMON + config syslog level -c xcvrd -l DEBUG --service pmon --pid 20 + + # Update the log level and send SIGHUP to PID 20 running in host + config syslog level -c xcvrd -l DEBUG --pid 20 + ``` + Go Back To [Beginning of the document](#) or [Beginning of this section](#syslog) ## System State diff --git a/tests/syslog_test.py b/tests/syslog_test.py index c1cbee1127..4915b9f95c 100644 --- a/tests/syslog_test.py +++ b/tests/syslog_test.py @@ -484,3 +484,79 @@ def side_effect(*args, **kwargs): config.config.commands["syslog"].commands["rate-limit-feature"].commands["disable"], obj=db ) assert result.exit_code == SUCCESS + + @mock.patch('config.syslog.clicommon.run_command') + def test_config_log_level(self, mock_run): + db = Db() + db.cfgdb.set_entry('LOGGER', 'log1', {'require_manual_refresh': 'true'}) + + runner = CliRunner() + + mock_run.return_value = ('something', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'component', '-l', 'DEBUG'], obj=db + ) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'component', '-l', 'DEBUG', '--pid', '123'], obj=db + ) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'component', '-l', 'DEBUG', '--service', 'pmon', '--pid', '123'], obj=db + ) + assert result.exit_code == SUCCESS + + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'component', '-l', 'DEBUG', '--service', 'pmon', '--program', 'xcvrd'], obj=db + ) + assert result.exit_code == SUCCESS + + @mock.patch('config.syslog.clicommon.run_command') + def test_config_log_level_negative(self, mock_run): + db = Db() + + runner = CliRunner() + + mock_run.return_value = ('something', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'log1', '-l', 'DEBUG', '--service', 'pmon'], obj=db + ) + assert result.exit_code != SUCCESS + + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'log1', '-l', 'DEBUG', '--program', 'xcvrd'], obj=db + ) + assert result.exit_code != SUCCESS + + mock_run.reset_mock() + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'log1', '-l', 'DEBUG', '--service', 'swss', '--program', 'orchagent'], obj=db + ) + assert result.exit_code == SUCCESS + # Verify it does not send signal to orchagent if require_manual_refresh is not true + assert mock_run.call_count == 1 + + mock_run.return_value = ('something', -1) + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'log1', '-l', 'DEBUG'], obj=db + ) + + assert result.exit_code != SUCCESS + + mock_run.side_effect = [('something', 0), ('something', -1)] + db.cfgdb.set_entry('LOGGER', 'log1', {'require_manual_refresh': 'true'}) + result = runner.invoke( + config.config.commands["syslog"].commands["level"], + ['-c', 'log1', '-l', 'DEBUG', '--service', 'pmon', '--program', 'xcvrd'], obj=db + ) + assert result.exit_code != SUCCESS