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
This commit is contained in:
Andrey Kurilin
2017-09-14 15:57:44 +03:00
parent 79dcb93957
commit 7165f31726
27 changed files with 494 additions and 224 deletions

View File

@@ -523,6 +523,7 @@ class _Task(APIGroup):
workload_obj = subtask_obj.add_workload( workload_obj = subtask_obj.add_workload(
name=workload["name"], description=workload["description"], name=workload["name"], description=workload["description"],
position=workload["position"], runner=workload["runner"], position=workload["position"], runner=workload["runner"],
runner_type=workload["runner_type"],
context=workload["context"], hooks=workload["hooks"], context=workload["context"], hooks=workload["hooks"],
sla=workload["sla"], args=workload["args"]) sla=workload["sla"], args=workload["args"])
chunk_size = CONF.raw_result_chunk_size chunk_size = CONF.raw_result_chunk_size

View File

@@ -578,8 +578,17 @@ class TaskCommands(object):
itr["atomic_actions"]).items() 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": { "key": {
"name": w["name"], "name": w["name"],
"description": w["description"], "description": w["description"],
@@ -589,7 +598,7 @@ class TaskCommands(object):
"runner": w["runner"], "runner": w["runner"],
"context": w["context"], "context": w["context"],
"sla": w["sla"], "sla": w["sla"],
"hooks": [r["config"] for r in w["hooks"]], "hooks": hooks,
} }
}, },
"result": w["data"], "result": w["data"],
@@ -597,9 +606,7 @@ class TaskCommands(object):
"hooks": w["hooks"], "hooks": w["hooks"],
"load_duration": w["load_duration"], "load_duration": w["load_duration"],
"full_duration": w["full_duration"], "full_duration": w["full_duration"],
"created_at": w["created_at"]} "created_at": w["created_at"]})
for w in itertools.chain(
*[s["workloads"] for s in task["subtasks"]])]
print(json.dumps(results, sort_keys=False, indent=4)) 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 += dt.timedelta(seconds=result["full_duration"])
updated_at = updated_at.strftime(consts.TimeFormat.ISO8601) updated_at = updated_at.strftime(consts.TimeFormat.ISO8601)
pass_sla = all(s.get("success") for s in result["sla"]) 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", workload = {"uuid": "n/a",
"name": result["key"]["name"], "name": result["key"]["name"],
"position": result["key"]["pos"], "position": result["key"]["pos"],
@@ -757,6 +771,7 @@ class TaskCommands(object):
"created_at": result["created_at"], "created_at": result["created_at"],
"updated_at": updated_at, "updated_at": updated_at,
"args": result["key"]["kw"]["args"], "args": result["key"]["kw"]["args"],
"runner_type": runner_type,
"runner": result["key"]["kw"]["runner"], "runner": result["key"]["kw"]["runner"],
"hooks": result["hooks"], "hooks": result["hooks"],
"sla": result["key"]["kw"]["sla"], "sla": result["key"]["kw"]["sla"],

View File

@@ -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()

View File

@@ -14,7 +14,6 @@
# under the License. # under the License.
import collections import collections
import copy
import datetime as dt import datetime as dt
import uuid import uuid
@@ -285,24 +284,27 @@ class Subtask(object):
def update_status(self, status): def update_status(self, status):
self._update({"status": status}) self._update({"status": status})
def add_workload(self, name, description, position, runner, context, hooks, def add_workload(self, name, description, position, runner, runner_type,
sla, args): 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"], return Workload(task_uuid=self.subtask["task_uuid"],
subtask_uuid=self.subtask["uuid"], name=name, subtask_uuid=self.subtask["uuid"], name=name,
description=description, position=position, description=description, position=position,
runner=runner, hooks=hooks, context=context, sla=sla, runner=runner, runner_type=runner_type, hooks=hooks,
args=args) context=context, sla=sla, args=args)
class Workload(object): class Workload(object):
"""Represents a workload object.""" """Represents a workload object."""
def __init__(self, task_uuid, subtask_uuid, name, description, position, 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( self.workload = db.workload_create(
task_uuid=task_uuid, subtask_uuid=subtask_uuid, name=name, task_uuid=task_uuid, subtask_uuid=subtask_uuid, name=name,
description=description, position=position, runner=runner, 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) args=args)
def __getitem__(self, key): def __getitem__(self, key):
@@ -326,6 +328,11 @@ class Workload(object):
@classmethod @classmethod
def to_task(cls, workload): 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 = collections.OrderedDict()
task["version"] = 2 task["version"] = 2
task["title"] = "A cropped version of a bigger task." task["title"] = "A cropped version of a bigger task."
@@ -337,9 +344,22 @@ class Workload(object):
subtask["title"] = workload["name"] subtask["title"] = workload["name"]
subtask["description"] = workload["description"] subtask["description"] = workload["description"]
subtask["scenario"] = {workload["name"]: workload["args"]} subtask["scenario"] = {workload["name"]: workload["args"]}
# 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"] subtask["contexts"] = workload["context"]
runner = copy.copy(workload["runner"]) else:
subtask["runner"] = {runner.pop("type"): runner} subtask["contexts"] = workload["contexts"]
subtask["hooks"] = [h["config"] for h in workload["hooks"]] 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"] subtask["sla"] = workload["sla"]
return task return task

View File

@@ -149,10 +149,6 @@ class ConstantScenarioRunner(runner.ScenarioRunner):
"type": "object", "type": "object",
"$schema": consts.JSON_SCHEMA, "$schema": consts.JSON_SCHEMA,
"properties": { "properties": {
"type": {
"type": "string",
"description": "Type of Runner."
},
"concurrency": { "concurrency": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
@@ -174,7 +170,6 @@ class ConstantScenarioRunner(runner.ScenarioRunner):
" from." " from."
} }
}, },
"required": ["type"],
"additionalProperties": False "additionalProperties": False
} }
@@ -263,10 +258,6 @@ class ConstantForDurationScenarioRunner(runner.ScenarioRunner):
"type": "object", "type": "object",
"$schema": consts.JSON_SCHEMA, "$schema": consts.JSON_SCHEMA,
"properties": { "properties": {
"type": {
"type": "string",
"description": "Type of Runner."
},
"concurrency": { "concurrency": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
@@ -284,7 +275,7 @@ class ConstantForDurationScenarioRunner(runner.ScenarioRunner):
"description": "Operation's timeout." "description": "Operation's timeout."
} }
}, },
"required": ["type", "duration"], "required": ["duration"],
"additionalProperties": False "additionalProperties": False
} }

View File

@@ -150,9 +150,6 @@ class RPSScenarioRunner(runner.ScenarioRunner):
"type": "object", "type": "object",
"$schema": consts.JSON_SCHEMA, "$schema": consts.JSON_SCHEMA,
"properties": { "properties": {
"type": {
"type": "string"
},
"times": { "times": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -205,7 +202,7 @@ class RPSScenarioRunner(runner.ScenarioRunner):
"minimum": 1 "minimum": 1
} }
}, },
"required": ["type", "times", "rps"], "required": ["times", "rps"],
"additionalProperties": False "additionalProperties": False
} }

View File

