From ccc9aba55a503c6d2fda62f59c851ddcf2a5fd94 Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sun, 13 Oct 2019 14:37:15 -0400 Subject: [PATCH] syslog://facility url support added --- apprise/plugins/NotifySyslog.py | 47 +++++++++++++++++++++++---------- test/test_syslog_plugin.py | 28 +++++++++++++++----- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/apprise/plugins/NotifySyslog.py b/apprise/plugins/NotifySyslog.py index fb2999f0..a6506648 100644 --- a/apprise/plugins/NotifySyslog.py +++ b/apprise/plugins/NotifySyslog.py @@ -124,16 +124,25 @@ class NotifySyslog(NotifyBase): # Define object templates templates = ( - '{schema}://_/', + '{schema}://', + '{schema}://{facility}', ) + # Define our template tokens + template_tokens = dict(NotifyBase.template_tokens, **{ + 'facility': { + 'name': _('Facility'), + 'type': 'choice:string', + 'values': [k for k in SYSLOG_FACILITY_MAP.keys()], + 'default': SyslogFacility.USER, + }, + }) + # Define our template arguments template_args = dict(NotifyBase.template_args, **{ 'facility': { - 'name': _('Facility'), - 'type': 'choice:int', - 'values': [k for k in SYSLOG_FACILITY_MAP.keys()], - 'default': SyslogFacility.USER, + # We map back to the same element defined in template_tokens + 'alias_of': 'facility', }, 'logpid': { 'name': _('Log PID'), @@ -168,7 +177,7 @@ class NotifySyslog(NotifyBase): else: self.facility = \ SYSLOG_FACILITY_MAP[ - self.template_args['facility']['default']] + self.template_tokens['facility']['default']] # Logging Options self.logoptions = 0 @@ -230,12 +239,13 @@ class NotifySyslog(NotifyBase): 'logpid': 'yes' if self.log_pid else 'no', 'format': self.notify_format, 'overflow': self.overflow_mode, - 'facility': 'info' if self.facility not in SYSLOG_FACILITY_RMAP - else SYSLOG_FACILITY_RMAP[self.facility], 'verify': 'yes' if self.verify_certificate else 'no', } - return '{schema}://_/?{args}'.format( + return '{schema}://{facility}/?{args}'.format( + facility=self.template_tokens['facility']['default'] + if self.facility not in SYSLOG_FACILITY_RMAP + else SYSLOG_FACILITY_RMAP[self.facility], schema=self.secure_protocol, args=NotifySyslog.urlencode(args), ) @@ -251,17 +261,26 @@ class NotifySyslog(NotifyBase): if not results: return results - if 'facility' in results['qsd'] and len(results['qsd']['facility']): - key = results['qsd']['facility'].lower() + # if specified; save hostname into facility + facility = None if not results['host'] \ + else NotifySyslog.unquote(results['host']) + # However if specified on the URL, that will over-ride what was + # identified + if 'facility' in results['qsd'] and len(results['qsd']['facility']): + facility = results['qsd']['facility'].lower() + + if facility and facility not in SYSLOG_FACILITY_MAP: # Find first match; if no match is found we set the result # to the matching key. This allows us to throw a TypeError # during the __init__() call. The benifit of doing this # check here is if we do have a valid match, we can support # short form matches like 'u' which will match against user - results['facility'] = \ - next((f for f in SYSLOG_FACILITY_MAP.keys() - if f.startswith(key)), key) + facility = next((f for f in SYSLOG_FACILITY_MAP.keys() + if f.startswith(facility)), facility) + + # Save facility + results['facility'] = facility # Include PID as part of the message logged results['log_pid'] = \ diff --git a/test/test_syslog_plugin.py b/test/test_syslog_plugin.py index 68aaa727..6aaf79ba 100644 --- a/test/test_syslog_plugin.py +++ b/test/test_syslog_plugin.py @@ -45,16 +45,18 @@ def test_notify_syslog_by_url(openlog, syslog): assert apprise.plugins.NotifySyslog.parse_url(42) is None assert apprise.plugins.NotifySyslog.parse_url(None) is None - assert isinstance( - apprise.Apprise.instantiate('syslog://'), apprise.plugins.NotifySyslog) + obj = apprise.Apprise.instantiate('syslog://') + assert obj.url().startswith('syslog://user') is True + assert re.search(r'logpid=yes', obj.url()) is not None + assert re.search(r'logperror=no', obj.url()) is not None assert isinstance( apprise.Apprise.instantiate( 'syslog://:@/'), apprise.plugins.NotifySyslog) - obj = apprise.Apprise.instantiate('syslog://_/?logpid=no&logperror=yes') + obj = apprise.Apprise.instantiate('syslog://?logpid=no&logperror=yes') assert isinstance(obj, apprise.plugins.NotifySyslog) - assert re.search(r'facility=user', obj.url()) is not None + assert obj.url().startswith('syslog://user') is True assert re.search(r'logpid=no', obj.url()) is not None assert re.search(r'logperror=yes', obj.url()) is not None @@ -66,7 +68,7 @@ def test_notify_syslog_by_url(openlog, syslog): obj = apprise.Apprise.instantiate('syslog://_/?facility=local5') assert isinstance(obj, apprise.plugins.NotifySyslog) - assert re.search(r'facility=local5', obj.url()) is not None + assert obj.url().startswith('syslog://local5') is True assert re.search(r'logpid=yes', obj.url()) is not None assert re.search(r'logperror=no', obj.url()) is not None @@ -76,10 +78,22 @@ def test_notify_syslog_by_url(openlog, syslog): # j will cause a search to take place and match to daemon obj = apprise.Apprise.instantiate('syslog://_/?facility=d') assert isinstance(obj, apprise.plugins.NotifySyslog) - assert re.search(r'facility=daemon', obj.url()) is not None + assert obj.url().startswith('syslog://daemon') is True assert re.search(r'logpid=yes', obj.url()) is not None assert re.search(r'logperror=no', obj.url()) is not None + # Facility can also be specified on the url as a hostname + obj = apprise.Apprise.instantiate('syslog://kern?logpid=no&logperror=y') + assert isinstance(obj, apprise.plugins.NotifySyslog) + assert obj.url().startswith('syslog://kern') is True + assert re.search(r'logpid=no', obj.url()) is not None + assert re.search(r'logperror=yes', obj.url()) is not None + + # Facilities specified as an argument always over-ride host + obj = apprise.Apprise.instantiate('syslog://kern?facility=d') + assert isinstance(obj, apprise.plugins.NotifySyslog) + assert obj.url().startswith('syslog://daemon') is True + @mock.patch('syslog.syslog') @mock.patch('syslog.openlog') @@ -92,7 +106,7 @@ def test_notify_syslog_by_class(openlog, syslog): # Default obj = apprise.plugins.NotifySyslog(facility=None) assert isinstance(obj, apprise.plugins.NotifySyslog) - assert re.search(r'facility=user', obj.url()) is not None + assert obj.url().startswith('syslog://user') is True assert re.search(r'logpid=yes', obj.url()) is not None assert re.search(r'logperror=no', obj.url()) is not None