def stop(self): """This is used by service binding to stop""" try: if not self.running: raise AssertionError('not running') self._task.stop() #clear the event dictionary and delete events while self.events: name, appevent = self.events.popitem() if appevent.loop and appevent.loop.running: appevent.loop.stop() ApplicationEvent.delete(appevent) except: self.debugReport() #remove this appmanager's actions AdminAction.delete(self.action)
def setup_actions(self): self.action = AdminAction('systemstats') self.action.expose("stop_collect", self.stop_collect, (), "stops collecting metrics") self.action.expose("start_collect", self.start_collect, (), "starts collecting metrics") self.action.expose("stop_output", self.stop_output, (), "stops output of metrics") self.action.expose("start_output", self.start_output, (), "starts output of metrics") adoc = "sets log level of this service. valid values are info,error,debug [info default]" self.action.expose("set_log_level", self.set_log_level,("level",),adoc) self.action.buildDoc()
def __init__(self): if 'services' in sys.modules: e = "Instantiate ServiceManager before the services module!" raise AssertionError(e) self.SERVICE_STATE = {} self._parent = None drone.log('Loading Services') import services mask = ('loadAll', ) #mask as private #truely become service module for var, val in vars(services).items(): if var.startswith('_'): continue if var in mask: continue setattr(self, var, val) #bind the module globals to self services.loadAll() #load all services before replacing module sys.modules['services'] = self #replace the services module from droned.models.action import AdminAction #droneblaster action hooks self._action = AdminAction('service') self._action.expose('start', self._startService, ('name', ), 'starts the service') self._action.expose('stop', self._stopService, ('name', ), 'stops the service') self._action.expose('disable', self._disableService, ('name', ), 'prevent the service from starting') self._action.expose('enable', self._enableService, ('name', ), 'allow the service to start') self._action.expose('status', self._statusService, ('name', ), 'status of the service') self._action.expose('list', lambda: \ self._action.resultContext('\n'.join([ i for i in \ self.EXPORTED_SERVICES.keys() ]), None), (), 'list all services' ) self._action.buildDoc() #finalize the admin action
def __init__(self): if 'services' in sys.modules: e = "Instantiate ServiceManager before the services module!" raise AssertionError(e) self.SERVICE_STATE = {} self._parent = None drone.log('Loading Services') import services mask = ('loadAll',) #mask as private #truely become service module for var, val in vars(services).items(): if var.startswith('_'): continue if var in mask: continue setattr(self, var, val) #bind the module globals to self services.loadAll() #load all services before replacing module sys.modules['services'] = self #replace the services module from droned.models.action import AdminAction #droneblaster action hooks self._action = AdminAction('service') self._action.expose('start', self._startService, ('name',), 'starts the service' ) self._action.expose('stop', self._stopService, ('name',), 'stops the service' ) self._action.expose('disable', self._disableService, ('name',), 'prevent the service from starting' ) self._action.expose('enable', self._enableService, ('name',), 'allow the service to start' ) self._action.expose('status', self._statusService, ('name',), 'status of the service' ) self._action.expose('list', lambda: \ self._action.resultContext('\n'.join([ i for i in \ self.EXPORTED_SERVICES.keys() ]), None), (), 'list all services' ) self._action.buildDoc() #finalize the admin action
class SystemStats(Service): writing = defer.succeed(None) def stop_collect(self,*args): log("disabling all metric collection") for h in self.handlers: h[1].disable_collect() def start_collect(self,*args): log("enabling metic collection") for h in self.handlers: h[1].enable_collect() def stop_output(self,*args): log("disabling all metric outputs") for h in self.handlers: h[1].disable_output() def start_output(self,*args): log("enabling all metric outputs") for h in self.handlers: h[1].enable_output() def set_log_level(self,level): log("adjusting to %s log level" % level) for h in self.handlers: h[1].set_log_level(level) def setup_actions(self): self.action = AdminAction('systemstats') self.action.expose("stop_collect", self.stop_collect, (), "stops collecting metrics") self.action.expose("start_collect", self.start_collect, (), "starts collecting metrics") self.action.expose("stop_output", self.stop_output, (), "stops output of metrics") self.action.expose("start_output", self.start_output, (), "starts output of metrics") adoc = "sets log level of this service. valid values are info,error,debug [info default]" self.action.expose("set_log_level", self.set_log_level,("level",),adoc) self.action.buildDoc() def startService(self): self.handlers = [] stat_handlers = StatBlockLoader.load() for STAT in SERVICECONFIG.STATS: STAT_BLOCK = STAT['STAT'] try: for sh in stat_handlers: if sh.TYPE == STAT_BLOCK['TYPE']: inst = sh(STAT_BLOCK) self.handlers.append((STAT_BLOCK,inst)) except: f = Failure() err("Error while initializing systemstats service.") self.setup_actions() Service.startService(self) def stopService(self): Service.stopService(self)
class ServiceManager(Entity): """ServiceManager Provides an Interface to get to any methods a service may provide. After the ServiceManager has been instantiated you can access services from any python package in the DroneD framework simply by placing ```import services``` in your code. """ parentService = property(lambda s: s._parent) serializable = False #would not be discovered in this file anyway def __init__(self): if 'services' in sys.modules: e = "Instantiate ServiceManager before the services module!" raise AssertionError(e) self.SERVICE_STATE = {} self._parent = None drone.log('Loading Services') import services mask = ('loadAll',) #mask as private #truely become service module for var, val in vars(services).items(): if var.startswith('_'): continue if var in mask: continue setattr(self, var, val) #bind the module globals to self services.loadAll() #load all services before replacing module sys.modules['services'] = self #replace the services module from droned.models.action import AdminAction #droneblaster action hooks self._action = AdminAction('service') self._action.expose('start', self._startService, ('name',), 'starts the service' ) self._action.expose('stop', self._stopService, ('name',), 'stops the service' ) self._action.expose('disable', self._disableService, ('name',), 'prevent the service from starting' ) self._action.expose('enable', self._enableService, ('name',), 'allow the service to start' ) self._action.expose('status', self._statusService, ('name',), 'status of the service' ) self._action.expose('list', lambda: \ self._action.resultContext('\n'.join([ i for i in \ self.EXPORTED_SERVICES.keys() ]), None), (), 'list all services' ) self._action.buildDoc() #finalize the admin action def _installServices(self, parentService): """Install Services for DroneD to run @param parentService (instance service.Application) @return None """ drone.log('Installing Services') self._parent = parentService dead = set() #track objects that blow up on setup for name,obj in self.EXPORTED_SERVICES.items(): try: #decorate start and stop methods for eventing obj.start = self._startDecorator(obj.start, name) obj.stop = self._stopDecorator(obj.stop, name) obj.install(self.parentService) #set the twisted parent service obj.parentService = self.parentService #make sure it's set except: err(Failure(), 'Exception while installing %s' % (name,)) dead.add(name) drone.log('Evaluating Services Startup') #start up services that should run after everything is setup for name,obj in self.EXPORTED_SERVICES.items(): if name in dead: continue #make sure we have a notion of dis/enabled if name not in self.SERVICE_STATE: self.SERVICE_STATE[name] = name in config.AUTOSTART_SERVICES #service is not in autostart and is not marked to start if name not in config.AUTOSTART_SERVICES: if not self.SERVICE_STATE[name]: continue #service is marked as down even though it is in AUTO_START elif not self.SERVICE_STATE[name]: continue try: self.SERVICE_STATE[name] = True #be safe self._startService(name) except: #logging is not setup yet use twisted's err facility err(Failure(), 'Exception while registering %s' % (name,)) def _statusService(self, name): """used by the AdminAction handler""" status = self.getService(name).running() and 'running and' or \ 'stopped and' status += self.SERVICE_STATE.get(name, False) and ' enabled' or \ ' disabled' return self._result(status, name) def _startService(self, name): """used by the AdminAction handler""" obj = self.getService(name) if not obj.running(): result = obj.start() if isinstance(result, Failure): return result return self._result('running', name) def _stopService(self, name): """used by the AdminAction handler""" obj = self.getService(name) if obj.running(): result = obj.stop() if isinstance(result, Failure): return result return self._result('stopped', name) def _enableService(self, name): """used by the AdminAction handler""" self.SERVICE_STATE[name] = True #mark service as runnable return self._result('enabled', name) def _disableService(self, name): """used by the AdminAction handler""" self.SERVICE_STATE[name] = False #mark service as unrunnable return self._result('disabled', name) def _result(self, description, name): template = '[%(application)s] %(description)s' context = {'application': name, 'description': description} return self._action.resultContext(template, None, **context) def _startDecorator(self, func, name): """decorate the service start method for eventing""" log = droned.logging.logWithContext(type=name) obj = self.getService(name) def newfunc(): try: if not self.SERVICE_STATE[name]: raise AssertionError('%s is disabled' % (name,)) if not obj.running(): log('Starting Service') func() #don't really care about the return if not obj.running(): raise AssertionError("%s not running" % (name,)) Event('service-started').fire(service=obj) log('Started Service') except: return Failure() return True return newfunc def _stopDecorator(self, func, name): """decorate the service stop method for eventing""" log = droned.logging.logWithContext(type=name) obj = self.getService(name) def newfunc(): try: if obj.running(): log('Stopping Service') func() #we don't really care about the return if obj.running(): raise AssertionError("%s is still running" % (name,)) Event('service-stopped').fire(service=obj) log('Stopped Service') except: return Failure() return True return newfunc def _stopAll(self): for name in self.EXPORTED_SERVICES.keys(): self._stopService(name) def __getattr__(self, param): #compatibility hack try: return self.EXPORTED_SERVICES[param] except KeyError: return object.__getattr__(self, param)
class AppManager(Entity): """This is a generic application container service. It's sole purpose is to provide an abstraction to the application plugin. Think of this as an application service container. """ implements(IDroneModelAppManager) serializable = True #global container lock globalLock = defer.DeferredLock() running = property(lambda s: hasattr(s, '_task') and s._task.running) model = property(lambda s: IDroneDApplication(s)) #late plugin lookup action = property(lambda s: AdminAction(s.name)) invoke = property(lambda s: s.action.invoke) resultContext = property(lambda s: s.action.resultContext) exposedMethodInfo = property(lambda s: s.action.exposedMethodInfo) exposedMethods = property(lambda s: s.action.exposedMethods) instances = property(lambda s: App(s.name).localappinstances) labels = property(lambda s: (i.label for i in s.instances)) #whether or not the application service should discover apps for us discover = property(lambda s: not all([i.running for i in s.instances])) def __init__(self, name): self.name = name #this is for user defined storage self.applicationContext = {} #allow the models to block methods from registering self.blockedMethods = set() #create a local lock self.busy = defer.DeferredLock() #track events self.events = {} def log(self, message, label=None): """route logging messages to the application log and allow for custom labeling to be applied @param message: (string) @param label: (string) or (None) @return None """ info = self.name if label: info += ',%(label)s' % locals() logWithContext(type=info, route='application')(message) def __getstate__(self): """used to serialize the application model""" return { 'name': self.name, 'applicationContext': self.applicationContext } @staticmethod def construct(state): """rebuild the model with context @param state: (dict) return AppManger(state['name']) """ manager = AppManager(state['name']) manager.applicationContext = state['applicationContext'] return manager def start(self): """This is used by service binding to start""" if self.running: raise AssertionError('already running') #not only is this a safety, but makes sure the model is bound #donot ever remove this, otherwise first run won't automatically #create appinstances or any other models. #should be self, but make sure we avoid a race if self.model.service != AppManager(self.name): raise InvalidPlugin('Plugin for %s is invalid' % (self.name, )) self.action.log = self.log #override default logging #create default exposed methods, the model can override any of these self.expose('add', self.addInstance, ('instance', ), "Configure the specified instance", BUSYLOCK=True) self.expose('remove', self.removeInstance, ('instance', ), "Unconfigure the specified instance", BUSYLOCK=True) self.expose('start', self.startInstance, ('instance', ), "Start the instance", BUSYLOCK=True, INSTANCED=True) self.expose('stop', self.stopInstance, ('instance', ), "Stop the instance", BUSYLOCK=True, INSTANCED=True) self.expose('status', self.statusInstance, ('instance', ), "Status the instance", INSTANCED=True) self.expose('enable', self.enableInstance, ('instance', ), "Enable the instance", INSTANCED=True) self.expose('disable', self.disableInstance, ('instance', ), "Disable the instance", INSTANCED=True) self.expose('debug', self.debug, ('bool', ), "Turn application container debugging on or off") self.expose( 'labels', lambda: self.resultContext('\n'.join(sorted(self.labels)), None, ** {'labels': sorted(self.labels)}), (), "lists all application instance labels") #build our documentation self.rebuildHelpDoc() #check conditional events self._task = LoopingCall(self.conditionalEvents) self._task.start(1.0) def conditionalEvents(self): """check the status of conditional events""" if self.busy.locked: return #skip conditional event processing while busy for appevent in self.events.values(): if not appevent.condition: continue appevent.occurred() def registerEvent(self, name, callback, **kwargs): """Interface to Register Service Events""" #the self parameter will help ensure this event is unique to the service self.events[name] = ApplicationEvent(self.name, name, callback, **kwargs) def triggerEvent(self, name, data=None, delay=0.0): """Interface to trigger an out of band service event""" assert name in self.events, "No such event '%s'" % (name, ) return self.events[name].trigger(data, delay) def disableEvent(self, name): """Interface to disable a previously registered service event""" assert name in self.events, "No such event '%s'" % (name, ) self.events[name].event.disable() def enableEvent(self, name): """Interface to enable a previously disabled registered service event """ assert name in self.events, "No such event '%s'" % (name, ) self.events[name].event.enable() def stop(self): """This is used by service binding to stop""" try: if not self.running: raise AssertionError('not running') self._task.stop() #clear the event dictionary and delete events while self.events: name, appevent = self.events.popitem() if appevent.loop and appevent.loop.running: appevent.loop.stop() ApplicationEvent.delete(appevent) except: self.debugReport() #remove this appmanager's actions AdminAction.delete(self.action) ########################################################################### # This part of the class exposes the Model API to outside world ########################################################################### @synchronizedDeferred(globalLock) def unexpose(self, name, blacklist=True): """Removes an exposed method, probably not a good idea to expose""" #add method to blocked list if blacklist: self.blockedMethods.add(name) if name in self.exposedMethods: del self.exposedMethods[name] info = None found = False for info in self.exposedMethodInfo: (n, a, d) = info if n == name: found == True break if info and found: self.exposedMethodInfo.remove(info) def rebuildHelpDoc(self): """rebuild exposed method documentation""" self.action.buildDoc() @synchronizedDeferred(globalLock) def expose(self, name, method, args, doc, **kwargs): """Wraps the models exposed methods for gremlin and make methods available via blaster protocol for action invocation. expose(self, name, method, methodSignature, description, **kwargs) name: (string) - This is the action parameter to expose method: (callable) - This is the function name to call args: (tuple) - layout for parsing args description: (string) - Help Documentation to expose kwargs: INSTANCED: (bool) - sets the instanceOperator decorator for administrator's ease of use. BUSYLOCK: (bool) - sets the synchronizedDeferred decorator for this AppManager. GLOBALLOCK: (bool) - sets the synchronizedDeferred decorator for synchronizing all AppManagers. """ if name in self.blockedMethods: return #method was blocked by the model, probably #allow models to override the defaults and print a warning if name in self.exposedMethods: self.log('Warning method "%s" is already exposed' % (name, )) return #These decorators must be applied in a specific order of precedence requireInstance = kwargs.pop('INSTANCED', False) requireBusyLock = kwargs.pop('BUSYLOCK', False) requireGlobalLock = kwargs.pop('GLOBALLOCK', False) #applying decorators at runtime if requireBusyLock or requireGlobalLock or requireInstance: #ordering is critical if requireInstance: #this bizarre decorator is used b/c we need instance info. method = self.instanceOperation(method) if requireBusyLock: sync = synchronizedDeferred(self.busy) method = sync(method) if requireGlobalLock: sync = synchronizedDeferred(self.globalLock) method = sync(method) self.exposedMethodInfo.append((name, args, doc)) self.exposedMethods[name] = method ########################################################################### # This part of the class is for Generic actions that all apps perform ########################################################################### #FIXME is this really needed? def debug(self, var): """Enable or Disable application model debugging. You should extend this if you know how to enable application debugging in your custom 'application model'. returns deferred - already called """ #assume blaster which is string based, sent the message var = str(var) #for safety a = var.lower() context = {'code': 0} template = '[%(application)s] Debug ' try: if a == 'true': self.model.debug = True defer.setDebugging(True) template += 'Enabled' elif a == 'false': self.model.debug = False defer.setDebugging(False) template += 'Disabled' else: raise TypeError('input must be a bool, True/False') except Exception, exc: template += str(exc) context['code'] = 1 return defer.succeed(self.resultContext(template, None, **context))
class ServiceManager(Entity): """ServiceManager Provides an Interface to get to any methods a service may provide. After the ServiceManager has been instantiated you can access services from any python package in the DroneD framework simply by placing ```import services``` in your code. """ parentService = property(lambda s: s._parent) serializable = False #would not be discovered in this file anyway def __init__(self): if 'services' in sys.modules: e = "Instantiate ServiceManager before the services module!" raise AssertionError(e) self.SERVICE_STATE = {} self._parent = None drone.log('Loading Services') import services mask = ('loadAll', ) #mask as private #truely become service module for var, val in vars(services).items(): if var.startswith('_'): continue if var in mask: continue setattr(self, var, val) #bind the module globals to self services.loadAll() #load all services before replacing module sys.modules['services'] = self #replace the services module from droned.models.action import AdminAction #droneblaster action hooks self._action = AdminAction('service') self._action.expose('start', self._startService, ('name', ), 'starts the service') self._action.expose('stop', self._stopService, ('name', ), 'stops the service') self._action.expose('disable', self._disableService, ('name', ), 'prevent the service from starting') self._action.expose('enable', self._enableService, ('name', ), 'allow the service to start') self._action.expose('status', self._statusService, ('name', ), 'status of the service') self._action.expose('list', lambda: \ self._action.resultContext('\n'.join([ i for i in \ self.EXPORTED_SERVICES.keys() ]), None), (), 'list all services' ) self._action.buildDoc() #finalize the admin action def _installServices(self, parentService): """Install Services for DroneD to run @param parentService (instance service.Application) @return None """ drone.log('Installing Services') self._parent = parentService dead = set() #track objects that blow up on setup for name, obj in self.EXPORTED_SERVICES.items(): try: #decorate start and stop methods for eventing obj.start = self._startDecorator(obj.start, name) obj.stop = self._stopDecorator(obj.stop, name) obj.install( self.parentService) #set the twisted parent service obj.parentService = self.parentService #make sure it's set except: err(Failure(), 'Exception while installing %s' % (name, )) dead.add(name) drone.log('Evaluating Services Startup') #start up services that should run after everything is setup for name, obj in self.EXPORTED_SERVICES.items(): if name in dead: continue #make sure we have a notion of dis/enabled if name not in self.SERVICE_STATE: self.SERVICE_STATE[name] = name in config.AUTOSTART_SERVICES #service is not in autostart and is not marked to start if name not in config.AUTOSTART_SERVICES: if not self.SERVICE_STATE[name]: continue #service is marked as down even though it is in AUTO_START elif not self.SERVICE_STATE[name]: continue try: self.SERVICE_STATE[name] = True #be safe self._startService(name) except: #logging is not setup yet use twisted's err facility err(Failure(), 'Exception while registering %s' % (name, )) def _statusService(self, name): """used by the AdminAction handler""" status = self.getService(name).running() and 'running and' or \ 'stopped and' status += self.SERVICE_STATE.get(name, False) and ' enabled' or \ ' disabled' return self._result(status, name) def _startService(self, name): """used by the AdminAction handler""" obj = self.getService(name) if not obj.running(): result = obj.start() if isinstance(result, Failure): return result return self._result('running', name) def _stopService(self, name): """used by the AdminAction handler""" obj = self.getService(name) if obj.running(): result = obj.stop() if isinstance(result, Failure): return result return self._result('stopped', name) def _enableService(self, name): """used by the AdminAction handler""" self.SERVICE_STATE[name] = True #mark service as runnable return self._result('enabled', name) def _disableService(self, name): """used by the AdminAction handler""" self.SERVICE_STATE[name] = False #mark service as unrunnable return self._result('disabled', name) def _result(self, description, name): template = '[%(application)s] %(description)s' context = {'application': name, 'description': description} return self._action.resultContext(template, None, **context) def _startDecorator(self, func, name): """decorate the service start method for eventing""" log = droned.logging.logWithContext(type=name) obj = self.getService(name) def newfunc(): try: if not self.SERVICE_STATE[name]: raise AssertionError('%s is disabled' % (name, )) if not obj.running(): log('Starting Service') func() #don't really care about the return if not obj.running(): raise AssertionError("%s not running" % (name, )) Event('service-started').fire(service=obj) log('Started Service') except: return Failure() return True return newfunc def _stopDecorator(self, func, name): """decorate the service stop method for eventing""" log = droned.logging.logWithContext(type=name) obj = self.getService(name) def newfunc(): try: if obj.running(): log('Stopping Service') func() #we don't really care about the return if obj.running(): raise AssertionError("%s is still running" % (name, )) Event('service-stopped').fire(service=obj) log('Stopped Service') except: return Failure() return True return newfunc def _stopAll(self): for name in self.EXPORTED_SERVICES.keys(): self._stopService(name) def __getattr__(self, param): #compatibility hack try: return self.EXPORTED_SERVICES[param] except KeyError: return object.__getattr__(self, param)