def _reload(self): """ Reloads the limits configuration from the database. If an error occurs loading the configuration, an error-level log message will be emitted. Additionally, the error message will be added to the set specified by the 'redis.errors_key' configuration ('errors' by default) and sent to the publishing channel specified by the 'redis.errors_channel' configuration ('errors' by default). """ # Acquire the pending semaphore. If we fail, exit--someone # else is already doing the reload if not self._pending.acquire(False): return # Do the remaining steps in a try/finally block so we make # sure to release the semaphore try: # Load all the limits key = self._config.get('limits_key', 'limits') lims = [limits.Limit.hydrate(self._db, msgpack.loads(lim)) for lim in self._db.zrange(key, 0, -1)] # Build the routes mapper mapper = routes.Mapper(register=False) for lim in lims: lim._route(mapper) # Install it self._middleware.limits = lims self._middleware.mapper = mapper except Exception: # Log an error LOG.exception("Could not load limits") # Get our error set and publish channel error_key = self._config.get('errors_key', 'errors') error_channel = self._config.get('errors_channel', 'errors') # Get an informative message msg = "Failed to load limits: " + traceback.format_exc() # Store the message into the error set. We use a set here # because it's likely that more than one node will # generate the same message if there is an error, and this # avoids an explosion in the size of the set. with utils.ignore_except(): self._db.sadd(error_key, msg) # Publish the message to a channel with utils.ignore_except(): self._db.publish(error_channel, msg) finally: self._pending.release()
def recheck_limits(self): """ Re-check that the cached limits are the current limits. """ limit_data = self.control_daemon.get_limits() try: # Get the new checksum and list of limits new_sum, new_limits = limit_data.get_limits(self.limit_sum) # Convert the limits list into a list of objects lims = database.limits_hydrate(self.db, new_limits) # Build a new mapper mapper = routes.Mapper(register=False) for lim in lims: lim._route(mapper) # Save the new data self.limits = lims self.limit_sum = new_sum self.mapper = mapper except control.NoChangeException: # No changes to process; just keep going... return except Exception: # Log an error LOG.exception("Could not load limits") # Get our error set and publish channel control_args = self.conf['control'] error_key = control_args.get('errors_key', 'errors') error_channel = control_args.get('errors_channel', 'errors') # Get an informative message msg = "Failed to load limits: " + traceback.format_exc() # Store the message into the error set. We use a set # here because it's likely that more than one node # will generate the same message if there is an error, # and this avoids an explosion in the size of the set. with utils.ignore_except(): self.db.sadd(error_key, msg) # Publish the message to a channel with utils.ignore_except(): self.db.publish(error_channel, msg)
def recheck_limits(self): """ Re-check that the cached limits are the current limits. """ limit_data = self.control_daemon.get_limits() try: # Get the new checksum and list of limits new_sum, new_limits = limit_data.get_limits(self.limit_sum) # Convert the limits list into a list of objects lims = database.limits_hydrate(self.db, new_limits) # Build a new mapper mapper = routes.Mapper(register=False) for lim in lims: lim._route(mapper) # Save the new data self.limits = lims self.limit_sum = new_sum self.mapper = mapper except control.NoChangeException: # No changes to process; just keep going... return except Exception: # Log an error LOG.exception("Could not load limits") # Get our error set and publish channel control_args = self.conf["control"] error_key = control_args.get("errors_key", "errors") error_channel = control_args.get("errors_channel", "errors") # Get an informative message msg = "Failed to load limits: " + traceback.format_exc() # Store the message into the error set. We use a set # here because it's likely that more than one node # will generate the same message if there is an error, # and this avoids an explosion in the size of the set. with utils.ignore_except(): self.db.sadd(error_key, msg) # Publish the message to a channel with utils.ignore_except(): self.db.publish(error_channel, msg)
def ping(self, channel, data=None): """ Process the 'ping' control message. :param channel: The publish channel to which to send the response. :param data: Optional extra data. Will be returned as the second argument of the response. Responds to the named channel with a command of 'pong' and with the node_name (if configured) and provided data as arguments. """ if not channel: # No place to reply to return # Get our configured node name node_name = self._config.get('node_name') # Format the response reply = ['pong'] if node_name or data: reply.append(node_name or '') if data: reply.append(data) # And send it with utils.ignore_except(): self._db.publish(channel, ':'.join(reply))
def ping(daemon, channel, data=None): """ Process the 'ping' control message. :param daemon: The control daemon; used to get at the configuration and the database. :param channel: The publish channel to which to send the response. :param data: Optional extra data. Will be returned as the second argument of the response. Responds to the named channel with a command of 'pong' and with the node_name (if configured) and provided data as arguments. """ if not channel: # No place to reply to return # Get our configured node name node_name = daemon.config['control'].get('node_name') # Format the response reply = ['pong'] if node_name or data: reply.append(node_name or '') if data: reply.append(data) # And send it with utils.ignore_except(): daemon.db.publish(channel, ':'.join(reply))
def reload(self): """ Reloads the limits configuration from the database. If an error occurs loading the configuration, an error-level log message will be emitted. Additionally, the error message will be added to the set specified by the 'redis.errors_key' configuration ('errors' by default) and sent to the publishing channel specified by the 'redis.errors_channel' configuration ('errors' by default). """ # Acquire the pending semaphore. If we fail, exit--someone # else is already doing the reload if not self.pending.acquire(False): return # Do the remaining steps in a try/finally block so we make # sure to release the semaphore control_args = self.config['control'] try: # Load all the limits key = control_args.get('limits_key', 'limits') self.limits.set_limits(self.db.zrange(key, 0, -1)) except Exception: # Log an error LOG.exception("Could not load limits") # Get our error set and publish channel error_key = control_args.get('errors_key', 'errors') error_channel = control_args.get('errors_channel', 'errors') # Get an informative message msg = "Failed to load limits: " + traceback.format_exc() # Store the message into the error set. We use a set here # because it's likely that more than one node will # generate the same message if there is an error, and this # avoids an explosion in the size of the set. with utils.ignore_except(): self.db.sadd(error_key, msg) # Publish the message to a channel with utils.ignore_except(): self.db.publish(error_channel, msg) finally: self.pending.release()
def test_ignore_except(self): step = 0 with utils.ignore_except(): step += 1 raise test_utils.TestException() step += 2 self.assertEqual(step, 1)
def test_ignore_except_no_error(self): # Shouldn't raise any exceptions finished = False with utils.ignore_except(): throw_except(None) finished = True self.assertEqual(finished, True)
def test_ignore_except_with_error(self): # Should raise an exception which should be ignored finished = False with utils.ignore_except(): throw_except(Exception) finished = True # Yes, this should be False here... self.assertEqual(finished, False)
def close(self): """ Close the connection. :param purge: If True (the default), the receive buffer will be purged. """ # Close the underlying socket if self._sock: with utils.ignore_except(): self._sock.close() self._sock = None # Purge the message buffers self._recvbuf = [] self._recvbuf_partial = ''
def listen(self): """ Listen for clients. This method causes the SimpleRPC object to switch to server mode. One thread will be created for each client. """ # Make sure we're in server mode if self.mode and self.mode != 'server': raise ValueError("%s is not in server mode" % self.__class__.__name__) self.mode = 'server' # Obtain a listening socket serv = _create_server(self.host, self.port) # If we have too many errors, we want to bail out err_thresh = 0 while True: # Accept a connection try: sock, addr = serv.accept() except Exception as exc: err_thresh += 1 if err_thresh >= self.max_err_thresh: LOG.exception("Too many errors accepting " "connections: %s" % str(exc)) break continue # Pragma: nocover # Decrement error count on successful connections err_thresh = max(err_thresh - 1, 0) # Log the connection attempt LOG.info("Accepted connection from %s port %s" % (addr[0], addr[1])) # And handle the connection eventlet.spawn_n(self.serve, self.connection_class(sock), addr) # Close the listening socket with utils.ignore_except(): serv.close()