Skip to content

Commit 252cf37

Browse files
authored
Revert "Revert "Add combiner for rsyslog_conf (#2121)" (#2818)"
This reverts commit a7fa07f.
1 parent a7fa07f commit 252cf37

File tree

6 files changed

+280
-93
lines changed

6 files changed

+280
-93
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
RsyslogConfAll - files ``/etc/rsyslog.conf`` and ``/etc/rsyslog.d/*.conf``
3+
==========================================================================
4+
5+
Combiner for accessing all the rsyslog comfiguration files. There may be
6+
multiple rsyslog configuration, and the main configuration file is
7+
``/etc/rsyslog.conf``. This combiner will not check same option in multi
8+
files, user needs to check this situation in plugin if it is necessary.
9+
10+
"""
11+
from insights.core.plugins import combiner
12+
from insights.parsers.rsyslog_conf import RsyslogConf
13+
14+
15+
@combiner(RsyslogConf)
16+
class RsyslogAllConf(dict):
17+
"""
18+
Combiner for accessing all the rsyslog configuration files.
19+
20+
Examples:
21+
>>> type(confs)
22+
<class 'insights.combiners.rsyslog_confs.RsyslogAllConf'>
23+
>>> len(confs)
24+
2
25+
>>> confs['/etc/rsyslog.conf'][0]
26+
'$ModLoad imuxsock'
27+
"""
28+
def __init__(self, confs):
29+
super(RsyslogAllConf, self).__init__()
30+
data = {}
31+
32+
# Combine rsyslog configuration files into one dict. Key is file name, value is content of configuration file.
33+
for conf in confs:
34+
if conf.file_path == "/etc/rsyslog.conf":
35+
# Check if there is include option, if not, only parse /etc/rsyslog.conf even
36+
# /etc/rsyslog.d/*.conf exist.
37+
if not any(["include(" in item or "$IncludeConfig" in item for item in conf]):
38+
data.clear()
39+
data[conf.file_path] = conf
40+
break
41+
data[conf.file_path] = conf
42+
43+
self.update(data)
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from insights.combiners.rsyslog_confs import RsyslogAllConf
2+
from insights.combiners import rsyslog_confs
3+
from insights.parsers.rsyslog_conf import RsyslogConf
4+
from insights.tests import context_wrap
5+
import doctest
6+
7+
8+
RSYSLOG_CONF_MAIN_7 = r"""
9+
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
10+
$ModLoad imjournal # provides access to the systemd journal
11+
$WorkDirectory /var/lib/rsyslog
12+
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
13+
$IncludeConfig /etc/rsyslog.d/*.conf
14+
$OmitLocalLogging on
15+
$IMJournalStateFile imjournal.state
16+
*.info;mail.none;authpriv.none;cron.none /var/log/messages
17+
authpriv.* /var/log/secure
18+
mail.* -/var/log/maillog
19+
cron.* /var/log/cron
20+
*.emerg :omusrmsg:*
21+
uucp,news.crit /var/log/spooler
22+
local7.* /var/log/boot.log
23+
""".strip()
24+
25+
RSYSLOG_CONF_MAIN_NO_INCLUDE_7 = r"""
26+
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
27+
$ModLoad imjournal # provides access to the systemd journal
28+
$WorkDirectory /var/lib/rsyslog
29+
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
30+
$OmitLocalLogging on
31+
$IMJournalStateFile imjournal.state
32+
*.info;mail.none;authpriv.none;cron.none /var/log/messages
33+
authpriv.* /var/log/secure
34+
mail.* -/var/log/maillog
35+
cron.* /var/log/cron
36+
*.emerg :omusrmsg:*
37+
uucp,news.crit /var/log/spooler
38+
local7.* /var/log/boot.log
39+
""".strip()
40+
41+
RSYSLOG_CONF_MAIN_CONF_DIR_7 = r"""
42+
$WorkDirectory /tmp
43+
*.info -/var/log/test.log
44+
""".strip()
45+
46+
RSYSLOG_CONF_MAIN_8 = r"""
47+
module(load="imuxsock" # provides support for local system logging (e.g. via logger command)
48+
SysSock.Use="off") # Turn off message reception via local log socket;
49+
module(load="imjournal" # provides access to the systemd journal
50+
StateFile="imjournal.state") # File to store the position in the journal
51+
global(workDirectory="/var/lib/rsyslog")
52+
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")
53+
include(file="/etc/rsyslog.d/*.conf" mode="optional")
54+
*.info;mail.none;authpriv.none;cron.none /var/log/messages
55+
authpriv.* /var/log/secure
56+
mail.* -/var/log/maillog
57+
cron.* /var/log/cron
58+
if $programname == 'prog1' then {
59+
action(type="omfile" file="/var/log/prog1.log")
60+
if $msg contains 'test' then
61+
action(type="omfile" file="/var/log/prog1test.log")
62+
else
63+
action(type="omfile" file="/var/log/prog1notest.log")
64+
}
65+
""".strip()
66+
67+
RSYSLOG_CONF_MAIN_NO_INCLUDE_8 = r"""
68+
module(load="imuxsock" # provides support for local system logging (e.g. via logger command)
69+
SysSock.Use="off") # Turn off message reception via local log socket;
70+
module(load="imjournal" # provides access to the systemd journal
71+
StateFile="imjournal.state") # File to store the position in the journal
72+
global(workDirectory="/var/lib/rsyslog")
73+
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")
74+
*.info;mail.none;authpriv.none;cron.none /var/log/messages
75+
authpriv.* /var/log/secure
76+
mail.* -/var/log/maillog
77+
cron.* /var/log/cron
78+
if $programname == 'prog1' then {
79+
action(type="omfile" file="/var/log/prog1.log")
80+
if $msg contains 'test' then
81+
action(type="omfile" file="/var/log/prog1test.log")
82+
else
83+
action(type="omfile" file="/var/log/prog1notest.log")
84+
}
85+
""".strip()
86+
87+
RSYSLOG_CONF_MAIN_CONF_DIR_8 = r"""
88+
*.info {
89+
action(
90+
type="omfile"
91+
name="hehe"
92+
file="/tmp/test")
93+
}
94+
""".strip()
95+
96+
97+
def test_conf_7_include_dir():
98+
rsyslog1 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_7, path="/etc/rsyslog.conf"))
99+
rsyslog2 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_CONF_DIR_7, path="/etc/rsyslog.d/test.conf"))
100+
result = RsyslogAllConf([rsyslog1, rsyslog2])
101+
assert len(result) == 2
102+
assert result['/etc/rsyslog.conf'][0] == '$ModLoad imuxsock'
103+
104+
105+
def test_conf_7_no_include_dir():
106+
rsyslog1 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_NO_INCLUDE_7, path="/etc/rsyslog.conf"))
107+
rsyslog2 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_CONF_DIR_7, path="/etc/rsyslog.d/test.conf"))
108+
result = RsyslogAllConf([rsyslog1, rsyslog2])
109+
assert len(result) == 1
110+
assert result['/etc/rsyslog.conf'][0] == '$ModLoad imuxsock'
111+
112+
113+
def test_conf_8_include_dir():
114+
rsyslog1 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_8, path="/etc/rsyslog.conf"))
115+
rsyslog2 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_CONF_DIR_8, path="/etc/rsyslog.d/test.conf"))
116+
result = RsyslogAllConf([rsyslog1, rsyslog2])
117+
assert len(result) == 2
118+
assert result['/etc/rsyslog.d/test.conf'] == ['*.info { action( type="omfile" name="hehe" file="/tmp/test") }']
119+
120+
121+
def test_conf_8_no_include_dir():
122+
rsyslog1 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_NO_INCLUDE_8, path="/etc/rsyslog.conf"))
123+
rsyslog2 = RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_CONF_DIR_8, path="/etc/rsyslog.d/test.conf"))
124+
result = RsyslogAllConf([rsyslog1, rsyslog2])
125+
assert len(result) == 1
126+
assert result['/etc/rsyslog.conf'][2] == 'global(workDirectory="/var/lib/rsyslog")'
127+
128+
129+
def test_rsyslog_confs_doc_examples():
130+
env = {
131+
'confs': RsyslogAllConf(
132+
[
133+
RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_7, path='/etc/rsyslog.conf')),
134+
RsyslogConf(context_wrap(RSYSLOG_CONF_MAIN_CONF_DIR_7, path='/etc/rsyslog.d/test.conf'))
135+
])
136+
}
137+
failed, total = doctest.testmod(rsyslog_confs, globs=env)
138+
assert failed == 0

insights/parsers/rsyslog_conf.py

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,70 +10,78 @@
1010
1111
Due to high parsing complexity, this parser presents a simple line-based
1212
view of the file that meets the needs of the current rules.
13-
14-
Example:
15-
>>> content = '''
16-
... :fromhost-ip, regex, "10.0.0.[0-9]" /tmp/my_syslog.log
17-
... $ModLoad imtcp
18-
... $InputTCPServerRun 10514"
19-
... '''.strip()
20-
>>> from insights.tests import context_wrap
21-
>>> rsl = RsyslogConf(context_wrap(content))
22-
>>> len(rsl)
23-
3
24-
>>> len(list(rsl))
25-
3
26-
>>> any('imtcp' in n for n in rsl)
27-
True
2813
"""
2914
from .. import Parser, parser, get_active_lines
30-
31-
import re
3215
from insights.specs import Specs
16+
from insights.core.filters import add_filter
17+
18+
19+
add_filter(Specs.rsyslog_conf, ["{", "}", "(", ")"])
3320

3421

3522
@parser(Specs.rsyslog_conf)
36-
class RsyslogConf(Parser):
23+
class RsyslogConf(Parser, list):
3724
"""
38-
Parses `/etc/rsyslog.conf` info simple lines.
25+
Parses `/etc/rsyslog.conf` content.
3926
4027
Skips lines that begin with hash ("#") or are only whitespace.
4128
4229
Attributes:
4330
data (list): List of lines in the file that don't start
4431
with '#' and aren't whitespace.
45-
config_items(dict): Configuration items opportunistically found in the
46-
configuration file, with their values as given.
32+
33+
Example:
34+
>>> type(rsysconf)
35+
<class 'insights.parsers.rsyslog_conf.RsyslogConf'>
36+
>>> len(rsysconf)
37+
13
38+
>>> rsysconf[2]
39+
'authpriv.* /var/log/secure'
4740
"""
41+
def __init__(self, *args, **kwargs):
42+
super(RsyslogConf, self).__init__(*args, **kwargs)
4843

4944
def parse_content(self, content):
50-
self.data = get_active_lines(content)
51-
52-
self.config_items = {}
53-
# Config items are e.g. "$Word value #optional comment"
54-
config_re = re.compile(r'^\s*\$(?P<name>\S+)\s+(?P<value>.*?)(?:\s+#.*)?$')
55-
for line in self.data:
56-
lstrip = line.strip()
57-
match = config_re.match(lstrip)
58-
if match:
59-
self.config_items[match.group('name')] = match.group('value')
60-
61-
def config_val(self, item, default=None):
62-
"""
63-
Return the given configuration item, or the default if not defined.
64-
65-
Parameters:
66-
item(str): The configuration item name
67-
default: The default if the item is not found (defaults to None)
68-
69-
Returns:
70-
The related value in the `config_items` dictionary.
71-
"""
72-
return self.config_items.get(item, default)
73-
74-
def __len__(self):
75-
return len(self.data)
76-
77-
def __iter__(self):
78-
for d in self.data:
79-
yield d
45+
data = []
46+
brace_flag = False
47+
parenthesis_flag = False
48+
parenthesis_string = ""
49+
brace_string = ""
50+
51+
for line in get_active_lines(content):
52+
l_strip = line.strip()
53+
# Combine multi lines in brace into one line
54+
if brace_flag:
55+
brace_string = brace_string + " " + l_strip
56+
if "}" in l_strip:
57+
data.append(brace_string)
58+
brace_string = ""
59+
brace_flag = False
60+
continue
61+
else:
62+
if "{" in l_strip:
63+
if "}" in l_strip:
64+
data.append(l_strip)
65+
else:
66+
brace_flag = True
67+
brace_string = l_strip
68+
continue
69+
# Combine multi lines in parenthesis and not in brace into one line
70+
if parenthesis_flag:
71+
parenthesis_string = parenthesis_string + " " + l_strip
72+
if ")" in l_strip:
73+
data.append(parenthesis_string)
74+
parenthesis_string = ""
75+
parenthesis_flag = False
76+
continue
77+
else:
78+
if "(" in l_strip:
79+
if ")" in l_strip:
80+
data.append(l_strip)
81+
else:
82+
parenthesis_flag = True
83+
parenthesis_string = l_strip
84+
continue
85+
else:
86+
data.append(l_strip)
87+
self.extend(data)

0 commit comments

Comments
 (0)