@@ -35,9 +35,6 @@ class SerialScenarioRunner(runner.ScenarioRunner):
"type": "object", "type": "object",
"$schema": consts.JSON_SCHEMA, "$schema": consts.JSON_SCHEMA,
"properties": { "properties": {
"type": {
"type": "string"
},
"times": { "times": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -270,7 +270,7 @@ class RequiredContextsValidator(validation.Validator):
def validate(self, context, config, plugin_cls, plugin_cfg): def validate(self, context, config, plugin_cls, plugin_cfg):
missing_contexts = [] missing_contexts = []
input_context = config.get("context", {}) input_context = config.get("contexts", {})
for name in self.contexts: for name in self.contexts:
if isinstance(name, tuple): if isinstance(name, tuple):
@@ -304,7 +304,7 @@ class RequiredParamOrContextValidator(validation.Validator):
msg = ("You should specify either scenario argument %s or" msg = ("You should specify either scenario argument %s or"
" use context %s." % (self.param_name, self.ctx_name)) " 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 return
if self.param_name in config.get("args", {}): if self.param_name in config.get("args", {}):
return return

View File

@@ -55,7 +55,7 @@ class ImageExistsValidator(validation.Validator):
if not image_args and self.nullable: if not image_args and self.nullable:
return return
image_context = config.get("context", {}).get("images", {}) image_context = config.get("contexts", {}).get("images", {})
image_ctx_name = image_context.get("image_name") image_ctx_name = image_context.get("image_name")
if not image_args: if not image_args:
@@ -162,11 +162,11 @@ class FlavorExistsValidator(validation.Validator):
self.param_name = param_name self.param_name = param_name
def _get_flavor_from_context(self, config, flavor_value): 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") self.fail("No flavors context")
flavors = [flavors_ctx.FlavorConfig(**f) 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, resource = types.obj_from_name(resource_config=flavor_value,
resources=flavors, typename="flavor") resources=flavors, typename="flavor")
flavor = flavors_ctx.FlavorConfig(**resource) flavor = flavors_ctx.FlavorConfig(**resource)
@@ -223,7 +223,7 @@ class ImageValidOnFlavorValidator(FlavorExistsValidator):
self.validate_disk = validate_disk self.validate_disk = validate_disk
def _get_validated_image(self, config, clients, param_name): 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_args = config.get("args", {}).get(param_name)
image_ctx_name = image_context.get("image_name") image_ctx_name = image_context.get("image_name")
@@ -387,7 +387,7 @@ class RequiredServicesValidator(validation.Validator):
for service in self.services: for service in self.services:
# NOTE(andreykurilin): validator should ignore services configured # NOTE(andreykurilin): validator should ignore services configured
# via context(a proper validation should be in context) # 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, {}) "api_versions@openstack", {}).get(service, {})
if (service not in available_services and if (service not in available_services and
@@ -507,7 +507,7 @@ class RequiredAPIVersionsValidator(validation.Validator):
"version": versions_str, "version": versions_str,
"found_version": "3"}) "found_version": "3"})
else: else:
av_ctx = config.get("context", {}).get( av_ctx = config.get("contexts", {}).get(
"api_versions@openstack", {}) "api_versions@openstack", {})
default_version = getattr(clients, default_version = getattr(clients,
self.component).choose_version() self.component).choose_version()
@@ -540,10 +540,10 @@ class VolumeTypeExistsValidator(validation.Validator):
def validate(self, context, config, plugin_cls, plugin_cfg): def validate(self, context, config, plugin_cls, plugin_cfg):
volume_type = config.get("args", {}).get(self.param, False) volume_type = config.get("args", {}).get(self.param, False)
if not volume_type and self.nullable: if not volume_type:
if self.nullable:
return return
if not volume_type:
self.fail("The parameter '%s' is required and should not be empty." self.fail("The parameter '%s' is required and should not be empty."
% self.param) % self.param)
@@ -551,7 +551,7 @@ class VolumeTypeExistsValidator(validation.Validator):
clients = user["credential"].clients() clients = user["credential"].clients()
vt_names = [vt.name for vt in vt_names = [vt.name for vt in
clients.cinder().volume_types.list()] clients.cinder().volume_types.list()]
ctx = config.get("context", {}).get("volume_types", []) ctx = config.get("contexts", {}).get("volume_types", [])
vt_names += ctx vt_names += ctx
if volume_type not in vt_names: if volume_type not in vt_names:
self.fail("Specified volume type %s not found for user %s." self.fail("Specified volume type %s not found for user %s."

View File

@@ -276,15 +276,15 @@ class TaskEngine(object):
plugin_cfg=None, plugin_cfg=None,
vtype=vtype)) vtype=vtype))
if workload["runner"]: if workload["runner_type"]:
results.extend(runner.ScenarioRunner.validate( results.extend(runner.ScenarioRunner.validate(
name=workload["runner"]["type"], name=workload["runner_type"],
context=vcontext, context=vcontext,
config=None, config=None,
plugin_cfg=workload["runner"], plugin_cfg=workload["runner"],
vtype=vtype)) 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( results.extend(context.Context.validate(
name=context_name, name=context_name,
context=vcontext, context=vcontext,
@@ -310,8 +310,7 @@ class TaskEngine(object):
vtype=vtype)) vtype=vtype))
for hook_conf in workload["hooks"]: for hook_conf in workload["hooks"]:
action_name, action_cfg = list( action_name, action_cfg = hook_conf["action"]
hook_conf["config"]["action"].items())[0]
results.extend(hook.HookAction.validate( results.extend(hook.HookAction.validate(
name=action_name, name=action_name,
context=vcontext, context=vcontext,
@@ -319,8 +318,7 @@ class TaskEngine(object):
plugin_cfg=action_cfg, plugin_cfg=action_cfg,
vtype=vtype)) vtype=vtype))
trigger_name, trigger_cfg = list( trigger_name, trigger_cfg = hook_conf["trigger"]
hook_conf["config"]["trigger"].items())[0]
results.extend(hook.HookTrigger.validate( results.extend(hook.HookTrigger.validate(
name=trigger_name, name=trigger_name,
context=vcontext, context=vcontext,
@@ -461,8 +459,9 @@ class TaskEngine(object):
description=workload["description"], description=workload["description"],
position=workload["position"], position=workload["position"],
runner=workload["runner"], runner=workload["runner"],
runner_type=workload["runner_type"],
hooks=workload["hooks"], hooks=workload["hooks"],
context=workload["context"], context=workload["contexts"],
sla=workload["sla"], sla=workload["sla"],
args=workload["args"]) args=workload["args"])
workload["uuid"] = workload_obj["uuid"] workload["uuid"] = workload_obj["uuid"]
@@ -474,10 +473,10 @@ class TaskEngine(object):
% {"position": workload["position"], % {"position": workload["position"],
"cfg": json.dumps(workload_cfg, indent=3)}) "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"]) runner_obj = runner_cls(self.task, workload["runner"])
context_obj = self._prepare_context( context_obj = self._prepare_context(
workload["context"], workload["name"], workload_obj["uuid"]) workload["contexts"], workload["name"], workload_obj["uuid"])
try: try:
with ResultConsumer(workload, self.task, subtask_obj, workload_obj, with ResultConsumer(workload, self.task, subtask_obj, workload_obj,
runner_obj, self.abort_on_sla_failure): runner_obj, self.abort_on_sla_failure):
@@ -746,14 +745,15 @@ class TaskConfig(object):
# validation step # validation step
pass pass
wconf["context"] = wconf.pop("contexts", {}) wconf.setdefault("contexts", {})
if "runner" in wconf: if "runner" in wconf:
runner_type, runner_cfg = list(wconf["runner"].items())[0] runner = list(wconf["runner"].items())[0]
runner_cfg["type"] = runner_type wconf["runner_type"], wconf["runner"] = runner
wconf["runner"] = runner_cfg
else: else:
wconf["runner"] = {"serial": {}} wconf["runner_type"] = "serial"
wconf["runner"] = {}
wconf.setdefault("sla", {"failure_rate": {"max": 0}}) wconf.setdefault("sla", {"failure_rate": {"max": 0}})
hooks = wconf.get("hooks", []) hooks = wconf.get("hooks", [])
@@ -764,14 +764,17 @@ class TaskConfig(object):
"Check task format documentation for more " "Check task format documentation for more "
"details.") "details.")
trigger_cfg = hook_cfg["trigger"] trigger_cfg = hook_cfg["trigger"]
wconf["hooks"].append({"config": { wconf["hooks"].append({
"description": hook_cfg["description"], "description": hook_cfg["description"],
"action": {hook_cfg["name"]: hook_cfg["args"]}, "action": (hook_cfg["name"], hook_cfg["args"]),
"trigger": { "trigger": (
trigger_cfg["name"]: trigger_cfg["args"]}} trigger_cfg["name"], trigger_cfg["args"])})
})
else: 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) workloads.append(wconf)
sconf["workloads"] = workloads sconf["workloads"] = workloads
@@ -815,7 +818,7 @@ class TaskConfig(object):
for hook_cfg in hooks: for hook_cfg in hooks:
trigger_cfg = hook_cfg["trigger"] trigger_cfg = hook_cfg["trigger"]
subtask["hooks"].append( subtask["hooks"].append(
{"description": hook_cfg["description"], {"description": hook_cfg.get("description"),
"action": { "action": {
hook_cfg["name"]: hook_cfg["args"]}, hook_cfg["name"]: hook_cfg["args"]},
"trigger": { "trigger": {

View File

@@ -43,10 +43,9 @@ class HookExecutor(object):
self.task = task self.task = task
self.triggers = collections.defaultdict(list) self.triggers = collections.defaultdict(list)
for hook in config.get("hooks", []): for hook_cfg in config.get("hooks", []):
hook_cfg = hook["config"] action_name = hook_cfg["action"][0]
action_name = list(hook_cfg["action"].keys())[0] trigger_name = hook_cfg["trigger"][0]
trigger_name = list(hook_cfg["trigger"].keys())[0]
action_cls = HookAction.get(action_name) action_cls = HookAction.get(action_name)
trigger_obj = HookTrigger.get( trigger_obj = HookTrigger.get(
trigger_name)(hook_cfg, self.task, action_cls) 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): def __init__(self, hook_cfg, task, hook_cls):
self.hook_cfg = hook_cfg 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.task = task
self.hook_cls = hook_cls self.hook_cls = hook_cls
self._runs = [] 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" LOG.info("Hook action %s is triggered for Task %s by %s=%s"
% (self.hook_cls.get_name(), self.task["uuid"], % (self.hook_cls.get_name(), self.task["uuid"],
event_type, value)) 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, action = self.hook_cls(self.task, action_cfg,
{"event_type": event_type, "value": value}) {"event_type": event_type, "value": value})
action.run_async() action.run_async()

View File

@@ -14,7 +14,6 @@
# under the License. # under the License.
import collections import collections
import copy
import datetime as dt import datetime as dt
import hashlib import hashlib
import itertools import itertools
@@ -33,7 +32,7 @@ def _process_hooks(hooks):
"""Prepare hooks data for report.""" """Prepare hooks data for report."""
hooks_ctx = [] hooks_ctx = []
for hook in hooks: 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", ""), "desc": hook["config"].get("description", ""),
"additive": [], "complete": []} "additive": [], "complete": []}
@@ -138,7 +137,7 @@ def _process_workload(workload, workload_cfg, pos):
"met": method, "met": method,
"pos": str(pos), "pos": str(pos),
"name": method + (pos and " [%d]" % (pos + 1) or ""), "name": method + (pos and " [%d]" % (pos + 1) or ""),
"runner": workload["runner"]["type"], "runner": workload["runner_type"],
"config": json.dumps(workload_cfg, indent=2), "config": json.dumps(workload_cfg, indent=2),
"hooks": _process_hooks(workload["hooks"]), "hooks": _process_hooks(workload["hooks"]),
"description": workload.get("description", ""), "description": workload.get("description", ""),
@@ -212,8 +211,8 @@ def _make_source(tasks):
workload_cfg["scenario"] = {workload["name"]: workload["args"]} workload_cfg["scenario"] = {workload["name"]: workload["args"]}
workload_cfg["description"] = workload["description"] workload_cfg["description"] = workload["description"]
workload_cfg["contexts"] = workload["context"] workload_cfg["contexts"] = workload["context"]
runner = copy.copy(workload["runner"]) workload_cfg["runner"] = {
workload_cfg["runner"] = {runner.pop("type"): runner} workload["runner_type"]: workload["runner"]}
workload_cfg["hooks"] = [h["config"] workload_cfg["hooks"] = [h["config"]
for h in workload["hooks"]] for h in workload["hooks"]]
workload_cfg["sla"] = workload["sla"] workload_cfg["sla"] = workload["sla"]

View File

@@ -31,8 +31,8 @@ class Trigger(hook.HookTrigger):
@property @property
def context(self): def context(self):
action_name, action_cfg = list(self.hook_cfg["action"].items())[0] action_name, action_cfg = self.hook_cfg["action"]
trigger_name, trigger_cfg = list(self.hook_cfg["trigger"].items())[0] trigger_name, trigger_cfg = self.hook_cfg["trigger"]
return {"description": self.hook_cfg["description"], return {"description": self.hook_cfg["description"],
"name": action_name, "name": action_name,
"args": action_cfg, "args": action_cfg,

View File

@@ -553,7 +553,8 @@ class TaskCommandsTestCase(test.TestCase):
"name": "Foo.bar", "description": "descr", "name": "Foo.bar", "description": "descr",
"position": 2, "position": 2,
"args": {"key1": "value1"}, "args": {"key1": "value1"},
"runner": {"type": "rruunneerr"}, "runner_type": "rruunneerr",
"runner": {"arg1": "args2"},
"hooks": [], "hooks": [],
"sla": {"failure_rate": {"max": 0}}, "sla": {"failure_rate": {"max": 0}},
"sla_results": {"sla": [{"success": True}]}, "sla_results": {"sla": [{"success": True}]},
@@ -565,10 +566,16 @@ class TaskCommandsTestCase(test.TestCase):
task_id = "foo_task_id" task_id = "foo_task_id"
task_obj = self._make_task(data=[{"atomic_actions": {"foo": 1.1}}]) 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"], result = map(lambda x: {"key": {"kw": {"sla": x["sla"],
"args": x["args"], "args": x["args"],
"context": x["context"], "context": x["context"],
"runner": x["runner"], "runner": fix_r(x),
"hooks": x["hooks"]}, "hooks": x["hooks"]},
"pos": x["position"], "pos": x["position"],
"name": x["name"], "name": x["name"],
@@ -1089,7 +1096,8 @@ class TaskCommandsTestCase(test.TestCase):
"subtasks": [{"workloads": [{ "subtasks": [{"workloads": [{
"name": "fake_name", "name": "fake_name",
"position": "fake_pos", "position": "fake_pos",
"args": {}, "runner": {}, "context": {}, "sla": {}, "args": {}, "runner_type": "foo",
"runner": {}, "context": {}, "sla": {},
"hooks": {}, "hooks": {},
"load_duration": 3.2, "load_duration": 3.2,
"full_duration": 3.5, "full_duration": 3.5,
@@ -1181,8 +1189,12 @@ class TaskCommandsTestCase(test.TestCase):
"name": "Foo.bar", "description": "descr", "name": "Foo.bar", "description": "descr",
"position": 2, "position": 2,
"args": {"key1": "value1"}, "args": {"key1": "value1"},
"runner": {"type": "rruunneerr"}, "runner_type": "constant",
"hooks": [{"config": {"type": "hookk"}}], "runner": {"time": 3},
"hooks": [{"config": {
"description": "descr",
"action": ("foo", {"arg1": "v1"}),
"trigger": ("t", {"a2", "v2"})}}],
"pass_sla": True, "pass_sla": True,
"sla": {"failure_rate": {"max": 0}}, "sla": {"failure_rate": {"max": 0}},
"sla_results": {"sla": [{"success": True}]}, "sla_results": {"sla": [{"success": True}]},
@@ -1196,14 +1208,25 @@ class TaskCommandsTestCase(test.TestCase):
} }
results = [ results = [
{"hooks": workload["hooks"], {"hooks": [{"config": {
"name": "foo",
"args": {"arg1": "v1"},
"description": "descr",
"trigger": {"name": "t",
"args": {"a2", "v2"}}}}],
"key": {"name": workload["name"], "key": {"name": workload["name"],
"description": workload["description"], "description": workload["description"],
"pos": workload["position"], "pos": workload["position"],
"kw": { "kw": {
"args": workload["args"], "args": workload["args"],
"runner": workload["runner"], "runner": {"type": "constant", "time": 3},
"hooks": [h["config"] for h in workload["hooks"]], "hooks": [{"name": "foo",
"args": {"arg1": "v1"},
"description": "descr",
"trigger": {
"name": "t",
"args": {"a2", "v2"}
}}],
"sla": workload["sla"], "sla": workload["sla"],
"context": workload["context"]}}, "context": workload["context"]}},
"sla": workload["sla_results"]["sla"], "sla": workload["sla_results"]["sla"],

View File

@@ -1965,3 +1965,135 @@ class MigrationWalkTestCase(rtest.DBTestCase,
conn.execute( conn.execute(
deployment_table.delete().where( deployment_table.delete().where(
deployment_table.c.uuid == deployment_uuid)) 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))

View File

@@ -311,21 +311,24 @@ class SubtaskTestCase(test.TestCase):
name = "w" name = "w"
description = "descr" description = "descr"
position = 0 position = 0
runner = {"type": "runner"} runner_type = "runner"
runner = {}
context = {"users": {}} context = {"users": {}}
sla = {"failure_rate": {"max": 0}} sla = {"failure_rate": {"max": 0}}
args = {"arg": "xxx"} args = {"arg": "xxx"}
hooks = [{"config": {"foo": "bar"}}] hooks = [{"foo": "bar"}]
workload = subtask.add_workload(name, description=description, workload = subtask.add_workload(
position=position, runner=runner, name, description=description, position=position,
context=context, sla=sla, args=args, runner_type=runner_type, runner=runner, context=context, sla=sla,
hooks=hooks) args=args, hooks=hooks)
mock_workload.assert_called_once_with( mock_workload.assert_called_once_with(
task_uuid=self.subtask["task_uuid"], task_uuid=self.subtask["task_uuid"],
subtask_uuid=self.subtask["uuid"], name=name, subtask_uuid=self.subtask["uuid"], name=name,
description=description, position=position, runner=runner, description=description, position=position,
context=context, sla=sla, args=args, hooks=hooks) 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) self.assertIs(workload, mock_workload.return_value)
@@ -345,14 +348,16 @@ class WorkloadTestCase(test.TestCase):
name = "w" name = "w"
description = "descr" description = "descr"
position = 0 position = 0
runner = {"type": "constant"} runner_type = "constant"
runner = {"times": 3}
context = {"users": {}} context = {"users": {}}
sla = {"failure_rate": {"max": 0}} sla = {"failure_rate": {"max": 0}}
args = {"arg": "xxx"} args = {"arg": "xxx"}
hooks = [{"config": {"foo": "bar"}}] hooks = [{"config": {"foo": "bar"}}]
workload = objects.Workload("uuid1", "uuid2", name=name, workload = objects.Workload("uuid1", "uuid2", name=name,
description=description, position=position, description=description, position=position,
runner=runner, context=context, sla=sla, runner=runner, runner_type=runner_type,
context=context, sla=sla,
args=args, hooks=hooks) args=args, hooks=hooks)
mock_workload_create.assert_called_once_with( mock_workload_create.assert_called_once_with(
task_uuid="uuid1", subtask_uuid="uuid2", name=name, hooks=hooks, 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 mock_workload_create.return_value = self.workload
workload = objects.Workload("uuid1", "uuid2", name="w", workload = objects.Workload("uuid1", "uuid2", name="w",
description="descr", position=0, description="descr", position=0,
runner={"type": "foo"}, context=None, runner_type="foo", runner={}, context=None,
sla=None, args=None, hooks=[]) sla=None, args=None, hooks=[])
workload.add_workload_data(0, {"data": "foo"}) workload.add_workload_data(0, {"data": "foo"})
@@ -383,7 +388,8 @@ class WorkloadTestCase(test.TestCase):
name = "w" name = "w"
description = "descr" description = "descr"
position = 0 position = 0
runner = {"type": "constant"} runner_type = "constant"
runner = {"times": 3}
context = {"users": {}} context = {"users": {}}
sla = {"failure_rate": {"max": 0}} sla = {"failure_rate": {"max": 0}}
args = {"arg": "xxx"} args = {"arg": "xxx"}
@@ -394,8 +400,9 @@ class WorkloadTestCase(test.TestCase):
hooks = [] hooks = []
workload = objects.Workload("uuid1", "uuid2", name=name, workload = objects.Workload("uuid1", "uuid2", name=name,
description=description, position=position, description=description, position=position,
runner=runner, context=context, sla=sla, runner=runner, runner_type=runner_type,
args=args, hooks=hooks) context=context, sla=sla, args=args,
hooks=hooks)
workload.set_results(load_duration=load_duration, workload.set_results(load_duration=load_duration,
full_duration=full_duration, full_duration=full_duration,
@@ -417,11 +424,15 @@ class WorkloadTestCase(test.TestCase):
"name": "Foo.bar", "name": "Foo.bar",
"description": "Make something useful (or not).", "description": "Make something useful (or not).",
"position": 3, "position": 3,
"runner": {"type": "constant", "times": 3}, "runner_type": "constant",
"context": {"users": {}}, "runner": {"times": 3},
"contexts": {"users": {}},
"sla": {"failure_rate": {"max": 0}}, "sla": {"failure_rate": {"max": 0}},
"args": {"key1": "value1"}, "args": {"key1": "value1"},
"hooks": [{"config": {"hook1": "xxx"}}], "hooks": [{"config": {
"action": ["foo", {"arg1": "v1"}],
"trigger": ["bar", {"arg2": "v2"}]
}}],
"sla_results": {"sla": []}, "sla_results": {"sla": []},
"context_execution": {}, "context_execution": {},
"start_time": "2997.23.12", "start_time": "2997.23.12",
@@ -444,8 +455,10 @@ class WorkloadTestCase(test.TestCase):
("title", workload["name"]), ("title", workload["name"]),
("description", workload["description"]), ("description", workload["description"]),
("scenario", {workload["name"]: workload["args"]}), ("scenario", {workload["name"]: workload["args"]}),
("contexts", workload["context"]), ("contexts", workload["contexts"]),
("runner", {"constant": {"times": 3}}), ("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"])])])]) ("sla", workload["sla"])])])])
self.assertEqual(expected_task, objects.Workload.to_task(workload)) self.assertEqual(expected_task, objects.Workload.to_task(workload))

View File

@@ -28,8 +28,8 @@ class EventTriggerTestCase(test.TestCase):
super(EventTriggerTestCase, self).setUp() super(EventTriggerTestCase, self).setUp()
self.hook_cls = mock.MagicMock(__name__="name") self.hook_cls = mock.MagicMock(__name__="name")
self.trigger = event.EventTrigger( self.trigger = event.EventTrigger(
{"trigger": {"event": {"unit": "iteration", "at": [1, 4, 5]}}, {"trigger": ("event", {"unit": "iteration", "at": [1, 4, 5]}),
"action": {"foo": {}}}, "action": ("foo", {})},
mock.MagicMock(), self.hook_cls) mock.MagicMock(), self.hook_cls)
@ddt.data((dict(unit="time", at=[0, 3, 5]), True), @ddt.data((dict(unit="time", at=[0, 3, 5]), True),

View File

@@ -28,8 +28,8 @@ class PeriodicTriggerTestCase(test.TestCase):
super(PeriodicTriggerTestCase, self).setUp() super(PeriodicTriggerTestCase, self).setUp()
self.hook_cls = mock.MagicMock(__name__="name") self.hook_cls = mock.MagicMock(__name__="name")
self.trigger = periodic.PeriodicTrigger( self.trigger = periodic.PeriodicTrigger(
{"trigger": {"periodic": {"unit": "iteration", "step": 2}}, {"trigger": ("periodic", {"unit": "iteration", "step": 2}),
"action": {"foo": {}}}, "action": ("foo", {})},
mock.MagicMock(), self.hook_cls) mock.MagicMock(), self.hook_cls)
@ddt.data((dict(unit="time", step=1), True), @ddt.data((dict(unit="time", step=1), True),
@@ -74,9 +74,9 @@ class PeriodicTriggerTestCase(test.TestCase):
@ddt.unpack @ddt.unpack
def test_on_event_start_end(self, value, should_call): def test_on_event_start_end(self, value, should_call):
trigger = periodic.PeriodicTrigger( trigger = periodic.PeriodicTrigger(
{"trigger": {"periodic": {"unit": "time", {"trigger": ("periodic", {"unit": "time",
"step": 3, "start": 2, "end": 9}}, "step": 3, "start": 2, "end": 9}),
"action": {"foo": {}}}, "action": ("foo", {})},
mock.MagicMock(), self.hook_cls) mock.MagicMock(), self.hook_cls)
trigger.on_event("time", value) trigger.on_event("time", value)
self.assertEqual(should_call, self.hook_cls.called) self.assertEqual(should_call, self.hook_cls.called)

View File

@@ -38,11 +38,13 @@ class ConstantScenarioRunnerTestCase(test.TestCase):
self.args = {"a": 1} self.args = {"a": 1}
self.task = mock.MagicMock() self.task = mock.MagicMock()
@ddt.data(({"times": 4, "concurrency": 2, @ddt.data(({"times": 4,
"timeout": 2, "type": "constant", "concurrency": 2,
"timeout": 2,
"max_cpu_count": 2}, True), "max_cpu_count": 2}, True),
({"times": 4, "concurrency": 5, ({"times": 4,
"timeout": 2, "type": "constant", "concurrency": 5,
"timeout": 2,
"max_cpu_count": 2}, False), "max_cpu_count": 2}, False),
({"foo": "bar"}, False)) ({"foo": "bar"}, False))
@ddt.unpack @ddt.unpack
@@ -267,8 +269,9 @@ class ConstantForDurationScenarioRunnerTestCase(test.TestCase):
self.context["iteration"] = 14 self.context["iteration"] = 14
self.args = {"a": 1} self.args = {"a": 1}
@ddt.data(({"duration": 0, "concurrency": 2, @ddt.data(({"duration": 0,
"timeout": 2, "type": "constant_for_duration"}, True), "concurrency": 2,
"timeout": 2}, True),
({"foo": "bar"}, False)) ({"foo": "bar"}, False))
@ddt.unpack @ddt.unpack
def test_validate(self, config, valid): def test_validate(self, config, valid):

View File

@@ -36,7 +36,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
@ddt.data( @ddt.data(
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 1, "start": 1,
"end": 3, "end": 3,
@@ -47,7 +46,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 1, "start": 1,
"end": 10, "end": 10,
@@ -58,7 +56,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 1, "start": 1,
"end": 2, "end": 2,
@@ -69,7 +66,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 2, "start": 2,
"end": 1, "end": 1,
@@ -81,7 +77,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 2, "start": 2,
"end": 1, "end": 1,
@@ -93,7 +88,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"times": 1, "times": 1,
"rps": 100, "rps": 100,
"max_concurrency": 50, "max_concurrency": 50,
@@ -103,14 +97,12 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": 0.000001 "rps": 0.000001
}, },
"valid": False "valid": False
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": { "rps": {
"start": 1, "start": 1,
"end": 10, "end": 10,
@@ -121,7 +113,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": 0, "rps": 0,
"times": 55 "times": 55
}, },
@@ -129,7 +120,6 @@ class RPSScenarioRunnerTestCase(test.TestCase):
}, },
{ {
"config": { "config": {
"type": "rps",
"rps": 2, "rps": 2,
"times": 55, "times": 55,
"foo": "bar" "foo": "bar"

View File

@@ -247,8 +247,8 @@ class RequiredContextsValidatorTestCase(test.TestCase):
"users": [mock.MagicMock()], }) "users": [mock.MagicMock()], })
@ddt.data( @ddt.data(
{"config": {"context": {"c1": 1, "c2": 2, "c3": 3}}}, {"config": {"contexts": {"c1": 1, "c2": 2, "c3": 3}}},
{"config": {"context": {"c1": 1, "c2": 2, "c3": 3, "a": 1}}} {"config": {"contexts": {"c1": 1, "c2": 2, "c3": 3, "a": 1}}}
) )
@ddt.unpack @ddt.unpack
def test_validate(self, config): def test_validate(self, config):
@@ -261,15 +261,18 @@ class RequiredContextsValidatorTestCase(test.TestCase):
contexts=("c1", "c2", "c3")) contexts=("c1", "c2", "c3"))
e = self.assertRaises( e = self.assertRaises(
validation.ValidationError, validation.ValidationError,
validator.validate, self.credentials, {"context": {"a": 1}}, validator.validate, self.credentials, {"contexts": {"a": 1}},
None, None) None, None)
self.assertEqual( self.assertEqual(
"The following context(s) are required but missing from " "The following context(s) are required but missing from "
"the input task file: c1, c2, c3", e.message) "the input task file: c1, c2, c3", e.message)
@ddt.data( @ddt.data(
{"config": {"context": {"c1": 1, "c2": 2, "c3": 3, "b1": 1, "a1": 1}}}, {"config": {
{"config": {"context": {"c1": 1, "c2": 2, "c3": 3, "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}}}, "b1": 1, "b2": 2, "a1": 1}}},
) )
@ddt.unpack @ddt.unpack
@@ -284,7 +287,7 @@ class RequiredContextsValidatorTestCase(test.TestCase):
e = self.assertRaises( e = self.assertRaises(
validation.ValidationError, validation.ValidationError,
validator.validate, self.credentials, validator.validate, self.credentials,
{"context": {"c1": 1, "c2": 2}}, None, None) {"contexts": {"c1": 1, "c2": 2}}, None, None)
self.assertEqual( self.assertEqual(
"The following context(s) are required but missing " "The following context(s) are required but missing "
"from the input task file: 'a1 or a2', 'b1 or b2'", e.message) "from the input task file: 'a1 or a2', 'b1 or b2'", e.message)
@@ -302,20 +305,20 @@ class RequiredParamOrContextValidatorTestCase(test.TestCase):
@ddt.data( @ddt.data(
{"config": {"args": {"image": {"name": ""}}, {"config": {"args": {"image": {"name": ""}},
"context": {"custom_image": {"name": "fake_image"}}}}, "contexts": {"custom_image": {"name": "fake_image"}}}},
{"config": {"context": {"custom_image": {"name": "fake_image"}}}}, {"config": {"contexts": {"custom_image": {"name": "fake_image"}}}},
{"config": {"args": {"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": "fake_image"}}}},
{"config": {"args": {"image": {"name": ""}}, {"config": {"args": {"image": {"name": ""}},
"context": {"custom_image": {"name": ""}}}} "contexts": {"custom_image": {"name": ""}}}}
) )
@ddt.unpack @ddt.unpack
def test_validate(self, config): def test_validate(self, config):
self.validator.validate(self.credentials, config, None, None) self.validator.validate(self.credentials, config, None, None)
@ddt.data( @ddt.data(
{"config": {"args": {}, "context": {}}, {"config": {"args": {}, "contexts": {}},
"err_msg": "You should specify either scenario argument image or " "err_msg": "You should specify either scenario argument image or "
"use context custom_image."}, "use context custom_image."},
{"config": {}, {"config": {},

View File

@@ -85,20 +85,19 @@ class ImageExistsValidatorTestCase(test.TestCase):
self.assertIsNone(result) self.assertIsNone(result)
def test_validator_image_from_context(self): def test_validator_image_from_context(self):
config = {"args": { config = {
"image": {"regex": r"^foo$"}}, "context": { "args": {"image": {"regex": r"^foo$"}},
"images": { "contexts": {"images": {"image_name": "foo"}}}
"image_name": "foo"}}}
self.validator.validate(self.context, config, None, None) self.validator.validate(self.context, config, None, None)
@mock.patch("%s.openstack_types.GlanceImage.transform" % PATH, @mock.patch("%s.openstack_types.GlanceImage.transform" % PATH,
return_value="image_id") return_value="image_id")
def test_validator_image_not_in_context(self, mock_glance_image_transform): def test_validator_image_not_in_context(self, mock_glance_image_transform):
config = {"args": { config = {
"image": "fake_image"}, "context": { "args": {"image": "fake_image"},
"images": { "contexts": {
"fake_image_name": "foo"}}} "images": {"fake_image_name": "foo"}}}
clients = self.context[ clients = self.context[
"users"][0]["credential"].clients.return_value "users"][0]["credential"].clients.return_value
@@ -266,7 +265,7 @@ class FlavorExistsValidatorTestCase(test.TestCase):
def test__get_flavor_from_context(self, mock_flavor_config, def test__get_flavor_from_context(self, mock_flavor_config,
mock_obj_from_name): mock_obj_from_name):
config = { config = {
"context": {"images": {"fake_parameter_name": "foo_image"}}} "contexts": {"images": {"fake_parameter_name": "foo_image"}}}
e = self.assertRaises( e = self.assertRaises(
validators.validation.ValidationError, validators.validation.ValidationError,
@@ -274,7 +273,7 @@ class FlavorExistsValidatorTestCase(test.TestCase):
config, "foo_flavor") config, "foo_flavor")
self.assertEqual("No flavors context", e.message) self.assertEqual("No flavors context", e.message)
config = {"context": {"images": {"fake_parameter_name": "foo_image"}, config = {"contexts": {"images": {"fake_parameter_name": "foo_image"},
"flavors": [{"flavor1": "fake_flavor1"}]}} "flavors": [{"flavor1": "fake_flavor1"}]}}
result = self.validator._get_flavor_from_context(config, "foo_flavor") result = self.validator._get_flavor_from_context(config, "foo_flavor")
self.assertEqual("<context flavor: %s>" % result.name, result.id) self.assertEqual("<context flavor: %s>" % result.name, result.id)
@@ -457,11 +456,12 @@ class ImageValidOnFlavorValidatorTestCase(test.TestCase):
"min_disk": 0 "min_disk": 0
} }
# Get image name from context # Get image name from context
result = self.validator._get_validated_image({"args": { result = self.validator._get_validated_image({
"image": {"regex": r"^foo$"}}, "context": { "args": {
"images": { "image": {"regex": r"^foo$"}},
"image_name": "foo"} "contexts": {
}}, mock.Mock(), "image") "images": {"image_name": "foo"}}},
mock.Mock(), "image")
self.assertEqual(image, result) self.assertEqual(image, result)
clients = mock.Mock() clients = mock.Mock()
@@ -819,7 +819,7 @@ class RequiredAPIVersionsValidatorTestCase(test.TestCase):
clients = self.context["users"][0]["credential"].clients() clients = self.context["users"][0]["credential"].clients()
clients.nova.choose_version.return_value = nova clients.nova.choose_version.return_value = nova
config = {"context": {"api_versions@openstack": {}}} config = {"contexts": {"api_versions@openstack": {}}}
if err_msg: if err_msg:
e = self.assertRaises( e = self.assertRaises(
@@ -839,7 +839,7 @@ class RequiredAPIVersionsValidatorTestCase(test.TestCase):
[version]) [version])
config = { config = {
"context": {"api_versions@openstack": {"nova": {"version": 2}}}} "contexts": {"api_versions@openstack": {"nova": {"version": 2}}}}
if err_msg: if err_msg:
e = self.assertRaises( e = self.assertRaises(
@@ -890,7 +890,7 @@ class VolumeTypeExistsValidatorTestCase(test.TestCase):
clients = self.context["users"][0]["credential"].clients() clients = self.context["users"][0]["credential"].clients()
clients.cinder().volume_types.list.return_value = [] clients.cinder().volume_types.list.return_value = []
ctx = {"args": {"volume_type": "fake_type"}, 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) result = self.validator.validate(self.context, ctx, None, None)
self.assertIsNone(result) self.assertIsNone(result)
@@ -899,7 +899,7 @@ class VolumeTypeExistsValidatorTestCase(test.TestCase):
clients = self.context["users"][0]["credential"].clients() clients = self.context["users"][0]["credential"].clients()
clients.cinder().volume_types.list.return_value = [] clients.cinder().volume_types.list.return_value = []
config = {"args": {"volume_type": "fake_type"}, config = {"args": {"volume_type": "fake_type"},
"context": {"volume_types": ["fake_type_2"]}} "contexts": {"volume_types": ["fake_type_2"]}}
e = self.assertRaises( e = self.assertRaises(
validators.validation.ValidationError, validators.validation.ValidationError,
self.validator.validate, self.context, config, None, None) self.validator.validate, self.context, config, None, None)

View File

@@ -51,7 +51,8 @@ class PlotTestCase(test.TestCase):
"sla_results": {"sla": {}}, "pass_sla": True, "sla_results": {"sla": {}}, "pass_sla": True,
"position": 0, "position": 0,
"name": "Foo.bar", "description": "Description!!", "name": "Foo.bar", "description": "Description!!",
"runner": {"type": "constant"}, "runner_type": "constant",
"runner": {},
"statistics": {"atomics": { "statistics": {"atomics": {
"foo_action": {"max_duration": 19, "min_duration": 10}}}, "foo_action": {"max_duration": 19, "min_duration": 10}}},
"full_duration": 40, "load_duration": 32, "full_duration": 40, "load_duration": 32,
@@ -90,8 +91,8 @@ class PlotTestCase(test.TestCase):
{ {
"config": { "config": {
"description": "Foo", "description": "Foo",
"action": {"sys_call": "foo cmd"}, "action": ("sys_call", "foo cmd"),
"trigger": {"event": {"at": [2, 5], "unit": "iteration"}}}, "trigger": ("event", {"at": [2, 5], "unit": "iteration"})},
"results": [ "results": [
{ {
"status": "success", "status": "success",
@@ -120,8 +121,8 @@ class PlotTestCase(test.TestCase):
"summary": {"success": 2}}, "summary": {"success": 2}},
{ {
"config": { "config": {
"action": {"sys_call": "bar cmd"}, "action": ("sys_call", "bar cmd"),
"trigger": {"event": {"at": [1, 2, 4], "unit": "time"}}}, "trigger": ("event", {"at": [1, 2, 4], "unit": "time"})},
"results": [ "results": [
{ {
"status": "success", "status": "success",
@@ -186,11 +187,14 @@ class PlotTestCase(test.TestCase):
"name": "Foo.bar_%s" % i, "name": "Foo.bar_%s" % i,
"description": "Make something useful (or not).", "description": "Make something useful (or not).",
"position": i, "position": i,
"runner": {"type": "constant", "times": 3}, "runner_type": "constant",
"context": {"users": {}}, "runner": {"times": 3},
"contexts": {"users": {}},
"sla": {"failure_rate": {"max": 0}}, "sla": {"failure_rate": {"max": 0}},
"args": {"key1": "value1"}, "args": {"key1": "value1"},
"hooks": [{"config": {"hook1": "xxx"}}], "hooks": [{"config": {
"action": ("foo", {}),
"trigger": ("xxx", {})}}],
"sla_results": {"sla": []}, "sla_results": {"sla": []},
"start_time": "2997.23.12", "start_time": "2997.23.12",
"load_duration": 42, "load_duration": 42,
@@ -225,7 +229,8 @@ class PlotTestCase(test.TestCase):
"args": {}, "args": {},
"context": {"key": "context"}, "context": {"key": "context"},
"sla": {"key": "sla"}, "sla": {"key": "sla"},
"runner": {"type": "crunner"}, "runner_type": "crunner",
"runner": {},
"hooks": []} "hooks": []}
]} ]}
]}] ]}]
@@ -262,7 +267,8 @@ class PlotTestCase(test.TestCase):
"args": {}, "args": {},
"context": {"key": "context"}, "context": {"key": "context"},
"sla": {"key": "sla"}, "sla": {"key": "sla"},
"runner": {"type": "crunner"}, "runner_type": "crunner",
"runner": {},
"hooks": []} "hooks": []}
]}, ]},
{"title": "subtask title3", {"title": "subtask title3",
@@ -274,7 +280,8 @@ class PlotTestCase(test.TestCase):
"args": {}, "args": {},
"context": {"key": "context"}, "context": {"key": "context"},
"sla": {"key": "sla"}, "sla": {"key": "sla"},
"runner": {"type": "crunner"}, "runner_type": "crunner",
"runner": {},
"hooks": []} "hooks": []}
]} ]}
]}) ]})

