Merge "Add Workload object"

This commit is contained in:
Jenkins 2015-12-04 16:01:54 +00:00 committed by Gerrit Code Review
commit e8ba46705b
4 changed files with 200 additions and 84 deletions

View File

@ -138,7 +138,7 @@ New format JSON schema:
}, },
"run_in_parallel": {"type": "boolean"}, "run_in_parallel": {"type": "boolean"},
"scenarios": { "workloads": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@ -172,7 +172,7 @@ New format JSON schema:
"type": "object" "type": "object"
} }
}, },
"required": ["title", "scenarios"] "required": ["title", "workloads"]
} }
} }
}, },
@ -231,8 +231,8 @@ New format sample:
run_in_parallel: False run_in_parallel: False
# Single scenario load can be generated by specifying only one element # Single scenario load can be generated by specifying only one element
# in "scenarios" section. # in "workloads" section.
scenarios: workloads:
- -
# Full name of scenario plugin # Full name of scenario plugin
name: "NovaServers.boot_and_delete" name: "NovaServers.boot_and_delete"
@ -278,7 +278,7 @@ New format sample:
# If we put 2 or more scenarios to `scenarios` section we will run # If we put 2 or more scenarios to `scenarios` section we will run
# all of them simultaneously which allows us to generate more real life # all of them simultaneously which allows us to generate more real life
# load # load
scenarios: workloads:
- -
name: "CinderVolumes.create_and_delete" name: "CinderVolumes.create_and_delete"
args: args:

View File

