diff --git a/rally-jobs/rally-watcher.yaml b/rally-jobs/rally-watcher.yaml new file mode 100644 index 0000000000..d4f8cc4eb1 --- /dev/null +++ b/rally-jobs/rally-watcher.yaml @@ -0,0 +1,20 @@ +--- + Watcher.create_audit_template_and_delete: + - + args: + goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 3 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 \ No newline at end of file diff --git a/rally/plugins/openstack/scenarios/watcher/__init__.py b/rally/plugins/openstack/scenarios/watcher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rally/plugins/openstack/scenarios/watcher/basic.py b/rally/plugins/openstack/scenarios/watcher/basic.py new file mode 100644 index 0000000000..cd19d6c8f8 --- /dev/null +++ b/rally/plugins/openstack/scenarios/watcher/basic.py @@ -0,0 +1,40 @@ +# 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 rally import consts +from rally.plugins.openstack import scenario +from rally.plugins.openstack.scenarios.watcher import utils +from rally.task import types +from rally.task import validation + + +class Watcher(utils.WatcherScenario): + """Benchmark scenarios for Watcher servers.""" + + @types.convert(strategy={"type": "watcher_strategy"}, + goal={"type": "watcher_goal"}) + @validation.required_services(consts.Service.WATCHER) + @validation.required_openstack(admin=True) + @scenario.configure(context={"admin_cleanup": ["watcher"]}) + def create_audit_template_and_delete(self, goal, strategy, extra=None): + """Create audit template and delete it. + + :param goal: The goal audit template is based on + :param strategy: The strategy used to provide resource optimization + algorithm + :param extra: This field is used to specify some audit template + options + """ + + extra = extra or {} + audit_template = self._create_audit_template(goal, strategy, extra) + self._delete_audit_template(audit_template.uuid) diff --git a/rally/plugins/openstack/scenarios/watcher/utils.py b/rally/plugins/openstack/scenarios/watcher/utils.py new file mode 100644 index 0000000000..e11d40075d --- /dev/null +++ b/rally/plugins/openstack/scenarios/watcher/utils.py @@ -0,0 +1,41 @@ +# 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 rally.plugins.openstack import scenario +from rally.task import atomic + + +class WatcherScenario(scenario.OpenStackScenario): + """Base class for Watcher scenarios with basic atomic actions.""" + + @atomic.action_timer("watcher.create_audit_template") + def _create_audit_template(self, goal_id, strategy_id, extra): + """Create Audit Template in DB + + :param goal_id: UUID Goal + :param strategy_id: UUID Strategy + :param extra: Audit Template Extra (JSON Dict) + :return: Audit Template object + """ + return self.admin_clients("watcher").audit_template.create( + goal=goal_id, + strategy=strategy_id, + name=self.generate_random_name(), + extra=extra or {}) + + @atomic.action_timer("watcher.delete_audit_template") + def _delete_audit_template(self, audit_template): + """Delete Audit Template from DB + + :param audit_template: Audit Template object + """ + self.admin_clients("watcher").audit_template.delete(audit_template) diff --git a/rally/plugins/openstack/types.py b/rally/plugins/openstack/types.py index 3efef732e8..3284472661 100644 --- a/rally/plugins/openstack/types.py +++ b/rally/plugins/openstack/types.py @@ -166,3 +166,51 @@ class NeutronNetwork(types.ResourceType): raise exceptions.InvalidScenarioArgument( "Neutron network with name '{name}' not found".format( name=resource_config.get("name"))) + + +@plugin.configure(name="watcher_strategy") +class WatcherStrategy(types.ResourceType): + + @classmethod + def transform(cls, clients, resource_config): + """Transform the resource config to id. + + :param clients: openstack admin client handles + :param resource_config: scenario config with `id`, `name` or `regex` + + :returns: id matching resource + """ + resource_id = resource_config.get("id") + if not resource_id: + watcherclient = clients.watcher() + resource_id = types._id_from_name( + resource_config=resource_config, + resources=[watcherclient.strategy.get( + resource_config.get("name"))], + typename="strategy", + id_attr="uuid") + return resource_id + + +@plugin.configure(name="watcher_goal") +class WatcherGoal(types.ResourceType): + + @classmethod + def transform(cls, clients, resource_config): + """Transform the resource config to id. + + :param clients: openstack admin client handles + :param resource_config: scenario config with `id`, `name` or `regex` + + :returns: id matching resource + """ + resource_id = resource_config.get("id") + if not resource_id: + watcherclient = clients.watcher() + resource_id = types._id_from_name( + resource_config=resource_config, + resources=[watcherclient.goal.get( + resource_config.get("name"))], + typename="goal", + id_attr="uuid") + return resource_id diff --git a/samples/tasks/scenarios/watcher/create-audit-template-and-delete.json b/samples/tasks/scenarios/watcher/create-audit-template-and-delete.json new file mode 100644 index 0000000000..ad764fc9ae --- /dev/null +++ b/samples/tasks/scenarios/watcher/create-audit-template-and-delete.json @@ -0,0 +1,20 @@ +{ + "Watcher.create_audit_template_and_delete": [ + { + "args": { + "goal": { + "name": "dummy" + }, + "strategy": { + "name": "dummy" + }, + "extra": {} + }, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + } + } + ] +} diff --git a/samples/tasks/scenarios/watcher/create-audit-template-and-delete.yaml b/samples/tasks/scenarios/watcher/create-audit-template-and-delete.yaml new file mode 100644 index 0000000000..fa19c17994 --- /dev/null +++ b/samples/tasks/scenarios/watcher/create-audit-template-and-delete.yaml @@ -0,0 +1,13 @@ +--- + Watcher.create_audit_template_and_delete: + - + args: + goal: + name: "dummy" + strategy: + name: "dummy" + extra: {} + runner: + type: "constant" + times: 10 + concurrency: 2 diff --git a/tests/unit/fakes.py b/tests/unit/fakes.py index 3bf278bb1f..92b5d6249a 100644 --- a/tests/unit/fakes.py +++ b/tests/unit/fakes.py @@ -434,15 +434,17 @@ class FakeImageManager(FakeManager): class FakeStrategyManager(FakeManager): - - def create(self): - return FakeStrategy(self) + def get(self, resource_name): + for key in self.resources_order: + if self.cache[key].name == resource_name: + return self.cache[key] class FakeGoalManager(FakeManager): - - def create(self): - return FakeGoal(self) + def get(self, resource_name): + for key in self.resources_order: + if self.cache[key].name == resource_name: + return self.cache[key] class FakePackageManager(FakeManager): diff --git a/tests/unit/plugins/openstack/scenarios/watcher/__init__.py b/tests/unit/plugins/openstack/scenarios/watcher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py b/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py new file mode 100644 index 0000000000..d343d1fcdb --- /dev/null +++ b/tests/unit/plugins/openstack/scenarios/watcher/test_basic.py @@ -0,0 +1,35 @@ +# Copyright 2016: Servionica LTD. +# 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 mock + +from rally.plugins.openstack.scenarios.watcher import basic +from tests.unit import test + + +class WatcherTestCase(test.ScenarioTestCase): + + def test_create_audit_template_and_delete(self): + scenario = basic.Watcher(self.context) + audit_template = mock.Mock() + scenario._create_audit_template = mock.MagicMock( + return_value=audit_template) + scenario._delete_audit_template = mock.MagicMock() + scenario.create_audit_template_and_delete("goal", "strategy", {}) + scenario._create_audit_template.assert_called_once_with("goal", + "strategy", + {}) + scenario._delete_audit_template.assert_called_once_with( + audit_template.uuid) diff --git a/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py b/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py new file mode 100644 index 0000000000..481e6c2320 --- /dev/null +++ b/tests/unit/plugins/openstack/scenarios/watcher/test_utils.py @@ -0,0 +1,47 @@ +# Copyright 2016: Servionica LTD. +# 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 mock + +from rally.plugins.openstack.scenarios.watcher import utils +from tests.unit import test + + +class WatcherScenarioTestCase(test.ScenarioTestCase): + + def setUp(self): + super(WatcherScenarioTestCase, self).setUp() + + def test_create_audit_template(self): + watcher_scenario = utils.WatcherScenario(self.context) + watcher_scenario.generate_random_name = mock.MagicMock( + return_value="mock_name") + watcher_scenario._create_audit_template("fake_goal", "fake_strategy", + {}) + self.admin_clients( + "watcher").audit_template.create.assert_called_once_with( + goal="fake_goal", strategy="fake_strategy", + name="mock_name", extra={}) + self._test_atomic_action_timer(watcher_scenario.atomic_actions(), + "watcher.create_audit_template") + + def test_delete_audit_template(self): + watcher_scenario = utils.WatcherScenario(self.context) + watcher_scenario._delete_audit_template("fake_audit_template") + self.admin_clients( + "watcher").audit_template.delete.assert_called_once_with( + "fake_audit_template") + self._test_atomic_action_timer(watcher_scenario.atomic_actions(), + "watcher.delete_audit_template") diff --git a/tests/unit/plugins/openstack/test_types.py b/tests/unit/plugins/openstack/test_types.py index 40c59c45d5..d4e8e28d8c 100644 --- a/tests/unit/plugins/openstack/test_types.py +++ b/tests/unit/plugins/openstack/test_types.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ddt import mock from rally import exceptions @@ -325,3 +326,51 @@ class NeutronNetworkTestCase(test.TestCase): self.assertRaises(exceptions.InvalidScenarioArgument, types.NeutronNetwork.transform, self.clients, resource_config) + + +@ddt.ddt +class WatcherStrategyTestCase(test.TestCase): + + def setUp(self): + super(WatcherStrategyTestCase, self).setUp() + self.clients = fakes.FakeClients() + self.strategy = self.clients.watcher().strategy._cache( + fakes.FakeResource(name="dummy", id="1")) + + @ddt.data({"resource_config": {"name": "dummy"}}) + @ddt.unpack + def test_transform_by_name(self, resource_config=None): + strategy_id = types.WatcherStrategy.transform(self.clients, + resource_config) + self.assertEqual(self.strategy.uuid, strategy_id) + + @ddt.data({"resource_config": {"name": "dummy-1"}}) + @ddt.unpack + def test_transform_by_name_no_match(self, resource_config=None): + self.assertRaises(exceptions.RallyException, + types.WatcherStrategy.transform, + self.clients, resource_config) + + +@ddt.ddt +class WatcherGoalTestCase(test.TestCase): + + def setUp(self): + super(WatcherGoalTestCase, self).setUp() + self.clients = fakes.FakeClients() + self.goal = self.clients.watcher().goal._cache( + fakes.FakeResource(name="dummy", id="1")) + + @ddt.data({"resource_config": {"name": "dummy"}}) + @ddt.unpack + def test_transform_by_name(self, resource_config=None): + goal_id = types.WatcherGoal.transform(self.clients, + resource_config) + self.assertEqual(self.goal.uuid, goal_id) + + @ddt.data({"resource_config": {"name": "dummy-1"}}) + @ddt.unpack + def test_transform_by_name_no_match(self, resource_config=None): + self.assertRaises(exceptions.RallyException, + types.WatcherGoal.transform, + self.clients, resource_config)