From b4515b4be4ead1ea5d0af205989cf313272e8770 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Mon, 19 Nov 2018 04:25:32 -0800 Subject: Add RFC5424 syslog protocol support (VPP-1139) Syslog protocol logging transport event messages across network over UDP protocol based on RFC5426. Change-Id: Ica74b40bcc2e6d0fbd41e9bf78e76395fbabab3c Signed-off-by: Matus Fabian --- test/Makefile | 2 +- test/test_syslog.py | 196 ++++++++++++++++++++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 50 ++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 test/test_syslog.py (limited to 'test') diff --git a/test/Makefile b/test/Makefile index 0338062882a..5f456e172c4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -73,7 +73,7 @@ PYTHON_EXTRA_DEPENDS= endif PYTHON_VENV_PATH=$(VPP_PYTHON_PREFIX)/virtualenv -PYTHON_DEPENDS=$(PYTHON_EXTRA_DEPENDS) psutil faulthandler six scapy==2.4.0 pexpect cryptography subprocess32 cffi git+https://github.com/vpp-dev/py-lispnetworking +PYTHON_DEPENDS=$(PYTHON_EXTRA_DEPENDS) psutil faulthandler six scapy==2.4.0 pexpect cryptography subprocess32 cffi syslog-rfc5424-parser git+https://github.com/vpp-dev/py-lispnetworking SCAPY_SOURCE=$(shell find $(PYTHON_VENV_PATH) -name site-packages) BUILD_COV_DIR=$(BR)/test-cov diff --git a/test/test_syslog.py b/test/test_syslog.py new file mode 100644 index 00000000000..db7d7bef2c7 --- /dev/null +++ b/test/test_syslog.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python + +from framework import VppTestCase, VppTestRunner +from util import ppp +from scapy.packet import Raw +from scapy.layers.inet import IP, UDP +from vpp_papi_provider import SYSLOG_SEVERITY +from syslog_rfc5424_parser import SyslogMessage, ParseError +from syslog_rfc5424_parser.constants import SyslogFacility, SyslogSeverity + + +class TestSyslog(VppTestCase): + """ Syslog Protocol Test Cases """ + + @classmethod + def setUpClass(cls): + super(TestSyslog, cls).setUpClass() + + try: + cls.create_pg_interfaces(range(1)) + cls.pg0.admin_up() + cls.pg0.config_ip4() + cls.pg0.resolve_arp() + + except Exception: + super(TestSyslog, cls).tearDownClass() + raise + + def syslog_generate(self, facility, severity, appname, msgid, sd=None, + msg=None): + """ + Generate syslog message + + :param facility: facility value + :param severity: severity level + :param appname: application name that originate message + :param msgid: message indetifier + :param sd: structured data (optional) + :param msg: free-form message (optional) + """ + facility_str = ['kernel', 'user-level', 'mail-system', + 'system-daemons', 'security-authorization', 'syslogd', + 'line-printer', 'network-news', 'uucp', 'clock-daemon', + '', 'ftp-daemon', 'ntp-subsystem', 'log-audit', + 'log-alert', '', 'local0', 'local1', 'local2', + 'local3', 'local4', 'local5', 'local6', 'local7'] + + severity_str = ['emergency', 'alert', 'critical', 'error', 'warning', + 'notice', 'informational', 'debug'] + + cli_str = "test syslog %s %s %s %s" % (facility_str[facility], + severity_str[severity], + appname, + msgid) + if sd is not None: + for sd_id, sd_params in sd.items(): + cli_str += " sd-id %s" % (sd_id) + for name, value in sd_params.items(): + cli_str += " sd-param %s %s" % (name, value) + if msg is not None: + cli_str += " %s" % (msg) + self.vapi.cli(cli_str) + + def syslog_verify(self, data, facility, severity, appname, msgid, sd=None, + msg=None): + """ + Verify syslog message + + :param data: syslog message + :param facility: facility value + :param severity: severity level + :param appname: application name that originate message + :param msgid: message indetifier + :param sd: structured data (optional) + :param msg: free-form message (optional) + """ + message = data.decode('utf-8') + if sd is None: + sd = {} + try: + message = SyslogMessage.parse(message) + self.assertEqual(message.facility, facility) + self.assertEqual(message.severity, severity) + self.assertEqual(message.appname, appname) + self.assertEqual(message.msgid, msgid) + self.assertEqual(message.msg, msg) + self.assertEqual(message.sd, sd) + self.assertEqual(message.version, 1) + self.assertEqual(message.hostname, self.pg0.local_ip4) + except ParseError as e: + self.logger.error(e) + + def test_syslog(self): + """ Syslog Protocol test """ + self.vapi.syslog_set_sender(self.pg0.remote_ip4n, self.pg0.local_ip4n) + config = self.vapi.syslog_get_sender() + self.assertEqual(config.collector_address.address, + self.pg0.remote_ip4n) + self.assertEqual(config.collector_port, 514) + self.assertEqual(config.src_address.address, self.pg0.local_ip4n) + self.assertEqual(config.vrf_id, 0) + self.assertEqual(config.max_msg_size, 480) + + appname = 'test' + msgid = 'testMsg' + msg = 'this is message' + sd1 = {'exampleSDID@32473': {'iut': '3', + 'eventSource': 'App', + 'eventID': '1011'}} + sd2 = {'exampleSDID@32473': {'iut': '3', + 'eventSource': 'App', + 'eventID': '1011'}, + 'examplePriority@32473': {'class': 'high'}} + + self.pg_enable_capture(self.pg_interfaces) + self.syslog_generate(SyslogFacility.local7, + SyslogSeverity.info, + appname, + msgid, + None, + msg) + capture = self.pg0.get_capture(1) + try: + self.assertEqual(capture[0][IP].src, self.pg0.local_ip4) + self.assertEqual(capture[0][IP].dst, self.pg0.remote_ip4) + self.assertEqual(capture[0][UDP].dport, 514) + self.assert_packet_checksums_valid(capture[0], False) + except: + self.logger.error(ppp("invalid packet:", capture[0])) + raise + self.syslog_verify(capture[0][Raw].load, + SyslogFacility.local7, + SyslogSeverity.info, + appname, + msgid, + None, + msg) + + self.pg_enable_capture(self.pg_interfaces) + self.vapi.syslog_set_filter(SYSLOG_SEVERITY.WARN) + filter = self.vapi.syslog_get_filter() + self.assertEqual(filter.severity, SYSLOG_SEVERITY.WARN) + self.syslog_generate(SyslogFacility.local7, + SyslogSeverity.info, + appname, + msgid, + None, + msg) + self.pg0.assert_nothing_captured() + + self.pg_enable_capture(self.pg_interfaces) + self.syslog_generate(SyslogFacility.local6, + SyslogSeverity.warning, + appname, + msgid, + sd1, + msg) + capture = self.pg0.get_capture(1) + self.syslog_verify(capture[0][Raw].load, + SyslogFacility.local6, + SyslogSeverity.warning, + appname, + msgid, + sd1, + msg) + + self.vapi.syslog_set_sender(self.pg0.remote_ip4n, + self.pg0.local_ip4n, + collector_port=12345) + config = self.vapi.syslog_get_sender() + self.assertEqual(config.collector_port, 12345) + + self.pg_enable_capture(self.pg_interfaces) + self.syslog_generate(SyslogFacility.local5, + SyslogSeverity.err, + appname, + msgid, + sd2, + None) + capture = self.pg0.get_capture(1) + try: + self.assertEqual(capture[0][UDP].dport, 12345) + except: + self.logger.error(ppp("invalid packet:", capture[0])) + raise + self.syslog_verify(capture[0][Raw].load, + SyslogFacility.local5, + SyslogSeverity.err, + appname, + msgid, + sd2, + None) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 8ed870ea0de..f8d0e6ca0f9 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -44,6 +44,17 @@ class QOS_SOURCE: IP = 3 +class SYSLOG_SEVERITY: + EMERG = 0 + ALERT = 1 + CRIT = 2 + ERR = 3 + WARN = 4 + NOTICE = 5 + INFO = 6 + DBG = 7 + + class UnexpectedApiReturnValueError(Exception): """ exception raised when the API return value is unexpected """ pass @@ -4026,3 +4037,42 @@ class VppPapiProvider(object): def svs_dump(self): return self.api(self.papi.svs_dump, {}) + + def syslog_set_sender( + self, + collector, + src, + collector_port=514, + vrf_id=0, + max_msg_size=480): + """Set syslog sender configuration + + :param collector: colector IP address + :param src: source IP address + :param collector_port: collector UDP port (Default value = 514) + :param vrf_id: VRF id (Default value = 0) + :param max_msg_size: maximum message length (Default value = 480) + """ + return self.api(self.papi.syslog_set_sender, + {'collector_address': { + 'address': collector}, + 'src_address': { + 'address': src}, + 'collector_port': collector_port, + 'vrf_id': vrf_id, + 'max_msg_size': max_msg_size}) + + def syslog_get_sender(self): + """Return syslog sender configuration""" + return self.api(self.papi.syslog_get_sender, {}) + + def syslog_set_filter(self, severity): + """Set syslog filter parameters + + :param severity: severity filter (specified severity and greater match) + """ + return self.api(self.papi.syslog_set_filter, {'severity': severity}) + + def syslog_get_filter(self): + """Return syslog filter parameters""" + return self.api(self.papi.syslog_get_filter, {}) -- cgit 1.2.3-korg