Example #1
0
def setup_limits(conf_file,
                 limits_file,
                 do_reload=True,
                 dry_run=False,
                 debug=False):
    """
    Set up or update limits in the Redis database.

    :param conf_file: Name of the configuration file, for connecting
                      to the Redis database.
    :param limits_file: Name of the XML file describing the limits to
                        configure.
    :param do_reload: Controls reloading behavior.  If True (the
                      default), a reload command is issued.  If False,
                      no reload command is issued.  String values
                      result in a reload command of the given load
                      type, and integer or float values result in a
                      reload command of type 'spread' with the given
                      spread interval.
    :param dry_run: If True, no changes are made to the database.
                    Implies debug=True.
    :param debug: If True, debugging messages are emitted while
                  loading the limits and updating the database.
    """

    # If dry_run is set, default debug to True
    if dry_run:
        debug = True

    # Connect to the database...
    conf = config.Config(conf_file=conf_file)
    db = conf.get_database()
    limits_key = conf['control'].get('limits_key', 'limits')
    control_channel = conf['control'].get('channel', 'control')

    # Parse the limits file
    limits_tree = etree.parse(limits_file)

    # Now, we parse the limits XML file
    lims = []
    for idx, lim in enumerate(limits_tree.getroot()):
        # Skip tags we don't recognize
        if lim.tag != 'limit':
            warnings.warn("Unrecognized tag %r in limits file at index %d" %
                          (lim.tag, idx))
            continue

        # Construct the limit and add it to the list of limits
        try:
            lims.append(parse_limit_node(db, idx, lim))
        except Exception as exc:
            warnings.warn("Couldn't understand limit at index %d: %s" %
                          (idx, exc))
            continue

    # Now that we have the limits, let's install them
    if debug:
        print >> sys.stderr, "Installing the following limits:"
        for lim in lims:
            print >> sys.stderr, "  %r" % lim
    if not dry_run:
        database.limit_update(db, limits_key, lims)

    # Were we requested to reload the limits?
    if do_reload is False:
        return

    # OK, figure out what kind of reload to do
    params = []
    if do_reload is True:
        # Nothing to do; use default semantics
        pass
    elif (isinstance(do_reload, (int, long, float))
          or (isinstance(do_reload, basestring) and do_reload.isdigit())):
        params = ['spread', do_reload]
    else:
        params = [str(do_reload)]

    # Issue the reload command
    if debug:
        cmd = ['reload']
        cmd.extend(params)
        print >> sys.stderr, ("Issuing command: %s" %
                              ' '.join(str(c) for c in cmd))
    if not dry_run:
        database.command(db, control_channel, 'reload', *params)
Example #2
0
def turnstile_command(conf_file,
                      command,
                      arguments=[],
                      channel=None,
                      debug=False):
    """
    Issue a command to all running control daemons.

    :param conf_file: Name of the configuration file.
    :param command: The command to execute.  Note that 'ping' is
                    handled specially; in particular, the "channel"
                    parameter is implied.  (A random value will be
                    used for the channel to listen on.)
    :param arguments: A list of arguments for the command.  Note that
                      the colon character (':') cannot be used.
    :param channel: If not None, specifies the name of a message
                    channel to listen for responses on.  Will wait
                    indefinitely; to terminate the listening loop, use
                    the keyboard interrupt sequence.
    :param debug: If True, debugging messages are emitted while
                  sending the command.
    """

    # Connect to the database...
    conf = config.Config(conf_file=conf_file)
    db = conf.get_database()
    control_channel = conf['control'].get('channel', 'control')

    # Now, set up the command
    command = command.lower()
    ts_conv = False
    if command == 'ping':
        # We handle 'ping' specially; first, figure out the channel
        if arguments:
            channel = arguments[0]
        else:
            channel = str(uuid.uuid4())
            arguments = [channel]

        # Next, add on a timestamp
        if len(arguments) < 2:
            arguments.append(time.time())
            ts_conv = True

        # Limit the argument list length
        arguments = arguments[:2]

    # OK, the command is all set up.  Let us now send the command...
    if debug:
        cmd = [command] + arguments
        print >> sys.stderr, ("Issuing command: %s" % ' '.join(cmd))
    database.command(db, control_channel, command, *arguments)

    # Were we asked to listen on a channel?
    if not channel:
        return

    # OK, let's subscribe to the channel...
    pubsub = db.pubsub()
    pubsub.subscribe(channel)

    # Now we listen...
    try:
        count = 0
        for msg in pubsub.listen():
            # Make sure the message is one we're interested in
            if debug:
                formatted = pprint.pformat(msg)
                print >> sys.stderr, "Received message: %s" % formatted
            if (msg['type'] not in ('pmessage', 'message')
                    or msg['channel'] != channel):
                continue

            count += 1

            # Figure out the response
            response = msg['data'].split(':')

            # If this is a 'pong' and ts_conv is true, add an RTT to
            # the response
            if ts_conv and response[0] == 'pong':
                try:
                    rtt = (time.time() - float(response[2])) * 100
                    response.append('(RTT %.2fms)' % rtt)
                except Exception:
                    # IndexError or ValueError, probably; ignore it
                    pass

            # Print out the response
            print "Response % 5d: %s" % (count, ' '.join(response))
    except KeyboardInterrupt:
        # We want to break out of the loop, but not return any error
        # to the caller...
        pass
Example #3
0
    def test_command(self):
        db = mock.Mock()

        database.command(db, 'channel', 'command', 'one', 2, 3.14)

        db.publish.assert_called_once_with('channel', 'command:one:2:3.14')