예제 #1
0
    def __init__(self, configfile=None, dprg="empd.py"):
        """Load the configuration files, create the CommRouter, and then create 
        the daemon. 
        """
        # get yourself a timer!
        self.start_time=time.time() 
        self.fm_start_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(self.start_time))

        # presets!
        self.registry = None #Registry object!!
        self.aman     = None #AttachmentManager
        self.router   = None #message router

        # now set up the internal configuration via a config file.
        self.config=EmpConfigParser(configfile)
        self.config.validateInternals()
        setup_logging(self.config)

        # adjust by the configurations....
        RDaemon.__init__(self, self.config.get("Daemon", "pid-file"),
                              pchannel=self.config.getint("Daemon", "port"),
                              dprog=dprg,
                              dargs=configfile)
예제 #2
0
class EmpDaemon(RDaemon):
    """ The Daemon for SMTG, all communication to this daemon goes through smtgd.py
    or the comm port via a DaemonClientSocket. To connect, get the port number and
    then connect either with a regular UDP-8 encoded TCP socket or the 
    DaemonClientSocket.

    The daemon handles the feed and connection loops along with all configuration
    validation. It does not handle interface registration, which should be done 
    through the registration steps handled by smtgd.py. Please look there if you
    want to know how to register your interface with the local smtg daemon.
    """
    
    def __init__(self, configfile=None, dprg="empd.py"):
        """Load the configuration files, create the CommRouter, and then create 
        the daemon. 
        """
        # get yourself a timer!
        self.start_time=time.time() 
        self.fm_start_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(self.start_time))

        # presets!
        self.registry = None #Registry object!!
        self.aman     = None #AttachmentManager
        self.router   = None #message router

        # now set up the internal configuration via a config file.
        self.config=EmpConfigParser(configfile)
        self.config.validateInternals()
        setup_logging(self.config)

        # adjust by the configurations....
        RDaemon.__init__(self, self.config.get("Daemon", "pid-file"),
                              pchannel=self.config.getint("Daemon", "port"),
                              dprog=dprg,
                              dargs=configfile)
         
    def startup(self):
        """ Startup the daemon if there is a variable called boot-launch and 
        it's True.
        """
        if self.config.getboolean("Daemon", "boot-launch"):
            RDaemon.start(self)
         
         
    def stop(self):
        """ Stops the daemon but then waits a while to make sure all the threads are done. """
        try: 
            RDaemon.stop(self)
            time.sleep(3)#dont worry about the magic number
        except: pass
         
    def restart(self):
        """ Restarts the daemon by killing both threads, and making sure their
        both down before trying to run the daemon again.
        """
        try:
            self.stop()
            time.sleep(2)#dont worry about the magic number
        except:pass
        self.start()

    
    def get_commands(self):
        if not hasattr(self, "_commands"):
            self.__setup_commands()
            
        return self._commands
    
    def __setup_commands(self):
        """ Sets up the daemon's commands. """
        self._commands = [Command("var",    trigger=self.__cmd_var, help="returns all internal variables and their values"),
                          Command("cvar",   trigger=self.__cmd_cvar, help="given a variable name and a value, it will change it to the given value."),
                          Command("trigger",trigger=self.__cmd_trigger, help="hand trigger an event given an id or event string"),
                          Command("status", trigger=self.__cmd_status, help="get the daemon status, or the status of a plugin/alert given the id."),
                          Command("events", trigger=self.__cmd_events, help="get a list of all the events that a given plug has"),
                          Command("alerts", trigger=self.__cmd_alerts, help="get a list of all the alerts that a given alarm has"),
                          Command("plugs",  trigger=self.__cmd_plugs, help="get a list of plug-ins ids to names."),
                          Command("cmds",   trigger=self.__cmd_cmds, help="get the daemon command list."),
                          Command("alarms", trigger=self.__cmd_alarms, help="get a list of alarms ids to names."),
                          Command("id",     trigger=self.__cmd_id, help="given an attachments name, it will return the ID"),
                          Command("idsearch",      trigger=self.__cmd_idsearch, help="returns whether a given id or name exists, returns a boolean"),
                          Command("curtriggered",  trigger=self.__cmd_curtriggered, help="the currently triggered events"),
                          Command("attachments",   trigger=self.__cmd_attachments, help="get a list of all attachments"),
                          Command("help",          trigger=self.__cmd_help, help="returns a help screen for the daemon, alerters, or a plug, or even all of the above."),
                          Command("activate",      trigger=self.__cmd_activate, help="activates an attachment given an id or target name"),
                          Command("deactivate",    trigger=self.__cmd_deactivate, help="deactivates an attachment given an id or target name"),
                          Command("subscribe",     trigger=self.__cmd_subscribe, help="subscribes a given alarm name/id to a given plug-in name/id or event id."),
                          Command("subscriptions", trigger=self.__cmd_subscriptions, help="get a list of target subscriptions"),
                          Command("unsubscribe",   trigger=self.__cmd_unsubscribe, help="unsubscribes a given alarm name/id to a given plug-in name/id or event id.")]


    def __cmd_status(self, *args):
        return "SMTG-D Running since: "+self.fm_start_time
    
    def __cmd_cmds(self, *args):
        cmdlst = CommandList(self.get_commands())
        return cmdlst.getNames()
    
    def __cmd_plugs(self, *args): 
        return self.aman.getPlugNames()
    
    def __cmd_alarms(self, *args):
        return self.aman.getAlarmNames()
    
    def __cmd_id(self, *args): 
        if len(args) > 0:
            return self.registry.getAttachId(args[0])
        else: raise Exception("Id command needs a target name.")
        
    def __cmd_idsearch(self, *args):
        if len(args) > 0:
            return self.registry.isRegistered(args[0])
        else: 
            raise Exception("idsearch command needs a target name or id.")
        
    def __cmd_var(self, *args):
        vars = {}
        for var in self.config.options("Daemon"):
            vars[var] = self.config.get("Daemon", var)
        for var in self.config.options("Logging"):
            vars[var] = self.config.get("Logging", var)
        return vars
    
    def __cmd_activate(self, *args):
        if len(args) > 0:
            attach = self.aman.getAttachment(args[0])
            if attach is None: raise Exception("Target name or id does not exist.")
            if attach.is_activated: raise Exception("Target already active!")
            attach.activate()
            return "Activated"
        else: raise Exception("activate command needs a target name or id.") 
    def __cmd_deactivate(self, *args):
        if len(args) > 0:
            attach = self.aman.getAttachment(args[0])
            if attach is None: raise Exception("Target name or id does not exist.")
            if not attach.is_activated: raise Exception("Target already inactive!")
            attach.deactivate()
            return "De-activated"
        else: raise Exception("deactivate command needs a target name or id.")
        
        
    def __cmd_subscribe(self, *args): #TODO: This is disgusting and looks like a hack. Needs to be cleaner parsing.
        """ Validate subscription arguments and returns """
        if len(args) != 2: 
            raise Exception("Subscribe command takes two arguments.")
        
        namea, suba, defaultsa = parsesubs(args[0])
        nameb, subb, defaultsb = parsesubs(args[1])
        logging.debug("-----SUBSCRIPION-VAL1 = (%s,%s,%s)"%(namea,suba,str(defaultsa)))
        logging.debug("-----SUBSCRIPION-VAL2 = (%s,%s,%s)"%(nameb,subb,str(defaultsb)))
        if len(defaultsa) > 0 or len(defaultsb) > 0:
            raise Exception("One or both of your arguments are not valid subscription strings.")
        
        #Find out what our first argument is:
        if suba is None and subb is None:
            ## we can pass our strings directly to subscribe, it'll figure it out.
            return self.registry.subscribe(namea, nameb)
        
        elif suba is not None and subb is None:
            ## We need to find out what attachment it is.
            id = self.registry.getAttachId( namea )
            if id is None: return False
            lid = self.registry.isAlertLoaded(suba, id)
            if lid is None:
                eid = self.registry.isEventLoaded(suba, id)
                if eid is None: return False
                id = eid
            else: id = lid
            return self.registry.subscribe(id, nameb)
        
        elif suba is None and subb is not None:
            ## We need to find out what attachment it is.
            id = self.registry.getAttachId( nameb )
            if id is None: return False
            lid = self.registry.isAlertLoaded(subb, id)
            if lid is None:
                eid = self.registry.isEventLoaded(subb, id)
                if eid is None: return False
                id = eid
            else: id = lid
            return self.registry.subscribe(id, namea)
        
        else:
            
            ## We need to find out what attachment it is.
            first = self.registry.getAttachId( namea )
            if first is None: return False
            lid = self.registry.isAlertLoaded(suba, first)
            if lid is None:
                eid = self.registry.isEventLoaded(suba, first)
                if eid is None: return False
                first = eid
            else: first = lid
            second = self.registry.getAttachId( nameb )
            if second is None: return False
            lid = self.registry.isAlertLoaded(subb, second)
            if lid is None:
                eid = self.registry.isEventLoaded(subb, second)
                if eid is None: return False
                second = eid
            else: second = lid
            logging.debug("TRYING TO SUBSCRIBE %s=%s"%(first, second))
            return self.registry.subscribe(first, second)
        
         
    def __cmd_subscriptions(self, *args): return notimplemented()
    def __cmd_unsubscribe(self, *args):   return notimplemented()
        
        
    def __cmd_trigger(self, *args):
        if len(args) != 1:
            raise Exception("Trigger command needs an event string or an event's id.")
        plug,event,others = parsesubs(args[0])
        if len(others)>0: raise Exception("Invalid event string or id.")
        try:
            if event is None: #could be either an id or just a plug name.
                pid = self.registry.getAttachId(plug)
                eids = self.registry.getPlugEventIds(pid)
                if len(eids)==0:
                    eid = self.registry.getEventId(plug)
                    if eid is None: raise Exception()
                    else: eids.append(eid)
                for eid in eids: triggerEvent(eid)
            else: #must be an event string
                pid = self.registry.getAttachId(plug)
                eid = self.registry.getPlugEventId(pid, event)
                if eid is not None: triggerEvent(eid)
                else: raise Exception("Could not find the given event to trigger!")
            return "Triggered!"
        except Exception as e: 
            logging.exception(e)
            raise e #explicit re-raise
        
    def __cmd_cvar(self, *args):    return notimplemented()        
    def __cmd_events(self, *args):  return notimplemented()
    def __cmd_alerts(self, *args):  return notimplemented()
    def __cmd_curtriggered(self, *args): return notimplemented()
    def __cmd_attachments(self, *args):  return notimplemented()
                
    def __cmd_help(self, *args):
        if len(args) <= 0: #cant be less but i like being complete.
            cmds = CommandList(self.get_commands())
            return cmds.getHelpDict()
        else:
            if args[0] == "all":
                cmds = CommandList(self.get_commands())
                all = {"Daemon":cmds.getHelpDict()}
                try:
                    logging.debug("trying to get other commands.")
                    alist = self.aman.getAllPlugins()
                    logging.debug("got %d attachments",len(alist))
                    for attach in self.aman.getAllPlugins():
                        logging.debug("getting commands from plug: %s", attach.module)
                        cmds = CommandList(attach.plugin_object.get_commands())
                        all[attach.name] = cmds.getHelpDict()
                except Exception as e:logging.exception(e)
                return all
            else: # its a plug or an alert id.
                if args[0] == "daemon":
                    cmds = CommandList(self.get_commands())
                    return cmds.getHelpDict()
                elif self.registry.isRegistered(args[0]):
                    attach = self.aman.getAttachment(args[0])
                    if attach is None: raise Exception("Could not get target's commands.")
                    cmds = CommandList(attach.get_commands())
                    return cmds.getHelpDict()
                else:
                    raise Exception("Invalid target")

  
    def _run(self):
        """ 
            Here is a quick outline of what each of the three threads are and 
        do, for a more in-depth look, look at the consecutive method calls:
        
            - Thread 1: the current one, this is the pull loop. It updates the
                LoopPlugins every time interval. This time interval is set by 
                the configuration file.
                
            - Thread 2: this is the MessageRouter.startRouter() method. It 
                handles all inter-thread communication. See CommReader for 
                more info.
                
            - Thread 3: this is _t2(), it handles incoming communication to the
                port that the SmtgDaemon listens to for Interfaces. See the
                interface API for more information on how to talk to the 
                daemon. 
        """
        try:
            # Load the registry from last time if it exists
            self.registry = Registry(self.config.getRegistryFile())
            self.ID       = self.registry.daemonId() 
            
            # load the attachment manager now and search for the user's 
            # attachments. It will only load them if they pass inspection.
            self.aman = AttachmentManager( self.config, 
                                           self.registry,
                                           EventManager(self.config, 
                                                        self.registry, 
                                                        self.isRunning))
            self.aman.collectPlugins()
            
            # Set up the router using the loaded registry
            self.router   = MessageRouter(self.registry, 
                                          self, self.aman)
            
            #activates the attachments based on user cfg file. 
            # if the attachment was a SignalPlugin, it will throw the plug's
            # run() function into a new thread.
            self.aman.activateAttachments()
    
            # starts the comm router running to send messages!!
            Thread(target=self.router.startRouter,
                   kwargs={"triggermethod":self.isRunning}).start()
            
            # start the interface server thread
            Thread(target=self.__t2).start()
    
            # start the pull loop.
            logging.debug("pull-loop thread started")
            while self.isRunning():
                
                # get all active loop plug-ins
                activePlugins = self.aman.getLoopPlugs()
                if activePlugins:
                    for plugin in activePlugins:
                        if plugin.plugin_object.is_activated:
                            # for each active feed run the update function with no
                            # arguments. The only time arguments are needed 
                            # is if the plug-in was force updated by a command.
                            logging.debug("pulling LoopPlugin: %s" % plugin.name)
                            try: plugin.plugin_object.update()
                            except Exception as e:
                                logging.error("%s failed to update: %s" % (plugin.name, e))    
                
                try: # sleep, and every five seconds check if still alive
                    count=0
                    sleep_time = self.config.getfloat("Daemon","update-speed") * 60.0
                    while(count<sleep_time):
                        count+=5
                        time.sleep(5)#every 5 seconds check state
                        if not self.isRunning(): break;
                except: pass
                
            #Stop all signal threads
            for signal in self.aman.getSignalPlugs():
                signal.plugin_object.deactivate()    
            
            #flush the router, and save all configurations.
            self.router.flush()
            self.config.save( self.aman.getAllPlugins() )
            self.registry.save()
            logging.debug("pull-loop thread is dead")

        except Exception as e:
            logging.error("Pull-loop thread was killed by: %s" % str(e))
            logging.exception(e)
        
            
    def __t2(self):
        """ Interface Server method, this runs in a new thread when the 
        daemon starts. See _run() method for more information.
        """
        
        logging.debug("communication-thread started")
        # Create socket and bind to address
        whitelist = self.config.getlist("Daemon", "whitelisted-ips")
        isocket = DaemonServerSocket(port=self.getComPort(),
                                     ip_whitelist=whitelist,
                                     externalBlock=self.config.getboolean("Daemon","local-only"),
                                     allowAll=self.config.getboolean("Daemon", "allow-all"))
        
        while self.isRunning(): # keep listening forever
            try:
                client_socket = isocket.accept()
                logging.debug("incoming message from interface.")
        
                # create an interface out of the socket
                # LATER: authentication can go here, before they connect. (eg logging in)
                interface = Interface(self.router, client_socket)
                self.registry.registerInterface(interface) #gives the interface its ID
                self.router.addInterface(interface)
            
                # since there are abilities that this person can perform throw
                # to new thread by way of the interface class
                self.router.sendMsg(makeCommandMsg("proceed", self.ID, dest=interface.ID))
                Thread(target=interface.receiver).start()
            except timeout:pass #catches a timeout and allows for daemon status checking
            except Exception as e: logging.exception(e)
        isocket.close()
        logging.debug("communication-thread is dead")