View File

@@ -37,15 +37,16 @@ class MyException(exceptions.RallyException):
class TaskEngineTestCase(test.TestCase): class TaskEngineTestCase(test.TestCase):
@staticmethod @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): sla=None, runner=None, hooks=None, position=0):
return {"uuid": "foo", return {"uuid": "foo",
"name": name, "name": name,
"position": position, "position": position,
"description": description, "description": description,
"args": args, "args": args,
"context": context or {}, "contexts": contexts or {},
"runner": runner or {"type": "serial"}, "runner_type": runner[0] if runner else "serial",
"runner": runner[1] if runner else {},
"sla": sla or {}, "sla": sla or {},
"hooks": hooks or []} "hooks": hooks or []}
@@ -153,12 +154,13 @@ class TaskEngineTestCase(test.TestCase):
scenario_name = "Foo.bar" scenario_name = "Foo.bar"
runner_type = "MegaRunner" runner_type = "MegaRunner"
hook_conf = {"action": {"c": "c_args"}, hook_conf = {"action": ("c", "c_args"),
"trigger": {"d": "d_args"}} "trigger": ("d", "d_args")}
workload = {"name": scenario_name, workload = {"name": scenario_name,
"runner": {"type": runner_type}, "runner_type": runner_type,
"context": {"a": "a_conf"}, "runner": {},
"hooks": [{"config": hook_conf}], "contexts": {"a": "a_conf"},
"hooks": [hook_conf],
"sla": {"foo_sla": "sla_conf"}, "sla": {"foo_sla": "sla_conf"},
"position": 2} "position": 2}
@@ -169,7 +171,7 @@ class TaskEngineTestCase(test.TestCase):
mock_scenario_runner_validate.assert_called_once_with( mock_scenario_runner_validate.assert_called_once_with(
name=runner_type, context=None, config=None, name=runner_type, context=None, config=None,
plugin_cfg={"type": runner_type}, vtype=None) plugin_cfg={}, vtype=None)
self.assertEqual([mock.call(name="a", self.assertEqual([mock.call(name="a",
context=None, context=None,
config=None, config=None,
@@ -204,7 +206,7 @@ class TaskEngineTestCase(test.TestCase):
"There is no such runner"] "There is no such runner"]
scenario_cls = mock_scenario_get.return_value scenario_cls = mock_scenario_get.return_value
scenario_cls.get_default_context.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(), eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(),
mock.Mock()) mock.Mock())
@@ -230,7 +232,7 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance.subtasks = [{"workloads": [ mock_task_instance.subtasks = [{"workloads": [
self._make_workload(name="sca"), self._make_workload(name="sca"),
self._make_workload(name="sca", position=1, self._make_workload(name="sca", position=1,
context={"a": "a_conf"}) contexts={"a": "a_conf"})
]}] ]}]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(),
mock.Mock()) mock.Mock())
@@ -282,12 +284,12 @@ class TaskEngineTestCase(test.TestCase):
scenario_cls = mock_scenario_get.return_value scenario_cls = mock_scenario_get.return_value
scenario_cls.get_default_context.return_value = {} scenario_cls.get_default_context.return_value = {}
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
hook_conf = {"action": {"c": "c_args"}, hook_conf = {"action": ("c", "c_args"),
"trigger": {"d": "d_args"}} "trigger": ("d", "d_args")}
mock_task_instance.subtasks = [{"workloads": [ mock_task_instance.subtasks = [{"workloads": [
self._make_workload(name="sca"), self._make_workload(name="sca"),
self._make_workload(name="sca", position=1, self._make_workload(name="sca", position=1,
hooks=[{"config": hook_conf}]) hooks=[hook_conf])
]}] ]}]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(),
mock.Mock()) mock.Mock())
@@ -315,12 +317,12 @@ class TaskEngineTestCase(test.TestCase):
scenario_cls = mock_scenario_get.return_value scenario_cls = mock_scenario_get.return_value
scenario_cls.get_default_context.return_value = {} scenario_cls.get_default_context.return_value = {}
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
hook_conf = {"action": {"c": "c_args"}, hook_conf = {"action": ("c", "c_args"),
"trigger": {"d": "d_args"}} "trigger": ("d", "d_args")}
mock_task_instance.subtasks = [{"workloads": [ mock_task_instance.subtasks = [{"workloads": [
self._make_workload(name="sca"), self._make_workload(name="sca"),
self._make_workload(name="sca", position=1, self._make_workload(name="sca", position=1,
hooks=[{"config": hook_conf}]) hooks=[hook_conf])
]}] ]}]
eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(), eng = engine.TaskEngine(mock.MagicMock(), mock.MagicMock(),
mock.Mock()) mock.Mock())
@@ -353,7 +355,7 @@ class TaskEngineTestCase(test.TestCase):
mock_task_instance = mock.MagicMock() mock_task_instance = mock.MagicMock()
wconf1 = self._make_workload(name="SomeScen.scenario", wconf1 = self._make_workload(name="SomeScen.scenario",
context={"users": {}}) contexts={"users": {}})
wconf2 = self._make_workload(name="SomeScen.scenario", wconf2 = self._make_workload(name="SomeScen.scenario",
position=1) position=1)
subtask1 = {"workloads": [wconf1, wconf2]} subtask1 = {"workloads": [wconf1, wconf2]}
@@ -452,9 +454,9 @@ class TaskEngineTestCase(test.TestCase):
"context": {}, "context": {},
"workloads": [ "workloads": [
self._make_workload(name="a.task", description="foo", 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", self._make_workload(name="a.task", description="foo",
context={"context_a": {"b": 2}}, contexts={"context_a": {"b": 2}},
position=2)]}] position=2)]}]
mock_task_config.return_value = mock_task_instance mock_task_config.return_value = mock_task_instance
@@ -1064,6 +1066,6 @@ class TaskConfigTestCase(test.TestCase):
workload = task.subtasks[0]["workloads"][0] workload = task.subtasks[0]["workloads"][0]
self.assertEqual( self.assertEqual(
{"description": "descr", {"description": "descr",
"action": {"hook_action": {"k1": "v1"}}, "action": ("hook_action", {"k1": "v1"}),
"trigger": {"hook_trigger": {"k2": "v2"}}}, "trigger": ("hook_trigger", {"k2": "v2"})},
workload["hooks"][0]["config"]) workload["hooks"][0])

