Merge pull request #15392 from ihrwein/f/add-syslog-ng-modules

F/add syslog ng modules
This commit is contained in:
Thomas S Hatch 2014-09-12 10:37:02 -06:00
commit d6e3e01c6d
9 changed files with 2161 additions and 88 deletions

View File

@ -218,6 +218,7 @@ Full list of builtin execution modules
svn
swift
sysbench
syslog_ng
sysmod
system
systemd

View File

@ -0,0 +1,6 @@
======================
salt.modules.syslog_ng
======================
.. automodule:: salt.modules.syslog_ng
:members:

View File

@ -123,6 +123,7 @@ Full list of builtin state modules
supervisord
svn
sysctl
syslog_ng
test
timezone
tomcat

View File

@ -0,0 +1,6 @@
=====================
salt.states.syslog_ng
=====================
.. automodule:: salt.states.syslog_ng
:members:

View File

@ -3,7 +3,10 @@
Syslog-ng usage
===============
The syslog\_ng state modul is to generate syslog-ng
Overview
--------
Syslog\_ng state module is for generating syslog-ng
configurations. You can do the following things:
- generate syslog-ng configuration from YAML,
@ -16,130 +19,199 @@ configuration, get the version and other information about syslog-ng.
Configuration
-------------
The following configuration is an example, how a complete syslog-ng
state configuration looks like:
Users can create syslog-ng configuration statements with the
:py:func:`syslog_ng.config <salt.states.syslog_ng.config>` function. It requires
a `name` and a `config` parameter. The `name` parameter determines the name of
the generated statement and the `config` parameter holds a parsed YAML structure.
A statement can be declared in the following forms (both are equivalent):
.. code-block:: yaml
source.s_localhost:
syslog_ng.config:
- config:
- tcp:
- ip: "127.0.0.1"
- port: 1233
.. code-block:: yaml
s_localhost:
syslog_ng.config:
- config:
source:
- tcp:
- ip: "127.0.0.1"
- port: 1233
The first one is called short form, because it needs less typing. Users can use lists
and dictionaries to specify their configuration. The format is quite self describing and
there are more examples [at the end](#examples) of this document.
Quotation
---------
The quotation can be tricky sometimes but here are some rules to follow:
* when a string meant to be ``"string"`` in the generated configuration, it should be like
``'"string"'`` in the YAML document
* similarly, users should write ``"'string'"`` to get ``'string'`` in the generated configuration
Full example
------------
The following configuration is an example, how a complete syslog-ng configuration looks like:
.. code-block:: yaml
# Set the location of the configuration file
"/home/tibi/install/syslog-ng/etc/syslog-ng.conf":
syslog_ng.set_config_file
set_location:
module.run:
- name: syslog_ng.set_config_file
- m_name: "/home/tibi/install/syslog-ng/etc/syslog-ng.conf"
# The syslog-ng and syslog-ng-ctl binaries are here. You needn't use
# The syslog-ng and syslog-ng-ctl binaries are here. You needn't use
# this method if these binaries can be found in a directory in your PATH.
"/home/tibi/install/syslog-ng/sbin":
syslog_ng.set_binary_path
set_bin_path:
module.run:
- name: syslog_ng.set_binary_path
- m_name: "/home/tibi/install/syslog-ng/sbin"
# Writes the first lines into the config file, also erases its previous
# content
"3.6":
syslog_ng.write_version
write_version:
module.run:
- name: syslog_ng.write_version
- m_name: "3.6"
# There is a shorter form to set the above variables
set_variables:
module.run:
- name: syslog_ng.set_parameters
- version: "3.6"
- binary_path: "/home/tibi/install/syslog-ng/sbin"
- config_file: "/home/tibi/install/syslog-ng/etc/syslog-ng.conf"
# Some global options
global_options:
options.global_options:
syslog_ng.config:
- config:
options:
- time_reap: 30
- mark_freq: 10
- keep_hostname: "yes"
- time_reap: 30
- mark_freq: 10
- keep_hostname: "yes"
s_localhost:
source.s_localhost:
syslog_ng.config:
- config:
source:
- tcp:
- ip: "127.0.0.1"
- port: 1233
- tcp:
- ip: "127.0.0.1"
- port: 1233
d_log_server:
destination.d_log_server:
syslog_ng.config:
- config:
destination:
- tcp:
- "127.0.0.1"
- port: 1234
- tcp:
- "127.0.0.1"
- port: 1234
l_log_to_central_server:
log.l_log_to_central_server:
syslog_ng.config:
- config:
log:
- source: s_localhost
- destination: d_log_server
- source: s_localhost
- destination: d_log_server
some_comment:
syslog_ng.write_config:
module.run:
- name: syslog_ng.write_config
- config: |
# Multi line
# comment
auto_start_or_reload:
{% set pids = salt["ps.pgrep"]("syslog-ng") %}
{% if pids == None or pids|length == 0 %}
syslog_ng.started:
- user: tibi
{% else %}
syslog_ng.reloaded
{% endif %}
# An other mode to use comments or existing configuration snippets
config.other_comment_form:
syslog_ng.config:
- config: |
# Multi line
# comment
#auto_stop:
# syslog_ng.stopped
The ``3.6``, ``s_devlog``, ``d_log_server``, etc. are identifiers. The
second lines in each block are functions and their first parameter is
their id. The ``- config`` is the second named parameter of the
``syslog_ng.config`` function. This function can generate the syslog-ng
configuration from YAML. If the statement (source, destination, parser,
The :py:func:`syslog_ng.reloaded <salt.states.syslog_ng.reloaded>` function can generate syslog-ng configuration from YAML. If the statement (source, destination, parser,
etc.) has a name, this function uses the id as the name, otherwise (log
statement) it's purpose is like a mandatory comment.
You can use ``set_binary_path`` to set the directory which contains the
syslog-ng and syslog-ng-ctl binaries. If this directory is in your PATH,
you don't need to use this function.
Under ``auto_start_or_reload`` you can see a Jinja template. If
syslog-ng isn't running it will start it, otherwise reload it. It uses
the process name ``syslog-ng`` to determine its running state. I suggest
that you use ``service`` state if it's available on your system.
After execution this example the syslog\_ng state will generate this
file:
.. code-block:: text
#Generated by Salt on 2014-06-19 16:53:11
@version: 3.6
#Generated by Salt on 2014-08-18 00:11:11
@version: 3.6
options {
time_reap(30);
mark_freq(10);
keep_hostname(yes);
};
options {
time_reap(
30
);
mark_freq(
10
);
keep_hostname(
yes
);
};
source s_localhost {
tcp(
ip("127.0.0.1"),
port(1233)
);
};
destination d_log_server {
tcp(
"127.0.0.1",
port(1234)
);
};
source s_localhost {
tcp(
ip(
127.0.0.1
),
port(
1233
)
);
};
log {
source(s_localhost);
destination(d_log_server);
};
# Multi line
# comment
destination d_log_server {
tcp(
127.0.0.1,
port(
1234
)
);
};
log {
source(
s_localhost
);
destination(
d_log_server
);
};
# Multi line
# comment
# Multi line
# comment
Users can include arbitrary texts in the generated configuration with
using the ``write_config`` function.
using the ``config`` statement (see the example above).
Syslog_ng module functions
--------------------------
You can use :py:func:`syslog_ng.set_binary_path <salt.modules.syslog_ng.set_binary_path>`
to set the directory which contains the
syslog-ng and syslog-ng-ctl binaries. If this directory is in your PATH,
you don't need to use this function. There is also a :py:func:`syslog_ng.set_config_file <salt.modules.syslog_ng.set_config_file>`
function to set the location of the configuration file.
Examples
--------
@ -165,7 +237,7 @@ Simple source
- config:
source:
- file:
- file: "/var/log/apache/access.log"
- file: ''"/var/log/apache/access.log"''
- follow_freq : 1
- flags:
- no-parse
@ -180,12 +252,26 @@ OR
- config:
source:
- file:
- "/var/log/apache/access.log"
- ''"/var/log/apache/access.log"''
- follow_freq : 1
- flags:
- no-parse
- validate-utf8
OR
.. code-block:: yaml
source.s_tail:
syslog_ng.config:
- config:
- file:
- ''"/var/log/apache/access.log"''
- follow_freq : 1
- flags:
- no-parse
- validate-utf8
Complex source
~~~~~~~~~~~~~~
@ -228,7 +314,7 @@ Filter
- config:
filter:
- match:
- "@json:"
- ''"@json:"''
Template
~~~~~~~~
@ -251,7 +337,7 @@ Template
-config:
template:
- template:
- "$ISODATE $HOST $MSG\n"
- '"$ISODATE $HOST $MSG\n"'
- template_escape:
- "no"
@ -274,8 +360,8 @@ Rewrite
- config:
rewrite:
- set:
- "${.json.message}"
- value : "$MESSAGE"
- '"${.json.message}"'
- value : '"$MESSAGE"'
Global options
~~~~~~~~~~~~~~
@ -353,7 +439,7 @@ Log
- rewrite: r_set_message_to_MESSAGE
- destination:
- file:
- "/tmp/json-input.log"
- '"/tmp/json-input.log"'
- template: t_gsoc2014
- flags: final
- channel:
@ -366,4 +452,3 @@ Log
- file:
- "/tmp/all.log"
- template: t_gsoc2014

1179
salt/modules/syslog_ng.py Normal file

File diff suppressed because it is too large Load Diff

122
salt/states/syslog_ng.py Normal file
View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
'''
State module for syslog_ng
==========================
:maintainer: Tibor Benke <btibi@sch.bme.hu>
:maturity: new
:depends: cmd, ps, syslog_ng
:platform: all
Users can generate syslog-ng configuration files from YAML format or use
plain ones and reload, start, or stop their syslog-ng by using this module.
Details
-------
The service module is not available on all system, so this module includes
:mod:`syslog_ng.reloaded <salt.states.syslog_ng.reloaded>`,
:mod:`syslog_ng.stopped <salt.states.syslog_ng.stopped>`,
and :mod:`syslog_ng.started <salt.states.syslog_ng.started>` functions.
If the service module is available on the computers, users should use that.
Users can generate syslog-ng configuration with
:mod:`syslog_ng.config <salt.states.syslog_ng.config>` function.
For more information see :doc:`syslog-ng state usage </topics/tutorials/syslog_ng-state-usage>`.
Syslog-ng configuration file format
-----------------------------------
The syntax of a configuration snippet in syslog-ng.conf:
..
object_type object_id {<options>};
These constructions are also called statements. There are options inside of them:
..
option(parameter1, parameter2); option2(parameter1, parameter2);
You can find more information about syslog-ng's configuration syntax in the
Syslog-ng Admin guide:
http://www.balabit.com/sites/default/files/documents/syslog-ng-ose-3.5-guides/en/syslog-ng-ose-v3.5-guide-admin/html-single/index.html#syslog-ng.conf.5
'''
from __future__ import generators, print_function, with_statement
import logging
log = logging.getLogger(__name__)
def config(name,
config,
write=True):
'''
Builds syslog-ng configuration.
name : the id of the Salt document
config : the parsed YAML code
write : if True, it writes the config into the configuration file,
otherwise just returns it
'''
return __salt__['syslog_ng.config'](name, config, write)
def stopped(name=None):
'''
Kills syslog-ng.
'''
return __salt__['syslog_ng.stop'](name)
def started(name=None,
user=None,
group=None,
chroot=None,
caps=None,
no_caps=False,
pidfile=None,
enable_core=False,
fd_limit=None,
verbose=False,
debug=False,
trace=False,
yydebug=False,
persist_file=None,
control=None,
worker_threads=None,
*args,
**kwargs):
'''
Ensures, that syslog-ng is started via the given parameters.
Users shouldn't use this function, if the service module is available on
their system.
'''
return __salt__['syslog_ng.start'](name=name,
user=user,
group=group,
chroot=chroot,
caps=caps,
no_caps=no_caps,
pidfile=pidfile,
enable_core=enable_core,
fd_limit=fd_limit,
verbose=verbose,
debug=debug,
trace=trace,
yydebug=yydebug,
persist_file=persist_file,
control=control,
worker_threads=worker_threads)
def reloaded(name):
'''
Reloads syslog-ng.
'''
return __salt__['syslog_ng.reload'](name)

View File

@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
'''
Test module for syslog_ng
'''
# Import Salt Testing libs
import salt
from salttesting import skipIf, TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch
from textwrap import dedent
ensure_in_syspath('../../')
from salt.modules import syslog_ng
syslog_ng.__salt__ = {}
syslog_ng.__opts__ = {}
_VERSION = "3.6.0alpha0"
_MODULES = ("syslogformat,json-plugin,basicfuncs,afstomp,afsocket,cryptofuncs,"
"afmongodb,dbparser,system-source,affile,pseudofile,afamqp,"
"afsocket-notls,csvparser,linux-kmsg-format,afuser,confgen,afprog")
VERSION_OUTPUT = """syslog-ng {0}
Installer-Version: {0}
Revision:
Compile-Date: Apr 4 2014 20:26:18
Error opening plugin module; module='afsocket-tls', error='/home/tibi/install/syslog-ng/lib/syslog-ng/libafsocket-tls.so: undefined symbol: tls_context_setup_session'
Available-Modules: {1}
Enable-Debug: on
Enable-GProf: off
Enable-Memtrace: off
Enable-IPv6: on
Enable-Spoof-Source: off
Enable-TCP-Wrapper: off
Enable-Linux-Caps: off""".format(_VERSION, _MODULES)
STATS_OUTPUT = """SourceName;SourceId;SourceInstance;State;Type;Number
center;;received;a;processed;0
destination;#anon-destination0;;a;processed;0
destination;#anon-destination1;;a;processed;0
source;s_gsoc2014;;a;processed;0
center;;queued;a;processed;0
global;payload_reallocs;;a;processed;0
global;sdata_updates;;a;processed;0
global;msg_clones;;a;processed;0"""
_SYSLOG_NG_NOT_INSTALLED_RETURN_VALUE = {
"retcode": -1, "stderr":
"Unable to execute the command 'syslog-ng'. It is not in the PATH."
}
_SYSLOG_NG_CTL_NOT_INSTALLED_RETURN_VALUE = {
"retcode": -1, "stderr":
"Unable to execute the command 'syslog-ng-ctl'. It is not in the PATH."
}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class SyslogNGTestCase(TestCase):
def test_statement_without_options(self):
s = syslog_ng.Statement("source", "s_local", options=[])
b = s.build()
self.assertEqual(dedent(
"""\
source s_local {
};
"""), b)
def test_non_empty_statement(self):
o1 = syslog_ng.Option("file")
o2 = syslog_ng.Option("tcp")
s = syslog_ng.Statement("source", "s_local", options=[o1, o2])
b = s.build()
self.assertEqual(dedent(
"""\
source s_local {
file(
);
tcp(
);
};
"""), b)
def test_option_with_parameters(self):
o1 = syslog_ng.Option("file")
p1 = syslog_ng.SimpleParameter('"/var/log/messages"')
p2 = syslog_ng.SimpleParameter()
p3 = syslog_ng.TypedParameter()
p3.type = "tls"
p2.value = '"/var/log/syslog"'
o1.add_parameter(p1)
o1.add_parameter(p2)
o1.add_parameter(p3)
b = o1.build()
self.assertEqual(dedent(
"""\
file(
"/var/log/messages",
"/var/log/syslog",
tls(
)
);
"""), b)
def test_parameter_with_values(self):
p = syslog_ng.TypedParameter()
p.type = "tls"
v1 = syslog_ng.TypedParameterValue()
v1.type = 'key_file'
v2 = syslog_ng.TypedParameterValue()
v2.type = 'cert_file'
p.add_value(v1)
p.add_value(v2)
b = p.build()
self.assertEqual(dedent(
"""\
tls(
key_file(
),
cert_file(
)
)"""), b)
def test_value_with_arguments(self):
t = syslog_ng.TypedParameterValue()
t.type = 'key_file'
a1 = syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"')
a2 = syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"')
t.add_argument(a1)
t.add_argument(a2)
b = t.build()
self.assertEqual(dedent(
'''\
key_file(
"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"
"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"
)'''), b)
def test_end_to_end_statement_generation(self):
s = syslog_ng.Statement('source', 's_tls')
o = syslog_ng.Option('tcp')
ip = syslog_ng.TypedParameter('ip')
ip.add_value(syslog_ng.SimpleParameterValue("'192.168.42.2'"))
o.add_parameter(ip)
port = syslog_ng.TypedParameter('port')
port.add_value(syslog_ng.SimpleParameterValue(514))
o.add_parameter(port)
tls = syslog_ng.TypedParameter('tls')
key_file = syslog_ng.TypedParameterValue('key_file')
key_file.add_argument(syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"'))
cert_file = syslog_ng.TypedParameterValue('cert_file')
cert_file.add_argument(syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/cert.d/syslog-ng.cert"'))
peer_verify = syslog_ng.TypedParameterValue('peer_verify')
peer_verify.add_argument(syslog_ng.Argument('optional-untrusted'))
tls.add_value(key_file)
tls.add_value(cert_file)
tls.add_value(peer_verify)
o.add_parameter(tls)
s.add_child(o)
b = s.build()
self.assertEqual(dedent(
'''\
source s_tls {
tcp(
ip(
'192.168.42.2'
),
port(
514
),
tls(
key_file(
"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"
),
cert_file(
"/opt/syslog-ng/etc/syslog-ng/cert.d/syslog-ng.cert"
),
peer_verify(
optional-untrusted
)
)
);
};
'''), b)
def test_version(self):
mock_return_value = {"retcode": 0, 'stdout': VERSION_OUTPUT}
expected_output = {"retcode": 0, "stdout": "3.6.0alpha0"}
mock_args = "syslog-ng -V"
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.version,
expected_output=expected_output)
def test_stats(self):
mock_return_value = {"retcode": 0, 'stdout': STATS_OUTPUT}
expected_output = {"retcode": 0, "stdout": STATS_OUTPUT}
mock_args = "syslog-ng-ctl stats"
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.stats,
expected_output=expected_output)
def test_modules(self):
mock_return_value = {"retcode": 0, 'stdout': VERSION_OUTPUT}
expected_output = {"retcode": 0, "stdout": _MODULES}
mock_args = "syslog-ng -V"
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.modules,
expected_output=expected_output)
def test_config_test_ok(self):
mock_return_value = {"retcode": 0, "stderr": "", "stdout": "Syslog-ng startup text..."}
mock_args = "syslog-ng --syntax-only"
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.config_test,
expected_output=mock_return_value)
def test_config_test_fails(self):
mock_return_value = {"retcode": 1, 'stderr': "Syntax error...", "stdout": ""}
mock_args = "syslog-ng --syntax-only"
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.config_test,
expected_output=mock_return_value)
def test_config_test_cfgfile(self):
cfgfile = "/path/to/syslog-ng.conf"
mock_return_value = {"retcode": 1, 'stderr': "Syntax error...", "stdout": ""}
mock_args = "syslog-ng --syntax-only --cfgfile={0}".format(cfgfile)
self._assert_template(mock_args,
mock_return_value,
function_to_call=syslog_ng.config_test,
function_args={"cfgfile": cfgfile},
expected_output=mock_return_value)
def _assert_template(self,
mock_funtion_args,
mock_return_value,
function_to_call,
expected_output,
function_args=None):
if function_args is None:
function_args = {}
installed = True
if not salt.utils.which("syslog-ng"):
installed = False
if "syslog-ng-ctl" in mock_funtion_args:
expected_output = _SYSLOG_NG_CTL_NOT_INSTALLED_RETURN_VALUE
else:
expected_output = _SYSLOG_NG_NOT_INSTALLED_RETURN_VALUE
mock_function = MagicMock(return_value=mock_return_value)
with patch.dict(syslog_ng.__salt__, {'cmd.run_all': mock_function}):
got = function_to_call(**function_args)
self.assertEqual(expected_output, got)
if installed:
self.assertTrue(mock_function.called)
self.assertEqual(len(mock_function.call_args), 2)
mock_param = mock_function.call_args
self.assertTrue(mock_param[0][0].endswith(mock_funtion_args))
if __name__ == '__main__':
from integration import run_tests
run_tests(SyslogNGTestCase, needs_daemon=False)

