diff options
| author | Adam Schultz <adam.schultz@live.com> | 2026-01-24 09:55:14 -0500 |
|---|---|---|
| committer | Adam Schultz <adam.schultz@live.com> | 2026-02-09 22:59:37 -0500 |
| commit | 696df602557dd7b70b353c3b9cfb110c0908a2fe (patch) | |
| tree | d7ec98633981069f21b0af09575bf1e53cf79f56 /smoketest/scripts/cli | |
| parent | 9707b90b9ddacd27b74ef235a6f5a8f585c2bc50 (diff) | |
| download | vyos-1x-696df602557dd7b70b353c3b9cfb110c0908a2fe.tar.gz vyos-1x-696df602557dd7b70b353c3b9cfb110c0908a2fe.zip | |
T7756: Fix rsyslog configuration generation
1.) Fix both syslog and logrotate writing /etc/logrotate.d/vyos-rsyslog
- system_syslog.py generates /etc/logrotate.d/vyos-rsyslog-user for user file
logrotate
- system_logs.py generates /etc/logrotate.d/vyos-rsyslog using configured
rotation parameters
2.) Fix /run/rsyslog/rsyslog.conf ignoring configured size for /var/log/messages
- system_syslog.py retrieves "system logs" config to set rotation limits
- default rotation size is now based on configuration defaults
3.) prifilt strings for rsyslog are modified to use override syntax
(e.g. ";facility.none") when all is combined with other facilities to honor
per facility user intent
Smoke test changes:
Commonize configuration of syslog facilities
Check that prifilt is sane based on configured facilities (i.e. overides only
present when required)
Test wildcard (all) only, specific facility only, and wildcard + specific facility
Diffstat (limited to 'smoketest/scripts/cli')
| -rwxr-xr-x | smoketest/scripts/cli/test_system_logs.py | 12 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_system_syslog.py | 148 |
2 files changed, 118 insertions, 42 deletions
diff --git a/smoketest/scripts/cli/test_system_logs.py b/smoketest/scripts/cli/test_system_logs.py index 81fbf8048..ccddcdec6 100755 --- a/smoketest/scripts/cli/test_system_logs.py +++ b/smoketest/scripts/cli/test_system_logs.py @@ -18,18 +18,20 @@ import re import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.utils.file import read_file +from vyos.xml_ref import default_value # path to logrotate configs logrotate_atop_file = '/etc/logrotate.d/vyos-atop' logrotate_rsyslog_file = '/etc/logrotate.d/vyos-rsyslog' -# default values -default_atop_maxsize = '10M' -default_atop_rotate = '10' -default_rsyslog_size = '1M' -default_rsyslog_rotate = '10' base_path = ['system', 'logs'] +# default values +default_atop_maxsize = f"{default_value(base_path + ['logrotate', 'atop', 'max-size'])}M" +default_atop_rotate = default_value(base_path + ['logrotate', 'atop', 'rotate']) +default_rsyslog_size = f"{default_value(base_path + ['logrotate', 'messages', 'max-size'])}M" +default_rsyslog_rotate = default_value(base_path + ['logrotate', 'messages', 'rotate']) + def logrotate_config_parse(file_path): # read the file diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py index 79fbe27ec..2d1e0aef4 100755 --- a/smoketest/scripts/cli/test_system_syslog.py +++ b/smoketest/scripts/cli/test_system_syslog.py @@ -30,6 +30,7 @@ RSYSLOG_CONF = '/run/rsyslog/rsyslog.conf' CERT_DIR = '/etc/rsyslog.d/certs' base_path = ['system', 'syslog'] +base_logs_path = ['system', 'logs'] pki_base = ['pki'] dummy_interface = 'dum372874' @@ -94,6 +95,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): # delete testing SYSLOG config self.cli_delete(base_path) + self.cli_delete(base_logs_path) self.cli_commit() # The default syslog implementation should make syslog.service a @@ -130,23 +132,115 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): ] ) + def _set_facilities(self, base, facility_map): + for facility, facility_options in facility_map.items(): + level = facility_options['level'] + self.cli_set(base + ['facility', facility, 'level'], value=level) + + # Build prifilt selector strings the same way as rsyslog.conf.j2, e.g. + # "auth.info,*.notice;auth.none" when specific facilities coexist with "all". + def _prifilt_selectors(self, facility_map): + keys = sorted(facility_map) + specific = [] + for facility in keys: + if facility != 'all': + specific.append(facility) + + selectors = [] + for facility in keys: + opts = facility_map[facility] + level = opts['level'].replace('all', 'debug') + if facility == 'all': + sel = f'*.{level}' + for sf in specific: + sel += f';{sf}.none' + else: + sel = f'{facility}.{level}' + selectors.append(sel) + + prifilt = ','.join(selectors) + self._assert_prifilt_sane(prifilt, facility_map) + return prifilt + + def _assert_prifilt_sane(self, prifilt, facility_map): + has_all = 'all' in facility_map + specific_facilities = sorted(f for f in facility_map if f != 'all') + self.assertTrue(prifilt) + self.assertNotIn(' ', prifilt) + parts = prifilt.split(',') + self.assertTrue(all(parts)) + wildcard_parts = [p for p in parts if p.startswith('*.')] + if has_all: + self.assertEqual(len(wildcard_parts), 1) + wildcard = wildcard_parts[0] + if specific_facilities: + base, *exclusions = wildcard.split(';') + self.assertTrue(base.startswith('*.')) + expected = {f'{fac}.none' for fac in specific_facilities} + self.assertEqual(set(exclusions), expected) + else: + self.assertNotIn(';', wildcard) + else: + self.assertEqual(len(wildcard_parts), 0) + self.assertNotIn(';', prifilt) + + for part in parts: + if ';' in part: + base, *exclusions = part.split(';') + self.assertTrue(base.startswith('*.')) + self.assertNotIn('*.none', base) + for ex in exclusions: + self.assertTrue(ex.endswith('.none')) + self.assertFalse(ex.startswith('*.')) + self.assertNotIn(',', ex) + else: + if part.startswith('*.'): + self.assertTrue(has_all) + continue + self.assertEqual(part.count('.'), 1) + self.assertFalse(part.startswith('*.')) + def test_console(self): - level = 'warning' - self.cli_set(base_path + ['console', 'facility', 'all', 'level'], value=level) + facility = { + 'all': {'level': 'warning'}, + } + self._set_facilities(base_path + ['console'], facility) self.cli_commit() rsyslog_conf = get_config() - config = [ - f'if prifilt("*.{level}") then {{', # {{ required to escape { in f-string - 'action(type="omfile" file="/dev/console")', - ] - for tmp in config: - self.assertIn(tmp, rsyslog_conf) + expected_prifilt = self._prifilt_selectors(facility) + self.assertIn(f'if prifilt("{expected_prifilt}") then {{', rsyslog_conf) + self.assertIn('action(type="omfile" file="/dev/console")', rsyslog_conf) + + self.cli_delete(base_path + ['console']) + facility = { + 'auth': {'level': 'info'}, + 'kern': {'level': 'debug'}, + } + self._set_facilities(base_path + ['console'], facility) + self.cli_commit() + + rsyslog_conf = get_config() + expected_prifilt = self._prifilt_selectors(facility) + self.assertIn(f'if prifilt("{expected_prifilt}") then {{', rsyslog_conf) + self.assertIn('action(type="omfile" file="/dev/console")', rsyslog_conf) + + facility['all'] = {'level': 'notice'} + self._set_facilities(base_path + ['console'], facility) + self.cli_commit() + + rsyslog_conf = get_config() + expected_prifilt = self._prifilt_selectors(facility) + self.assertIn(f'if prifilt("{expected_prifilt}") then {{', rsyslog_conf) + self.assertIn('action(type="omfile" file="/dev/console")', rsyslog_conf) def test_basic(self): hostname = 'vyos123' domain_name = 'example.local' default_marker_interval = default_value(base_path + ['marker', 'interval']) + default_rsyslog_max_size = default_value( + base_logs_path + ['logrotate', 'messages', 'max-size'] + ) facility = { 'auth': {'level': 'info'}, @@ -158,9 +252,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): self.cli_set(['system', 'domain-name'], value=domain_name) self.cli_set(base_path + ['preserve-fqdn']) - for tmp, tmp_options in facility.items(): - level = tmp_options['level'] - self.cli_set(base_path + ['local', 'facility', tmp, 'level'], value=level) + self._set_facilities(base_path + ['local'], facility) self.cli_commit() @@ -174,21 +266,13 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): self.assertIn(e, config) config = get_config('#### GLOBAL LOGGING ####') - prifilt = [] - for tmp, tmp_options in facility.items(): - if tmp == 'all': - tmp = '*' - level = tmp_options['level'] - prifilt.append(f'{tmp}.{level}') - - prifilt.sort() - prifilt = ','.join(prifilt) - - self.assertIn(f'if prifilt("{prifilt}") then {{', config) + expected_prifilt = self._prifilt_selectors(facility) + self.assertIn(f'if prifilt("{expected_prifilt}") then {{', config) self.assertIn( ' action(', config) self.assertIn( ' type="omfile"', config) self.assertIn( ' file="/var/log/messages"', config) - self.assertIn( ' rotation.sizeLimit="524288"', config) + size_limit = int(default_rsyslog_max_size) * 1024 * 1024 + self.assertIn(f' rotation.sizeLimit="{size_limit}"', config) self.assertIn( ' rotation.sizeLimitCommand="/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog"', config) self.cli_set(base_path + ['marker', 'disable']) @@ -234,10 +318,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): self.cli_set(remote_base + ['port'], value=remote_options['port']) if 'facility' in remote_options: - for facility, facility_options in remote_options['facility'].items(): - level = facility_options['level'] - self.cli_set(remote_base + ['facility', facility, 'level'], - value=level) + self._set_facilities(remote_base, remote_options['facility']) if 'format' in remote_options: for format in remote_options['format']: @@ -261,16 +342,9 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): config = read_file(RSYSLOG_CONF) for remote, remote_options in rhosts.items(): config = get_config(f'# Remote syslog to {remote}') - prifilt = [] + prifilt = '' if 'facility' in remote_options: - for facility, facility_options in remote_options['facility'].items(): - level = facility_options['level'] - if facility == 'all': - facility = '*' - prifilt.append(f'{facility}.{level}') - - prifilt.sort() - prifilt = ','.join(prifilt) + prifilt = self._prifilt_selectors(remote_options['facility']) if not prifilt: # Skip test - as we do not render anything if no facility is set continue @@ -429,7 +503,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): self.assertIn(f'StreamDriverPermittedPeers="{value}"', config) if not tls: - self.assertIn(f'StreamDriverAuthMode="anon"', config) + self.assertIn('StreamDriverAuthMode="anon"', config) def test_remote_tls_protocol_udp(self): remote_base = base_path + ['remote', '172.11.0.1'] |