View File

@@ -55,18 +55,18 @@ class HookExecutorTestCase(test.TestCase):
super(HookExecutorTestCase, self).setUp() super(HookExecutorTestCase, self).setUp()
self.conf = { self.conf = {
"hooks": [ "hooks": [
{"config": { {
"description": "dummy_action", "description": "dummy_action",
"action": { "action": (
"dummy_hook": {"status": consts.HookStatus.SUCCESS} "dummy_hook", {"status": consts.HookStatus.SUCCESS}
}, ),
"trigger": { "trigger": (
"event": { "event", {
"unit": "iteration", "unit": "iteration",
"at": [1], "at": [1],
} }
)
} }
}}
] ]
} }
self.task = mock.MagicMock() self.task = mock.MagicMock()
@@ -78,7 +78,7 @@ class HookExecutorTestCase(test.TestCase):
hook_executor.on_event(event_type="iteration", value=1) hook_executor.on_event(event_type="iteration", value=1)
self.assertEqual( self.assertEqual(
[{"config": self.conf["hooks"][0]["config"], [{"config": self.conf["hooks"][0],
"results": [{ "results": [{
"triggered_by": {"event_type": "iteration", "value": 1}, "triggered_by": {"event_type": "iteration", "value": 1},
"started_at": fakes.FakeTimer().timestamp(), "started_at": fakes.FakeTimer().timestamp(),
@@ -90,15 +90,16 @@ class HookExecutorTestCase(test.TestCase):
@mock.patch("rally.task.hook.HookExecutor._timer_method") @mock.patch("rally.task.hook.HookExecutor._timer_method")
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
def test_result_optional(self, mock_timer, mock__timer_method): 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["error"] = ["Exception", "Description", "Traceback"]
hook_args["output"] = {"additive": None, "complete": None} hook_args["output"] = {"additive": None, "complete": None}
hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor = hook.HookExecutor(self.conf, self.task)
hook_executor.on_event(event_type="iteration", value=1) hook_executor.on_event(event_type="iteration", value=1)
self.assertEqual( self.assertEqual(
[{"config": self.conf["hooks"][0]["config"], [{"config": self.conf["hooks"][0],
"results": [{ "results": [{
"triggered_by": {"event_type": "iteration", "value": 1}, "triggered_by": {"event_type": "iteration", "value": 1},
"started_at": fakes.FakeTimer().timestamp(), "started_at": fakes.FakeTimer().timestamp(),
@@ -112,7 +113,7 @@ class HookExecutorTestCase(test.TestCase):
def test_empty_result(self): def test_empty_result(self):
hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor = hook.HookExecutor(self.conf, self.task)
self.assertEqual([{"config": self.conf["hooks"][0]["config"], self.assertEqual([{"config": self.conf["hooks"][0],
"results": [], "results": [],
"summary": {}}], "summary": {}}],
hook_executor.results()) hook_executor.results())
@@ -127,7 +128,7 @@ class HookExecutorTestCase(test.TestCase):
hook_executor.on_event(event_type="iteration", value=1) hook_executor.on_event(event_type="iteration", value=1)
self.assertEqual( self.assertEqual(
[{"config": self.conf["hooks"][0]["config"], [{"config": self.conf["hooks"][0],
"results": [{ "results": [{
"triggered_by": {"event_type": "iteration", "value": 1}, "triggered_by": {"event_type": "iteration", "value": 1},
"error": {"etype": "Exception", "error": {"etype": "Exception",
@@ -140,15 +141,14 @@ class HookExecutorTestCase(test.TestCase):
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
def test_time_event(self, mock_timer): def test_time_event(self, mock_timer):
trigger_args = list( trigger_args = self.conf["hooks"][0]["trigger"][1]
self.conf["hooks"][0]["config"]["trigger"].values())[0]
trigger_args["unit"] = "time" trigger_args["unit"] = "time"
hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor = hook.HookExecutor(self.conf, self.task)
hook_executor.on_event(event_type="time", value=1) hook_executor.on_event(event_type="time", value=1)
self.assertEqual( self.assertEqual(
[{"config": self.conf["hooks"][0]["config"], [{"config": self.conf["hooks"][0],
"results": [{ "results": [{
"triggered_by": {"event_type": "time", "value": 1}, "triggered_by": {"event_type": "time", "value": 1},
"started_at": fakes.FakeTimer().timestamp(), "started_at": fakes.FakeTimer().timestamp(),
@@ -159,8 +159,8 @@ class HookExecutorTestCase(test.TestCase):
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
def test_time_periodic(self, mock_timer): def test_time_periodic(self, mock_timer):
self.conf["hooks"][0]["config"]["trigger"] = { self.conf["hooks"][0]["trigger"] = ("periodic",
"periodic": {"unit": "time", "step": 2}} {"unit": "time", "step": 2})
hook_executor = hook.HookExecutor(self.conf, self.task) hook_executor = hook.HookExecutor(self.conf, self.task)
for i in range(1, 7): for i in range(1, 7):
@@ -168,7 +168,7 @@ class HookExecutorTestCase(test.TestCase):
self.assertEqual( self.assertEqual(
[{ [{
"config": self.conf["hooks"][0]["config"], "config": self.conf["hooks"][0],
"results":[ "results":[
{ {
"triggered_by": {"event_type": "time", "value": 2}, "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.Stopwatch", autospec=True)
@mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer) @mock.patch("rally.common.utils.Timer", side_effect=fakes.FakeTimer)
def test_timer_thread(self, mock_timer, mock_stopwatch): def test_timer_thread(self, mock_timer, mock_stopwatch):
trigger_args = list( trigger_args = self.conf["hooks"][0]["trigger"][1]
self.conf["hooks"][0]["config"]["trigger"].values())[0]
trigger_args["unit"] = "time" trigger_args["unit"] = "time"
hook_executor = hook.HookExecutor(self.conf, self.task) 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.assertTrue(hook_executor._timer_stop_event.wait(1))
self.assertEqual( self.assertEqual(
[{"config": self.conf["hooks"][0]["config"], [{"config": self.conf["hooks"][0],
"results": [{ "results": [{
"triggered_by": {"event_type": "time", "value": 1}, "triggered_by": {"event_type": "time", "value": 1},
"started_at": fakes.FakeTimer().timestamp(), "started_at": fakes.FakeTimer().timestamp(),
@@ -316,8 +315,8 @@ class TriggerTestCase(test.TestCase):
# test_on_event and test_get_results in one test. # test_on_event and test_get_results in one test.
right_values = [5, 7, 12, 13] right_values = [5, 7, 12, 13]
cfg = {"trigger": {self.DummyTrigger.get_name(): right_values}, cfg = {"trigger": (self.DummyTrigger.get_name(), right_values),
"action": {"fake": {}}} "action": ("fake", {})}
task = mock.MagicMock() task = mock.MagicMock()
hook_cls = mock.MagicMock(__name__="fake") hook_cls = mock.MagicMock(__name__="fake")
dummy_trigger = self.DummyTrigger(cfg, task, hook_cls) dummy_trigger = self.DummyTrigger(cfg, task, hook_cls)

View File

@@ -36,7 +36,7 @@ class TriggerTestCase(test.TestCase):
@mock.patch("rally.task.trigger.LOG.warning") @mock.patch("rally.task.trigger.LOG.warning")
def test_warning(self, mock_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( mock_log_warning.assert_called_once_with(
"Please contact Rally plugin maintainer. The plugin '%s'" "Please contact Rally plugin maintainer. The plugin '%s'"
@@ -52,8 +52,8 @@ class TriggerTestCase(test.TestCase):
descr = "descr" descr = "descr"
trigger_obj = self.DummyTrigger({ trigger_obj = self.DummyTrigger({
"trigger": {trigger_name: trigger_cfg}, "trigger": (trigger_name, trigger_cfg),
"action": {action_name: action_cfg}, "action": (action_name, action_cfg),
"description": descr}, None, None) "description": descr}, None, None)
self.assertEqual( self.assertEqual(

View File

@@ -477,6 +477,7 @@ class TaskAPITestCase(test.TestCase):
"start_time": 23.77, "start_time": 23.77,
"position": 77, "position": 77,
"runner": "runner-config", "runner": "runner-config",
"runner_type": "runner-type",
"context": "ctx-config", "context": "ctx-config",
"hooks": "hooks-config", "hooks": "hooks-config",
"sla": "sla-config", "sla": "sla-config",
@@ -507,6 +508,7 @@ class TaskAPITestCase(test.TestCase):
sub_task.add_workload.assert_called_once_with( sub_task.add_workload.assert_called_once_with(
name=workload["name"], description=workload["description"], name=workload["name"], description=workload["description"],
position=workload["position"], runner=workload["runner"], position=workload["position"], runner=workload["runner"],
runner_type=workload["runner_type"],
context=workload["context"], sla=workload["sla"], context=workload["context"], sla=workload["sla"],
hooks=workload["hooks"], args=workload["args"] hooks=workload["hooks"], args=workload["args"]
) )
@@ -538,6 +540,7 @@ class TaskAPITestCase(test.TestCase):
"start_time": 23.77, "start_time": 23.77,
"position": 77, "position": 77,
"runner": "runner-config", "runner": "runner-config",
"runner_type": "runner-type",
"context": "ctx-config", "context": "ctx-config",
"hooks": "hooks-config", "hooks": "hooks-config",
"sla": "sla-config", "sla": "sla-config",
@@ -569,6 +572,7 @@ class TaskAPITestCase(test.TestCase):
sub_task.add_workload.assert_called_once_with( sub_task.add_workload.assert_called_once_with(
name=workload["name"], description=workload["description"], name=workload["name"], description=workload["description"],
position=workload["position"], runner=workload["runner"], position=workload["position"], runner=workload["runner"],
runner_type=workload["runner_type"],
context=workload["context"], sla=workload["sla"], context=workload["context"], sla=workload["sla"],
hooks=workload["hooks"], args=workload["args"] hooks=workload["hooks"], args=workload["args"]
) )