Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
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))
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    def test_ignore_except(self):
        step = 0
        with utils.ignore_except():
            step += 1
            raise test_utils.TestException()
            step += 2

        self.assertEqual(step, 1)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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 = ''
Ejemplo n.º 11
0
    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 = ''
Ejemplo n.º 12
0
    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()
Ejemplo n.º 13
0
    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()