def __init__(self, config, addresses=[], queue=True): """ Constructor. Requires a WMCore.Configuration object with configuration information. The addresses of recipients and the flag for queue/handling mode can be set with setAdress or setQueue methods also (have a look at their docstring for further help). The listener for messages needs to be started with the startListener method, meanwhile only publication capabilities are available. """ self.myconfig = config sections = self.myconfig.listSections_() if not "RemoteMsg" in sections: msg = "Cannot create RemoteMsg object without " msg += "RemoteMsg section in config file" raise Exception(msg) self.mylogger = None self.logMsg = None self.__setLogging__() self.mylogger.info("\n\n>>>>>RemoteMsg object being created <<<<<<\n") self.myComp = None if hasattr(self.myconfig.RemoteMsg, "inComponent"): self.myComp = self.myconfig.RemoteMsg.inComponent self.queueMode = queue self.addresses = addresses self.user = None self.passwd = None self.msgLock = threading.Lock() self.handlerLock = threading.Lock() self.factory = WMFactory("RemoteMsg") # If this is instantiated by a WMCore component, get its DB interface if self.myComp: # Get a reference to our invoking component's DB factory myThread = threading.currentThread() self.dbFactory = myThread.dbFactory self.dialect = myThread.dialect # TODO: Our msg queue is just in memo for now, but it might be in a DB # We already have the DB interface, but we would need our own Create # and Queries modules (in principle, different from those of the comp) # self.factory = WMFactory("threadPool", "WMCore.ThreadPool."+ \ # myThread.dialect) # self.queries = factory.loadObject(self.myComp+"Database"+ \ # myThread.dialect+"Queries") self.msgQueue = [] self.handlerMap = {} # Formatter for responses (change only if in synchronous mode) formatter = "RemoteMsgComp.DefaultFormatter" if hasattr(self.myconfig.RemoteMsg, "formatter"): formatter = self.myconfig.RemoteMsg.formatter formatterObj = self.factory.loadObject(formatter) params = { "msgQueue": self.msgQueue, "handlerMap": self.handlerMap, "msgLock": self.msgLock, "formatter": formatterObj, "queueMode": self.queueMode, } if self.myComp: params["component"] = self.myComp params["dbFactory"] = self.dbFactory params["dialect"] = self.dialect self.httpTree = HttpTree(params) self.sender = None self.__createSender__() self.listener = None
class RemoteMsg(object): """ _RemoteMsg_ Main interface of the RemoteMsg module. Clients wishing to use the RemoteMsg should instantiate an object of this class and interface it using the public methods declared by it. """ def __init__(self, config, addresses=[], queue=True): """ Constructor. Requires a WMCore.Configuration object with configuration information. The addresses of recipients and the flag for queue/handling mode can be set with setAdress or setQueue methods also (have a look at their docstring for further help). The listener for messages needs to be started with the startListener method, meanwhile only publication capabilities are available. """ self.myconfig = config sections = self.myconfig.listSections_() if not "RemoteMsg" in sections: msg = "Cannot create RemoteMsg object without " msg += "RemoteMsg section in config file" raise Exception(msg) self.mylogger = None self.logMsg = None self.__setLogging__() self.mylogger.info("\n\n>>>>>RemoteMsg object being created <<<<<<\n") self.myComp = None if hasattr(self.myconfig.RemoteMsg, "inComponent"): self.myComp = self.myconfig.RemoteMsg.inComponent self.queueMode = queue self.addresses = addresses self.user = None self.passwd = None self.msgLock = threading.Lock() self.handlerLock = threading.Lock() self.factory = WMFactory("RemoteMsg") # If this is instantiated by a WMCore component, get its DB interface if self.myComp: # Get a reference to our invoking component's DB factory myThread = threading.currentThread() self.dbFactory = myThread.dbFactory self.dialect = myThread.dialect # TODO: Our msg queue is just in memo for now, but it might be in a DB # We already have the DB interface, but we would need our own Create # and Queries modules (in principle, different from those of the comp) # self.factory = WMFactory("threadPool", "WMCore.ThreadPool."+ \ # myThread.dialect) # self.queries = factory.loadObject(self.myComp+"Database"+ \ # myThread.dialect+"Queries") self.msgQueue = [] self.handlerMap = {} # Formatter for responses (change only if in synchronous mode) formatter = "RemoteMsgComp.DefaultFormatter" if hasattr(self.myconfig.RemoteMsg, "formatter"): formatter = self.myconfig.RemoteMsg.formatter formatterObj = self.factory.loadObject(formatter) params = { "msgQueue": self.msgQueue, "handlerMap": self.handlerMap, "msgLock": self.msgLock, "formatter": formatterObj, "queueMode": self.queueMode, } if self.myComp: params["component"] = self.myComp params["dbFactory"] = self.dbFactory params["dialect"] = self.dialect self.httpTree = HttpTree(params) self.sender = None self.__createSender__() self.listener = None def __del__(self): # Tell cherrypy to die self.mylogger.info("Asking listener to die") if self.listener: self.listener.terminate() self.listener.join() def __setLogging__(self): """ Initializes logging. Use by the constructor. """ compSect = self.myconfig.RemoteMsg # Logging if not hasattr(compSect, "logFile"): compSect.logFile = os.path.join(compSect.RemoteMsgDir, "remoteMsg.log") print("Log file is: " + compSect.logFile) if not hasattr(compSect, "listenerLogFile"): compSect.listenerLogFile = os.path.join(compSect.RemoteMsgDir, "listener.log") print("Listener log file is: " + compSect.listenerLogFile) logHandler = RotatingFileHandler(compSect.logFile, "a", 1000000, 3) logFormatter = logging.Formatter("%(asctime)s:%(levelname)s:%(filename)s:%(message)s") logHandler.setFormatter(logFormatter) self.mylogger = logging.getLogger("RemoteMsg") self.mylogger.addHandler(logHandler) self.mylogger.setLevel(logging.INFO) # map log strings to integer levels: self.logMsg = { "DEBUG": logging.DEBUG, "ERROR": logging.ERROR, "NOTSET": logging.NOTSET, "CRITICAL": logging.CRITICAL, "WARNING": logging.WARNING, "INFO": logging.INFO, } ## 'SQLDEBUG' : logging.SQLDEBUG } if hasattr(compSect, "logLevel") and compSect.logLevel in self.logMsg.keys(): self.mylogger.setLevel(self.logMsg[compSect.logLevel]) def __createSender__(self): """ Initializes the sender object. Used by the constructor. """ # Sender is not a new thread, so it does not need a lock params = {} params["addresses"] = self.addresses params["port"] = "8030" if hasattr(self.myconfig.RemoteMsg, "senderPort"): params["port"] = self.myconfig.RemoteMsg.senderPort params["service"] = "msg" if hasattr(self.myconfig.RemoteMsg, "senderService"): params["service"] = self.myconfig.RemoteMsg.senderService params["user"] = None if hasattr(self.myconfig.RemoteMsg, "senderUser"): params["user"] = self.myconfig.RemoteMsg.senderUser params["pwd"] = None if hasattr(self.myconfig.RemoteMsg, "senderPwd"): params["pwd"] = self.myconfig.RemoteMsg.senderPwd params["realm"] = "RemoteMsg" if hasattr(self.myconfig.RemoteMsg, "realm"): params["realm"] = self.myconfig.RemoteMsg.senderRealm self.sender = Sender(self.msgQueue, params) def startListener(self): """ Starts a listener for incoming remote messages. The method will refuse to create a listener if there is already one """ if not self.listener: self.mylogger.info("Starting listener") self.listener = Listener(self.httpTree, self.myconfig) self.listener.start() else: msg = "Refusing to start listener (there is already one)." self.mylogger.info(msg) def setAddress(self, addresses): """ Sets the addresses of the remote ends. Argument should be a list of IPs or hostnames. The publish method will send messages to all members of this list. """ self.addresses = addresses self.sender.setAddress(self.addresses) def setAuthentication(self, user, passwd): """ Sets the user/password for authentication with the remote application. Has to be done before sending a message. """ self.mylogger.debug("Setting user and passwd") self.user = user self.passwd = passwd def setQueue(self, value): """ This is an option that either allows messages that are being received to be put in a local queue (so a get method can retrieve them), or if set to False messages are handled directly through the handler framework. """ self.queueMode = value self.httpTree.setQueue(self.queueMode) def get(self): """ Gets messages from the local buffer (those not handled as explained in the setQueue method). The first message of the queue is retrieved and returned. This method is only used when the queue is set to True. If queue is set to False or there are no stored messages, None is returned. """ if self.queueMode: self.msgLock.acquire() if self.msgQueue: result = self.msgQueue.pop(0) else: result = None self.msgLock.release() return result else: return None def publish(self, msgType, payload, sync=False): """ Sends a message to the remote end. If sync is set to True, the remote server will complete the message handling before replying with some generated data. Otherwise, the remote end will immediately reply with some "Message received" indication and execute the handler asynchronously (if the remote end is in queue mode, there is no handler execution, so this flag is meaningless). In any case, the response of the message (e.g. a json string product of the handling of the HTTP request) is returned. Can throw an HTTP exception in case of error connecting with the remote end. """ return self.sender.send(msgType, payload, sync) def setHandler(self, msgType, handler): """ Maps the specified handler to the indicated message type. The handler must be the name of a class which can be called (e.g. RemoteMsg.SimpleHandler). The handler will only be called if queue mode is set to False. """ msg = "Setting new handler %s for msgType %s" % (handler, msgType) self.mylogger.debug(msg) # Factory to dynamically load handlers params = {} if self.myComp: params["component"] = self.myComp newHandler = self.factory.loadObject(handler, params) self.handlerLock.acquire() self.handlerMap[msgType] = newHandler self.handlerLock.release()