Add possibility to balance usage of users
For the moment all users for tasks are taken randomly and there is no way to balance them between tasks. It may be very useful when we have difference between first usage of tenant/user and all consecutive. In this case we get different load results. Therefore, add enum config option 'user_choice_method' to 'users' context that defines approach for picking up users. Two values are available: - random - round_robin Default one is compatible with old approach - "random". Also, update one of scenarios to use "round_robin" approach to make sure it works. Change-Id: I26fb090eb89c22f5d50529cb73b6ed54fc3d7e15
This commit is contained in:
parent
8bc64b424e
commit
ff7b05094a
@ -23,12 +23,13 @@
|
||||
detailed: True
|
||||
runner:
|
||||
type: "constant"
|
||||
times: 10
|
||||
times: 12
|
||||
concurrency: 1
|
||||
context:
|
||||
users:
|
||||
tenants: 1
|
||||
users_per_tenant: 1
|
||||
tenants: 3
|
||||
users_per_tenant: 4
|
||||
user_choice_method: "round_robin"
|
||||
sla:
|
||||
failure_rate:
|
||||
max: 0
|
||||
|
@ -56,6 +56,20 @@ CONF.register_opts(USER_CONTEXT_OPTS,
|
||||
|
||||
class UserContextMixin(object):
|
||||
|
||||
@property
|
||||
def user_choice_method(self):
|
||||
if not hasattr(self, "_user_choice_method"):
|
||||
self._user_choice_method = self.context["config"].get(
|
||||
"users", {}).get("user_choice_method")
|
||||
if self._user_choice_method is None:
|
||||
# NOTE(vponomaryov): consider 'existing_users' context
|
||||
# picking up value for 'user_choice_method'
|
||||
# when it is supported there.
|
||||
# Until it happens we use old "random" approach for
|
||||
# 'existing_users' context.
|
||||
self.user_choice_method = "random"
|
||||
return self._user_choice_method
|
||||
|
||||
def map_for_scenario(self, context_obj):
|
||||
"""Pass only context of one user and related to it tenant to scenario.
|
||||
|
||||
@ -67,8 +81,19 @@ class UserContextMixin(object):
|
||||
if key not in ["users", "tenants"]:
|
||||
scenario_ctx[key] = value
|
||||
|
||||
user = random.choice(context_obj["users"])
|
||||
tenant = context_obj["tenants"][user["tenant_id"]]
|
||||
if self.user_choice_method == "random":
|
||||
user = random.choice(context_obj["users"])
|
||||
tenant = context_obj["tenants"][user["tenant_id"]]
|
||||
else:
|
||||
# Second and last case - 'round_robin'.
|
||||
tenants_amount = len(context_obj["tenants"])
|
||||
tenant_id = sorted(context_obj["tenants"].keys())[
|
||||
context_obj["iteration"] % tenants_amount]
|
||||
tenant = context_obj["tenants"][tenant_id]
|
||||
users = context_obj["tenants"][tenant_id]["users"]
|
||||
user = users[
|
||||
int(context_obj["iteration"] / tenants_amount) % len(users)]
|
||||
|
||||
scenario_ctx["user"], scenario_ctx["tenant"] = user, tenant
|
||||
|
||||
return scenario_ctx
|
||||
@ -100,6 +125,9 @@ class UserGenerator(UserContextMixin, context.Context):
|
||||
"user_domain": {
|
||||
"type": "string",
|
||||
},
|
||||
"user_choice_method": {
|
||||
"enum": ["random", "round_robin"],
|
||||
},
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
@ -110,7 +138,8 @@ class UserGenerator(UserContextMixin, context.Context):
|
||||
"resource_management_workers":
|
||||
cfg.CONF.users_context.resource_management_workers,
|
||||
"project_domain": cfg.CONF.users_context.project_domain,
|
||||
"user_domain": cfg.CONF.users_context.user_domain
|
||||
"user_domain": cfg.CONF.users_context.user_domain,
|
||||
"user_choice_method": "random",
|
||||
}
|
||||
|
||||
def __init__(self, context):
|
||||
@ -179,7 +208,7 @@ class UserGenerator(UserContextMixin, context.Context):
|
||||
cache["client"] = keystone.wrap(clients.keystone())
|
||||
tenant = cache["client"].create_project(
|
||||
self.generate_random_name(), domain)
|
||||
tenant_dict = {"id": tenant.id, "name": tenant.name}
|
||||
tenant_dict = {"id": tenant.id, "name": tenant.name, "users": []}
|
||||
tenants.append(tenant_dict)
|
||||
|
||||
# NOTE(msdubov): consume() will fill the tenants list in the closure.
|
||||
@ -284,6 +313,8 @@ class UserGenerator(UserContextMixin, context.Context):
|
||||
LOG.debug("Creating %(users)d users using %(threads)s threads" %
|
||||
{"users": users_num, "threads": threads})
|
||||
self.context["users"] = self._create_users()
|
||||
for user in self.context["users"]:
|
||||
self.context["tenants"][user["tenant_id"]]["users"].append(user)
|
||||
|
||||
if len(self.context["users"]) < users_num:
|
||||
raise exceptions.ContextSetupFailure(
|
||||
|
@ -6,13 +6,14 @@
|
||||
},
|
||||
"runner": {
|
||||
"type": "constant",
|
||||
"times": 10,
|
||||
"times": 12,
|
||||
"concurrency": 1
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1
|
||||
"tenants": 3,
|
||||
"users_per_tenant": 4,
|
||||
"user_choice_method": "round_robin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@
|
||||
detailed: True
|
||||
runner:
|
||||
type: "constant"
|
||||
times: 10
|
||||
times: 12
|
||||
concurrency: 1
|
||||
context:
|
||||
users:
|
||||
tenants: 1
|
||||
users_per_tenant: 1
|
||||
tenants: 3
|
||||
users_per_tenant: 4
|
||||
user_choice_method: "round_robin"
|
||||
|
@ -26,16 +26,34 @@ CTX = "rally.plugins.openstack.context.keystone.users"
|
||||
|
||||
class UserContextMixinTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.mixin = users.UserContextMixin()
|
||||
self.mixin.context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"user_choice_method": "random",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@mock.patch("%s.random.choice" % CTX, side_effect=lambda x: x[1])
|
||||
def test_map_for_scenario(self, mock_choice):
|
||||
def test_map_for_scenario_random(self, mock_choice):
|
||||
self.mixin.context["config"]["users"]["user_choice_method"] = (
|
||||
"random")
|
||||
users_ = []
|
||||
tenants = {}
|
||||
|
||||
for i in range(2):
|
||||
tenants[str(i)] = {"name": str(i)}
|
||||
for j in range(3):
|
||||
users_.append({"id": "%s_%s" % (i, j),
|
||||
"tenant_id": str(i), "credential": "credential"})
|
||||
for i in ("0", "1"):
|
||||
tenants[i] = {"id": i, "name": i, "users": []}
|
||||
for j in ("a", "b", "c"):
|
||||
user = {
|
||||
"id": "%s_%s" % (i, j),
|
||||
"tenant_id": i,
|
||||
"credential": "credential",
|
||||
}
|
||||
users_.append(user)
|
||||
tenants[i]["users"].append(user)
|
||||
|
||||
context = {
|
||||
"admin": mock.MagicMock(),
|
||||
@ -44,21 +62,62 @@ class UserContextMixinTestCase(test.TestCase):
|
||||
"some_random_key": {
|
||||
"nested": mock.MagicMock(),
|
||||
"one_more": 10
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"users": {
|
||||
"user_choice_method": "random",
|
||||
},
|
||||
},
|
||||
}
|
||||
chosen_tenant = context["tenants"][context["users"][1]["tenant_id"]]
|
||||
expected_context = {
|
||||
"admin": context["admin"],
|
||||
"user": context["users"][1],
|
||||
"tenant": chosen_tenant,
|
||||
"some_random_key": context["some_random_key"]
|
||||
"some_random_key": context["some_random_key"],
|
||||
"config": context["config"]
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
expected_context,
|
||||
users.UserContextMixin().map_for_scenario(context)
|
||||
self.mixin.map_for_scenario(context)
|
||||
)
|
||||
|
||||
@mock.patch("%s.random.choice" % CTX,
|
||||
side_effect=Exception("Should not be raised"))
|
||||
def test_map_for_scenario_round_robin(self, mock_choice):
|
||||
self.mixin.context["config"]["users"]["user_choice_method"] = (
|
||||
"round_robin")
|
||||
tenants = {s: {"name": s, "users": []} for s in ("0", "1")}
|
||||
users_ = []
|
||||
for tenant_id, tenant in tenants.items():
|
||||
for i in ("0", "1"):
|
||||
user = {"id": "%s_%s" % (tenant_id, i), "tenant_id": tenant_id,
|
||||
"endpoint": "endpoint"}
|
||||
users_.append(user)
|
||||
tenant["users"].append(user)
|
||||
context = {
|
||||
"admin": mock.MagicMock(),
|
||||
"users": users_,
|
||||
"tenants": tenants,
|
||||
"some_random_key": {
|
||||
"nested": mock.MagicMock(),
|
||||
"one_more": 10
|
||||
},
|
||||
"config": {
|
||||
"users": {
|
||||
"user_choice_method": "round_robin",
|
||||
},
|
||||
},
|
||||
}
|
||||
expected_ids = ["0_0", "1_0", "0_1", "1_1"] * 4
|
||||
mapped_ids = []
|
||||
for i in range(16):
|
||||
context["iteration"] = i
|
||||
user = self.mixin.map_for_scenario(context)
|
||||
mapped_ids.append(user["user"]["id"])
|
||||
self.assertEqual(expected_ids, mapped_ids)
|
||||
|
||||
|
||||
class UserGeneratorTestCase(test.ScenarioTestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user