@ -196,8 +196,8 @@ class TaskEngine(object):
specified = set() specified = set()
for subtask in config.subtasks: for subtask in config.subtasks:
for s in subtask.scenarios: for s in subtask.workloads:
specified.add(s["name"]) specified.add(s.name)
if not specified.issubset(available): if not specified.issubset(available):
names = ", ".join(specified - available) names = ", ".join(specified - available)
@ -206,29 +206,27 @@ class TaskEngine(object):
@logging.log_task_wrapper(LOG.info, _("Task validation of syntax.")) @logging.log_task_wrapper(LOG.info, _("Task validation of syntax."))
def _validate_config_syntax(self, config): def _validate_config_syntax(self, config):
for subtask in config.subtasks: for subtask in config.subtasks:
for pos, scenario_obj in enumerate(subtask.scenarios): for pos, workload in enumerate(subtask.workloads):
try: try:
runner.ScenarioRunner.validate( runner.ScenarioRunner.validate(workload.runner)
scenario_obj.get("runner", {}))
context.ContextManager.validate( context.ContextManager.validate(
scenario_obj.get("context", {}), non_hidden=True) workload.context, non_hidden=True)
sla.SLA.validate(scenario_obj.get("sla", {})) sla.SLA.validate(workload.sla)
except (exceptions.RallyException, except (exceptions.RallyException,
jsonschema.ValidationError) as e: jsonschema.ValidationError) as e:
raise exceptions.InvalidTaskConfig(
name=scenario_obj["name"],
pos=pos, config=scenario_obj,
reason=six.text_type(e)
)
def _validate_config_semantic_helper(self, admin, user, name, pos, kw = workload.make_exception_args(
deployment, kwargs): pos, six.text_type(e))
raise exceptions.InvalidTaskConfig(**kw)
def _validate_config_semantic_helper(self, admin, user, workload, pos,
deployment):
try: try:
scenario.Scenario.validate( scenario.Scenario.validate(
name, kwargs, admin=admin, users=[user], deployment=deployment) workload.name, workload.to_dict(),
admin=admin, users=[user], deployment=deployment)
except exceptions.InvalidScenarioArgument as e: except exceptions.InvalidScenarioArgument as e:
kw = {"name": name, "pos": pos, kw = workload.make_exception_args(pos, six.text_type(e))
"config": kwargs, "reason": six.text_type(e)}
raise exceptions.InvalidTaskConfig(**kw) raise exceptions.InvalidTaskConfig(**kw)
def _get_user_ctx_for_validation(self, ctx): def _get_user_ctx_for_validation(self, ctx):
@ -260,10 +258,10 @@ class TaskEngine(object):
for u in ctx_conf["users"]: for u in ctx_conf["users"]:
user = osclients.Clients(u["credential"]) user = osclients.Clients(u["credential"])
for subtask in config.subtasks: for subtask in config.subtasks:
for pos, scenario_obj in enumerate(subtask.scenarios): for pos, workload in enumerate(subtask.workloads):
self._validate_config_semantic_helper( self._validate_config_semantic_helper(
admin, user, scenario_obj["name"], admin, user, workload,
pos, deployment, scenario_obj) pos, deployment)
@logging.log_task_wrapper(LOG.info, _("Task validation.")) @logging.log_task_wrapper(LOG.info, _("Task validation."))
def validate(self): def validate(self):
@ -279,8 +277,8 @@ class TaskEngine(object):
raise exceptions.InvalidTaskException(str(e)) raise exceptions.InvalidTaskException(str(e))
def _get_runner(self, config): def _get_runner(self, config):
conf = config.get("runner", {"type": "serial"}) config = config or {"type": "serial"}
return runner.ScenarioRunner.get(conf["type"])(self.task, conf) return runner.ScenarioRunner.get(config["type"])(self.task, config)
def _prepare_context(self, ctx, name, credential): def _prepare_context(self, ctx, name, credential):
scenario_context = copy.deepcopy( scenario_context = copy.deepcopy(
@ -312,7 +310,7 @@ class TaskEngine(object):
self.task.update_status(consts.TaskStatus.RUNNING) self.task.update_status(consts.TaskStatus.RUNNING)
for subtask in self.config.subtasks: for subtask in self.config.subtasks:
for pos, scenario_obj in enumerate(subtask.scenarios): for pos, workload in enumerate(subtask.workloads):
if ResultConsumer.is_task_in_aborting_status( if ResultConsumer.is_task_in_aborting_status(
self.task["uuid"]): self.task["uuid"]):
@ -320,19 +318,18 @@ class TaskEngine(object):
self.task.update_status(consts.TaskStatus.ABORTED) self.task.update_status(consts.TaskStatus.ABORTED)
return return
name = scenario_obj["name"] key = workload.make_key(pos)
key = {"name": name, "pos": pos, "kw": scenario_obj}
LOG.info("Running benchmark with key: \n%s" LOG.info("Running benchmark with key: \n%s"
% json.dumps(key, indent=2)) % json.dumps(key, indent=2))
runner_obj = self._get_runner(scenario_obj) runner_obj = self._get_runner(workload.runner)
context_obj = self._prepare_context( context_obj = self._prepare_context(
scenario_obj.get("context", {}), name, self.admin) workload.context, workload.name, self.admin)
try: try:
with ResultConsumer(key, self.task, runner_obj, with ResultConsumer(key, self.task, runner_obj,
self.abort_on_sla_failure): self.abort_on_sla_failure):
with context.ContextManager(context_obj): with context.ContextManager(context_obj):
runner_obj.run(name, context_obj, runner_obj.run(workload.name, context_obj,
scenario_obj.get("args", {})) workload.args)
except Exception as e: except Exception as e:
LOG.exception(e) LOG.exception(e)
@ -397,7 +394,7 @@ class TaskConfig(object):
}, },
"run_in_parallel": {"type": "boolean"}, "run_in_parallel": {"type": "boolean"},
"scenarios": { "workloads": {
"type": "array", "type": "array",
"minItems": 1, "minItems": 1,
"maxItems": 1, "maxItems": 1,
@ -424,7 +421,7 @@ class TaskConfig(object):
} }
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["title", "scenarios"] "required": ["title", "workloads"]
} }
} }
}, },
@ -474,12 +471,12 @@ class TaskConfig(object):
return [SubTask(s) for s in config["subtasks"]] return [SubTask(s) for s in config["subtasks"]]
elif self.version == 1: elif self.version == 1:
subtasks = [] subtasks = []
for name, v1_scenarios in six.iteritems(config): for name, v1_workloads in six.iteritems(config):
for v1_scenario in v1_scenarios: for v1_workload in v1_workloads:
v2_scenario = copy.deepcopy(v1_scenario) v2_workload = copy.deepcopy(v1_workload)
v2_scenario["name"] = name v2_workload["name"] = name
subtasks.append( subtasks.append(
SubTask({"title": name, "scenarios": [v2_scenario]})) SubTask({"title": name, "workloads": [v2_workload]}))
return subtasks return subtasks
@ -496,5 +493,59 @@ class SubTask(object):
self.tags = config.get("tags", []) self.tags = config.get("tags", [])
self.group = config.get("group") self.group = config.get("group")
self.description = config.get("description") self.description = config.get("description")
self.scenarios = config["scenarios"] self.workloads = [Workload(wconf)
for wconf
in config["workloads"]]
self.context = config.get("context", {}) self.context = config.get("context", {})
class Workload(object):
"""Workload -- workload configuration in SubTask.
"""
def __init__(self, config):
self.name = config["name"]
self.runner = config.get("runner", {})
self.sla = config.get("sla", {})
self.context = config.get("context", {})
self.args = config.get("args", {})
def to_dict(self):
workload = {"runner": self.runner}
for prop in "sla", "args", "context":
value = getattr(self, prop)
if value:
workload[prop] = value
return workload
def to_task(self):
"""Make task configuration for the workload.
This method returns a dict representing full configuration
of the task containing a single subtask with this single
workload.
:return: dict containing full task configuration
"""
# NOTE(ikhudoshyn): Result of this method will be used
# to store full task configuration in DB so that
# subtask configuration in reports would be given
# in the same format as it was provided by user.
# Temporarily it returns to_dict() in order not
# to break existing reports. It should be
# properly implemented in a patch that will update reports.
# return {self.name: [self.to_dict()]}
return self.to_dict()
def make_key(self, pos):
return {"name": self.name,
"pos": pos,
"kw": self.to_task()}
def make_exception_args(self, pos, reason):
return {"name": self.name,
"pos": pos,
"config": self.to_dict(),
"reason": reason}

