Merge "Add possibility to balance usage of users"
This commit is contained in:
commit
d4d3501ed5
@ -23,12 +23,13 @@
|
|||||||
detailed: True
|
detailed: True
|
||||||
runner:
|
runner:
|
||||||
type: "constant"
|
type: "constant"
|
||||||
times: 10
|
times: 12
|
||||||
concurrency: 1
|
concurrency: 1
|
||||||
context:
|
context:
|
||||||
users:
|
users:
|
||||||
tenants: 1
|
tenants: 3
|
||||||
users_per_tenant: 1
|
users_per_tenant: 4
|
||||||
|
user_choice_method: "round_robin"
|
||||||
sla:
|
sla:
|
||||||
failure_rate:
|
failure_rate:
|
||||||
max: 0
|
max: 0
|
||||||
|
@ -56,6 +56,20 @@ CONF.register_opts(USER_CONTEXT_OPTS,
|
|||||||
|
|
||||||
class UserContextMixin(object):
|
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):
|
def map_for_scenario(self, context_obj):
|
||||||
"""Pass only context of one user and related to it tenant to scenario.
|
"""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"]:
|
if key not in ["users", "tenants"]:
|
||||||
scenario_ctx[key] = value
|
scenario_ctx[key] = value
|
||||||
|
|
||||||
user = random.choice(context_obj["users"])
|
if self.user_choice_method == "random":
|
||||||
tenant = context_obj["tenants"][user["tenant_id"]]
|
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
|
scenario_ctx["user"], scenario_ctx["tenant"] = user, tenant
|
||||||
|
|
||||||
return scenario_ctx
|
return scenario_ctx
|
||||||
@ -100,6 +125,9 @@ class UserGenerator(UserContextMixin, context.Context):
|
|||||||
"user_domain": {
|
"user_domain": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"user_choice_method": {
|
||||||
|
"enum": ["random", "round_robin"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"additionalProperties": False
|
"additionalProperties": False
|
||||||
}
|
}
|
||||||
@ -110,7 +138,8 @@ class UserGenerator(UserContextMixin, context.Context):
|
|||||||
"resource_management_workers":
|
"resource_management_workers":
|
||||||
cfg.CONF.users_context.resource_management_workers,
|
cfg.CONF.users_context.resource_management_workers,
|
||||||
"project_domain": cfg.CONF.users_context.project_domain,
|
"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):
|
def __init__(self, context):
|
||||||
@ -179,7 +208,7 @@ class UserGenerator(UserContextMixin, context.Context):
|
|||||||
cache["client"] = keystone.wrap(clients.keystone())
|
cache["client"] = keystone.wrap(clients.keystone())
|
||||||
tenant = cache["client"].create_project(
|
tenant = cache["client"].create_project(
|
||||||
self.generate_random_name(), domain)
|
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)
|
tenants.append(tenant_dict)
|
||||||
|
|
||||||
# NOTE(msdubov): consume() will fill the tenants list in the closure.
|
# 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" %
|
LOG.debug("Creating %(users)d users using %(threads)s threads" %
|
||||||
{"users": users_num, "threads": threads})
|
{"users": users_num, "threads": threads})
|
||||||
self.context["users"] = self._create_users()
|
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:
|
if len(self.context["users"]) < users_num:
|
||||||
raise exceptions.ContextSetupFailure(
|
raise exceptions.ContextSetupFailure(
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
},
|
},
|
||||||
"runner": {
|
"runner": {
|
||||||
"type": "constant",
|
"type": "constant",
|
||||||
"times": 10,
|
"times": 12,
|
||||||
"concurrency": 1
|
"concurrency": 1
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"users": {
|
"users": {
|
||||||
"tenants": 1,
|
"tenants": 3,
|
||||||
"users_per_tenant": 1
|
"users_per_tenant": 4,
|
||||||
|
"user_choice_method": "round_robin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
detailed: True
|
detailed: True
|
||||||
runner:
|
runner:
|
||||||
type: "constant"
|
type: "constant"
|
||||||
times: 10
|
times: 12
|
||||||
concurrency: 1
|
concurrency: 1
|
||||||
context:
|
context:
|
||||||
users:
|
users:
|
||||||
tenants: 1
|
tenants: 3
|
||||||
users_per_tenant: 1
|
users_per_tenant: 4
|
||||||
|
user_choice_method: "round_robin"
|
||||||
|
@ -26,16 +26,34 @@ CTX = "rally.plugins.openstack.context.keystone.users"
|
|||||||
|
|
||||||
class UserContextMixinTestCase(test.TestCase):
|
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])
|
@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_ = []
|
users_ = []
|
||||||
tenants = {}
|
tenants = {}
|
||||||
|
|
||||||
for i in range(2):
|
for i in ("0", "1"):
|
||||||
tenants[str(i)] = {"name": str(i)}
|
tenants[i] = {"id": i, "name": i, "users": []}
|
||||||
for j in range(3):
|
for j in ("a", "b", "c"):
|
||||||
users_.append({"id": "%s_%s" % (i, j),
|
user = {
|
||||||
"tenant_id": str(i), "credential": "credential"})
|
"id": "%s_%s" % (i, j),
|
||||||
|
"tenant_id": i,
|
||||||
|
"credential": "credential",
|
||||||
|
}
|
||||||
|
users_.append(user)
|
||||||
|
tenants[i]["users"].append(user)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"admin": mock.MagicMock(),
|
"admin": mock.MagicMock(),
|
||||||
@ -44,21 +62,62 @@ class UserContextMixinTestCase(test.TestCase):
|
|||||||
"some_random_key": {
|
"some_random_key": {
|
||||||
"nested": mock.MagicMock(),
|
"nested": mock.MagicMock(),
|
||||||
"one_more": 10
|
"one_more": 10
|
||||||
}
|
},
|
||||||
|
"config": {
|
||||||
|
"users": {
|
||||||
|
"user_choice_method": "random",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
chosen_tenant = context["tenants"][context["users"][1]["tenant_id"]]
|
chosen_tenant = context["tenants"][context["users"][1]["tenant_id"]]
|
||||||
expected_context = {
|
expected_context = {
|
||||||
"admin": context["admin"],
|
"admin": context["admin"],
|
||||||
"user": context["users"][1],
|
"user": context["users"][1],
|
||||||
"tenant": chosen_tenant,
|
"tenant": chosen_tenant,
|
||||||
"some_random_key": context["some_random_key"]
|
"some_random_key": context["some_random_key"],
|
||||||
|
"config": context["config"]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
expected_context,
|
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):
|
class UserGeneratorTestCase(test.ScenarioTestCase):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user