From 7165f31726e033c3a87f6676b40a968242db5ffc Mon Sep 17 00:00:00 2001 From: Andrey Kurilin Date: Thu, 14 Sep 2017 15:57:44 +0300 Subject: [PATCH] Port inner stuff to the new task format * use new format of a runner section the schemas of existing plugins are changed to not include "type" * use "contexts" key instead of "context" note: the database model still operates the word "context". hope it will be fixed soon while extendind abilities of contexts * use new format of a hook section Change-Id: I2ef6ba7a24b542fb001bce378cadf8c83c774b01 --- rally/api.py | 1 + rally/cli/commands/task.py | 27 +++- ...46a38742e89_port_configs_to_new_formats.py | 72 ++++++++++ rally/common/objects/task.py | 42 ++++-- rally/plugins/common/runners/constant.py | 11 +- rally/plugins/common/runners/rps.py | 5 +- rally/plugins/common/runners/serial.py | 3 - rally/plugins/common/validators.py | 4 +- rally/plugins/openstack/validators.py | 20 +-- rally/task/engine.py | 47 ++++--- rally/task/hook.py | 11 +- rally/task/processing/plot.py | 9 +- rally/task/trigger.py | 4 +- tests/unit/cli/commands/test_task.py | 39 ++++-- tests/unit/common/db/test_migrations.py | 132 ++++++++++++++++++ tests/unit/common/objects/test_task.py | 51 ++++--- .../common/hook/triggers/test_event.py | 4 +- .../common/hook/triggers/test_periodic.py | 10 +- .../plugins/common/runners/test_constant.py | 15 +- tests/unit/plugins/common/runners/test_rps.py | 10 -- tests/unit/plugins/common/test_validators.py | 27 ++-- .../unit/plugins/openstack/test_validators.py | 40 +++--- tests/unit/task/processing/test_plot.py | 29 ++-- tests/unit/task/test_engine.py | 48 ++++--- tests/unit/task/test_hook.py | 47 +++---- tests/unit/task/test_trigger.py | 6 +- tests/unit/test_api.py | 4 + 27 files changed, 494 insertions(+), 224 deletions(-) create mode 100644 rally/common/db/sqlalchemy/migrations/versions/046a38742e89_port_configs_to_new_formats.py diff --git a/rally/api.py b/rally/api.py index 09bdd7a954..acba93f5ca 100644 --- a/rally/api.py +++ b/rally/api.py @@ -523,6 +523,7 @@ class _Task(APIGroup): workload_obj = subtask_obj.add_workload( name=workload["name"], description=workload["description"], position=workload["position"], runner=workload["runner"], + runner_type=workload["runner_type"], context=workload["context"], hooks=workload["hooks"], sla=workload["sla"], args=workload["args"]) chunk_size = CONF.raw_result_chunk_size diff --git a/rally/cli/commands/task.py b/rally/cli/commands/task.py index fc6aa21cbd..8fc6066c4f 100644 --- a/rally/cli/commands/task.py +++ b/rally/cli/commands/task.py @@ -578,8 +578,17 @@ class TaskCommands(object): itr["atomic_actions"]).items() ) - results = [ - { + results = [] + for w in itertools.chain(*[s["workloads"] for s in task["subtasks"]]): + w["runner"]["type"] = w["runner_type"] + hooks = [ + {"name": h["config"]["action"][0], + "args": h["config"]["action"][1], + "description": h["config"].get("description"), + "trigger": {"name": h["config"]["trigger"][0], + "args": h["config"]["trigger"][1]}} + for h in w["hooks"]] + results.append({ "key": { "name": w["name"], "description": w["description"], @@ -589,7 +598,7 @@ class TaskCommands(object): "runner": w["runner"], "context": w["context"], "sla": w["sla"], - "hooks": [r["config"] for r in w["hooks"]], + "hooks": hooks, } }, "result": w["data"], @@ -597,9 +606,7 @@ class TaskCommands(object): "hooks": w["hooks"], "load_duration": w["load_duration"], "full_duration": w["full_duration"], - "created_at": w["created_at"]} - for w in itertools.chain( - *[s["workloads"] for s in task["subtasks"]])] + "created_at": w["created_at"]}) print(json.dumps(results, sort_keys=False, indent=4)) @@ -742,6 +749,13 @@ class TaskCommands(object): updated_at += dt.timedelta(seconds=result["full_duration"]) updated_at = updated_at.strftime(consts.TimeFormat.ISO8601) pass_sla = all(s.get("success") for s in result["sla"]) + runner_type = result["key"]["kw"]["runner"].pop("type") + for h in result["hooks"]: + trigger = h["config"]["trigger"] + h["config"] = { + "description": h["config"].get("description"), + "action": (h["config"]["name"], h["config"]["args"]), + "trigger": (trigger["name"], trigger["args"])} workload = {"uuid": "n/a", "name": result["key"]["name"], "position": result["key"]["pos"], @@ -757,6 +771,7 @@ class TaskCommands(object): "created_at": result["created_at"], "updated_at": updated_at, "args": result["key"]["kw"]["args"], + "runner_type": runner_type, "runner": result["key"]["kw"]["runner"], "hooks": result["hooks"], "sla": result["key"]["kw"]["sla"], diff --git a/rally/common/db/sqlalchemy/migrations/versions/046a38742e89_port_configs_to_new_formats.py b/rally/common/db/sqlalchemy/migrations/versions/046a38742e89_port_configs_to_new_formats.py new file mode 100644 index 0000000000..8ddbd8d37c --- /dev/null +++ b/rally/common/db/sqlalchemy/migrations/versions/046a38742e89_port_configs_to_new_formats.py @@ -0,0 +1,72 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""port-configs-to-new-formats + +Revision ID: 046a38742e89 +Revises: fab4f4f31f8a +Create Date: 2017-09-14 15:58:28.950132 + +""" + +from alembic import op +import json +import sqlalchemy as sa + +from rally import exceptions + +# revision identifiers, used by Alembic. +revision = "046a38742e89" +down_revision = "fab4f4f31f8a" +branch_labels = None +depends_on = None + + +workload_helper = sa.Table( + "workloads", + sa.MetaData(), + sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), + sa.Column("uuid", sa.String(36), nullable=False), + + sa.Column("runner", sa.Text), + sa.Column("hooks", sa.Text) +) + + +def upgrade(): + connection = op.get_bind() + + for workload in connection.execute(workload_helper.select()): + runner = json.loads(workload["runner"]) + runner.pop("type") + values = {"runner": json.dumps(runner)} + hooks = workload["hooks"] + if hooks: + values["hooks"] = [] + for hook in json.loads(hooks): + hook_cfg = hook["config"] + trigger_cfg = hook_cfg["trigger"] + hook["config"] = { + "description": hook_cfg.get("description"), + "action": (hook_cfg["name"], hook_cfg["args"]), + "trigger": (trigger_cfg["name"], trigger_cfg["args"])} + values["hooks"].append(hook) + values["hooks"] = json.dumps(values["hooks"]) + connection.execute(workload_helper.update().where( + workload_helper.c.uuid == workload.uuid).values( + **values)) + + +def downgrade(): + raise exceptions.DowngradeNotSupported() diff --git a/rally/common/objects/task.py b/rally/common/objects/task.py index 0a3334e255..8e417ef159 100644 --- a/rally/common/objects/task.py +++ b/rally/common/objects/task.py @@ -14,7 +14,6 @@ # under the License. import collections -import copy import datetime as dt import uuid @@ -285,24 +284,27 @@ class Subtask(object): def update_status(self, status): self._update({"status": status}) - def add_workload(self, name, description, position, runner, context, hooks, - sla, args): + def add_workload(self, name, description, position, runner, runner_type, + context, hooks, sla, args): + # store hooks config as it will look after adding results + if hooks: + hooks = [{"config": hook} for hook in hooks] return Workload(task_uuid=self.subtask["task_uuid"], subtask_uuid=self.subtask["uuid"], name=name, description=description, position=position, - runner=runner, hooks=hooks, context=context, sla=sla, - args=args) + runner=runner, runner_type=runner_type, hooks=hooks, + context=context, sla=sla, args=args) class Workload(object): """Represents a workload object.""" def __init__(self, task_uuid, subtask_uuid, name, description, position, - runner, hooks, context, sla, args): + runner, runner_type, hooks, context, sla, args): self.workload = db.workload_create( task_uuid=task_uuid, subtask_uuid=subtask_uuid, name=name, description=description, position=position, runner=runner, - runner_type=runner["type"], hooks=hooks, context=context, sla=sla, + runner_type=runner_type, hooks=hooks, context=context, sla=sla, args=args) def __getitem__(self, key): @@ -326,6 +328,11 @@ class Workload(object): @classmethod def to_task(cls, workload): + """Format a single workload as a full Task to launch. + + :param workload: A workload config as it stores in database or like in + input file (the difference in hook format). + """ task = collections.OrderedDict() task["version"] = 2 task["title"] = "A cropped version of a bigger task." @@ -337,9 +344,22 @@ class Workload(object): subtask["title"] = workload["name"] subtask["description"] = workload["description"] subtask["scenario"] = {workload["name"]: workload["args"]} - subtask["contexts"] = workload["context"] - runner = copy.copy(workload["runner"]) - subtask["runner"] = {runner.pop("type"): runner} - subtask["hooks"] = [h["config"] for h in workload["hooks"]] + # TODO(andreykurilin): fix database model as soon as the work related + # contexts execution stats will start. + if "context" in workload: + # it is an object from database + subtask["contexts"] = workload["context"] + else: + subtask["contexts"] = workload["contexts"] + subtask["runner"] = {workload["runner_type"]: workload["runner"]} + subtask["hooks"] = [] + for hook in workload["hooks"]: + if "config" in hook: + # it is an object from database + hook = hook["config"] + subtask["hooks"].append({ + "description": hook.get("description"), + "action": dict([hook["action"]]), + "trigger": dict([hook["trigger"]])}) subtask["sla"] = workload["sla"] return task diff --git a/rally/plugins/common/runners/constant.py b/rally/plugins/common/runners/constant.py index 73083b09a6..244b0fa05e 100644 --- a/rally/plugins/common/runners/constant.py +++ b/rally/plugins/common/runners/constant.py @@ -149,10 +149,6 @@ class ConstantScenarioRunner(runner.ScenarioRunner): "type": "object", "$schema": consts.JSON_SCHEMA, "properties": { - "type": { - "type": "string", - "description": "Type of Runner." - }, "concurrency": { "type": "integer", "minimum": 1, @@ -174,7 +170,6 @@ class ConstantScenarioRunner(runner.ScenarioRunner): " from." } }, - "required": ["type"], "additionalProperties": False } @@ -263,10 +258,6 @@ class ConstantForDurationScenarioRunner(runner.ScenarioRunner): "type": "object", "$schema": consts.JSON_SCHEMA, "properties": { - "type": { - "type": "string", - "description": "Type of Runner." - }, "concurrency": { "type": "integer", "minimum": 1, @@ -284,7 +275,7 @@ class ConstantForDurationScenarioRunner(runner.ScenarioRunner): "description": "Operation's timeout." } }, - "required": ["type", "duration"], + "required": ["duration"], "additionalProperties": False } diff --git a/rally/plugins/common/runners/rps.py b/rally/plugins/common/runners/rps.py index 26eb630cbe..ba2cf24740 100644 --- a/rally/plugins/common/runners/rps.py +++ b/rally/plugins/common/runners/rps.py @@ -150,9 +150,6 @@ class RPSScenarioRunner(runner.ScenarioRunner): "type": "object", "$schema": consts.JSON_SCHEMA, "properties": { - "type": { - "type": "string" - }, "times": { "type": "integer", "minimum": 1 @@ -205,7 +202,7 @@ class RPSScenarioRunner(runner.ScenarioRunner): "minimum": 1 } }, - "required": ["type", "times", "rps"], + "required": ["times", "rps"], "additionalProperties": False } diff --git a/rally/plugins/common/runners/serial.py b/rally/plugins/common/runners/serial.py index 3a3fb75a93..e981440d47 100644 --- a/rally/plugins/common/runners/serial.py +++ b/rally/plugins/common/runners/serial.py @@ -35,9 +35,6 @@ class SerialScenarioRunner(runner.ScenarioRunner): "type": "object", "$schema": consts.JSON_SCHEMA, "properties": { - "type": { - "type": "string" - }, "times": { "type": "integer", "minimum": 1 diff --git a/rally/plugins/common/validators.py b/rally/plugins/common/validators.py index 2112aae851..4039fe58b0 100644 --- a/rally/plugins/common/validators.py +++ b/rally/plugins/common/validators.py @@ -270,7 +270,7 @@ class RequiredContextsValidator(validation.Validator): def validate(self, context, config, plugin_cls, plugin_cfg): missing_contexts = [] - input_context = config.get("context", {}) + input_context = config.get("contexts", {}) for name in self.contexts: if isinstance(name, tuple): @@ -304,7 +304,7 @@ class RequiredParamOrContextValidator(validation.Validator): msg = ("You should specify either scenario argument %s or" " use context %s." % (self.param_name, self.ctx_name)) - if self.ctx_name in config.get("context", {}): + if self.ctx_name in config.get("contexts", {}): return if self.param_name in config.get("args", {}): return diff --git a/rally/plugins/openstack/validators.py b/rally/plugins/openstack/validators.py index 066cd723bf..76db24a98d 100644 --- a/rally/plugins/openstack/validators.py +++ b/rally/plugins/openstack/validators.py @@ -55,7 +55,7 @@ class ImageExistsValidator(validation.Validator): if not image_args and self.nullable: return - image_context = config.get("context", {}).get("images", {}) + image_context = config.get("contexts", {}).get("images", {}) image_ctx_name = image_context.get("image_name") if not image_args: @@ -162,11 +162,11 @@ class FlavorExistsValidator(validation.Validator): self.param_name = param_name def _get_flavor_from_context(self, config, flavor_value): - if "flavors" not in config.get("context", {}): + if "flavors" not in config.get("contexts", {}): self.fail("No flavors context") flavors = [flavors_ctx.FlavorConfig(**f) - for f in config["context"]["flavors"]] + for f in config["contexts"]["flavors"]] resource = types.obj_from_name(resource_config=flavor_value, resources=flavors, typename="flavor") flavor = flavors_ctx.FlavorConfig(**resource) @@ -223,7 +223,7 @@ class ImageValidOnFlavorValidator(FlavorExistsValidator): self.validate_disk = validate_disk def _get_validated_image(self, config, clients, param_name): - image_context = config.get("context", {}).get("images", {}) + image_context = config.get("contexts", {}).get("images", {}) image_args = config.get("args", {}).get(param_name) image_ctx_name = image_context.get("image_name") @@ -387,7 +387,7 @@ class RequiredServicesValidator(validation.Validator): for service in self.services: # NOTE(andreykurilin): validator should ignore services configured # via context(a proper validation should be in context) - service_config = config.get("context", {}).get( + service_config = config.get("contexts", {}).get( "api_versions@openstack", {}).get(service, {}) if (service not in available_services and @@ -507,7 +507,7 @@ class RequiredAPIVersionsValidator(validation.Validator): "version": versions_str, "found_version": "3"}) else: - av_ctx = config.get("context", {}).get( + av_ctx = config.get("contexts", {}).get( "api_versions@openstack", {}) default_version = getattr(clients, self.component).choose_version() @@ -540,10 +540,10 @@ class VolumeTypeExistsValidator(validation.Validator): def validate(self, context, config, plugin_cls, plugin_cfg): volume_type = config.get("args", {}).get(self.param, False) - if not volume_type and self.nullable: - return - if not volume_type: + if self.nullable: + return + self.fail("The parameter '%s' is required and should not be empty." % self.param) @@ -551,7 +551,7 @@ class VolumeTypeExistsValidator(validation.Validator): clients = user["credential"].clients() vt_names = [vt.name for vt in clients.cinder().volume_types.list()] - ctx = config.get("context", {}).get("volume_types", []) + ctx = config.get("contexts", {}).get("volume_types", []) vt_names += ctx if volume_type not in vt_names: self.fail("Specified volume type %s not found for user %s." diff --git a/rally/task/engine.py b/rally/task/engine.py index 446bc002ca..6a1d4b1b4c 100644 --- a/rally/task/engine.py +++ b/rally/task/engine.py @@ -276,15 +276,15 @@ class TaskEngine(object): plugin_cfg=None, vtype=vtype)) - if workload["runner"]: + if workload["runner_type"]: results.extend(runner.ScenarioRunner.validate( - name=workload["runner"]["type"], + name=workload["runner_type"], context=vcontext, config=None, plugin_cfg=workload["runner"], vtype=vtype)) - for context_name, context_conf in workload["context"].items(): + for context_name, context_conf in workload["contexts"].items(): results.extend(context.Context.validate( name=context_name, context=vcontext, @@ -310,8 +310,7 @@ class TaskEngine(object): vtype=vtype)) for hook_conf in workload["hooks"]: - action_name, action_cfg = list( - hook_conf["config"]["action"].items())[0] + action_name, action_cfg = hook_conf["action"] results.extend(hook.HookAction.validate( name=action_name, context=vcontext, @@ -319,8 +318,7 @@ class TaskEngine(object): plugin_cfg=action_cfg, vtype=vtype)) - trigger_name, trigger_cfg = list( - hook_conf["config"]["trigger"].items())[0] + trigger_name, trigger_cfg = hook_conf["trigger"] results.extend(hook.HookTrigger.validate( name=trigger_name, context=vcontext, @@ -461,8 +459,9 @@ class TaskEngine(object): description=workload["description"], position=workload["position"], runner=workload["runner"], + runner_type=workload["runner_type"], hooks=workload["hooks"], - context=workload["context"], + context=workload["contexts"], sla=workload["sla"], args=workload["args"]) workload["uuid"] = workload_obj["uuid"] @@ -474,10 +473,10 @@ class TaskEngine(object): % {"position": workload["position"], "cfg": json.dumps(workload_cfg, indent=3)}) - runner_cls = runner.ScenarioRunner.get(workload["runner"]["type"]) + runner_cls = runner.ScenarioRunner.get(workload["runner_type"]) runner_obj = runner_cls(self.task, workload["runner"]) context_obj = self._prepare_context( - workload["context"], workload["name"], workload_obj["uuid"]) + workload["contexts"], workload["name"], workload_obj["uuid"]) try: with ResultConsumer(workload, self.task, subtask_obj, workload_obj, runner_obj, self.abort_on_sla_failure): @@ -746,14 +745,15 @@ class TaskConfig(object): # validation step pass - wconf["context"] = wconf.pop("contexts", {}) + wconf.setdefault("contexts", {}) if "runner" in wconf: - runner_type, runner_cfg = list(wconf["runner"].items())[0] - runner_cfg["type"] = runner_type - wconf["runner"] = runner_cfg + runner = list(wconf["runner"].items())[0] + wconf["runner_type"], wconf["runner"] = runner else: - wconf["runner"] = {"serial": {}} + wconf["runner_type"] = "serial" + wconf["runner"] = {} + wconf.setdefault("sla", {"failure_rate": {"max": 0}}) hooks = wconf.get("hooks", []) @@ -764,14 +764,17 @@ class TaskConfig(object): "Check task format documentation for more " "details.") trigger_cfg = hook_cfg["trigger"] - wconf["hooks"].append({"config": { + wconf["hooks"].append({ "description": hook_cfg["description"], - "action": {hook_cfg["name"]: hook_cfg["args"]}, - "trigger": { - trigger_cfg["name"]: trigger_cfg["args"]}} - }) + "action": (hook_cfg["name"], hook_cfg["args"]), + "trigger": ( + trigger_cfg["name"], trigger_cfg["args"])}) else: - wconf["hooks"].append({"config": hook_cfg}) + hook_cfg["action"] = list( + hook_cfg["action"].items())[0] + hook_cfg["trigger"] = list( + hook_cfg["trigger"].items())[0] + wconf["hooks"].append(hook_cfg) workloads.append(wconf) sconf["workloads"] = workloads @@ -815,7 +818,7 @@ class TaskConfig(object): for hook_cfg in hooks: trigger_cfg = hook_cfg["trigger"] subtask["hooks"].append( - {"description": hook_cfg["description"], + {"description": hook_cfg.get("description"), "action": { hook_cfg["name"]: hook_cfg["args"]}, "trigger": { diff --git a/rally/task/hook.py b/rally/task/hook.py index 75d75cfdb8..c048910c88 100644 --- a/rally/task/hook.py +++ b/rally/task/hook.py @@ -43,10 +43,9 @@ class HookExecutor(object): self.task = task self.triggers = collections.defaultdict(list) - for hook in config.get("hooks", []): - hook_cfg = hook["config"] - action_name = list(hook_cfg["action"].keys())[0] - trigger_name = list(hook_cfg["trigger"].keys())[0] + for hook_cfg in config.get("hooks", []): + action_name = hook_cfg["action"][0] + trigger_name = hook_cfg["trigger"][0] action_cls = HookAction.get(action_name) trigger_obj = HookTrigger.get( trigger_name)(hook_cfg, self.task, action_cls) @@ -216,7 +215,7 @@ class HookTrigger(plugin.Plugin, validation.ValidatablePluginMixin): def __init__(self, hook_cfg, task, hook_cls): self.hook_cfg = hook_cfg - self.config = self.hook_cfg["trigger"][self.get_name()] + self.config = self.hook_cfg["trigger"][1] self.task = task self.hook_cls = hook_cls self._runs = [] @@ -230,7 +229,7 @@ class HookTrigger(plugin.Plugin, validation.ValidatablePluginMixin): LOG.info("Hook action %s is triggered for Task %s by %s=%s" % (self.hook_cls.get_name(), self.task["uuid"], event_type, value)) - action_cfg = list(self.hook_cfg["action"].values())[0] + action_cfg = self.hook_cfg["action"][1] action = self.hook_cls(self.task, action_cfg, {"event_type": event_type, "value": value}) action.run_async() diff --git a/rally/task/processing/plot.py b/rally/task/processing/plot.py index 08ae80ec9f..d925fa9597 100644 --- a/rally/task/processing/plot.py +++ b/rally/task/processing/plot.py @@ -14,7 +14,6 @@ # under the License. import collections -import copy import datetime as dt import hashlib import itertools @@ -33,7 +32,7 @@ def _process_hooks(hooks): """Prepare hooks data for report.""" hooks_ctx = [] for hook in hooks: - hook_ctx = {"name": list(hook["config"]["action"].keys())[0], + hook_ctx = {"name": hook["config"]["action"][0], "desc": hook["config"].get("description", ""), "additive": [], "complete": []} @@ -138,7 +137,7 @@ def _process_workload(workload, workload_cfg, pos): "met": method, "pos": str(pos), "name": method + (pos and " [%d]" % (pos + 1) or ""), - "runner": workload["runner"]["type"], + "runner": workload["runner_type"], "config": json.dumps(workload_cfg, indent=2), "hooks": _process_hooks(workload["hooks"]), "description": workload.get("description", ""), @@ -212,8 +211,8 @@ def _make_source(tasks): workload_cfg["scenario"] = {workload["name"]: workload["args"]} workload_cfg["description"] = workload["description"] workload_cfg["contexts"] = workload["context"] - runner = copy.copy(workload["runner"]) - workload_cfg["runner"] = {runner.pop("type"): runner} + workload_cfg["runner"] = { + workload["runner_type"]: workload["runner"]} workload_cfg["hooks"] = [h["config"] for h in workload["hooks"]] workload_cfg["sla"] = workload["sla"] diff --git a/rally/task/trigger.py b/rally/task/trigger.py index afe93fc978..0441da234d 100644 --- a/rally/task/trigger.py +++ b/rally/task/trigger.py @@ -31,8 +31,8 @@ class Trigger(hook.HookTrigger): @property def context(self): - action_name, action_cfg = list(self.hook_cfg["action"].items())[0] - trigger_name, trigger_cfg = list(self.hook_cfg["trigger"].items())[0] + action_name, action_cfg = self.hook_cfg["action"] + trigger_name, trigger_cfg = self.hook_cfg["trigger"] return {"description": self.hook_cfg["description"], "name": action_name, "args": action_cfg, diff --git a/tests/unit/cli/commands/test_task.py b/tests/unit/cli/commands/test_task.py index 119376e208..a08ee97b14 100644 --- a/tests/unit/cli/commands/test_task.py +++ b/tests/unit/cli/commands/test_task.py @@ -553,7 +553,8 @@ class TaskCommandsTestCase(test.TestCase): "name": "Foo.bar", "description": "descr", "position": 2, "args": {"key1": "value1"}, - "runner": {"type": "rruunneerr"}, + "runner_type": "rruunneerr", + "runner": {"arg1": "args2"}, "hooks": [], "sla": {"failure_rate": {"max": 0}}, "sla_results": {"sla": [{"success": True}]}, @@ -565,10 +566,16 @@ class TaskCommandsTestCase(test.TestCase): task_id = "foo_task_id" task_obj = self._make_task(data=[{"atomic_actions": {"foo": 1.1}}]) + + def fix_r(workload): + cfg = workload["runner"] + cfg["type"] = workload["runner_type"] + return cfg + result = map(lambda x: {"key": {"kw": {"sla": x["sla"], "args": x["args"], "context": x["context"], - "runner": x["runner"], + "runner": fix_r(x), "hooks": x["hooks"]}, "pos": x["position"], "name": x["name"], @@ -1089,7 +1096,8 @@ class TaskCommandsTestCase(test.TestCase): "subtasks": [{"workloads": [{ "name": "fake_name", "position": "fake_pos", - "args": {}, "runner": {}, "context": {}, "sla": {}, + "args": {}, "runner_type": "foo", + "runner": {}, "context": {}, "sla": {}, "hooks": {}, "load_duration": 3.2, "full_duration": 3.5, @@ -1181,8 +1189,12 @@ class TaskCommandsTestCase(test.TestCase): "name": "Foo.bar", "description": "descr", "position": 2, "args": {"key1": "value1"}, - "runner": {"type": "rruunneerr"}, - "hooks": [{"config": {"type": "hookk"}}], + "runner_type": "constant", + "runner": {"time": 3}, + "hooks": [{"config": { + "description": "descr", + "action": ("foo", {"arg1": "v1"}), + "trigger": ("t", {"a2", "v2"})}}], "pass_sla": True, "sla": {"failure_rate": {"max": 0}}, "sla_results": {"sla": [{"success": True}]}, @@ -1196,14 +1208,25 @@ class TaskCommandsTestCase(test.TestCase): } results = [ - {"hooks": workload["hooks"], + {"hooks": [{"config": { + "name": "foo", + "args": {"arg1": "v1"}, + "description": "descr", + "trigger": {"name": "t", + "args": {"a2", "v2"}}}}], "key": {"name": workload["name"], "description": workload["description"], "pos": workload["position"], "kw": { "args": workload["args"], - "runner": workload["runner"], - "hooks": [h["config"] for h in workload["hooks"]], + "runner": {"type": "constant", "time": 3}, + "hooks": [{"name": "foo", + "args": {"arg1": "v1"}, + "description": "descr", + "trigger": { + "name": "t", + "args": {"a2", "v2"} + }}], "sla": workload["sla"], "context": workload["context"]}}, "sla": workload["sla_results"]["sla"], diff --git a/tests/unit/common/db/test_migrations.py b/tests/unit/common/db/test_migrations.py index e89ac89772..d0bfe809bb 100644 --- a/tests/unit/common/db/test_migrations.py +++ b/tests/unit/common/db/test_migrations.py @@ -1965,3 +1965,135 @@ class MigrationWalkTestCase(rtest.DBTestCase, conn.execute( deployment_table.delete().where( deployment_table.c.uuid == deployment_uuid)) + + def _pre_upgrade_046a38742e89(self, engine): + deployment_table = db_utils.get_table(engine, "deployments") + task_table = db_utils.get_table(engine, "tasks") + subtask_table = db_utils.get_table(engine, "subtasks") + workload_table = db_utils.get_table(engine, "workloads") + + self._046a38742e89_deployment_uuid = str(uuid.uuid4()) + self._046a38742e89_task_uuid = str(uuid.uuid4()) + subtask_uuid = str(uuid.uuid4()) + workloads = [ + { + "runner": {"type": "constant", + "times": 1000}}, + { + "runner": {"type": "rps", + "rps": 300}, + "hooks": [ + { + "config": {"args": {"arg1": "v1"}, + "description": "descr", + "name": "foo", + "trigger": {"name": "bar", + "args": {"arg2": "v2"}}}} + ] + } + ] + + with engine.connect() as conn: + conn.execute( + deployment_table.insert(), + [{ + "uuid": self._046a38742e89_deployment_uuid, + "name": str(uuid.uuid4()), + "config": "{}", + "enum_deployments_status": consts.DeployStatus.DEPLOY_INIT, + "credentials": six.b(json.dumps([])), + "users": six.b(json.dumps([])) + }] + ) + + conn.execute( + task_table.insert(), + [{ + "uuid": self._046a38742e89_task_uuid, + "created_at": timeutils.utcnow(), + "updated_at": timeutils.utcnow(), + "status": consts.TaskStatus.FINISHED, + "validation_result": six.b(json.dumps({})), + "deployment_uuid": self._046a38742e89_deployment_uuid + }] + ) + + conn.execute( + subtask_table.insert(), + [{ + "uuid": subtask_uuid, + "created_at": timeutils.utcnow(), + "updated_at": timeutils.utcnow(), + "task_uuid": self._046a38742e89_task_uuid, + "context": six.b(json.dumps([])), + "sla": six.b(json.dumps([])), + "run_in_parallel": False + }] + ) + + for workload in workloads: + conn.execute( + workload_table.insert(), + [{ + "uuid": str(uuid.uuid4()), + "name": "foo", + "task_uuid": self._046a38742e89_task_uuid, + "subtask_uuid": subtask_uuid, + "created_at": timeutils.utcnow(), + "updated_at": timeutils.utcnow(), + "position": 0, + "runner": json.dumps(workload["runner"]), + "runner_type": "", + "context": "", + "context_execution": "", + "statistics": "", + "hooks": json.dumps(workload.get("hooks", "")), + "sla": "", + "sla_results": "", + "args": "", + "load_duration": 0, + "pass_sla": True, + "min_duration": 0, + "max_duration": 1 + }] + ) + + def _check_046a38742e89(self, engine, data): + deployment_table = db_utils.get_table(engine, "deployments") + task_table = db_utils.get_table(engine, "tasks") + subtask_table = db_utils.get_table(engine, "subtasks") + workload_table = db_utils.get_table(engine, "workloads") + + subtask_uuid = None + + with engine.connect() as conn: + task_uuid = self._046a38742e89_task_uuid + for workload in conn.execute(workload_table.select().where( + workload_table.c.task_uuid == task_uuid)).fetchall(): + if subtask_uuid is None: + subtask_uuid = workload.subtask_uuid + + runner = json.loads(workload.runner) + self.assertNotIn("type", runner) + + hooks = json.loads(workload.hooks) + if hooks: + for hook in hooks: + hook_cfg = hook["config"] + self.assertEqual(2, len(hook_cfg["action"])) + self.assertEqual(2, len(hook_cfg["trigger"])) + + conn.execute( + workload_table.delete().where( + workload_table.c.uuid == workload.uuid)) + conn.execute( + subtask_table.delete().where( + subtask_table.c.uuid == subtask_uuid)) + + conn.execute( + task_table.delete().where(task_table.c.uuid == task_uuid)) + + deployment_uuid = self._046a38742e89_deployment_uuid + conn.execute( + deployment_table.delete().where( + deployment_table.c.uuid == deployment_uuid)) diff --git a/tests/unit/common/objects/test_task.py b/tests/unit/common/objects/test_task.py index d783fbc67f..23f9b5ac04 100644 --- a/tests/unit/common/objects/test_task.py +++ b/tests/unit/common/objects/test_task.py @@ -311,21 +311,24 @@ class SubtaskTestCase(test.TestCase): name = "w" description = "descr" position = 0 - runner = {"type": "runner"} + runner_type = "runner" + runner = {} context = {"users": {}} sla = {"failure_rate": {"max": 0}} args = {"arg": "xxx"} - hooks = [{"config": {"foo": "bar"}}] + hooks = [{"foo": "bar"}] - workload = subtask.add_workload(name, description=description, - position=position, runner=runner, - context=context, sla=sla, args=args, - hooks=hooks) + workload = subtask.add_workload( + name, description=description, position=position, + runner_type=runner_type, runner=runner, context=context, sla=sla, + args=args, hooks=hooks) mock_workload.assert_called_once_with( task_uuid=self.subtask["task_uuid"], subtask_uuid=self.subtask["uuid"], name=name, - description=description, position=position, runner=runner, - context=context, sla=sla, args=args, hooks=hooks) + description=description, position=position, + runner_type=runner_type, runner=runner, + context=context, sla=sla, args=args, + hooks=[{"config": h} for h in hooks]) self.assertIs(workload, mock_workload.return_value) @@ -345,14 +348,16 @@ class WorkloadTestCase(test.TestCase): name = "w" description = "descr" position = 0 - runner = {"type": "constant"} + runner_type = "constant" + runner = {"times": 3} context = {"users": {}} sla = {"failure_rate": {"max": 0}} args = {"arg": "xxx"} hooks = [{"config": {"foo": "bar"}}] workload = objects.Workload("uuid1", "uuid2", name=name, description=description, position=position, - runner=runner, context=context, sla=sla, + runner=runner, runner_type=runner_type, + context=context, sla=sla, args=args, hooks=hooks) mock_workload_create.assert_called_once_with( task_uuid="uuid1", subtask_uuid="uuid2", name=name, hooks=hooks, @@ -367,7 +372,7 @@ class WorkloadTestCase(test.TestCase): mock_workload_create.return_value = self.workload workload = objects.Workload("uuid1", "uuid2", name="w", description="descr", position=0, - runner={"type": "foo"}, context=None, + runner_type="foo", runner={}, context=None, sla=None, args=None, hooks=[]) workload.add_workload_data(0, {"data": "foo"}) @@ -383,7 +388,8 @@ class WorkloadTestCase(test.TestCase): name = "w" description = "descr" position = 0 - runner = {"type": "constant"} + runner_type = "constant" + runner = {"times": 3} context = {"users": {}} sla = {"failure_rate": {"max": 0}} args = {"arg": "xxx"} @@ -394,8 +400,9 @@ class WorkloadTestCase(test.TestCase): hooks = [] workload = objects.Workload("uuid1", "uuid2", name=name, description=description, position=position, - runner=runner, context=context, sla=sla, - args=args, hooks=hooks) + runner=runner, runner_type=runner_type, + context=context, sla=sla, args=args, + hooks=hooks) workload.set_results(load_duration=load_duration, full_duration=full_duration, @@ -417,11 +424,15 @@ class WorkloadTestCase(test.TestCase): "name": "Foo.bar", "description": "Make something useful (or not).", "position": 3, - "runner": {"type": "constant", "times": 3}, - "context": {"users": {}}, + "runner_type": "constant", + "runner": {"times": 3}, + "contexts": {"users": {}}, "sla": {"failure_rate": {"max": 0}}, "args": {"key1": "value1"}, - "hooks": [{"config": {"hook1": "xxx"}}], + "hooks": [{"config": { + "action": ["foo", {"arg1": "v1"}], + "trigger": ["bar", {"arg2": "v2"}] + }}], "sla_results": {"sla": []}, "context_execution": {}, "start_time": "2997.23.12", @@ -444,8 +455,10 @@ class WorkloadTestCase(test.TestCase): ("title", workload["name"]), ("description", workload["description"]), ("scenario", {workload["name"]: workload["args"]}), - ("contexts", workload["context"]), + ("contexts", workload["contexts"]), ("runner", {"constant": {"times": 3}}), - ("hooks", [h["config"] for h in workload["hooks"]]), + ("hooks", [{"action": {"foo": {"arg1": "v1"}}, + "trigger": {"bar": {"arg2": "v2"}}, + "description": None}]), ("sla", workload["sla"])])])]) self.assertEqual(expected_task, objects.Workload.to_task(workload)) diff --git a/tests/unit/plugins/common/hook/triggers/test_event.py b/tests/unit/plugins/common/hook/triggers/test_event.py index 0c25eade61..a3fdaaa54a 100644 --- a/tests/unit/plugins/common/hook/triggers/test_event.py +++ b/tests/unit/plugins/common/hook/triggers/test_event.py @@ -28,8 +28,8 @@ class EventTriggerTestCase(test.TestCase): super(EventTriggerTestCase, self).setUp() self.hook_cls = mock.MagicMock(__name__="name") self.trigger = event.EventTrigger( - {"trigger": {"event": {"unit": "iteration", "at": [1, 4, 5]}}, - "action": {"foo": {}}}, + {"trigger": ("event", {"unit": "iteration", "at": [1, 4, 5]}), + "action": ("foo", {})}, mock.MagicMock(), self.hook_cls) @ddt.data((dict(unit="time", at=[0, 3, 5]), True), diff --git a/tests/unit/plugins/common/hook/triggers/test_periodic.py b/tests/unit/plugins/common/hook/triggers/test_periodic.py index 7bd55375cb..dc69db57b4 100644 --- a/tests/unit/plugins/common/hook/triggers/test_periodic.py +++ b/tests/unit/plugins/common/hook/triggers/test_periodic.py @@ -28,8 +28,8 @@ class PeriodicTriggerTestCase(test.TestCase): super(PeriodicTriggerTestCase, self).setUp() self.hook_cls = mock.MagicMock(__name__="name") self.trigger = periodic.PeriodicTrigger( - {"trigger": {"periodic": {"unit": "iteration", "step": 2}}, - "action": {"foo": {}}}, + {"trigger": ("periodic", {"unit": "iteration", "step": 2}), + "action": ("foo", {})}, mock.MagicMock(), self.hook_cls) @ddt.data((dict(unit="time", step=1), True), @@ -74,9 +74,9 @@ class PeriodicTriggerTestCase(test.TestCase): @ddt.unpack def test_on_event_start_end(self, value, should_call): trigger = periodic.PeriodicTrigger( - {"trigger": {"periodic": {"unit": "time", - "step": 3, "start": 2, "end": 9}}, - "action": {"foo": {}}}, + {"trigger": ("periodic", {"unit": "time", + "step": 3, "start": 2, "end": 9}), + "action": ("foo", {})}, mock.MagicMock(), self.hook_cls) trigger.on_event("time", value) self.assertEqual(should_call, self.hook_cls.called) diff --git a/tests/unit/plugins/common/runners/test_constant.py b/tests/unit/plugins/common/runners/test_constant.py index f852d2636f..3ec5a10158 100644 --- a/tests/unit/plugins/common/runners/test_constant.py +++ b/tests/unit/plugins/common/runners/test_constant.py @@ -38,11 +38,13 @@ class ConstantScenarioRunnerTestCase(test.TestCase): self.args = {"a": 1} self.task = mock.MagicMock() - @ddt.data(({"times": 4, "concurrency": 2, - "timeout": 2, "type": "constant", + @ddt.data(({"times": 4, + "concurrency": 2, + "timeout": 2, "max_cpu_count": 2}, True), - ({"times": 4, "concurrency": 5, - "timeout": 2, "type": "constant", + ({"times": 4, + "concurrency": 5, + "timeout": 2, "max_cpu_count": 2}, False), ({"foo": "bar"}, False)) @ddt.unpack @@ -267,8 +269,9 @@ class ConstantForDurationScenarioRunnerTestCase(test.TestCase): self.context["iteration"] = 14 self.args = {"a": 1} - @ddt.data(({"duration": 0, "concurrency": 2, - "timeout": 2, "type": "constant_for_duration"}, True), + @ddt.data(({"duration": 0, + "concurrency": 2, + "timeout": 2}, True), ({"foo": "bar"}, False)) @ddt.unpack def test_validate(self, config, valid): diff --git a/tests/unit/plugins/common/runners/test_rps.py b/tests/unit/plugins/common/runners/test_rps.py index efee202871..c6c31432ff 100644 --- a/tests/unit/plugins/common/runners/test_rps.py +++ b/tests/unit/plugins/common/runners/test_rps.py @@ -36,7 +36,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): @ddt.data( { "config": { - "type": "rps", "rps": { "start": 1, "end": 3, @@ -47,7 +46,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": { "start": 1, "end": 10, @@ -58,7 +56,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": { "start": 1, "end": 2, @@ -69,7 +66,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": { "start": 2, "end": 1, @@ -81,7 +77,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": { "start": 2, "end": 1, @@ -93,7 +88,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "times": 1, "rps": 100, "max_concurrency": 50, @@ -103,14 +97,12 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": 0.000001 }, "valid": False }, { "config": { - "type": "rps", "rps": { "start": 1, "end": 10, @@ -121,7 +113,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": 0, "times": 55 }, @@ -129,7 +120,6 @@ class RPSScenarioRunnerTestCase(test.TestCase): }, { "config": { - "type": "rps", "rps": 2, "times": 55, "foo": "bar" diff --git a/tests/unit/plugins/common/test_validators.py b/tests/unit/plugins/common/test_validators.py index 7f58a3bd64..9b4d6e582a 100644 --- a/tests/unit/plugins/common/test_validators.py +++ b/tests/unit/plugins/common/test_validators.py @@ -247,8 +247,8 @@ class RequiredContextsValidatorTestCase(test.TestCase): "users": [mock.MagicMock()], }) @ddt.data( - {"config": {"context": {"c1": 1, "c2": 2, "c3": 3}}}, - {"config": {"context": {"c1": 1, "c2": 2, "c3": 3, "a": 1}}} + {"config": {"contexts": {"c1": 1, "c2": 2, "c3": 3}}}, + {"config": {"contexts": {"c1": 1, "c2": 2, "c3": 3, "a": 1}}} ) @ddt.unpack def test_validate(self, config): @@ -261,16 +261,19 @@ class RequiredContextsValidatorTestCase(test.TestCase): contexts=("c1", "c2", "c3")) e = self.assertRaises( validation.ValidationError, - validator.validate, self.credentials, {"context": {"a": 1}}, + validator.validate, self.credentials, {"contexts": {"a": 1}}, None, None) self.assertEqual( "The following context(s) are required but missing from " "the input task file: c1, c2, c3", e.message) @ddt.data( - {"config": {"context": {"c1": 1, "c2": 2, "c3": 3, "b1": 1, "a1": 1}}}, - {"config": {"context": {"c1": 1, "c2": 2, "c3": 3, - "b1": 1, "b2": 2, "a1": 1}}}, + {"config": { + "contexts": {"c1": 1, "c2": 2, "c3": 3, + "b1": 1, "a1": 1}}}, + {"config": { + "contexts": {"c1": 1, "c2": 2, "c3": 3, + "b1": 1, "b2": 2, "a1": 1}}}, ) @ddt.unpack def test_validate_with_or(self, config): @@ -284,7 +287,7 @@ class RequiredContextsValidatorTestCase(test.TestCase): e = self.assertRaises( validation.ValidationError, validator.validate, self.credentials, - {"context": {"c1": 1, "c2": 2}}, None, None) + {"contexts": {"c1": 1, "c2": 2}}, None, None) self.assertEqual( "The following context(s) are required but missing " "from the input task file: 'a1 or a2', 'b1 or b2'", e.message) @@ -302,20 +305,20 @@ class RequiredParamOrContextValidatorTestCase(test.TestCase): @ddt.data( {"config": {"args": {"image": {"name": ""}}, - "context": {"custom_image": {"name": "fake_image"}}}}, - {"config": {"context": {"custom_image": {"name": "fake_image"}}}}, + "contexts": {"custom_image": {"name": "fake_image"}}}}, + {"config": {"contexts": {"custom_image": {"name": "fake_image"}}}}, {"config": {"args": {"image": {"name": "fake_image"}}, - "context": {"custom_image": ""}}}, + "contexts": {"custom_image": ""}}}, {"config": {"args": {"image": {"name": "fake_image"}}}}, {"config": {"args": {"image": {"name": ""}}, - "context": {"custom_image": {"name": ""}}}} + "contexts": {"custom_image": {"name": ""}}}} ) @ddt.unpack def test_validate(self, config): self.validator.validate(self.credentials, config, None, None) @ddt.data( - {"config": {"args": {}, "context": {}}, + {"config": {"args": {}, "contexts": {}}, "err_msg": "You should specify either scenario argument image or " "use context custom_image."}, {"config": {}, diff --git a/tests/unit/plugins/openstack/test_validators.py b/tests/unit/plugins/openstack/test_validators.py index b9b7c20c08..754ce30d27 100644 --- a/tests/unit/plugins/openstack/test_validators.py +++ b/tests/unit/plugins/openstack/test_validators.py @@ -85,20 +85,19 @@ class ImageExistsValidatorTestCase(test.TestCase): self.assertIsNone(result) def test_validator_image_from_context(self): - config = {"args": { - "image": {"regex": r"^foo$"}}, "context": { - "images": { - "image_name": "foo"}}} + config = { + "args": {"image": {"regex": r"^foo$"}}, + "contexts": {"images": {"image_name": "foo"}}} self.validator.validate(self.context, config, None, None) @mock.patch("%s.openstack_types.GlanceImage.transform" % PATH, return_value="image_id") def test_validator_image_not_in_context(self, mock_glance_image_transform): - config = {"args": { - "image": "fake_image"}, "context": { - "images": { - "fake_image_name": "foo"}}} + config = { + "args": {"image": "fake_image"}, + "contexts": { + "images": {"fake_image_name": "foo"}}} clients = self.context[ "users"][0]["credential"].clients.return_value @@ -266,7 +265,7 @@ class FlavorExistsValidatorTestCase(test.TestCase): def test__get_flavor_from_context(self, mock_flavor_config, mock_obj_from_name): config = { - "context": {"images": {"fake_parameter_name": "foo_image"}}} + "contexts": {"images": {"fake_parameter_name": "foo_image"}}} e = self.assertRaises( validators.validation.ValidationError, @@ -274,8 +273,8 @@ class FlavorExistsValidatorTestCase(test.TestCase): config, "foo_flavor") self.assertEqual("No flavors context", e.message) - config = {"context": {"images": {"fake_parameter_name": "foo_image"}, - "flavors": [{"flavor1": "fake_flavor1"}]}} + config = {"contexts": {"images": {"fake_parameter_name": "foo_image"}, + "flavors": [{"flavor1": "fake_flavor1"}]}} result = self.validator._get_flavor_from_context(config, "foo_flavor") self.assertEqual("" % result.name, result.id) @@ -457,11 +456,12 @@ class ImageValidOnFlavorValidatorTestCase(test.TestCase): "min_disk": 0 } # Get image name from context - result = self.validator._get_validated_image({"args": { - "image": {"regex": r"^foo$"}}, "context": { - "images": { - "image_name": "foo"} - }}, mock.Mock(), "image") + result = self.validator._get_validated_image({ + "args": { + "image": {"regex": r"^foo$"}}, + "contexts": { + "images": {"image_name": "foo"}}}, + mock.Mock(), "image") self.assertEqual(image, result) clients = mock.Mock() @@ -819,7 +819,7 @@ class RequiredAPIVersionsValidatorTestCase(test.TestCase): clients = self.context["users"][0]["credential"].clients() clients.nova.choose_version.return_value = nova - config = {"context": {"api_versions@openstack": {}}} + config = {"contexts": {"api_versions@openstack": {}}} if err_msg: e = self.assertRaises( @@ -839,7 +839,7 @@ class RequiredAPIVersionsValidatorTestCase(test.TestCase): [version]) config = { - "context": {"api_versions@openstack": {"nova": {"version": 2}}}} + "contexts": {"api_versions@openstack": {"nova": {"version": 2}}}} if err_msg: e = self.assertRaises( @@ -890,7 +890,7 @@ class VolumeTypeExistsValidatorTestCase(test.TestCase): clients = self.context["users"][0]["credential"].clients() clients.cinder().volume_types.list.return_value = [] ctx = {"args": {"volume_type": "fake_type"}, - "context": {"volume_types": ["fake_type"]}} + "contexts": {"volume_types": ["fake_type"]}} result = self.validator.validate(self.context, ctx, None, None) self.assertIsNone(result) @@ -899,7 +899,7 @@ class VolumeTypeExistsValidatorTestCase(test.TestCase): clients = self.context["users"][0]["credential"].clients() clients.cinder().volume_types.list.return_value = [] config = {"args": {"volume_type": "fake_type"}, - "context": {"volume_types": ["fake_type_2"]}} + "contexts": {"volume_types": ["fake_type_2"]}} e = self.assertRaises( validators.validation.ValidationError, self.validator.validate, self.context, config, None, None) diff --git a/tests/unit/task/processing/test_plot.py b/tests/unit/task/processing/test_plot.py index 3a144a8cd9..feb7ca4f0f 100644 --- a/tests/unit/task/processing/test_plot.py +++ b/tests/unit/task/processing/test_plot.py @@ -51,7 +51,8 @@ class PlotTestCase(test.TestCase): "sla_results": {"sla": {}}, "pass_sla": True, "position": 0, "name": "Foo.bar", "description": "Description!!", - "runner": {"type": "constant"}, + "runner_type": "constant", + "runner": {}, "statistics": {"atomics": { "foo_action": {"max_duration": 19, "min_duration": 10}}}, "full_duration": 40, "load_duration": 32, @@ -90,8 +91,8 @@ class PlotTestCase(test.TestCase): { "config": { "description": "Foo", - "action": {"sys_call": "foo cmd"}, - "trigger": {"event": {"at": [2, 5], "unit": "iteration"}}}, + "action": ("sys_call", "foo cmd"), + "trigger": ("event", {"at": [2, 5], "unit": "iteration"})}, "results": [ { "status": "success", @@ -120,8 +121,8 @@ class PlotTestCase(test.TestCase): "summary": {"success": 2}}, { "config": { - "action": {"sys_call": "bar cmd"}, - "trigger": {"event": {"at": [1, 2, 4], "unit": "time"}}}, + "action": ("sys_call", "bar cmd"), + "trigger": ("event", {"at": [1, 2, 4], "unit": "time"})}, "results": [ { "status": "success", @@ -186,11 +187,14 @@ class PlotTestCase(test.TestCase): "name": "Foo.bar_%s" % i, "description": "Make something useful (or not).", "position": i, - "runner": {"type": "constant", "times": 3}, - "context": {"users": {}}, + "runner_type": "constant", + "runner": {"times": 3}, + "contexts": {"users": {}}, "sla": {"failure_rate": {"max": 0}}, "args": {"key1": "value1"}, - "hooks": [{"config": {"hook1": "xxx"}}], + "hooks": [{"config": { + "action": ("foo", {}), + "trigger": ("xxx", {})}}], "sla_results": {"sla": []}, "start_time": "2997.23.12", "load_duration": 42, @@ -225,7 +229,8 @@ class PlotTestCase(test.TestCase): "args": {}, "context": {"key": "context"}, "sla": {"key": "sla"}, - "runner": {"type": "crunner"}, + "runner_type": "crunner", + "runner": {}, "hooks": []} ]} ]}] @@ -262,7 +267,8 @@ class PlotTestCase(test.TestCase): "args": {}, "context": {"key": "context"}, "sla": {"key": "sla"}, - "runner": {"type": "crunner"}, + "runner_type": "crunner", + "runner": {}, "hooks": []} ]}, {"title": "subtask title3", @@ -274,7 +280,8 @@ class PlotTestCase(test.TestCase): "args": {}, "context": {"key": "context"}, "sla": {"key": "sla"}, - "runner": {"type": "crunner"}, + "runner_type": "crunner", + "runner": {}, "hooks": []} ]} ]}) diff --git a/tests/unit/task/test_engine.py b/tests/unit/task/test_engine.py index ec05a22cfb..6d274210cc 100644 --- a/tests/unit/task/test_engine.py +++ b/tests/unit/task/test_engine.py @@ -37,15 +37,16 @@ class MyException(exceptions.RallyException): class TaskEngineTestCase(test.TestCase): @staticmethod - def _make_workload(name, args=None, description=None, context=None, + def _make_workload(name, args=None, description=None, contexts=None, sla=None, runner=None, hooks=None, position=0): return {"uuid": "foo", "name": name, "position": position, "description": description, "args": args, - "context": context or {}, - "runner": runner or {"type": "serial"}, + "contexts": contexts or {}, + "runner_type": runner[0] if runner else "serial", + "runner": runner[1] if runner else {}, "sla": sla or {}, "hooks": hooks or []} @@ -153,12 +154,13 @@ class TaskEngineTestCase(test.TestCase): scenario_name = "Foo.bar" runner_type = "MegaRunner" - hook_conf = {"action": {"c": "c_args"}, - "trigger": {"d": "d_args"}} + hook_conf = {"action": ("c", "c_args"), + "trigger": ("d", "d_args")} workload = {"name": scenario_name, - "runner": {"type": runner_type}, - "context": {"a": "a_conf"}, - "hooks": [{"config": hook_conf}], + "runner_type": runner_type, + "runner": {}, + "contexts": {"a": "a_conf"}, + "hooks": [hook_conf], "sla": {"foo_sla": "sla_conf"}, "position": 2} @@ -169,7 +171,7 @@ class TaskEngineTestCase(test.TestCase): mock_scenario_runner_validate.assert_called_once_with( name=runner_type, context=None, config=None, - plugin_cfg={"type": runner_type}, vtype=None) + plugin_cfg={}, vtype=None) self.assertEqual([mock.call(name="a", context=None, config=None, @@ -204,7 +206,7 @@ class TaskEngineTestCase(test.TestCase): "There is no such runner"] scenario_cls = mock_scenario_get.return_value scenario_cls.get_default_context.return_value = {} - workload = self._make_workload(name="sca", runner={"type": "b"}) + workload = self._make_workload(name="sca", runner=("b", {})) eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), mock.Mock()) @@ -230,7 +232,7 @@ class TaskEngineTestCase(test.TestCase): mock_task_instance.subtasks = [{"workloads": [ self._make_workload(name="sca"), self._make_workload(name="sca", position=1, - context={"a": "a_conf"}) + contexts={"a": "a_conf"}) ]}] eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), mock.Mock()) @@ -282,12 +284,12 @@ class TaskEngineTestCase(test.TestCase): scenario_cls = mock_scenario_get.return_value scenario_cls.get_default_context.return_value = {} mock_task_instance = mock.MagicMock() - hook_conf = {"action": {"c": "c_args"}, - "trigger": {"d": "d_args"}} + hook_conf = {"action": ("c", "c_args"), + "trigger": ("d", "d_args")} mock_task_instance.subtasks = [{"workloads": [ self._make_workload(name="sca"), self._make_workload(name="sca", position=1, - hooks=[{"config": hook_conf}]) + hooks=[hook_conf]) ]}] eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), mock.Mock()) @@ -315,12 +317,12 @@ class TaskEngineTestCase(test.TestCase): scenario_cls = mock_scenario_get.return_value scenario_cls.get_default_context.return_value = {} mock_task_instance = mock.MagicMock() - hook_conf = {"action": {"c": "c_args"}, - "trigger": {"d": "d_args"}} + hook_conf = {"action": ("c", "c_args"), + "trigger": ("d", "d_args")} mock_task_instance.subtasks = [{"workloads": [ self._make_workload(name="sca"), self._make_workload(name="sca", position=1, - hooks=[{"config": hook_conf}]) + hooks=[hook_conf]) ]}] eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), mock.Mock()) @@ -353,7 +355,7 @@ class TaskEngineTestCase(test.TestCase): mock_task_instance = mock.MagicMock() wconf1 = self._make_workload(name="SomeScen.scenario", - context={"users": {}}) + contexts={"users": {}}) wconf2 = self._make_workload(name="SomeScen.scenario", position=1) subtask1 = {"workloads": [wconf1, wconf2]} @@ -452,9 +454,9 @@ class TaskEngineTestCase(test.TestCase): "context": {}, "workloads": [ self._make_workload(name="a.task", description="foo", - context={"context_a": {"a": 1}}), + contexts={"context_a": {"a": 1}}), self._make_workload(name="a.task", description="foo", - context={"context_a": {"b": 2}}, + contexts={"context_a": {"b": 2}}, position=2)]}] mock_task_config.return_value = mock_task_instance @@ -1064,6 +1066,6 @@ class TaskConfigTestCase(test.TestCase): workload = task.subtasks[0]["workloads"][0] self.assertEqual( {"description": "descr", - "action": {"hook_action": {"k1": "v1"}}, - "trigger": {"hook_trigger": {"k2": "v2"}}}, - workload["hooks"][0]["config"]) + "action": ("hook_action", {"k1": "v1"}), + "trigger": ("hook_trigger", {"k2": "v2"})}, + workload["hooks"][0]) diff --git a/tests/unit/task/test_hook.py b/tests/unit/task/test_hook.py index 314f80dcff..62171a80e2 100644 --- a/tests/unit/task/test_hook.py +++ b/tests/unit/task/test_hook.py @@ -55,18 +55,18 @@ class HookExecutorTestCase(test.TestCase): super(HookExecutorTestCase, self).setUp() self.conf = { "hooks": [ - {"config": { + { "description": "dummy_action", - "action": { - "dummy_hook": {"status": consts.HookStatus.SUCCESS} - }, - "trigger": { - "event": { + "action": ( + "dummy_hook", {"status": consts.HookStatus.SUCCESS} + ), + "trigger": ( + "event", { "unit": "iteration", "at": [1], } - } - }} + ) + } ] } self.task = mock.MagicMock() @@ -78,7 +78,7 @@ class HookExecutorTestCase(test.TestCase): hook_executor.on_event(event_type="iteration", value=1) self.assertEqual( - [{"config": self.conf["hooks"][0]["config"], + [{"config": self.conf["hooks"][0], "results": [{ "triggered_by": {"event_type": "iteration", "value": 1}, "started_at": fakes.FakeTimer().timestamp(), @@ -90,15 +90,16 @@ class HookExecutorTestCase(test.TestCase): @mock.patch("rally.task.hook.HookExecutor._timer_method") @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) def test_result_optional(self, mock_timer, mock__timer_method): - hook_args = list(self.conf["hooks"][0]["config"]["action"].values())[0] + hook_args = self.conf["hooks"][0]["action"][1] hook_args["error"] = ["Exception", "Description", "Traceback"] + hook_args["output"] = {"additive": None, "complete": None} hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor.on_event(event_type="iteration", value=1) self.assertEqual( - [{"config": self.conf["hooks"][0]["config"], + [{"config": self.conf["hooks"][0], "results": [{ "triggered_by": {"event_type": "iteration", "value": 1}, "started_at": fakes.FakeTimer().timestamp(), @@ -112,7 +113,7 @@ class HookExecutorTestCase(test.TestCase): def test_empty_result(self): hook_executor = hook.HookExecutor(self.conf, self.task) - self.assertEqual([{"config": self.conf["hooks"][0]["config"], + self.assertEqual([{"config": self.conf["hooks"][0], "results": [], "summary": {}}], hook_executor.results()) @@ -127,7 +128,7 @@ class HookExecutorTestCase(test.TestCase): hook_executor.on_event(event_type="iteration", value=1) self.assertEqual( - [{"config": self.conf["hooks"][0]["config"], + [{"config": self.conf["hooks"][0], "results": [{ "triggered_by": {"event_type": "iteration", "value": 1}, "error": {"etype": "Exception", @@ -140,15 +141,14 @@ class HookExecutorTestCase(test.TestCase): @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) def test_time_event(self, mock_timer): - trigger_args = list( - self.conf["hooks"][0]["config"]["trigger"].values())[0] + trigger_args = self.conf["hooks"][0]["trigger"][1] trigger_args["unit"] = "time" hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor.on_event(event_type="time", value=1) self.assertEqual( - [{"config": self.conf["hooks"][0]["config"], + [{"config": self.conf["hooks"][0], "results": [{ "triggered_by": {"event_type": "time", "value": 1}, "started_at": fakes.FakeTimer().timestamp(), @@ -159,8 +159,8 @@ class HookExecutorTestCase(test.TestCase): @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) def test_time_periodic(self, mock_timer): - self.conf["hooks"][0]["config"]["trigger"] = { - "periodic": {"unit": "time", "step": 2}} + self.conf["hooks"][0]["trigger"] = ("periodic", + {"unit": "time", "step": 2}) hook_executor = hook.HookExecutor(self.conf, self.task) for i in range(1, 7): @@ -168,7 +168,7 @@ class HookExecutorTestCase(test.TestCase): self.assertEqual( [{ - "config": self.conf["hooks"][0]["config"], + "config": self.conf["hooks"][0], "results":[ { "triggered_by": {"event_type": "time", "value": 2}, @@ -196,8 +196,7 @@ class HookExecutorTestCase(test.TestCase): @mock.patch("rally.common.utils.Stopwatch", autospec=True) @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) def test_timer_thread(self, mock_timer, mock_stopwatch): - trigger_args = list( - self.conf["hooks"][0]["config"]["trigger"].values())[0] + trigger_args = self.conf["hooks"][0]["trigger"][1] trigger_args["unit"] = "time" hook_executor = hook.HookExecutor(self.conf, self.task) @@ -212,7 +211,7 @@ class HookExecutorTestCase(test.TestCase): self.assertTrue(hook_executor._timer_stop_event.wait(1)) self.assertEqual( - [{"config": self.conf["hooks"][0]["config"], + [{"config": self.conf["hooks"][0], "results": [{ "triggered_by": {"event_type": "time", "value": 1}, "started_at": fakes.FakeTimer().timestamp(), @@ -316,8 +315,8 @@ class TriggerTestCase(test.TestCase): # test_on_event and test_get_results in one test. right_values = [5, 7, 12, 13] - cfg = {"trigger": {self.DummyTrigger.get_name(): right_values}, - "action": {"fake": {}}} + cfg = {"trigger": (self.DummyTrigger.get_name(), right_values), + "action": ("fake", {})} task = mock.MagicMock() hook_cls = mock.MagicMock(__name__="fake") dummy_trigger = self.DummyTrigger(cfg, task, hook_cls) diff --git a/tests/unit/task/test_trigger.py b/tests/unit/task/test_trigger.py index 137eda5b7f..bf7086a224 100644 --- a/tests/unit/task/test_trigger.py +++ b/tests/unit/task/test_trigger.py @@ -36,7 +36,7 @@ class TriggerTestCase(test.TestCase): @mock.patch("rally.task.trigger.LOG.warning") def test_warning(self, mock_log_warning): - self.DummyTrigger({"trigger": {self.id(): {}}}, None, None) + self.DummyTrigger({"trigger": (self.id(), {})}, None, None) mock_log_warning.assert_called_once_with( "Please contact Rally plugin maintainer. The plugin '%s'" @@ -52,8 +52,8 @@ class TriggerTestCase(test.TestCase): descr = "descr" trigger_obj = self.DummyTrigger({ - "trigger": {trigger_name: trigger_cfg}, - "action": {action_name: action_cfg}, + "trigger": (trigger_name, trigger_cfg), + "action": (action_name, action_cfg), "description": descr}, None, None) self.assertEqual( diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index c316810a7b..2088e7c0f6 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -477,6 +477,7 @@ class TaskAPITestCase(test.TestCase): "start_time": 23.77, "position": 77, "runner": "runner-config", + "runner_type": "runner-type", "context": "ctx-config", "hooks": "hooks-config", "sla": "sla-config", @@ -507,6 +508,7 @@ class TaskAPITestCase(test.TestCase): sub_task.add_workload.assert_called_once_with( name=workload["name"], description=workload["description"], position=workload["position"], runner=workload["runner"], + runner_type=workload["runner_type"], context=workload["context"], sla=workload["sla"], hooks=workload["hooks"], args=workload["args"] ) @@ -538,6 +540,7 @@ class TaskAPITestCase(test.TestCase): "start_time": 23.77, "position": 77, "runner": "runner-config", + "runner_type": "runner-type", "context": "ctx-config", "hooks": "hooks-config", "sla": "sla-config", @@ -569,6 +572,7 @@ class TaskAPITestCase(test.TestCase): sub_task.add_workload.assert_called_once_with( name=workload["name"], description=workload["description"], position=workload["position"], runner=workload["runner"], + runner_type=workload["runner_type"], context=workload["context"], sla=workload["sla"], hooks=workload["hooks"], args=workload["args"] )