View File

@ -55,7 +55,7 @@ class TaskTestCase(unittest.TestCase):
"description": "The first subtask in dummy task", "description": "The first subtask in dummy task",
"tags": ["dummy", "functional_test"], "tags": ["dummy", "functional_test"],
"run_in_parallel": False, "run_in_parallel": False,
"scenarios": [{ "workloads": [{
"name": "Dummy.dummy", "name": "Dummy.dummy",
"args": { "args": {
"sleep": 0 "sleep": 0
@ -79,7 +79,7 @@ class TaskTestCase(unittest.TestCase):
"description": "The second subtask in dummy task", "description": "The second subtask in dummy task",
"tags": ["dummy", "functional_test"], "tags": ["dummy", "functional_test"],
"run_in_parallel": False, "run_in_parallel": False,
"scenarios": [{ "workloads": [{
"name": "Dummy.dummy", "name": "Dummy.dummy",
"args": { "args": {
"sleep": 1 "sleep": 1

View File

@ -113,9 +113,9 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "a"}, engine.Workload({"name": "a"}),
{"name": "b"} engine.Workload({"name": "b"})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
@ -134,10 +134,10 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "exist"}, engine.Workload({"name": "exist"}),
{"name": "nonexist1"}, engine.Workload({"name": "nonexist1"}),
{"name": "nonexist2"} engine.Workload({"name": "nonexist2"})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
mock_scenario.list_benchmark_scenarios.return_value = ["exist", "aaa"] mock_scenario.list_benchmark_scenarios.return_value = ["exist", "aaa"]
@ -157,9 +157,9 @@ class TaskEngineTestCase(test.TestCase):
): ):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "sca", "context": "a"}, engine.Workload({"name": "sca", "context": "a"}),
{"name": "scb", "runner": "b"} engine.Workload({"name": "sca", "runner": "b"})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock()) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock())
@ -178,9 +178,9 @@ class TaskEngineTestCase(test.TestCase):
mock_scenario_runner, mock_task_config): mock_scenario_runner, mock_task_config):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "sca", "context": "a"}, engine.Workload({"name": "sca", "context": "a"}),
{"name": "scb", "runner": "b"} engine.Workload({"name": "sca", "runner": "b"})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock()) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock())
@ -198,9 +198,9 @@ class TaskEngineTestCase(test.TestCase):
mock_task_config): mock_task_config):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "sca", "context": "a"}, engine.Workload({"name": "sca", "context": "a"}),
{"name": "scb", "runner": "b"} engine.Workload({"name": "sca", "runner": "b"})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock()) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock())
@ -216,10 +216,13 @@ class TaskEngineTestCase(test.TestCase):
mock_task_config): mock_task_config):
deployment = mock.MagicMock() deployment = mock.MagicMock()
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock()) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock())
eng._validate_config_semantic_helper("admin", "user", "name", "pos", workload = engine.Workload(
deployment, {"args": "args"}) {"name": "name", "runner": "runner", "args": "args"})
eng._validate_config_semantic_helper("admin", "user", workload,
"pos", deployment)
mock_scenario_validate.assert_called_once_with( mock_scenario_validate.assert_called_once_with(
"name", {"args": "args"}, admin="admin", users=["user"], "name", {"runner": "runner", "args": "args"},
admin="admin", users=["user"],
deployment=deployment) deployment=deployment)
@mock.patch("rally.task.engine.TaskConfig") @mock.patch("rally.task.engine.TaskConfig")
@ -229,9 +232,10 @@ class TaskEngineTestCase(test.TestCase):
self, mock_scenario_validate, mock_task_config): self, mock_scenario_validate, mock_task_config):
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock()) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock())
workload = engine.Workload({"name": "name"})
self.assertRaises(exceptions.InvalidTaskConfig, self.assertRaises(exceptions.InvalidTaskConfig,
eng._validate_config_semantic_helper, "a", "u", "n", eng._validate_config_semantic_helper, "a", "u",
"p", mock.MagicMock(), {}) workload, "p", mock.MagicMock())
@mock.patch("rally.task.engine.TaskConfig") @mock.patch("rally.task.engine.TaskConfig")
@mock.patch("rally.task.engine.existing_users.ExistingUsers") @mock.patch("rally.task.engine.existing_users.ExistingUsers")
@ -267,15 +271,13 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask1 = mock.MagicMock() mock_subtask1 = mock.MagicMock()
mock_subtask1.scenarios = [ wconf1 = engine.Workload({"name": "a", "runner": "ra"})
{"name": "a", "kw": 0}, wconf2 = engine.Workload({"name": "a", "runner": "rb"})
{"name": "a", "kw": 1} mock_subtask1.workloads = [wconf1, wconf2]
]
mock_subtask2 = mock.MagicMock() mock_subtask2 = mock.MagicMock()
mock_subtask2.scenarios = [ wconf3 = engine.Workload({"name": "b", "runner": "ra"})
{"name": "b", "kw": 0}, mock_subtask2.workloads = [wconf3]
]
mock_task_instance.subtasks = [mock_subtask1, mock_subtask2] mock_task_instance.subtasks = [mock_subtask1, mock_subtask2]
fake_task = mock.MagicMock() fake_task = mock.MagicMock()
@ -296,12 +298,9 @@ class TaskEngineTestCase(test.TestCase):
admin = user = mock_clients.return_value admin = user = mock_clients.return_value
fake_deployment = mock_deployment_get.return_value fake_deployment = mock_deployment_get.return_value
expected_calls = [ expected_calls = [
mock.call(admin, user, "a", 0, fake_deployment, mock.call(admin, user, wconf1, 0, fake_deployment),
{"name": "a", "kw": 0}), mock.call(admin, user, wconf2, 1, fake_deployment),
mock.call(admin, user, "a", 1, fake_deployment, mock.call(admin, user, wconf3, 0, fake_deployment)
{"name": "a", "kw": 1}),
mock.call(admin, user, "b", 0, fake_deployment,
{"name": "b", "kw": 0})
] ]
mock__validate_config_semantic_helper.assert_has_calls( mock__validate_config_semantic_helper.assert_has_calls(
expected_calls, any_order=True) expected_calls, any_order=True)
@ -345,9 +344,11 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
mock_subtask = mock.MagicMock() mock_subtask = mock.MagicMock()
mock_subtask.scenarios = [ mock_subtask.workloads = [
{"name": "a.task", "context": {"context_a": {"a": 1}}}, engine.Workload(
{"name": "b.task", "context": {"context_b": {"b": 2}}} {"name": "a.task", "context": {"context_a": {"a": 1}}}),
engine.Workload(
{"name": "b.task", "context": {"context_b": {"b": 2}}})
] ]
mock_task_instance.subtasks = [mock_subtask] mock_task_instance.subtasks = [mock_subtask]
@ -699,15 +700,15 @@ class TaskTestCase(test.TestCase):
mock_sub_task.assert_has_calls([ mock_sub_task.assert_has_calls([
mock.call({ mock.call({
"title": "a.task", "title": "a.task",
"scenarios": [{"s": 1, "name": "a.task"}] "workloads": [{"s": 1, "name": "a.task"}]
}), }),
mock.call({ mock.call({
"title": "a.task", "title": "a.task",
"scenarios": [{"s": 2, "name": "a.task"}] "workloads": [{"s": 2, "name": "a.task"}]
}), }),
mock.call({ mock.call({
"title": "b.task", "title": "b.task",
"scenarios": [{"s": 3, "name": "b.task"}] "workloads": [{"s": 3, "name": "b.task"}]
}) })
], any_order=True) ], any_order=True)
@ -724,3 +725,67 @@ class TaskTestCase(test.TestCase):
mock_sub_task.assert_has_calls([ mock_sub_task.assert_has_calls([
mock.call(subtask_conf1), mock.call(subtask_conf1),
mock.call(subtask_conf2)]) mock.call(subtask_conf2)])
class WorkloadTestCase(test.TestCase):
def setUp(self):
super(WorkloadTestCase, self).setUp()
self.wconf = engine.Workload({
"name": "n",
"runner": "r",
"context": "c",
"sla": "s",
"args": "a"
})
def test_to_dict(self):
expected_dict = {
"runner": "r",
"context": "c",
"sla": "s",
"args": "a"
}
self.assertEqual(expected_dict, self.wconf.to_dict())
def test_to_task(self):
expected_dict = {
"runner": "r",
"context": "c",
"sla": "s",
"args": "a"
}
self.assertEqual(expected_dict, self.wconf.to_task())
def test_make_key(self):
expected_key = {
"name": "n",
"pos": "p",
"kw": {
"runner": "r",
"context": "c",
"sla": "s",
"args": "a"
}
}
self.assertEqual(expected_key, self.wconf.make_key("p"))
def test_make_exception_args(self):
expected_args = {
"name": "n",
"pos": "p",
"reason": "r",
"config": {
"runner": "r",
"context": "c",
"sla": "s",
"args": "a"
}
}
self.assertEqual(expected_args,
self.wconf.make_exception_args("p", "r"))