From 87cce74e29dc148618252c19c4aed62452685542 Mon Sep 17 00:00:00 2001 From: Moritz Venn Date: Mon, 31 Jan 2011 19:35:05 +0100 Subject: [PATCH] growlee: add syslog backend only client end tested so far, feel free to send me bugreports ;-) --- growlee/src/GrowleeConnection.py | 9 +++- growlee/src/Syslog.py | 110 +++++++++++++++++++++++++++++++++++++++ growlee/src/plugin.py | 2 +- 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 growlee/src/Syslog.py diff --git a/growlee/src/GrowleeConnection.py b/growlee/src/GrowleeConnection.py index 43cbf79..e6c099a 100644 --- a/growlee/src/GrowleeConnection.py +++ b/growlee/src/GrowleeConnection.py @@ -7,6 +7,10 @@ from twisted.internet import reactor from . import NOTIFICATIONID def emergencyDisable(*args, **kwargs): + if args: + try: args[0].printTraceback() + except Exception: pass + global growleeConnection if growleeConnection: growleeConnection.stop() @@ -67,9 +71,12 @@ class GrowleeConnection: elif proto == "growl": from GrowlTalk import GrowlTalkAbstraction connection = GrowlTalkAbstraction(host) - else: # proto == "snarl": + elif proto == "snarl": from SNP import SnarlNetworkProtocolAbstraction connection = SnarlNetworkProtocolAbstraction(host) + else: # proto == "syslog": + from Syslog import SyslogAbstraction + connection = SyslogAbstraction(host) self.connections.append((connection, host)) diff --git a/growlee/src/Syslog.py b/growlee/src/Syslog.py new file mode 100644 index 0000000..e4cd90c --- /dev/null +++ b/growlee/src/Syslog.py @@ -0,0 +1,110 @@ +from twisted.internet.protocol import DatagramProtocol +from twisted.internet import reactor +from twisted.internet import reactor +from time import strftime, strptime, localtime +from os import uname + +from Screens.MessageBox import MessageBox +from Tools import Notifications + +from GrowleeConnection import emergencyDisable +from . import NOTIFICATIONID + +SYSLOG_UDP_PORT = 514 + +FACILITY = { + 'kern': 0, 'user': 1, 'mail': 2, 'daemon': 3, + 'auth': 4, 'syslog': 5, 'lpr': 6, 'news': 7, + 'uucp': 8, 'cron': 9, 'authpriv': 10, 'ftp': 11, + 'local0': 16, 'local1': 17, 'local2': 18, 'local3': 19, + 'local4': 20, 'local5': 21, 'local6': 22, 'local7': 23, +} + +SEVERITY = { + 'emerg': 0, 'alert':1, 'crit': 2, 'err': 3, + 'warning': 4, 'notice': 5, 'info': 6, 'debug': 7 +} + +reverse = lambda map: dict(zip(map.values(), map.keys())) + +SEVERITYMAP = { + MessageBox.TYPE_YESNO: SEVERITY['debug'], + MessageBox.TYPE_INFO: SEVERITY['info'], + MessageBox.TYPE_WARNING: SEVERITY['warning'], + MessageBox.TYPE_ERROR: SEVERITY['err'], +} + +class SyslogNetworkProtocol(DatagramProtocol): + addr = None + def __init__(self, host): + self.host = host + + def gotIP(self, ip): + self.addr = (ip, SYSLOG_UDP_PORT) + + def noIP(self, error): + print "--------------------------------", error + emergencyDisable() + + def startProtocol(self): + reactor.resolve(self.host.address.value).addCallback(self.gotIP).addErrback(self.noIP) + + def sendNotification(self, title='No title.', description='No message.', priority=0): + if not self.transport or not self.addr or not self.host.enable_outgoing.value: + return + + ltime = localtime() + day = strftime("%d", ltime) + if day[0] == "0": + day = " " + day[1:] + value = strftime("%b %%s %H:%M:%S", ltime) + timestamp = value % (day,) + payload = "<%d>%s %s growlee: (%s) %s" % (FACILITY['local0'] * 8 + SEVERITYMAP[priority], timestamp, uname()[1], title, description.replace('\n', ' '),) + self.transport.write(payload, self.addr) + + def datagramReceived(self, data, addr): + if not self.host.enable_incoming.value: + return + + Len = len(data) + if Len > 1024: # invalid according to rfc + return + + # read prio field + prio, data = data.split('>', 1) + prio = int(prio[1:]) + facility, severity = divmod(prio, 8) # just the ids + #facility = reverse(FACILITY)[facility] + type = reverse(SEVERITYMAP).get(severity, MessageBox.TYPE_ERROR) + + # parse remaining header + try: + # try to parse timestamp to determine validity + timestamp = strptime(payload[:15], '%b %d %H:%M:%S') + except ValueError: + message = payload + else: + hostname, payload = payload[16:].split(' ', 1) + # NOTE: we could re-process timestamp to get a customized display format, + # but lets just keep this for now + message = hostname + '@' + payload[:15] + ':' + payload + + Notifications.AddNotificationWithID( + NOTIFICATIONID, + MessageBox, + text = message, + type = type, + timeout = 10, # XXX: un-hardcode timeout? + close_on_any_key = True, + ) + +class SyslogAbstraction: + def __init__(self, host): + self.syslog = SyslogNetworkProtocol(host) + self.serverPort = reactor.listenUDP(SYSLOG_UDP_PORT, self.syslog) + + def sendNotification(self, title='No title.', description='No description.', priority=-1, timeout=-1): + self.syslog.sendNotification(title=title, description=description, priority=priority) + + def stop(self): + return self.serverPort.stopListening() diff --git a/growlee/src/plugin.py b/growlee/src/plugin.py index 428349a..423db9d 100644 --- a/growlee/src/plugin.py +++ b/growlee/src/plugin.py @@ -27,7 +27,7 @@ def addHost(name): s.enable_outgoing = ConfigYesNo(default=False) s.address = ConfigText(fixed_size=False) s.password = ConfigPassword() - s.protocol = ConfigSelection(default="growl", choices=[("growl", "Growl"), ("snarl", "Snarl"), ("prowl", "Prowl")]) + s.protocol = ConfigSelection(default="growl", choices=[("growl", "Growl"), ("snarl", "Snarl"), ("prowl", "Prowl"), ("syslog", "Syslog UDP")]) s.level = ConfigSelection(default="-1", choices=[("-1", _("Low (Yes/No)")), ("0", _("Normal (Information)")), ("1", _("High (Warning)")), ("2", _("Highest (Emergency)"))]) s.blacklist = ConfigSet(choices=[]) config.plugins.growlee.hosts.append(s) -- 2.7.4