[task] Move TaskConfig to separate module
The validation and processing of input task config to the unified version-aware object is done by special class TaskConfig. It supports both v1 and v2 formats. The size of this helper class is quite big and since we are planning to extend it, it is reasonable to move it to separate module - rally.task.task_cfg . NOTE: we do not need to deprecate the old place, since TaskConfig is our inner class and should not be used outside our code base. Change-Id: I875b677a689e002413d0f07d43bacc960e965df4
This commit is contained in:
parent
b5ba6d5ea9
commit
b2ce5da5d8
rally
tests
@ -36,6 +36,7 @@ from rally import consts
|
||||
from rally import exceptions
|
||||
from rally.task import engine
|
||||
from rally.task import exporter as texporter
|
||||
from rally.task import task_cfg
|
||||
from rally.verification import context as vcontext
|
||||
from rally.verification import manager as vmanager
|
||||
from rally.verification import reporter as vreporter
|
||||
@ -350,7 +351,7 @@ class _Task(APIGroup):
|
||||
deployment = objects.Deployment.get(deployment)
|
||||
|
||||
try:
|
||||
config = engine.TaskConfig(config)
|
||||
config = task_cfg.TaskConfig(config)
|
||||
except Exception as e:
|
||||
if logging.is_debug():
|
||||
LOG.exception("Invalid Task")
|
||||
@ -394,7 +395,7 @@ class _Task(APIGroup):
|
||||
status=deployment["status"])
|
||||
|
||||
try:
|
||||
config = engine.TaskConfig(config)
|
||||
config = task_cfg.TaskConfig(config)
|
||||
except Exception as e:
|
||||
if logging.is_debug():
|
||||
LOG.exception("Invalid Task")
|
||||
|
@ -19,8 +19,6 @@ import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import jsonschema
|
||||
|
||||
from rally.common import cfg
|
||||
from rally.common import logging
|
||||
from rally.common import objects
|
||||
@ -239,7 +237,7 @@ class TaskEngine(object):
|
||||
def __init__(self, config, task, env, abort_on_sla_failure=False):
|
||||
"""TaskEngine constructor.
|
||||
|
||||
:param config: An instance of a TaskConfig
|
||||
:param config: An instance of a rally.task.config.TaskConfig
|
||||
:param task: Instance of Task,
|
||||
the current task which is being performed
|
||||
:param env: Instance of Environment,
|
||||
@ -510,344 +508,3 @@ class TaskEngine(object):
|
||||
except Exception:
|
||||
LOG.exception("Unexpected exception during the workload execution")
|
||||
# TODO(astudenov): save error to DB
|
||||
|
||||
|
||||
class TaskConfig(object):
|
||||
"""Version-aware wrapper around task.
|
||||
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA_V1 = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"patternProperties": {
|
||||
".*": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"args": {"type": "object"},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"runner": {
|
||||
"type": "object",
|
||||
"properties": {"type": {"type": "string"}},
|
||||
"required": ["type"]
|
||||
},
|
||||
"context": {"type": "object"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"hook": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"args": {},
|
||||
"trigger": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"args": {},
|
||||
},
|
||||
"required": ["name", "args"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"required": ["name", "args", "trigger"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA_V2 = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"version": {"type": "number"},
|
||||
"title": {"type": "string", "maxLength": 128},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
|
||||
"subtasks": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/subtask-workload"},
|
||||
{"$ref": "#/definitions/subtask-workloads"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "subtasks"],
|
||||
"definitions": {
|
||||
"singleEntity": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"patternProperties": {
|
||||
".*": {"type": "object"}
|
||||
}
|
||||
},
|
||||
"subtask-workload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "maxLength": 128},
|
||||
"group": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "maxLength": 255}
|
||||
},
|
||||
"scenario": {"$ref": "#/definitions/singleEntity"},
|
||||
"runner": {"$ref": "#/definitions/singleEntity"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
},
|
||||
"contexts": {"type": "object"}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "scenario", "runner"]
|
||||
},
|
||||
"subtask-workloads": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "maxLength": 255}
|
||||
},
|
||||
"run_in_parallel": {"type": "boolean"},
|
||||
"workloads": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scenario": {
|
||||
"$ref": "#/definitions/singleEntity"},
|
||||
"description": {"type": "string"},
|
||||
"runner": {
|
||||
"$ref": "#/definitions/singleEntity"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
},
|
||||
"contexts": {"type": "object"}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["scenario"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "workloads"]
|
||||
},
|
||||
"hook": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"args": {},
|
||||
"trigger": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"args": {},
|
||||
},
|
||||
"required": ["name", "args"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"required": ["name", "args", "trigger"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"patternProperties": {".*": {}}
|
||||
},
|
||||
"trigger": {"$ref": "#/definitions/singleEntity"},
|
||||
"description": {"type": "string"},
|
||||
},
|
||||
"required": ["action", "trigger"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_SCHEMAS = {1: CONFIG_SCHEMA_V1, 2: CONFIG_SCHEMA_V2}
|
||||
|
||||
def __init__(self, config):
|
||||
"""TaskConfig constructor.
|
||||
|
||||
Validates and represents different versions of task configuration in
|
||||
unified form.
|
||||
|
||||
:param config: Dict with configuration of specified task
|
||||
:raises Exception: in case of validation error. (This gets reraised as
|
||||
InvalidTaskException. if we raise it here as InvalidTaskException,
|
||||
then "Task config is invalid: " gets prepended to the message twice
|
||||
"""
|
||||
if config is None:
|
||||
raise Exception("Input task is empty")
|
||||
|
||||
self.version = self._get_version(config)
|
||||
self._validate_version()
|
||||
self._validate_json(config)
|
||||
|
||||
if self.version == 1:
|
||||
config = self._adopt_task_format_v1(config)
|
||||
|
||||
self.title = config.get("title", "Task")
|
||||
self.tags = config.get("tags", [])
|
||||
self.description = config.get("description")
|
||||
|
||||
self.subtasks = []
|
||||
for sconf in config["subtasks"]:
|
||||
sconf = copy.deepcopy(sconf)
|
||||
|
||||
# fill all missed properties of a SubTask
|
||||
sconf.setdefault("tags", [])
|
||||
sconf.setdefault("description", "")
|
||||
|
||||
# port the subtask to a single format before validating
|
||||
if "workloads" not in sconf and "scenario" in sconf:
|
||||
workload = sconf
|
||||
sconf = {"title": workload.pop("title"),
|
||||
"description": workload.pop("description"),
|
||||
"tags": workload.pop("tags"),
|
||||
"workloads": [workload]}
|
||||
|
||||
# it is not supported feature yet, but the code expects this
|
||||
# variable
|
||||
sconf.setdefault("contexts", {})
|
||||
|
||||
workloads = []
|
||||
for position, wconf in enumerate(sconf["workloads"]):
|
||||
# fill all missed properties of a Workload
|
||||
|
||||
wconf["name"], wconf["args"] = list(
|
||||
wconf["scenario"].items())[0]
|
||||
del wconf["scenario"]
|
||||
|
||||
wconf["position"] = position
|
||||
|
||||
if not wconf.get("description", ""):
|
||||
try:
|
||||
wconf["description"] = scenario.Scenario.get(
|
||||
wconf["name"]).get_info()["title"]
|
||||
except (exceptions.PluginNotFound,
|
||||
exceptions.MultiplePluginsFound):
|
||||
# let's fail an issue with loading plugin at a
|
||||
# validation step
|
||||
pass
|
||||
|
||||
wconf.setdefault("contexts", {})
|
||||
|
||||
if "runner" in wconf:
|
||||
runner = list(wconf["runner"].items())[0]
|
||||
wconf["runner_type"], wconf["runner"] = runner
|
||||
else:
|
||||
wconf["runner_type"] = "serial"
|
||||
wconf["runner"] = {}
|
||||
|
||||
wconf.setdefault("sla", {"failure_rate": {"max": 0}})
|
||||
|
||||
hooks = wconf.get("hooks", [])
|
||||
wconf["hooks"] = []
|
||||
for hook_cfg in hooks:
|
||||
if "name" in hook_cfg:
|
||||
LOG.warning("The deprecated format of hook is found. "
|
||||
"Check task format documentation for more "
|
||||
"details.")
|
||||
trigger_cfg = hook_cfg["trigger"]
|
||||
wconf["hooks"].append({
|
||||
"description": hook_cfg["description"],
|
||||
"action": (hook_cfg["name"], hook_cfg["args"]),
|
||||
"trigger": (
|
||||
trigger_cfg["name"], trigger_cfg["args"])})
|
||||
else:
|
||||
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
|
||||
self.subtasks.append(sconf)
|
||||
|
||||
# if self.version == 1:
|
||||
# TODO(ikhudoshyn): Warn user about deprecated format
|
||||
|
||||
@staticmethod
|
||||
def _get_version(config):
|
||||
return config.get("version", 1)
|
||||
|
||||
def _validate_version(self):
|
||||
if self.version not in self.CONFIG_SCHEMAS:
|
||||
allowed = ", ".join([str(k) for k in self.CONFIG_SCHEMAS])
|
||||
msg = ("Task configuration version %s is not supported. "
|
||||
"Supported versions: %s") % (self.version, allowed)
|
||||
raise exceptions.InvalidTaskException(msg)
|
||||
|
||||
def _validate_json(self, config):
|
||||
try:
|
||||
jsonschema.validate(config, self.CONFIG_SCHEMAS[self.version])
|
||||
except Exception as e:
|
||||
raise exceptions.InvalidTaskException(str(e))
|
||||
|
||||
@staticmethod
|
||||
def _adopt_task_format_v1(config):
|
||||
subtasks = []
|
||||
for name, v1_workloads in config.items():
|
||||
for v1_workload in v1_workloads:
|
||||
subtask = copy.deepcopy(v1_workload)
|
||||
subtask["scenario"] = {name: subtask.pop("args", {})}
|
||||
subtask["contexts"] = subtask.pop("context", {})
|
||||
subtask["title"] = name
|
||||
if "runner" in subtask:
|
||||
runner_type = subtask["runner"].pop("type")
|
||||
subtask["runner"] = {runner_type: subtask["runner"]}
|
||||
if "hooks" in subtask:
|
||||
hooks = subtask["hooks"]
|
||||
subtask["hooks"] = []
|
||||
for hook_cfg in hooks:
|
||||
trigger_cfg = hook_cfg["trigger"]
|
||||
subtask["hooks"].append(
|
||||
{"description": hook_cfg.get("description"),
|
||||
"action": {
|
||||
hook_cfg["name"]: hook_cfg["args"]},
|
||||
"trigger": {
|
||||
trigger_cfg["name"]: trigger_cfg["args"]}}
|
||||
)
|
||||
subtasks.append(subtask)
|
||||
return {"title": "Task (adopted from task format v1)",
|
||||
"subtasks": subtasks}
|
||||
|
367
rally/task/task_cfg.py
Normal file
367
rally/task/task_cfg.py
Normal file
@ -0,0 +1,367 @@
|
||||
# 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.
|
||||
|
||||
import copy
|
||||
|
||||
import jsonschema
|
||||
|
||||
from rally.common import cfg
|
||||
from rally.common import logging
|
||||
from rally import consts
|
||||
from rally import exceptions
|
||||
from rally.task import scenario
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TaskConfig(object):
|
||||
"""Version-aware wrapper around task config."""
|
||||
|
||||
CONFIG_SCHEMA_V1 = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"patternProperties": {
|
||||
".*": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"args": {"type": "object"},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"runner": {
|
||||
"type": "object",
|
||||
"properties": {"type": {"type": "string"}},
|
||||
"required": ["type"]
|
||||
},
|
||||
"context": {"type": "object"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"hook": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"args": {},
|
||||
"trigger": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"args": {},
|
||||
},
|
||||
"required": ["name", "args"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"required": ["name", "args", "trigger"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA_V2 = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"version": {"type": "number"},
|
||||
"title": {"type": "string", "maxLength": 128},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
|
||||
"subtasks": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/subtask-workload"},
|
||||
{"$ref": "#/definitions/subtask-workloads"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "subtasks"],
|
||||
"definitions": {
|
||||
"singleEntity": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"patternProperties": {
|
||||
".*": {"type": "object"}
|
||||
}
|
||||
},
|
||||
"subtask-workload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string", "maxLength": 128},
|
||||
"group": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "maxLength": 255}
|
||||
},
|
||||
"scenario": {"$ref": "#/definitions/singleEntity"},
|
||||
"runner": {"$ref": "#/definitions/singleEntity"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
},
|
||||
"contexts": {"type": "object"}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "scenario", "runner"]
|
||||
},
|
||||
"subtask-workloads": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string"},
|
||||
"group": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "maxLength": 255}
|
||||
},
|
||||
"run_in_parallel": {"type": "boolean"},
|
||||
"workloads": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scenario": {
|
||||
"$ref": "#/definitions/singleEntity"},
|
||||
"description": {"type": "string"},
|
||||
"runner": {
|
||||
"$ref": "#/definitions/singleEntity"},
|
||||
"sla": {"type": "object"},
|
||||
"hooks": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/hook"},
|
||||
},
|
||||
"contexts": {"type": "object"}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["scenario"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["title", "workloads"]
|
||||
},
|
||||
"hook": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"args": {},
|
||||
"trigger": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"args": {},
|
||||
},
|
||||
"required": ["name", "args"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"required": ["name", "args", "trigger"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"patternProperties": {".*": {}}
|
||||
},
|
||||
"trigger": {"$ref": "#/definitions/singleEntity"},
|
||||
"description": {"type": "string"},
|
||||
},
|
||||
"required": ["action", "trigger"],
|
||||
"additionalProperties": False
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_SCHEMAS = {1: CONFIG_SCHEMA_V1, 2: CONFIG_SCHEMA_V2}
|
||||
|
||||
def __init__(self, config):
|
||||
"""TaskConfig constructor.
|
||||
|
||||
Validates and represents different versions of task configuration in
|
||||
unified form.
|
||||
|
||||
:param config: Dict with configuration of specified task
|
||||
:raises Exception: in case of validation error. (This gets reraised as
|
||||
InvalidTaskException. if we raise it here as InvalidTaskException,
|
||||
then "Task config is invalid: " gets prepended to the message twice
|
||||
"""
|
||||
if config is None:
|
||||
raise Exception("Input task is empty")
|
||||
|
||||
self.version = self._get_version(config)
|
||||
self._validate_version()
|
||||
self._validate_json(config)
|
||||
|
||||
if self.version == 1:
|
||||
config = self._adopt_task_format_v1(config)
|
||||
|
||||
self.title = config.get("title", "Task")
|
||||
self.tags = config.get("tags", [])
|
||||
self.description = config.get("description")
|
||||
|
||||
self.subtasks = []
|
||||
for sconf in config["subtasks"]:
|
||||
sconf = copy.deepcopy(sconf)
|
||||
|
||||
# fill all missed properties of a SubTask
|
||||
sconf.setdefault("tags", [])
|
||||
sconf.setdefault("description", "")
|
||||
|
||||
# port the subtask to a single format before validating
|
||||
if "workloads" not in sconf and "scenario" in sconf:
|
||||
workload = sconf
|
||||
sconf = {"title": workload.pop("title"),
|
||||
"description": workload.pop("description"),
|
||||
"tags": workload.pop("tags"),
|
||||
"workloads": [workload]}
|
||||
|
||||
# it is not supported feature yet, but the code expects this
|
||||
# variable
|
||||
sconf.setdefault("contexts", {})
|
||||
|
||||
workloads = []
|
||||
for position, wconf in enumerate(sconf["workloads"]):
|
||||
# fill all missed properties of a Workload
|
||||
|
||||
wconf["name"], wconf["args"] = list(
|
||||
wconf["scenario"].items())[0]
|
||||
del wconf["scenario"]
|
||||
|
||||
wconf["position"] = position
|
||||
|
||||
if not wconf.get("description", ""):
|
||||
try:
|
||||
wconf["description"] = scenario.Scenario.get(
|
||||
wconf["name"]).get_info()["title"]
|
||||
except (exceptions.PluginNotFound,
|
||||
exceptions.MultiplePluginsFound):
|
||||
# let's fail an issue with loading plugin at a
|
||||
# validation step
|
||||
pass
|
||||
|
||||
wconf.setdefault("contexts", {})
|
||||
|
||||
if "runner" in wconf:
|
||||
runner = list(wconf["runner"].items())[0]
|
||||
wconf["runner_type"], wconf["runner"] = runner
|
||||
else:
|
||||
wconf["runner_type"] = "serial"
|
||||
wconf["runner"] = {}
|
||||
|
||||
wconf.setdefault("sla", {"failure_rate": {"max": 0}})
|
||||
|
||||
hooks = wconf.get("hooks", [])
|
||||
wconf["hooks"] = []
|
||||
for hook_cfg in hooks:
|
||||
if "name" in hook_cfg:
|
||||
LOG.warning("The deprecated format of hook is found. "
|
||||
"Check task format documentation for more "
|
||||
"details.")
|
||||
trigger_cfg = hook_cfg["trigger"]
|
||||
wconf["hooks"].append({
|
||||
"description": hook_cfg["description"],
|
||||
"action": (hook_cfg["name"], hook_cfg["args"]),
|
||||
"trigger": (
|
||||
trigger_cfg["name"], trigger_cfg["args"])})
|
||||
else:
|
||||
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
|
||||
self.subtasks.append(sconf)
|
||||
|
||||
# if self.version == 1:
|
||||
# TODO(ikhudoshyn): Warn user about deprecated format
|
||||
|
||||
@staticmethod
|
||||
def _get_version(config):
|
||||
return config.get("version", 1)
|
||||
|
||||
def _validate_version(self):
|
||||
if self.version not in self.CONFIG_SCHEMAS:
|
||||
allowed = ", ".join([str(k) for k in self.CONFIG_SCHEMAS])
|
||||
msg = ("Task configuration version %s is not supported. "
|
||||
"Supported versions: %s") % (self.version, allowed)
|
||||
raise exceptions.InvalidTaskException(msg)
|
||||
|
||||
def _validate_json(self, config):
|
||||
try:
|
||||
jsonschema.validate(config, self.CONFIG_SCHEMAS[self.version])
|
||||
except Exception as e:
|
||||
raise exceptions.InvalidTaskException(str(e))
|
||||
|
||||
@staticmethod
|
||||
def _adopt_task_format_v1(config):
|
||||
subtasks = []
|
||||
for name, v1_workloads in config.items():
|
||||
for v1_workload in v1_workloads:
|
||||
subtask = copy.deepcopy(v1_workload)
|
||||
subtask["scenario"] = {name: subtask.pop("args", {})}
|
||||
subtask["contexts"] = subtask.pop("context", {})
|
||||
subtask["title"] = name
|
||||
if "runner" in subtask:
|
||||
runner_type = subtask["runner"].pop("type")
|
||||
subtask["runner"] = {runner_type: subtask["runner"]}
|
||||
if "hooks" in subtask:
|
||||
hooks = subtask["hooks"]
|
||||
subtask["hooks"] = []
|
||||
for hook_cfg in hooks:
|
||||
trigger_cfg = hook_cfg["trigger"]
|
||||
subtask["hooks"].append(
|
||||
{"description": hook_cfg.get("description"),
|
||||
"action": {
|
||||
hook_cfg["name"]: hook_cfg["args"]},
|
||||
"trigger": {
|
||||
trigger_cfg["name"]: trigger_cfg["args"]}}
|
||||
)
|
||||
subtasks.append(subtask)
|
||||
return {"title": "Task (adopted from task format v1)",
|
||||
"subtasks": subtasks}
|
@ -26,6 +26,7 @@ from rally import api
|
||||
from rally.task import context
|
||||
from rally.task import engine
|
||||
from rally.task import scenario
|
||||
from rally.task import task_cfg
|
||||
from tests.unit import test
|
||||
|
||||
RALLY_PATH = os.path.dirname(os.path.dirname(rally.__file__))
|
||||
@ -85,7 +86,7 @@ class TaskSampleTestCase(test.TestCase):
|
||||
except Exception:
|
||||
print(traceback.format_exc())
|
||||
self.fail("Invalid JSON file: %s" % path)
|
||||
eng = engine.TaskEngine(engine.TaskConfig(task_config),
|
||||
eng = engine.TaskEngine(task_cfg.TaskConfig(task_config),
|
||||
mock.MagicMock(), mock.Mock())
|
||||
eng.validate(only_syntax=True)
|
||||
except Exception:
|
||||
|
@ -26,6 +26,7 @@ from rally import exceptions
|
||||
from rally.task import context
|
||||
from rally.task import engine
|
||||
from rally.task import scenario
|
||||
from rally.task import task_cfg
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
@ -73,8 +74,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
mock_validate.platforms.assert_called_once_with(config)
|
||||
mock_validate.semantic.assert_called_once_with(config)
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test_validate__wrong_syntax(self, mock_task_config):
|
||||
def test_validate__wrong_syntax(self):
|
||||
task = mock.MagicMock()
|
||||
eng = engine.TaskEngine(mock.MagicMock(), task, mock.Mock())
|
||||
eng._validate_config_syntax = mock.MagicMock(
|
||||
@ -87,8 +87,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
# the next validation step should not be processed
|
||||
self.assertFalse(eng._validate_config_platforms.called)
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test_validate__wrong_semantic(self, mock_task_config):
|
||||
def test_validate__wrong_semantic(self):
|
||||
task = mock.MagicMock()
|
||||
eng = engine.TaskEngine(mock.MagicMock(), task, mock.Mock())
|
||||
eng._validate_config_syntax = mock.MagicMock()
|
||||
@ -108,13 +107,11 @@ class TaskEngineTestCase(test.TestCase):
|
||||
@mock.patch("rally.task.sla.SLA.validate")
|
||||
@mock.patch("rally.task.hook.HookTrigger.validate")
|
||||
@mock.patch("rally.task.hook.HookAction.validate")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.task.engine.runner.ScenarioRunner.validate")
|
||||
@mock.patch("rally.task.engine.context.Context.validate")
|
||||
def test__validate_workload(
|
||||
self, mock_context_validate,
|
||||
mock_scenario_runner_validate,
|
||||
mock_task_config,
|
||||
mock_hook_action_validate,
|
||||
mock_hook_trigger_validate,
|
||||
mock_sla_validate,
|
||||
@ -173,10 +170,9 @@ class TaskEngineTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("rally.task.engine.json.dumps")
|
||||
@mock.patch("rally.task.engine.scenario.Scenario.get")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.task.engine.runner.ScenarioRunner.validate")
|
||||
def test___validate_workload__wrong_runner(
|
||||
self, mock_scenario_runner_validate, mock_task_config,
|
||||
self, mock_scenario_runner_validate,
|
||||
mock_scenario_get, mock_dumps):
|
||||
mock_dumps.return_value = "<JSON>"
|
||||
mock_scenario_runner_validate.return_value = [
|
||||
@ -196,10 +192,9 @@ class TaskEngineTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("rally.task.engine.json.dumps")
|
||||
@mock.patch("rally.task.engine.scenario.Scenario.get")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.task.engine.context.Context.validate")
|
||||
def test__validate_config_syntax__wrong_context(
|
||||
self, mock_context_validate, mock_task_config, mock_scenario_get,
|
||||
self, mock_context_validate, mock_scenario_get,
|
||||
mock_dumps):
|
||||
mock_dumps.return_value = "<JSON>"
|
||||
mock_context_validate.return_value = ["context_error"]
|
||||
@ -223,10 +218,8 @@ class TaskEngineTestCase(test.TestCase):
|
||||
@mock.patch("rally.task.engine.json.dumps")
|
||||
@mock.patch("rally.task.engine.scenario.Scenario.get")
|
||||
@mock.patch("rally.task.sla.SLA.validate")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test__validate_config_syntax__wrong_sla(
|
||||
self, mock_task_config, mock_sla_validate, mock_scenario_get,
|
||||
mock_dumps):
|
||||
self, mock_sla_validate, mock_scenario_get, mock_dumps):
|
||||
mock_dumps.return_value = "<JSON>"
|
||||
mock_sla_validate.return_value = ["sla_error"]
|
||||
scenario_cls = mock_scenario_get.return_value
|
||||
@ -250,9 +243,8 @@ class TaskEngineTestCase(test.TestCase):
|
||||
@mock.patch("rally.task.engine.scenario.Scenario.get")
|
||||
@mock.patch("rally.task.hook.HookAction.validate")
|
||||
@mock.patch("rally.task.hook.HookTrigger.validate")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test__validate_config_syntax__wrong_hook(
|
||||
self, mock_task_config, mock_hook_trigger_validate,
|
||||
self, mock_hook_trigger_validate,
|
||||
mock_hook_action_validate,
|
||||
mock_scenario_get, mock_dumps):
|
||||
mock_dumps.return_value = "<JSON>"
|
||||
@ -283,9 +275,8 @@ class TaskEngineTestCase(test.TestCase):
|
||||
@mock.patch("rally.task.engine.scenario.Scenario.get")
|
||||
@mock.patch("rally.task.hook.HookTrigger.validate")
|
||||
@mock.patch("rally.task.hook.HookAction.validate")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test__validate_config_syntax__wrong_trigger(
|
||||
self, mock_task_config, mock_hook_action_validate,
|
||||
self, mock_hook_action_validate,
|
||||
mock_hook_trigger_validate,
|
||||
mock_scenario_get, mock_dumps):
|
||||
mock_dumps.return_value = "<JSON>"
|
||||
@ -314,9 +305,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("rally.task.engine.context.ContextManager.cleanup")
|
||||
@mock.patch("rally.task.engine.context.ContextManager.setup")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test__validate_config_semantic(self, mock_task_config,
|
||||
mock_context_manager_setup,
|
||||
def test__validate_config_semantic(self, mock_context_manager_setup,
|
||||
mock_context_manager_cleanup):
|
||||
env = mock.MagicMock(uuid="env_uuid")
|
||||
env.check_health.return_value = {
|
||||
@ -354,10 +343,9 @@ class TaskEngineTestCase(test.TestCase):
|
||||
eng._validate_config_semantic,
|
||||
mock_task_instance)
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.task.engine.TaskEngine._validate_workload")
|
||||
def test__validate_config_platforms(
|
||||
self, mock__validate_workload, mock_task_config):
|
||||
self, mock__validate_workload):
|
||||
|
||||
foo_cred = {"admin": "admin", "users": ["user1"]}
|
||||
env = mock.MagicMock(data={
|
||||
@ -385,7 +373,6 @@ class TaskEngineTestCase(test.TestCase):
|
||||
mock__validate_workload.call_args_list)
|
||||
|
||||
@mock.patch("rally.common.objects.Task.get_status")
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.task.engine.ResultConsumer")
|
||||
@mock.patch("rally.task.engine.context.ContextManager.cleanup")
|
||||
@mock.patch("rally.task.engine.context.ContextManager.setup")
|
||||
@ -394,7 +381,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
def test_run__update_status(
|
||||
self, mock_scenario_runner, mock_scenario,
|
||||
mock_context_manager_setup, mock_context_manager_cleanup,
|
||||
mock_result_consumer, mock_task_config, mock_task_get_status):
|
||||
mock_result_consumer, mock_task_get_status):
|
||||
|
||||
task = mock.MagicMock()
|
||||
mock_task_get_status.return_value = consts.TaskStatus.ABORTING
|
||||
@ -461,7 +448,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
mock_result_consumer.is_task_in_aborting_status.side_effect = [False,
|
||||
False,
|
||||
True]
|
||||
config = engine.TaskConfig({
|
||||
config = task_cfg.TaskConfig({
|
||||
"a.task": [{"runner": {"type": "a", "b": 1},
|
||||
"description": "foo"}],
|
||||
"b.task": [{"runner": {"type": "a", "b": 1},
|
||||
@ -498,7 +485,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
mock_context_manager_setup, mock_context_manager_cleanup,
|
||||
mock_result_consumer, mock_task_get_status):
|
||||
task = mock.MagicMock(spec=objects.Task)
|
||||
config = engine.TaskConfig({
|
||||
config = task_cfg.TaskConfig({
|
||||
"a.task": [{"runner": {"type": "a", "b": 1}}],
|
||||
"b.task": [{"runner": {"type": "a", "b": 1}}],
|
||||
"c.task": [{"runner": {"type": "a", "b": 1}}]
|
||||
@ -530,7 +517,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
subtask_obj = task.add_subtask.return_value
|
||||
subtask_obj.add_workload.side_effect = MyException()
|
||||
mock_result_consumer.is_task_in_aborting_status.return_value = False
|
||||
config = engine.TaskConfig({
|
||||
config = task_cfg.TaskConfig({
|
||||
"a.task": [{"runner": {"type": "a", "b": 1}}],
|
||||
"b.task": [{"runner": {"type": "a", "b": 1}}],
|
||||
"c.task": [{"runner": {"type": "a", "b": 1}}]
|
||||
@ -549,8 +536,7 @@ class TaskEngineTestCase(test.TestCase):
|
||||
subtask_obj.update_status.assert_called_once_with(
|
||||
consts.SubtaskStatus.CRASHED)
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
def test__prepare_context(self, mock_task_config):
|
||||
def test__prepare_context(self):
|
||||
|
||||
@context.configure("test1", 1, platform="testing")
|
||||
class TestContext1(context.Context):
|
||||
@ -972,126 +958,3 @@ class ResultConsumerTestCase(test.TestCase):
|
||||
self.assertFalse(runner.abort.called)
|
||||
# test task.get_status is checked until is_done is not set
|
||||
self.assertEqual(4, mock_task_get_status.call_count)
|
||||
|
||||
|
||||
class TaskConfigTestCase(test.TestCase):
|
||||
|
||||
def test_init_empty_config(self):
|
||||
config = None
|
||||
exception = self.assertRaises(Exception, # noqa
|
||||
engine.TaskConfig, config)
|
||||
self.assertIn("Input task is empty", str(exception))
|
||||
|
||||
@mock.patch("jsonschema.validate")
|
||||
def test_validate_json(self, mock_validate):
|
||||
config = {}
|
||||
engine.TaskConfig(config)
|
||||
mock_validate.assert_has_calls([
|
||||
mock.call(config, engine.TaskConfig.CONFIG_SCHEMA_V1)])
|
||||
|
||||
@mock.patch("jsonschema.validate")
|
||||
def test_validate_json_v2(self, mock_validate):
|
||||
config = {"version": 2, "subtasks": []}
|
||||
engine.TaskConfig(config)
|
||||
mock_validate.assert_has_calls([
|
||||
mock.call(config, engine.TaskConfig.CONFIG_SCHEMA_V2)])
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig._get_version")
|
||||
@mock.patch("rally.task.engine.TaskConfig._validate_json")
|
||||
def test_validate_version(self, mock_task_config__validate_json,
|
||||
mock_task_config__get_version):
|
||||
mock_task_config__get_version.return_value = 1
|
||||
engine.TaskConfig(mock.MagicMock())
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig._get_version")
|
||||
@mock.patch("rally.task.engine.TaskConfig._validate_json")
|
||||
def test_validate_version_wrong_version(
|
||||
self, mock_task_config__validate_json,
|
||||
mock_task_config__get_version):
|
||||
|
||||
mock_task_config__get_version.return_value = "wrong"
|
||||
self.assertRaises(exceptions.InvalidTaskException, engine.TaskConfig,
|
||||
mock.MagicMock)
|
||||
|
||||
def test__adopt_task_format_v1(self):
|
||||
|
||||
# mock all redundant checks :)
|
||||
class TaskConfig(engine.TaskConfig):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
config = collections.OrderedDict()
|
||||
config["a.task"] = [{"s": 1, "context": {"foo": "bar"}}, {"s": 2}]
|
||||
config["b.task"] = [{"s": 3, "sla": {"key": "value"}}]
|
||||
config["c.task"] = [{"s": 5,
|
||||
"hooks": [{"name": "foo",
|
||||
"args": "bar",
|
||||
"description": "DESCR!!!",
|
||||
"trigger": {
|
||||
"name": "mega-trigger",
|
||||
"args": {"some": "thing"}
|
||||
}}]
|
||||
}]
|
||||
self.assertEqual(
|
||||
{"title": "Task (adopted from task format v1)",
|
||||
"subtasks": [
|
||||
{
|
||||
"title": "a.task",
|
||||
"scenario": {"a.task": {}},
|
||||
"s": 1,
|
||||
"contexts": {"foo": "bar"}
|
||||
},
|
||||
{
|
||||
"title": "a.task",
|
||||
"s": 2,
|
||||
"scenario": {"a.task": {}},
|
||||
"contexts": {}
|
||||
},
|
||||
{
|
||||
"title": "b.task",
|
||||
"s": 3,
|
||||
"scenario": {"b.task": {}},
|
||||
"sla": {"key": "value"},
|
||||
"contexts": {}
|
||||
},
|
||||
{
|
||||
"title": "c.task",
|
||||
"s": 5,
|
||||
"scenario": {"c.task": {}},
|
||||
"contexts": {},
|
||||
"hooks": [
|
||||
{"description": "DESCR!!!",
|
||||
"action": {"foo": "bar"},
|
||||
"trigger": {"mega-trigger": {"some": "thing"}}}
|
||||
]
|
||||
}]},
|
||||
TaskConfig._adopt_task_format_v1(config))
|
||||
|
||||
def test_hook_config_compatibility(self):
|
||||
cfg = {
|
||||
"title": "foo",
|
||||
"version": 2,
|
||||
"subtasks": [
|
||||
{
|
||||
"title": "foo",
|
||||
"scenario": {"xxx": {}},
|
||||
"runner": {"yyy": {}},
|
||||
"hooks": [
|
||||
{"description": "descr",
|
||||
"name": "hook_action",
|
||||
"args": {"k1": "v1"},
|
||||
"trigger": {
|
||||
"name": "hook_trigger",
|
||||
"args": {"k2": "v2"}
|
||||
}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
task = engine.TaskConfig(cfg)
|
||||
workload = task.subtasks[0]["workloads"][0]
|
||||
self.assertEqual(
|
||||
{"description": "descr",
|
||||
"action": ("hook_action", {"k1": "v1"}),
|
||||
"trigger": ("hook_trigger", {"k2": "v2"})},
|
||||
workload["hooks"][0])
|
||||
|
144
tests/unit/task/test_task_cfg.py
Normal file
144
tests/unit/task/test_task_cfg.py
Normal file
@ -0,0 +1,144 @@
|
||||
# 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.
|
||||
|
||||
import collections
|
||||
|
||||
import mock
|
||||
|
||||
from rally import exceptions
|
||||
from rally.task import task_cfg
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
class TaskConfigTestCase(test.TestCase):
|
||||
|
||||
def test_init_empty_config(self):
|
||||
config = None
|
||||
exception = self.assertRaises(Exception, # noqa
|
||||
task_cfg.TaskConfig, config)
|
||||
self.assertIn("Input task is empty", str(exception))
|
||||
|
||||
@mock.patch("jsonschema.validate")
|
||||
def test_validate_json(self, mock_validate):
|
||||
config = {}
|
||||
task_cfg.TaskConfig(config)
|
||||
mock_validate.assert_has_calls([
|
||||
mock.call(config, task_cfg.TaskConfig.CONFIG_SCHEMA_V1)])
|
||||
|
||||
@mock.patch("jsonschema.validate")
|
||||
def test_validate_json_v2(self, mock_validate):
|
||||
config = {"version": 2, "subtasks": []}
|
||||
task_cfg.TaskConfig(config)
|
||||
mock_validate.assert_has_calls([
|
||||
mock.call(config, task_cfg.TaskConfig.CONFIG_SCHEMA_V2)])
|
||||
|
||||
@mock.patch("rally.task.task_cfg.TaskConfig._get_version")
|
||||
@mock.patch("rally.task.task_cfg.TaskConfig._validate_json")
|
||||
def test_validate_version(self, mock_task_config__validate_json,
|
||||
mock_task_config__get_version):
|
||||
mock_task_config__get_version.return_value = 1
|
||||
task_cfg.TaskConfig(mock.MagicMock())
|
||||
|
||||
@mock.patch("rally.task.task_cfg.TaskConfig._get_version")
|
||||
@mock.patch("rally.task.task_cfg.TaskConfig._validate_json")
|
||||
def test_validate_version_wrong_version(
|
||||
self, mock_task_config__validate_json,
|
||||
mock_task_config__get_version):
|
||||
|
||||
mock_task_config__get_version.return_value = "wrong"
|
||||
self.assertRaises(exceptions.InvalidTaskException, task_cfg.TaskConfig,
|
||||
mock.MagicMock)
|
||||
|
||||
def test__adopt_task_format_v1(self):
|
||||
|
||||
# mock all redundant checks :)
|
||||
class TaskConfig(task_cfg.TaskConfig):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
config = collections.OrderedDict()
|
||||
config["a.task"] = [{"s": 1, "context": {"foo": "bar"}}, {"s": 2}]
|
||||
config["b.task"] = [{"s": 3, "sla": {"key": "value"}}]
|
||||
config["c.task"] = [{"s": 5,
|
||||
"hooks": [{"name": "foo",
|
||||
"args": "bar",
|
||||
"description": "DESCR!!!",
|
||||
"trigger": {
|
||||
"name": "mega-trigger",
|
||||
"args": {"some": "thing"}
|
||||
}}]
|
||||
}]
|
||||
self.assertEqual(
|
||||
{"title": "Task (adopted from task format v1)",
|
||||
"subtasks": [
|
||||
{
|
||||
"title": "a.task",
|
||||
"scenario": {"a.task": {}},
|
||||
"s": 1,
|
||||
"contexts": {"foo": "bar"}
|
||||
},
|
||||
{
|
||||
"title": "a.task",
|
||||
"s": 2,
|
||||
"scenario": {"a.task": {}},
|
||||
"contexts": {}
|
||||
},
|
||||
{
|
||||
"title": "b.task",
|
||||
"s": 3,
|
||||
"scenario": {"b.task": {}},
|
||||
"sla": {"key": "value"},
|
||||
"contexts": {}
|
||||
},
|
||||
{
|
||||
"title": "c.task",
|
||||
"s": 5,
|
||||
"scenario": {"c.task": {}},
|
||||
"contexts": {},
|
||||
"hooks": [
|
||||
{"description": "DESCR!!!",
|
||||
"action": {"foo": "bar"},
|
||||
"trigger": {"mega-trigger": {"some": "thing"}}}
|
||||
]
|
||||
}]},
|
||||
TaskConfig._adopt_task_format_v1(config))
|
||||
|
||||
def test_hook_config_compatibility(self):
|
||||
cfg = {
|
||||
"title": "foo",
|
||||
"version": 2,
|
||||
"subtasks": [
|
||||
{
|
||||
"title": "foo",
|
||||
"scenario": {"xxx": {}},
|
||||
"runner": {"yyy": {}},
|
||||
"hooks": [
|
||||
{"description": "descr",
|
||||
"name": "hook_action",
|
||||
"args": {"k1": "v1"},
|
||||
"trigger": {
|
||||
"name": "hook_trigger",
|
||||
"args": {"k2": "v2"}
|
||||
}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
task = task_cfg.TaskConfig(cfg)
|
||||
workload = task.subtasks[0]["workloads"][0]
|
||||
self.assertEqual(
|
||||
{"description": "descr",
|
||||
"action": ("hook_action", {"k1": "v1"}),
|
||||
"trigger": ("hook_trigger", {"k2": "v2"})},
|
||||
workload["hooks"][0])
|
@ -67,7 +67,7 @@ class TaskAPITestCase(test.TestCase):
|
||||
mock_api.endpoint_url = None
|
||||
self.task_inst = api._Task(mock_api)
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.api.task_cfg.TaskConfig")
|
||||
@mock.patch("rally.api.objects.Task")
|
||||
@mock.patch("rally.api.objects.Deployment.get")
|
||||
@mock.patch("rally.api.engine.TaskEngine")
|
||||
@ -261,7 +261,7 @@ class TaskAPITestCase(test.TestCase):
|
||||
self.task_inst.create, deployment=deployment_id,
|
||||
tags=["a"])
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.api.task_cfg.TaskConfig")
|
||||
@mock.patch("rally.api.objects.Task")
|
||||
@mock.patch("rally.api.objects.Deployment.get")
|
||||
@mock.patch("rally.api.engine.TaskEngine")
|
||||
@ -337,7 +337,7 @@ class TaskAPITestCase(test.TestCase):
|
||||
config="config",
|
||||
task=fake_task["uuid"])
|
||||
|
||||
@mock.patch("rally.task.engine.TaskConfig")
|
||||
@mock.patch("rally.api.task_cfg.TaskConfig")
|
||||
@mock.patch("rally.api.objects.Task")
|
||||
@mock.patch("rally.api.objects.Deployment.get")
|
||||
@mock.patch("rally.api.engine.TaskEngine")
|
||||
|
Loading…
x
Reference in New Issue
Block a user