def resultContext(self, template, instance=None, **context): """Creates a dict containg relevant contextual information about a result. You can override this method and tailor it to your liking. We typically use this to pass verbose structured data to a master DroneD controller (not provided with DroneD core) so that it may quickly make decisions based on the result of it's previous command and control activities. IF you set 'error' in the **context this will raise a server error at the remote end. This can be good or bad depending on your outlook on exceptions. Consider this your only warning. return dict """ if 'application' not in context: context['application'] = self.action failure = context.pop('error', False) if isinstance(failure, Failure): if 'description' not in context: context['description'] = '[%s] %s: %s' % \ (self.action, getException(failure), failure.getErrorMessage()) if 'code' not in context: context['code'] = -2 context['error'] = True context['stacktrace'] = failure.getTraceback() self.log('Result context during exception\n%(stacktrace)s' % context) return context #failed so bad we need to shortcut out else: context['error'] = bool(failure) if instance: #this was made for AppManager's if hasattr(instance, 'version'): context['version'] = instance.version if hasattr(instance, 'label'): context['label'] = instance.label if hasattr(instance, 'running'): context['running'] = instance.running try: #fail-safe in case someone is a bonehead context['description'] = template % context except: failure = Failure() context['description'] = '[%s] %s: %s' % \ (self.action, getException(failure), failure.getErrorMessage()) context['stacktrace'] = failure.getTraceback() if 'code' not in context: context['code'] = -2 #be nice to blaster api and the remote client context.update({'code': context.get('code', 0)}) return context
def resultContext(self, template, instance=None, **context): """Creates a dict containg relevant contextual information about a result. You can override this method and tailor it to your liking. We typically use this to pass verbose structured data to a master DroneD controller (not provided with DroneD core) so that it may quickly make decisions based on the result of it's previous command and control activities. IF you set 'error' in the **context this will raise a server error at the remote end. This can be good or bad depending on your outlook on exceptions. Consider this your only warning. return dict """ if 'application' not in context: context['application'] = self.action failure = context.pop('error', False) if isinstance(failure, Failure): if 'description' not in context: context['description'] = '[%s] %s: %s' % \ (self.action, getException(failure), failure.getErrorMessage()) if 'code' not in context: context['code'] = -2 context['error'] = True context['stacktrace'] = failure.getTraceback() self.log('Result context during exception\n%(stacktrace)s' % context) return context #failed so bad we need to shortcut out else: context['error'] = bool(failure) if instance: #this was made for AppManager's if hasattr(instance, 'version'): context['version'] = instance.version if hasattr(instance, 'label'): context['label'] = instance.label if hasattr(instance, 'running'): context['running'] = instance.running try: #fail-safe in case someone is a bonehead context['description'] = template % context except: failure = Failure() context['description'] = '[%s] %s: %s' % \ (self.action, getException(failure), failure.getErrorMessage()) context['stacktrace'] = failure.getTraceback() if 'code' not in context: context['code'] = -2 #be nice to blaster api and the remote client context.update({'code' : context.get('code',0) }) return context
def _start_stop_common(self, label, action): result = {} try: function = getattr(self.model, action) d = defer.maybeDeferred(function, label) wfd = defer.waitForDeferred(d) yield wfd result = wfd.getResult() #take this time to update the instance if isinstance(result, dict): thisInst = self.model.getInstance(label) thisInst.updateInfo(result) except: failure = Failure() if failure.check(DroneCommandFailed): result = failure.value.resultContext else: #log the error, allowing for debugging self.debugReport() #be nice and return something to the end user template = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': failure, 'code': -2} result = self.resultContext(template, None, **context) #finally wrap the failure into a known type result = Failure(DroneCommandFailed(result)) #AppInstances need a moment to be updated d = defer.Deferred() config.reactor.callLater(1.0, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult() yield result
def __call__(self, argstr): args = argstr.split() resultContext = None if not args: #return command usage methods = {} for name, args, doc in self.exposedMethodInfo: methods[name] = {'args': args, 'doc': doc} resultContext = dict(description=self.__doc__, methods=methods) yield resultContext else: method = args.pop(0) try: wfd = defer.waitForDeferred(self.invoke(method, args)) yield wfd resultContext = wfd.getResult() except: failure = Failure() if failure.check(DroneCommandFailed): resultContext = failure.value.resultContext else: #be nice and return something to the end user template = "[%(application)s] " template += "%s: %s" % (getException(failure), failure.getErrorMessage()) context = { 'error': True, 'code': -2, 'stacktrace': failure.getTraceback() } resultContext = self.resultContext(template, None, **context) yield resultContext
def __call__(self, argstr): args = argstr.split() resultContext = None if not args: #return command usage methods = {} for name,args,doc in self.exposedMethodInfo: methods[name] = {'args' : args, 'doc' : doc} resultContext = dict(description=self.__doc__, methods=methods) yield resultContext else: method = args.pop(0) try: wfd = defer.waitForDeferred( self.invoke(method,args) ) yield wfd resultContext = wfd.getResult() except: failure = Failure() if failure.check(DroneCommandFailed): resultContext = failure.value.resultContext else: #be nice and return something to the end user template = "[%(application)s] " template += "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': True, 'code': -2, 'stacktrace': failure.getTraceback()} resultContext = self.resultContext(template, None, **context ) yield resultContext
def decorator(*args, **kwargs): try: return func(*args, **kwargs) except: failure = Failure() msg = getException(failure) msg += ': ' + failure.getErrorMessage() return NoResource(msg)
def _start_stop_common(self, label, action): result = {} try: function = getattr(self.model, action) d = defer.maybeDeferred(function, label) wfd = defer.waitForDeferred(d) yield wfd result = wfd.getResult() #take this time to update the instance if isinstance(result, dict): thisInst = self.model.getInstance(label) thisInst.updateInfo(result) except: failure = Failure() if failure.check(DroneCommandFailed): result = failure.value.resultContext else: #log the error, allowing for debugging self.debugReport() #be nice and return something to the end user template = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error':failure,'code':-2} result = self.resultContext(template, None, **context) #finally wrap the failure into a known type result = Failure(DroneCommandFailed(result)) #AppInstances need a moment to be updated d = defer.Deferred() reactor.callLater(1.0, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult() yield result
def invoke(self, name, args): """Invoke Exposed Methods @param name (str) - name of method to invoke @param args (tuple) - arguments to pass to invoked method @return (defer.Deferred) """ if name not in self.exposedMethods: return defer.fail( DroneCommandFailed( self.resultContext( "[%(application)s] Unknown method '%(method)s'", method=name, error='unknown method'))) try: #our own form of maybeDeferred d = self.exposedMethods[name](*args) if isinstance(d, defer.Deferred): action = Action(' '.join([str(i) for i in \ (self.action, name) + tuple(args)]), d) return action.deferred elif isinstance(d, DroneCommandFailed): return defer.fail(d) elif isinstance(d, dict): return defer.succeed(d) elif isinstance(d, type(None)): #this just feels dirty return defer.succeed(d) elif isinstance(d, Failure): d.raiseException() #sigh #probably from a triggerred Event callback elif type(d) == types.InstanceType: return defer.succeed(None) return defer.fail(FormatError("Result is not formatted correctly you " + \ "must return self.resultContext or DroneCommandFailed." + \ "\nResult: <%s>" % (str(d),))) except: failure = Failure() if failure.check(DroneCommandFailed): template = "[%(application)s] %(description)s" context = failure.value.resultContext if not 'description' in context: context['description'] = failure.getErrorMessage() else: template = "[%(application)s] " + "%s: %s" % ( getException(failure), failure.getErrorMessage()) context = { 'error': True, 'code': -2, 'stacktrace': failure.getTraceback() } return defer.fail( DroneCommandFailed( self.resultContext(template, None, **context)))
def handleDeferreds(labels): """Remember last yield is the return value, don't use return""" results = {} descriptions = [] ret = {} code = 0 for l in labels: try: d = defer.maybeDeferred(func, l, *args[1:], **kwargs) wfd = defer.waitForDeferred(d) yield wfd ret = wfd.getResult() except: failure = Failure() des = "%s: %s" % \ (getException(failure),failure.getErrorMessage()) if failure.check(DroneCommandFailed): result[l] = failure.value.resultContext if 'description' not in result[l]: result[l]['description'] = des result[l]['stacktrace'] = failure.getTraceback() result[l]['error'] = True if 'code' not in result[l]: result[l]['code'] = 1 else: ret = { 'description': des, 'code': 1, 'error': True, 'stacktrace': failure.getTraceback() } if not ret: #NoneType detection ret = {'description': str(ret), 'code': 0} if 'code' in ret: code += abs(ret['code']) results[l] = ret try: descriptions.append(results[l]['description']) except: self.debugReport() results['code'] = code try: results['description'] = '\n'.join(descriptions) except: results['description'] = None if len(labels) == 0: Label = labels[0] else: Label = None ret = self.resultContext('%(description)s', label=Label, **results) yield ret
def handleDeferreds(labels): """Remember last yield is the return value, don't use return""" results = {} descriptions = [] ret = {} code = 0 for l in labels: try: d = defer.maybeDeferred(func, l, *args[1:], **kwargs) wfd = defer.waitForDeferred(d) yield wfd ret = wfd.getResult() except: failure = Failure() des = "%s: %s" % \ (getException(failure),failure.getErrorMessage()) if failure.check(DroneCommandFailed): result[l] = failure.value.resultContext if 'description' not in result[l]: result[l]['description'] = des result[l]['stacktrace'] = failure.getTraceback() result[l]['error'] = True if 'code' not in result[l]: result[l]['code'] = 1 else: ret = { 'description': des, 'code': 1, 'error': True, 'stacktrace': failure.getTraceback() } if not ret: #NoneType detection ret = {'description' : str(ret), 'code' : 0} if 'code' in ret: code += abs(ret['code']) results[l] = ret try: descriptions.append(results[l]['description']) except: self.debugReport() results['code'] = code try: results['description'] = '\n'.join(descriptions) except: results['description'] = None if len(labels) == 0: Label = labels[0] else: Label = None ret = self.resultContext('%(description)s',label=Label,**results) yield ret
def invoke(self, name, args): """Invoke Exposed Methods @param name (str) - name of method to invoke @param args (tuple) - arguments to pass to invoked method @return (defer.Deferred) """ if name not in self.exposedMethods: return defer.fail(DroneCommandFailed(self.resultContext( "[%(application)s] Unknown method '%(method)s'", method=name, error='unknown method')) ) try: #our own form of maybeDeferred d = self.exposedMethods[name](*args) if isinstance(d, defer.Deferred): action = Action(' '.join([str(i) for i in \ (self.action, name) + tuple(args)]), d) return action.deferred elif isinstance(d, DroneCommandFailed): return defer.fail(d) elif isinstance(d, dict): return defer.succeed(d) elif isinstance(d, type(None)): #this just feels dirty return defer.succeed(d) elif isinstance(d, Failure): d.raiseException() #sigh #probably from a triggerred Event callback elif type(d) == types.InstanceType: return defer.succeed(None) return defer.fail(FormatError("Result is not formatted correctly you " + \ "must return self.resultContext or DroneCommandFailed." + \ "\nResult: <%s>" % (str(d),))) except: failure = Failure() if failure.check(DroneCommandFailed): template = "[%(application)s] %(description)s" context = failure.value.resultContext if not 'description' in context: context['description'] = failure.getErrorMessage() else: template = "[%(application)s] " + "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': True, 'code':-2, 'stacktrace': failure.getTraceback()} return defer.fail(DroneCommandFailed(self.resultContext(template, None, **context)) )
def _killInstance(self, result, instanceRef): """Last resort to stop your process""" bagOfTricks = [ (lambda: os.kill(instanceRef.process.pid, signal.SIGTERM), 'forcefully'), (lambda: os.kill(instanceRef.process.pid, signal.SIGKILL), 'viciously'), ] #trap the failure and set a default message if isinstance(result, Failure): result.trap(Exception) result = 'Not Running' #give the protocol handler and OS a moment to do their thing d = defer.Deferred() config.reactor.callLater(0.2, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult() if instanceRef.running: pid = instanceRef.process.pid #ninja's need tricks too for kung, fu in bagOfTricks: d = defer.Deferred() try: self.log("Trying to shutdown %d %s" % (pid, fu)) kung() x = defer.Deferred() #wait for the OS to reap the PID config.reactor.callLater(5.0, x.callback, None) wfd = defer.waitForDeferred(x) yield wfd wfd.getResult() #check for the pid if instanceRef.running: self.log("Failed to shutdown process %s" % (fu, )) else: result = "process %d %s shutdown" % (pid, fu) except Exception, exc: self.log("%s while trying to %s shutdown process %d: %s" % \ (getException(), fu, pid, exc)) if not instanceRef.running: break config.reactor.callLater(10, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult()
def _killInstance(self, result, instanceRef): """Last resort to stop your process""" bagOfTricks = [ (lambda: os.kill(instanceRef.process.pid, signal.SIGTERM),'forcefully'), (lambda: os.kill(instanceRef.process.pid, signal.SIGKILL),'viciously'), ] #trap the failure and set a default message if isinstance(result, Failure): result.trap(Exception) result = 'Not Running' #give the protocol handler and OS a moment to do their thing d = defer.Deferred() reactor.callLater(0.2, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult() if instanceRef.running: pid = instanceRef.process.pid #ninja's need tricks too for kung, fu in bagOfTricks: d = defer.Deferred() try: self.log("Trying to shutdown %d %s" % (pid,fu)) kung() x = defer.Deferred() #wait for the OS to reap the PID reactor.callLater(5.0, x.callback, None) wfd = defer.waitForDeferred(x) yield wfd wfd.getResult() #check for the pid if instanceRef.running: self.log("Failed to shutdown process %s" % (fu,)) else: result = "process %d %s shutdown" % (pid, fu) except Exception, exc: self.log("%s while trying to %s shutdown process %d: %s" % \ (getException(), fu, pid, exc)) if not instanceRef.running: break reactor.callLater(10, d.callback, None) wfd = defer.waitForDeferred(d) yield wfd wfd.getResult()
def startInstance(self, label): """Starts an application instance by label @param label: (string) @fires Event('instance-started') return defer.Deferred() """ template = '[%(application)s,%(label)s] %(description)s' context = {'description': 'Failed to Start', 'code': 254} result = {} thisInst = None try: if self.model.getInstance(label).running: context.update(self.model.statusInstance(label)) raise DroneCommandFailed(context) d = self._start_stop_common(label, 'startInstance') wfd = defer.waitForDeferred(d) yield wfd result = wfd.getResult() d = self.statusInstance(label) wfd = defer.waitForDeferred(d) yield wfd result.update(wfd.getResult()) #refresh the instance as it can change thisInst = self.model.getInstance(label) if isinstance(result, dict): context.update(result) elif isinstance(result, DroneCommandFailed): context.update(result.resultContext) if thisInst.running: Event('instance-started').fire(instance=thisInst) context['code'] = 0 raise AssertionError('ignore') raise DroneCommandFailed(context) except AssertionError: #update the instance model wfd = defer.waitForDeferred(self.statusInstance(label)) yield wfd result = wfd.getResult() except: thisInst = self.model.getInstance(label) failure = Failure() if failure.check(DroneCommandFailed): template = '%(description)s' context = failure.value.resultContext else: #log the error, allowing for debugging self.debugReport() #be nice and return something to the end user temp = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': failure, 'code': 253, 'description': temp} result = self.resultContext(template, thisInst, **context) try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = True except: pass yield result
def stopInstance(self, label): """Stops an application instance by label @param label: (string) @fires Event('instance-stopped') return defer.Deferred() """ result = {} template = '[%(application)s,%(label)s] %(description)s' context = {'code': 254} thisInst = None try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = False if not thisInst.running: context.update(self.model.statusInstance(label)) raise DroneCommandFailed(context) pid = thisInst.process.pid self.log("Trying to shutdown %d gracefully" % (pid,)) def failed(result): """attempting to be consistant""" self.log("Failed to shutdown process gracefully") return result def success(result): """attempting to be consistant""" self.log("process %d gracefully shutdown" % (pid,)) return result d = self._start_stop_common(label, 'stopInstance') d.addCallback(success) d.addErrback(failed) d.addErrback(self._killInstance, thisInst) wfd = defer.waitForDeferred(d) yield wfd #refresh the instance as it can change thisInst = self.model.getInstance(label) result = wfd.getResult() if isinstance(result, dict): context.update(result) elif isinstance(result, DroneCommandFailed): context.update(result.resultContext) if not thisInst.running: context['code'] = 0 Event('instance-stopped').fire(instance=thisInst) raise AssertionError('ignore me') raise DroneCommandFailed(context) except AssertionError: #update the instance model wfd = defer.waitForDeferred(self.statusInstance(label)) yield wfd result = wfd.getResult() result['code'] = context['code'] except: failure = Failure() if failure.check(DroneCommandFailed): context = failure.value.resultContext template = '%(description)s' else: temp = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': failure, 'code': 253, 'description': temp} result = self.resultContext(template, thisInst, **context) try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = False except: pass yield result
def startInstance(self, label): """Starts an application instance by label @param label: (string) @fires Event('instance-started') return defer.Deferred() """ template = '[%(application)s,%(label)s] %(description)s' context = { 'description': 'Failed to Start', 'code': 254 } result = {} thisInst = None try: if self.model.getInstance(label).running: context.update(self.model.statusInstance(label)) raise DroneCommandFailed(context) d = self._start_stop_common(label, 'startInstance') wfd = defer.waitForDeferred(d) yield wfd result = wfd.getResult() d = self.statusInstance(label) wfd = defer.waitForDeferred(d) yield wfd result.update(wfd.getResult()) #refresh the instance as it can change thisInst = self.model.getInstance(label) if isinstance(result, dict): context.update(result) elif isinstance(result, DroneCommandFailed): context.update(result.resultContext) if thisInst.running: Event('instance-started').fire(instance=thisInst) context['code'] = 0 raise AssertionError('ignore') raise DroneCommandFailed(context) except AssertionError: #update the instance model wfd = defer.waitForDeferred(self.statusInstance(label)) yield wfd result = wfd.getResult() except: thisInst = self.model.getInstance(label) failure = Failure() if failure.check(DroneCommandFailed): template = '%(description)s' context = failure.value.resultContext else: #log the error, allowing for debugging self.debugReport() #be nice and return something to the end user temp = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': failure, 'code': 253, 'description': temp} result = self.resultContext(template, thisInst, **context) try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = True except: pass yield result
def stopInstance(self, label): """Stops an application instance by label @param label: (string) @fires Event('instance-stopped') return defer.Deferred() """ result = {} template = '[%(application)s,%(label)s] %(description)s' context = {'code': 254} thisInst = None try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = False if not thisInst.running: context.update(self.model.statusInstance(label)) raise DroneCommandFailed(context) pid = thisInst.process.pid self.log("Trying to shutdown %d gracefully" % (pid, )) def failed(result): """attempting to be consistant""" self.log("Failed to shutdown process gracefully") return result def success(result): """attempting to be consistant""" self.log("process %d gracefully shutdown" % (pid, )) return result d = self._start_stop_common(label, 'stopInstance') d.addCallback(success) d.addErrback(failed) d.addErrback(self._killInstance, thisInst) wfd = defer.waitForDeferred(d) yield wfd #refresh the instance as it can change thisInst = self.model.getInstance(label) result = wfd.getResult() if isinstance(result, dict): context.update(result) elif isinstance(result, DroneCommandFailed): context.update(result.resultContext) if not thisInst.running: context['code'] = 0 Event('instance-stopped').fire(instance=thisInst) raise AssertionError('ignore me') raise DroneCommandFailed(context) except AssertionError: #update the instance model wfd = defer.waitForDeferred(self.statusInstance(label)) yield wfd result = wfd.getResult() result['code'] = context['code'] except: failure = Failure() if failure.check(DroneCommandFailed): context = failure.value.resultContext template = '%(description)s' else: temp = "%s: %s" % (getException(failure), failure.getErrorMessage()) context = {'error': failure, 'code': 253, 'description': temp} result = self.resultContext(template, thisInst, **context) try: thisInst = self.model.getInstance(label) thisInst.shouldBeRunning = False except: pass yield result