Merge "Move rally.benchmark.scenarios.utils"

This commit is contained in:
Jenkins 2015-06-08 14:11:27 +00:00 committed by Gerrit Code Review
commit c6ac4ef45e
6 changed files with 278 additions and 311 deletions

View File

@ -86,7 +86,7 @@ In a toy example below, we define a scenario class *MyScenario* with one benchma
::
from rally.benchmark.scenarios import base
from rally.benchmark.scenarios import utils
from rally.benchmark import utils
class MyScenario(base.Scenario):

View File

@ -1,124 +0,0 @@
# Copyright 2013 IBM Corp.
#
# 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 jsonschema
from rally import consts
class ActionBuilder(object):
"""Builder class for mapping and creating action objects.
An action list is an array of single key/value dicts which takes
the form:
[{"action": times}, {"action": times}...]
Here 'action' is a string which indicates a action to perform and
'times' is a non-zero positive integer which specifies how many
times to run the action in sequence.
This utility builder class will build and return methods which
wrapper the action call the given amount of times.
"""
SCHEMA_TEMPLATE = {
"type": "array",
"$schema": consts.JSON_SCHEMA,
"items": {
"type": "object",
"properties": {},
"additionalProperties": False,
"minItems": 0
}
}
ITEM_TEMPLATE = {
"type": "integer",
"minimum": 0,
"exclusiveMinimum": True,
"optional": True
}
def __init__(self, action_keywords):
"""Create a new instance of the builder for the given action keywords.
:param action_keywords: A list of strings which are the keywords this
instance of the builder supports.
"""
self._bindings = {}
self.schema = dict(ActionBuilder.SCHEMA_TEMPLATE)
for kw in action_keywords:
self.schema["items"]["properties"][kw] = (
ActionBuilder.ITEM_TEMPLATE)
def bind_action(self, action_key, action, *args, **kwargs):
"""Bind an action to an action key.
Static args/kwargs can be optionally binded.
:param action_key: The action keyword to bind the action to.
:param action: A method/function to call for the action.
:param args: (optional) Static positional args to prepend
to all invocations of the action.
:param kwargs: (optional) Static kwargs to prepend to all
invocations of the action.
"""
self.validate([{action_key: 1}])
self._bindings[action_key] = {
"action": action,
"args": args or (),
"kwargs": kwargs or {}
}
def validate(self, actions):
"""Validate the list of action objects against the builder schema.
:param actions: The list of action objects to validate.
"""
jsonschema.validate(actions, self.schema)
def _build(self, func, times, *args, **kwargs):
"""Build the wrapper action call."""
def _f():
for i in range(times):
func(*args, **kwargs)
return _f
def build_actions(self, actions, *args, **kwargs):
"""Build a list of callable actions.
A list of callable actions based on the given action object list and
the actions bound to this builder.
:param actions: A list of action objects to build callable
action for.
:param args: (optional) Positional args to pass into each
built action. These will be appended to any args set for the
action via its binding.
:param kwargs: (optional) Keyword args to pass into each built
action. These will be appended to any kwards set for the action
via its binding.
"""
self.validate(actions)
bound_actions = []
for action in actions:
action_key = list(action)[0]
times = action.get(action_key)
binding = self._bindings.get(action_key)
dft_kwargs = dict(binding["kwargs"])
dft_kwargs.update(kwargs or {})
bound_actions.append(
self._build(binding["action"], times,
*(binding["args"] + args), **dft_kwargs))
return bound_actions

View File

