def execute(self, method_name, params): """Execute the given method on the handler. Checks to make sure the method is valid and allowed perform executing the method. """ if method_name in self._meta.allowed_methods: try: method = getattr(self, method_name) except AttributeError: raise HandlerNoSuchMethodError(method_name) else: # Handler methods are predominantly transactional and thus # blocking/synchronous. Genuinely non-blocking/asynchronous # methods must out themselves explicitly. if IAsynchronous.providedBy(method): # The @asynchronous decorator will DTRT. return method(params) else: # This is going to block and hold a database connection so # we limit its concurrency. return concurrency.webapp.run( deferToDatabase, transactional(method), params) else: raise HandlerNoSuchMethodError(method_name)
def query(self, system_id, context): """Performs the power query action for `system_id`.""" exc_info = None, None, None for waiting_time in self.wait_time: try: # Power queries are predominantly transactional and thus # blocking/synchronous. Genuinely non-blocking/asynchronous # methods must out themselves explicitly. if IAsynchronous.providedBy(self.power_query): # The @asynchronous decorator will DTRT. state = yield self.power_query(system_id, context) else: state = yield deferToThread( self.power_query, system_id, context ) except PowerFatalError: raise # Don't retry. except PowerError: exc_info = sys.exc_info() # Wait before retrying. yield pause(waiting_time, self.clock) else: returnValue(state) else: raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
def execute(self, method_name, params): """Execute the given method on the handler. Checks to make sure the method is valid and allowed perform executing the method. """ if method_name in self._meta.allowed_methods: try: method = getattr(self, method_name) except AttributeError: raise HandlerNoSuchMethodError(method_name) else: # Handler methods are predominantly transactional and thus # blocking/synchronous. Genuinely non-blocking/asynchronous # methods must out themselves explicitly. if IAsynchronous.providedBy( method ) or asyncio.iscoroutinefunction(method): # Running in the io thread so clear RBAC now. rbac.clear() # Reload the user from the database. d = concurrency.webapp.run( deferToDatabase, transactional(self.user.refresh_from_db), ) d.addCallback(lambda _: ensureDeferred(method(params))) return d else: @wraps(method) @transactional def prep_user_execute(params): # Clear RBAC and reload the user to ensure that # its up to date. `rbac.clear` must be done inside # the thread because it uses thread locals internally. rbac.clear() self.user.refresh_from_db() # Perform the work in the database. return self._call_method_track_queries( method_name, method, params ) # Force the name of the function to include the handler # name so the debug logging is useful. prep_user_execute.__name__ = "%s.%s" % ( self.__class__.__name__, method_name, ) # This is going to block and hold a database connection so # we limit its concurrency. return concurrency.webapp.run( deferToDatabase, prep_user_execute, params ) else: raise HandlerNoSuchMethodError(method_name)
def perform_power(self, power_func, state_desired, system_id, context): """Provides the logic to perform the power actions. :param power_func: Function used to change the power state of the node. Typically this will be `self.power_on` or `self.power_off`. :param state_desired: The desired state for this node to be in, typically "on" or "off". :param system_id: The node's system ID. """ state = "unknown" exc_info = None, None, None for waiting_time in self.wait_time: # Try to change state. try: # Power methods are predominantly transactional and thus # blocking/synchronous. Genuinely non-blocking/asynchronous # methods must out themselves explicitly. if IAsynchronous.providedBy(power_func): # The @asynchronous decorator will DTRT. yield power_func(system_id, context) else: yield deferToThread(power_func, system_id, context) except PowerFatalError: raise # Don't retry. except PowerError: exc_info = sys.exc_info() # Wait before retrying. yield pause(waiting_time, self.clock) else: # LP:1768659 - If the power driver isn't queryable(manual) # checking the power state will always fail. if not self.queryable: return # Wait before checking state. yield pause(waiting_time, self.clock) # Try to get power state. try: # Power queries are predominantly transactional and thus # blocking/synchronous. Genuinely non-blocking/asynchronous # methods must out themselves explicitly. if IAsynchronous.providedBy(self.power_query): # The @asynchronous decorator will DTRT. state = yield self.power_query(system_id, context) else: state = yield deferToThread(self.power_query, system_id, context) except PowerFatalError: raise # Don't retry. except PowerError: exc_info = sys.exc_info() else: # If state is now the correct state, done. if state == state_desired: return if exc_info == (None, None, None): # No error found, so communication to the BMC is good, state must # have not changed in the elapsed time. That is the only reason we # should make it this far. raise PowerError( "Failed to power %s. BMC never transitioned from %s to %s." % (system_id, state, state_desired)) else: # Report the last error. raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])