📄 dispatcher.py
字号:
#! /usr/bin/env python# -*- coding: ISO8859-1 -*-## Copyright 2006 UNINETT AS## This file is part of Network Administration Visualized (NAV)## NAV is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## NAV is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with NAV; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA#"""Class with common functions inherited/overrided by other dispatchers."""__copyright__ = "Copyright 2006 UNINETT AS"__license__ = "GPL"__author__ = "Stein Magnus Jodal (stein.magnus.jodal@uninett.no)"__id__ = "$Id: gammudispatcher.py 3464 2006-06-22 08:58:05Z jodal $"import loggingimport sysimport timeimport nav.smsd # eval() wants itclass DispatcherError(Exception): """Base class for all exceptions raised by dispatchers."""class PermanentDispatcherError(DispatcherError): """Thrown for permanent errors in dispatchers."""class DispatcherHandler(object): """ Handler for communication with the dispatchers. This layer makes it possible to use multiple dispatchers which works as failovers for each other. """ def __init__(self, config): """Constructor.""" # Create logger self.logger = logging.getLogger("nav.smsd.dispatcher") # Get config try: self.dispatcherretry = config['dispatcher']['dispatcherretry'] except KeyError, error: self.dispatcherretry = 300 # 5 min self.logger.debug("Dispatcher retry time not set, using default.") # Get dispatchers self.dispatchers = [] for pri in range(len(config['dispatcher']) + 1): key = 'dispatcher' + str(pri) if key in config['dispatcher']: dispatcher = config['dispatcher'][key] self.logger.debug("Init dispatcher %d: %s", pri, dispatcher) # Import dispatcher module modulename = 'nav.smsd.' + dispatcher.lower() try: module = self.importbyname(modulename) self.logger.debug("Imported module %s", modulename) except DispatcherError, error: self.logger.warning("Failed to import %s: %s", dispatcher, error) continue except Exception, error: self.logger.exception("Unknown exception: %s", error) # Initialize dispatcher try: instance = eval("%s.%s(%s)" % \ (module.__name__, dispatcher, config[dispatcher])) self.dispatchers.append((dispatcher, instance)) self.logger.debug("Dispatcher loaded: %s", dispatcher) except DispatcherError, error: self.logger.warning("Failed to init %s: %s", dispatcher, error) continue except Exception, error: self.logger.exception("Unknown exception: %s", error) def importbyname(self, name): """Import module given by name.""" mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod def sendsms(self, phone, msgs): """ Formats and sends with help of the wanted dispatcher. Arguments: ``phone'' is the phone number the messages are to be dispatched to. ``msgs'' is a list of messages ordered with the most severe first. Each message is a tuple with ID, text and severity of the message. Returns four values: The formatted SMS. A list of IDs of sent messages. A list of IDs of ignored messages. An integer which is the sending ID if available or 0 otherwise. Raises a DispatcherError if it doesn't find a working dispatcher and succeeds in sending the SMS. """ for i, (dispatchername, dispatcher) in enumerate(self.dispatchers): if dispatcher.lastfailed: sincelastfail = int(time.time()) - dispatcher.lastfailed if sincelastfail < self.dispatcherretry: self.logger.debug("%s last failed %ds ago. Skipping.", dispatchername, sincelastfail) continue # Skip this dispatcher for now try: self.logger.debug("Trying %s...", dispatchername) (sms, sent, ignored, result, smsid) = \ dispatcher.sendsms(phone, msgs) except PermanentDispatcherError, error: self.logger.error("%s failed permanently to send SMS: %s", dispatchername, error) self.logger.info("Removing failed dispatcher %s.", dispatchername) del self.dispatchers[i] continue # Skip to next dispatcher except DispatcherError, error: self.logger.warning("%s failed to send SMS: %s", dispatchername, error) dispatcher.lastfailed = int(time.time()) continue # Skip to next dispatcher except Exception, error: self.logger.exception("Unknown exception: %s", error) if result is False: self.logger.warning("%s failed to send SMS: Returned false.", dispatchername) dispatcher.lastfailed = int(time.time()) continue # Skip to next dispatcher # No exception and true result? Success! return (sms, sent, ignored, smsid) # Still running? All dispatchers failed permanently. if len(self.dispatchers) == 0: raise PermanentDispatcherError, \ "No dispatchers available. None configured " + \ "or all dispatchers failed permanently." # Still running? All dispatchers failed! raise DispatcherError, "All dispatchers failed to send SMS."class Dispatcher(object): """The SMS dispatcher mother class.""" def __init__(self): """Constructor.""" # Create logger self.logger = logging.getLogger("nav.smsd.dispatcher") # Field for last failed timestamp self.lastfailed = None # Max length of SMS self.maxlen = 160 # Max length of ignored message. 15 gives us up to four digits. self.ignlen = 15 def formatsms(self, msgs): """ Format a SMS from one or more messages. ``msgs'' is a list of messages ordered with the most severe first. Each message is a tuple with ID, text and severity of the message. Returns a tuple with the SMS, a list of IDs of sent messages and a list of IDs of ignored messages. Pseudo code: If one message SMS = message If multiple messages SMS = as many msgs as possible + how many was ignored """ # Copies so we can modify them without wreaking the next SMS maxlen = self.maxlen ignlen = self.ignlen msgcount = len(msgs) # Number of messages msgno = 0 # Number of messages processed addmsg = True # Whether we shall continue to add msgs to the SMS tmpsms = "" # We format first and then checks the length # The empty result sms = "" sent = [] ignored = [] # Concatenate as many msgs as possible for msg in msgs: msgno += 1 # If this is the last message we don't need to reserve space for # the ignored count. If we have enough space, we can add the # message itself instead. If we don't have enough space, the "+1 # see web" is added as normal. if msgno == msgcount: ignlen = 0 # We create a temporary SMS first and then afterwards checks if # it's short enough to be accepted. This makes it easy to also # count all extra space, numbering, etc. if msgno == 1: tmpsms = msg[1] elif msgno == 2: tmpsms = "1: %s; 2: %s" % (sms, msg[1]) else: tmpsms = "%s; %d: %s" % (sms, msgno, msg[1]) # If we have enough space... if len(tmpsms) < (maxlen - ignlen) and addmsg: # Accept updated SMS sms = tmpsms sent.append(msg[0]) else: # Ignore message ignored.append(msg[0]) # Stop adding messages when the first fail to fit addmsg = False # Tell how many was ignored if len(ignored) > 0: sms = "%s +%d see web." % (sms, len(ignored)) return (sms, sent, ignored) def sendsms(self, phone, msgs): """ Empty shell for the sendsms method implemented by subclasses. Arguments: ``phone'' is the phone number the messages are to be dispatched to. ``msgs'' is a list of messages ordered with the most severe first. Each message is a tuple with ID, text and severity of the message. Returns five values: The formatted SMS. A list of IDs of sent messages. A list of IDs of ignored messages. A boolean which is true for success and false for failure. An integer which is the sending ID if available or 0 otherwise. """ # Format SMS (sms, sent, ignored) = self.formatsms(msgs) # Send SMS result = False smsid = 0 return (sms, sent, ignored, result, smsid)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -