def test_limit_update_retry(self, mock_dumps): limits = [ mock.Mock(**{'dehydrate.return_value': 'limit1'}), mock.Mock(**{'dehydrate.return_value': 'limit2'}), mock.Mock(**{'dehydrate.return_value': 'limit3'}), mock.Mock(**{'dehydrate.return_value': 'limit4'}), mock.Mock(**{'dehydrate.return_value': 'limit5'}), mock.Mock(**{'dehydrate.return_value': 'limit6'}), ] pipe = mock.MagicMock(**{ 'zrange.return_value': [ 'limit2', 'limit4', 'limit6', 'limit8', ], 'execute.side_effect': [redis.WatchError, None], }) pipe.__enter__.return_value = pipe pipe.__exit__.return_value = False db = mock.Mock(**{'pipeline.return_value': pipe}) database.limit_update(db, 'limit_key', limits) for lim in limits: lim.dehydrate.assert_called_once_with() mock_dumps.assert_has_calls([ mock.call('limit1'), mock.call('limit2'), mock.call('limit3'), mock.call('limit4'), mock.call('limit5'), mock.call('limit6'), ]) db.pipeline.assert_called_once_with() pipe.assert_has_calls([ mock.call.__enter__(), mock.call.watch('limit_key'), mock.call.zrange('limit_key', 0, -1), mock.call.multi(), mock.call.zrem('limit_key', 'limit8'), mock.call.zadd('limit_key', 10, 'limit1'), mock.call.zadd('limit_key', 20, 'limit2'), mock.call.zadd('limit_key', 30, 'limit3'), mock.call.zadd('limit_key', 40, 'limit4'), mock.call.zadd('limit_key', 50, 'limit5'), mock.call.zadd('limit_key', 60, 'limit6'), mock.call.execute(), mock.call.watch('limit_key'), mock.call.zrange('limit_key', 0, -1), mock.call.multi(), mock.call.zrem('limit_key', 'limit8'), mock.call.zadd('limit_key', 10, 'limit1'), mock.call.zadd('limit_key', 20, 'limit2'), mock.call.zadd('limit_key', 30, 'limit3'), mock.call.zadd('limit_key', 40, 'limit4'), mock.call.zadd('limit_key', 50, 'limit5'), mock.call.zadd('limit_key', 60, 'limit6'), mock.call.execute(), mock.call.__exit__(None, None, None), ])
def test_limit_update(self, mock_dumps): limits = [ mock.Mock(**{'dehydrate.return_value': 'limit1'}), mock.Mock(**{'dehydrate.return_value': 'limit2'}), mock.Mock(**{'dehydrate.return_value': 'limit3'}), mock.Mock(**{'dehydrate.return_value': 'limit4'}), mock.Mock(**{'dehydrate.return_value': 'limit5'}), mock.Mock(**{'dehydrate.return_value': 'limit6'}), ] pipe = mock.MagicMock(**{ 'zrange.return_value': [ 'limit2', 'limit4', 'limit6', 'limit8', ], }) pipe.__enter__.return_value = pipe pipe.__exit__.return_value = False db = mock.Mock(**{'pipeline.return_value': pipe}) database.limit_update(db, 'limit_key', limits) for lim in limits: lim.dehydrate.assert_called_once_with() mock_dumps.assert_has_calls([ mock.call('limit1'), mock.call('limit2'), mock.call('limit3'), mock.call('limit4'), mock.call('limit5'), mock.call('limit6'), ]) db.pipeline.assert_called_once_with() pipe.assert_has_calls([ mock.call.__enter__(), mock.call.watch('limit_key'), mock.call.zrange('limit_key', 0, -1), mock.call.multi(), mock.call.zrem('limit_key', 'limit8'), mock.call.zadd('limit_key', 10, 'limit1'), mock.call.zadd('limit_key', 20, 'limit2'), mock.call.zadd('limit_key', 30, 'limit3'), mock.call.zadd('limit_key', 40, 'limit4'), mock.call.zadd('limit_key', 50, 'limit5'), mock.call.zadd('limit_key', 60, 'limit6'), mock.call.execute(), mock.call.__exit__(None, None, None), ])
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)
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)