Merge "Improve required_context validator to support platforms"

This commit is contained in:
Zuul 2024-01-03 15:13:31 +00:00 committed by Gerrit Code Review
commit 0740ece61a
3 changed files with 83 additions and 18 deletions

View File

@ -37,6 +37,19 @@ Added
* CI checks for Python 3.11 compatibility
* Support for specifying platform of context as a part of required_context
validator like bellow:
.. code-block:: python
from rally.task import scenario
from rally.task import validation
@scenario.configure(name="Dummy.scenario")
@validation.add("required_context", contexts=["ctx_name@platform"])
class ElasticsearchLogInstanceName(scenario.Scenario):
def run(self):
pass
Removed
~~~~~~~

View File

@ -19,6 +19,8 @@ import jsonschema
from rally.common import logging
from rally.common import validation
from rally import exceptions
from rally.task import context as context_lib
LOG = logging.getLogger(__name__)
@ -308,7 +310,7 @@ class RestrictedParametersValidator(validation.Validator):
@validation.configure(name="required_contexts")
class RequiredContextsValidator(validation.Validator):
def __init__(self, contexts, *args):
def __init__(self, *args, contexts=None):
"""Validator checks if required contexts are specified.
:param contexts: list of strings and tuples with context names that
@ -323,25 +325,48 @@ class RequiredContextsValidator(validation.Validator):
if args:
LOG.warning("Positional argument is not what "
"'required_context' decorator expects. "
"Use `contexts` argument instead")
"Use only `contexts` argument instead")
else:
# it is old way validator
self.contexts = [contexts]
# it is an old way validator
self.contexts = []
if contexts:
self.contexts.append(contexts)
self.contexts.extend(args)
@staticmethod
def _match(requested_ctx_name, input_contexts):
requested_ctx_name_extended = f"{requested_ctx_name}@"
for input_ctx_name in input_contexts:
if (requested_ctx_name == input_ctx_name
or input_ctx_name.startswith(requested_ctx_name_extended)):
return True
if "@" in requested_ctx_name:
platform_aware_name, platform = requested_ctx_name.split("@")
if platform_aware_name in input_contexts:
try:
ctx_cls = context_lib.Context.get(requested_ctx_name)
except (exceptions.PluginNotFound,
exceptions.MultiplePluginsFound):
return False
return ctx_cls.get_platform() == platform
return False
def validate(self, context, config, plugin_cls, plugin_cfg):
missing_contexts = []
input_context = config.get("contexts", {})
input_contexts = config.get("contexts", {})
for name in self.contexts:
if isinstance(name, tuple):
if not set(name) & set(input_context):
for required_ctx in self.contexts:
if isinstance(required_ctx, tuple):
if not any(self._match(r_ctx, input_contexts)
for r_ctx in required_ctx):
# formatted string like: 'foo or bar or baz'
formatted_names = "'%s'" % " or ".join(name)
formatted_names = "'%s'" % " or ".join(required_ctx)
missing_contexts.append(formatted_names)
else:
if name not in input_context:
missing_contexts.append(name)
if not self._match(required_ctx, input_contexts):
missing_contexts.append(required_ctx)
if missing_contexts:
self.fail("The following context(s) are required but missing from "

View File

@ -20,6 +20,7 @@ import ddt
from rally.common.plugin import plugin
from rally.common import validation
from rally.plugins.common import validators
from rally.plugins.task.contexts import dummy as dummy_ctx
from tests.unit import test
@ -256,27 +257,53 @@ class RequiredContextsValidatorTestCase(test.TestCase):
def test_validate_failed(self):
validator = validators.RequiredContextsValidator(
contexts=("c1", "c2", "c3"))
contexts=("c1@bar", "c2", "c3"))
e = self.assertRaises(
validation.ValidationError,
validator.validate, self.credentials, {"contexts": {"a": 1}},
None, None)
self.assertEqual(
"The following context(s) are required but missing from "
"the input task file: c1, c2, c3", e.message)
"the input task file: c1@bar, c2, c3", e.message)
@ddt.data(
{"config": {
"contexts": {"c1": 1, "c2": 2, "c3": 3,
"b1": 1, "a1": 1}}},
"contexts": {
"a1": 1, "b1": 1, "c1@foo": 1, "c2": 2, "c3": 3
}
}},
{"config": {
"contexts": {"c1": 1, "c2": 2, "c3": 3,
"b1": 1, "b2": 2, "a1": 1}}},
"contexts": {
"a1@foo": 1, "b1": 1, "c1": 1, "c2": 2, "c3": 3, "b2": 2
}
}},
)
@ddt.unpack
def test_validate_with_or(self, config):
@dummy_ctx.context.configure(name="a1", platform="foo", order=0)
class A1(dummy_ctx.DummyContext):
pass
@dummy_ctx.context.configure(name="c1", platform="foo", order=0)
class C1(dummy_ctx.DummyContext):
pass
@dummy_ctx.context.configure(name="c2", platform="foo", order=0)
class C2(dummy_ctx.DummyContext):
pass
self.addCleanup(A1.unregister)
self.addCleanup(C1.unregister)
self.addCleanup(C2.unregister)
validator = validators.RequiredContextsValidator(
contexts=[("a1", "a2"), "c1", ("b1", "b2"), "c2"])
contexts=[("a1@foo", "a2"), "c1", ("b1", "b2"), "c2@foo"])
validator.validate(self.credentials, config, None, None)
# check arg that should be ignored
validator = validators.RequiredContextsValidator(
"ignore@me",
contexts=[("a1@foo", "a2"), "c1", ("b1", "b2"), "c2@foo"])
validator.validate(self.credentials, config, None, None)
def test_validate_with_or_failed(self):