@ -17,11 +17,13 @@ import itertools
import time
import traceback
import jsonschema
from novaclient import exceptions as nova_exc
import six
from rally.common.i18n import _
from rally.common import log as logging
from rally import consts
from rally import exceptions
@ -175,3 +177,110 @@ def check_service_status(client, service_name):
"nova. Pre-Grizzly OpenStack deployment?"))
return False
return False
class ActionBuilder(object):
"""Builder class for mapping and creating action objects.
An action list is an array of single key/value dicts which takes
the form:
[{"action": times}, {"action": times}...]
Here 'action' is a string which indicates a action to perform and
'times' is a non-zero positive integer which specifies how many
times to run the action in sequence.
This utility builder class will build and return methods which
wrapper the action call the given amount of times.
"""
SCHEMA_TEMPLATE = {
"type": "array",
"$schema": consts.JSON_SCHEMA,
"items": {
"type": "object",
"properties": {},
"additionalProperties": False,
"minItems": 0
}
}
ITEM_TEMPLATE = {
"type": "integer",
"minimum": 0,
"exclusiveMinimum": True,
"optional": True
}
def __init__(self, action_keywords):
"""Create a new instance of the builder for the given action keywords.
:param action_keywords: A list of strings which are the keywords this
instance of the builder supports.
"""
self._bindings = {}
self.schema = dict(ActionBuilder.SCHEMA_TEMPLATE)
for kw in action_keywords:
self.schema["items"]["properties"][kw] = (
ActionBuilder.ITEM_TEMPLATE)
def bind_action(self, action_key, action, *args, **kwargs):
"""Bind an action to an action key.
Static args/kwargs can be optionally binded.
:param action_key: The action keyword to bind the action to.
:param action: A method/function to call for the action.
:param args: (optional) Static positional args to prepend
to all invocations of the action.
:param kwargs: (optional) Static kwargs to prepend to all
invocations of the action.
"""
self.validate([{action_key: 1}])
self._bindings[action_key] = {
"action": action,
"args": args or (),
"kwargs": kwargs or {}
}
def validate(self, actions):
"""Validate the list of action objects against the builder schema.
:param actions: The list of action objects to validate.
"""
jsonschema.validate(actions, self.schema)
def _build(self, func, times, *args, **kwargs):
"""Build the wrapper action call."""
def _f():
for i in range(times):
func(*args, **kwargs)
return _f
def build_actions(self, actions, *args, **kwargs):
"""Build a list of callable actions.
A list of callable actions based on the given action object list and
the actions bound to this builder.
:param actions: A list of action objects to build callable
action for.
:param args: (optional) Positional args to pass into each
built action. These will be appended to any args set for the
action via its binding.
:param kwargs: (optional) Keyword args to pass into each built
action. These will be appended to any kwards set for the action
via its binding.
"""
self.validate(actions)
bound_actions = []
for action in actions:
action_key = list(action)[0]
times = action.get(action_key)
binding = self._bindings.get(action_key)
dft_kwargs = dict(binding["kwargs"])
dft_kwargs.update(kwargs or {})
bound_actions.append(
self._build(binding["action"], times,
*(binding["args"] + args), **dft_kwargs))
return bound_actions

View File

@ -16,8 +16,8 @@
import jsonschema
from rally.benchmark.scenarios import base
from rally.benchmark.scenarios import utils as scenario_utils
from rally.benchmark import types as types
from rally.benchmark import utils as bench_utils
from rally.benchmark import validation
from rally.common import log as logging
from rally import consts
@ -301,7 +301,7 @@ class NovaServers(utils.NovaScenario,
def _bind_actions(self):
actions = ["hard_reboot", "soft_reboot", "stop_start",
"rescue_unrescue"]
action_builder = scenario_utils.ActionBuilder(actions)
action_builder = bench_utils.ActionBuilder(actions)
action_builder.bind_action("hard_reboot", self._reboot_server)
action_builder.bind_action("soft_reboot", self._soft_reboot_server)
action_builder.bind_action("stop_start",

View File

@ -1,184 +0,0 @@
# Copyright 2013 IBM Corp.
#
# 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.
from jsonschema import exceptions as schema_exceptions
import mock
from rally.benchmark.scenarios import utils
from tests.unit import test
def action_one(self, *args, **kwargs):
pass
def action_two(self, *args, **kwargs):
pass
class ActionBuilderTestCase(test.TestCase):
def setUp(self):
super(ActionBuilderTestCase, self).setUp()
self.mock_one = "%s.action_one" % __name__
self.mock_two = "%s.action_two" % __name__
def test_invalid_keyword(self):
builder = utils.ActionBuilder(["action_one", "action_two"])
self.assertRaises(schema_exceptions.ValidationError,
builder.build_actions, [{"missing": 1}])
def test_invalid_bind(self):
builder = utils.ActionBuilder(["action_one"])
self.assertRaises(schema_exceptions.ValidationError,
builder.bind_action, "missing", action_one)
def test_invalid_schema(self):
builder = utils.ActionBuilder(["action_one", "action_two"])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_oone": 1},
{"action_twoo": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_one": -1},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_one": 0},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{1: 0},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_two": "action_two"}])
def test_positional_args(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "a", "b")
builder.bind_action("action_two", mock_action_two, "c")
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}])
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("a", "b"))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("c"))
mock_action_two.assert_has_calls(mock_calls)
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "a", "b")
builder.bind_action("action_two", mock_action_two, "c")
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
"d", 5)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("a", "b", "d", 5))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("c", "d", 5))
mock_action_two.assert_has_calls(mock_calls)
def test_kwargs(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, a=1, b=2)
builder.bind_action("action_two", mock_action_two, c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}])
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call(a=1, b=2))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call(c=3))
mock_action_two.assert_has_calls(mock_calls)
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, a=1, b=2)
builder.bind_action("action_two", mock_action_two, c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
d=4, e=5)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call(a=1, b=2, d=4, e=5))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call(c=3, d=4, e=5))
mock_action_two.assert_has_calls(mock_calls)
def test_mixed_args(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "one",
a=1, b=2)
builder.bind_action("action_two", mock_action_two, "two", c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
"three", d=4)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("one", "three", a=1, b=2, d=4))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("two", "three", c=3, d=4))
mock_action_two.assert_has_calls(mock_calls)