View File

@ -0,0 +1,388 @@
# -*- coding: utf-8 -*-
'''
Test module for syslog_ng state
'''
import yaml
import re
import tempfile
import os
from salttesting import skipIf, TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch
ensure_in_syspath('../../')
from salt.states import syslog_ng
from salt.modules import syslog_ng as syslog_ng_module
syslog_ng.__salt__ = {}
syslog_ng_module.__salt__ = {}
syslog_ng_module.__opts__ = {'test': False}
SOURCE_1_CONFIG = {
"id": "s_tail",
"config": (
"""
source:
- file:
- '"/var/log/apache/access.log"'
- follow_freq : 1
- flags:
- no-parse
- validate-utf8
""")
}
SOURCE_1_EXPECTED = (
"""
source s_tail {
file(
"/var/log/apache/access.log",
follow_freq(1),
flags(no-parse, validate-utf8)
);
};
"""
)
SOURCE_2_CONFIG = {
"id": "s_gsoc2014",
"config": (
"""
source:
- tcp:
- ip: '"0.0.0.0"'
- port: 1234
- flags: no-parse
"""
)
}
SOURCE_2_EXPECTED = (
"""
source s_gsoc2014 {
tcp(
ip("0.0.0.0"),
port(1234),
flags(no-parse)
);
};"""
)
FILTER_1_CONFIG = {
"id": "f_json",
"config": (
"""
filter:
- match:
- '"@json:"'
"""
)
}
FILTER_1_EXPECTED = (
"""
filter f_json {
match(
"@json:"
);
};
"""
)
TEMPLATE_1_CONFIG = {
"id": "t_demo_filetemplate",
"config": (
"""
template:
- template:
- '"$ISODATE $HOST $MSG\n"'
- template_escape:
- "no"
"""
)
}
TEMPLATE_1_EXPECTED = (
"""
template t_demo_filetemplate {
template(
"$ISODATE $HOST $MSG "
);
template_escape(
no
);
};
"""
)
REWRITE_1_CONFIG = {
"id": "r_set_message_to_MESSAGE",
"config": (
"""
rewrite:
- set:
- '"${.json.message}"'
- value : '"$MESSAGE"'
"""
)
}
REWRITE_1_EXPECTED = (
"""
rewrite r_set_message_to_MESSAGE {
set(
"${.json.message}",
value("$MESSAGE")
);
};
"""
)
LOG_1_CONFIG = {
"id": "l_gsoc2014",
"config": (
"""
log:
- source: s_gsoc2014
- junction:
- channel:
- filter: f_json
- parser: p_json
- rewrite: r_set_json_tag
- rewrite: r_set_message_to_MESSAGE
- destination:
- file:
- '"/tmp/json-input.log"'
- template: t_gsoc2014
- flags: final
- channel:
- filter: f_not_json
- parser:
- syslog-parser: []
- rewrite: r_set_syslog_tag
- flags: final
- destination:
- file:
- '"/tmp/all.log"'
- template: t_gsoc2014
"""
)
}
LOG_1_EXPECTED = (
"""
log {
source(s_gsoc2014);
junction {
channel {
filter(f_json);
parser(p_json);
rewrite(r_set_json_tag);
rewrite(r_set_message_to_MESSAGE);
destination {
file(
"/tmp/json-input.log",
template(t_gsoc2014)
);
};
flags(final);
};
channel {
filter(f_not_json);
parser {
syslog-parser(
);
};
rewrite(r_set_syslog_tag);
flags(final);
};
};
destination {
file(
"/tmp/all.log",
template(t_gsoc2014)
);
};
};
"""
)
OPTIONS_1_CONFIG = {
"id": "global_options",
"config": (
"""
options:
- time_reap: 30
- mark_freq: 10
- keep_hostname: "yes"
"""
)
}
OPTIONS_1_EXPECTED = (
"""
options {
time_reap(30);
mark_freq(10);
keep_hostname(yes);
};
"""
)
SHORT_FORM_CONFIG = {
"id": "source.s_gsoc",
"config": (
"""
- tcp:
- ip: '"0.0.0.0"'
- port: 1234
- flags: no-parse
"""
)
}
SHORT_FORM_EXPECTED = (
"""
source s_gsoc {
tcp(
ip(
"0.0.0.0"
),
port(
1234
),
flags(
no-parse
)
);
};
"""
)
GIVEN_CONFIG = {
'id': "config.some_name",
'config': (
""" |
source s_gsoc {
tcp(
ip(
"0.0.0.0"
),
port(
1234
),
flags(
no-parse
)
);
};
"""
)
}
_SALT_VAR_WITH_MODULE_METHODS = {
'syslog_ng.config': syslog_ng_module.config,
'syslog_ng.start': syslog_ng_module.start,
'syslog_ng.reload': syslog_ng_module.reload,
'syslog_ng.stop': syslog_ng_module.stop,
'syslog_ng.write_version': syslog_ng_module.write_version,
'syslog_ng.write_config': syslog_ng_module.write_config
}
def remove_whitespaces(source):
return re.sub(r"\s+", "", source.strip())
@skipIf(NO_MOCK, NO_MOCK_REASON)
# @skipIf(syslog_ng.__virtual__() is False, 'Syslog-ng must be installed')
class SyslogNGTestCase(TestCase):
def test_generate_source_config(self):
self._config_generator_template(SOURCE_1_CONFIG, SOURCE_1_EXPECTED)
def test_generate_log_config(self):
self._config_generator_template(LOG_1_CONFIG, LOG_1_EXPECTED)
def test_generate_tcp_source_config(self):
self._config_generator_template(SOURCE_2_CONFIG, SOURCE_2_EXPECTED)
def test_generate_filter_config(self):
self._config_generator_template(FILTER_1_CONFIG, FILTER_1_EXPECTED)
def test_generate_template_config(self):
self._config_generator_template(TEMPLATE_1_CONFIG, TEMPLATE_1_EXPECTED)
def test_generate_rewrite_config(self):
self._config_generator_template(REWRITE_1_CONFIG, REWRITE_1_EXPECTED)
def test_generate_global_options_config(self):
self._config_generator_template(OPTIONS_1_CONFIG, OPTIONS_1_EXPECTED)
def test_generate_short_form_statement(self):
self._config_generator_template(SHORT_FORM_CONFIG, SHORT_FORM_EXPECTED)
def test_generate_given_config(self):
self._config_generator_template(GIVEN_CONFIG, SHORT_FORM_EXPECTED)
def _config_generator_template(self, yaml_input, expected):
parsed_yaml_config = yaml.load(yaml_input["config"])
id = yaml_input["id"]
with patch.dict(syslog_ng.__salt__, _SALT_VAR_WITH_MODULE_METHODS):
got = syslog_ng.config(id, config=parsed_yaml_config, write=False)
config = got["changes"]["new"]
self.assertEqual(remove_whitespaces(expected), remove_whitespaces(config))
self.assertEqual(False, got["result"])
def test_write_config(self):
yaml_inputs = (
SOURCE_2_CONFIG, SOURCE_1_CONFIG, FILTER_1_CONFIG, TEMPLATE_1_CONFIG, REWRITE_1_CONFIG, LOG_1_CONFIG
)
expected_outputs = (
SOURCE_2_EXPECTED, SOURCE_1_EXPECTED, FILTER_1_EXPECTED, TEMPLATE_1_EXPECTED, REWRITE_1_EXPECTED,
LOG_1_EXPECTED
)
config_file_fd, config_file_name = tempfile.mkstemp()
os.close(config_file_fd)
with patch.dict(syslog_ng.__salt__, _SALT_VAR_WITH_MODULE_METHODS):
syslog_ng_module.set_config_file(config_file_name)
syslog_ng_module.write_version("3.6")
syslog_ng_module.write_config(config='@include "scl.conf"')
for i in yaml_inputs:
parsed_yaml_config = yaml.load(i["config"])
id = i["id"]
got = syslog_ng.config(id, config=parsed_yaml_config, write=True)
written_config = ""
with open(config_file_name, "r") as f:
written_config = f.read()
config_without_whitespaces = remove_whitespaces(written_config)
for i in expected_outputs:
without_whitespaces = remove_whitespaces(i)
self.assertIn(without_whitespaces, config_without_whitespaces)
syslog_ng_module.set_config_file("")
os.remove(config_file_name)
def test_started_state_generate_valid_cli_command(self):
mock_func = MagicMock(return_value={"retcode": 0, "stdout": "", "pid": 1000})
with patch.dict(syslog_ng.__salt__, _SALT_VAR_WITH_MODULE_METHODS):
with patch.dict(syslog_ng_module.__salt__, {'cmd.run_all': mock_func}):
got = syslog_ng.started(user="joe", group="users", enable_core=True)
command = got["changes"]["new"]
self.assertTrue(
command.endswith("syslog-ng --user=joe --group=users --enable-core --cfgfile=/etc/syslog-ng.conf"))
if __name__ == '__main__':
from integration import run_tests
run_tests(SyslogNGTestCase, needs_daemon=False)