View File

@ -15,6 +15,7 @@
import datetime
from jsonschema import exceptions as schema_exceptions
import mock
from rally.benchmark import utils
@ -232,3 +233,168 @@ class WaitForTestCase(test.TestCase):
self.assertIn("FakeResource", str(exc))
self.assertIn("fake_new_status", str(exc))
def action_one(self, *args, **kwargs):
pass
def action_two(self, *args, **kwargs):
pass
class ActionBuilderTestCase(test.TestCase):
def setUp(self):
super(ActionBuilderTestCase, self).setUp()
self.mock_one = "%s.action_one" % __name__
self.mock_two = "%s.action_two" % __name__
def test_invalid_keyword(self):
builder = utils.ActionBuilder(["action_one", "action_two"])
self.assertRaises(schema_exceptions.ValidationError,
builder.build_actions, [{"missing": 1}])
def test_invalid_bind(self):
builder = utils.ActionBuilder(["action_one"])
self.assertRaises(schema_exceptions.ValidationError,
builder.bind_action, "missing", action_one)
def test_invalid_schema(self):
builder = utils.ActionBuilder(["action_one", "action_two"])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_oone": 1},
{"action_twoo": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_one": -1},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_one": 0},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{1: 0},
{"action_two": 2}])
self.assertRaises(schema_exceptions.ValidationError,
builder.validate, [{"action_two": "action_two"}])
def test_positional_args(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "a", "b")
builder.bind_action("action_two", mock_action_two, "c")
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}])
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("a", "b"))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("c"))
mock_action_two.assert_has_calls(mock_calls)
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "a", "b")
builder.bind_action("action_two", mock_action_two, "c")
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
"d", 5)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("a", "b", "d", 5))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("c", "d", 5))
mock_action_two.assert_has_calls(mock_calls)
def test_kwargs(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, a=1, b=2)
builder.bind_action("action_two", mock_action_two, c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}])
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call(a=1, b=2))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call(c=3))
mock_action_two.assert_has_calls(mock_calls)
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, a=1, b=2)
builder.bind_action("action_two", mock_action_two, c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
d=4, e=5)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call(a=1, b=2, d=4, e=5))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call(c=3, d=4, e=5))
mock_action_two.assert_has_calls(mock_calls)
def test_mixed_args(self):
with mock.patch(self.mock_one) as mock_action_one:
with mock.patch(self.mock_two) as mock_action_two:
builder = utils.ActionBuilder(["action_one", "action_two"])
builder.bind_action("action_one", mock_action_one, "one",
a=1, b=2)
builder.bind_action("action_two", mock_action_two, "two", c=3)
actions = builder.build_actions([{"action_two": 3},
{"action_one": 4}],
"three", d=4)
for action in actions:
action()
self.assertEqual(4, mock_action_one.call_count,
"action one not called 4 times")
mock_calls = []
for i in range(4):
mock_calls.append(mock.call("one", "three", a=1, b=2, d=4))
mock_action_one.assert_has_calls(mock_calls)
self.assertEqual(3, mock_action_two.call_count,
"action two not called 3 times")
mock_calls = []
for i in range(3):
mock_calls.append(mock.call("two", "three", c=3, d=4))
mock_action_two.assert_has_calls(mock_calls)