diff --git a/rally-jobs/nova.yaml b/rally-jobs/nova.yaml index 4927d12d..fd2816b8 100755 --- a/rally-jobs/nova.yaml +++ b/rally-jobs/nova.yaml @@ -163,6 +163,26 @@ failure_rate: max: 0 + NovaServerGroups.create_and_list_server_groups: + {% for s in (["affinity"], ["anti-affinity"]) %} + - + args: + kwargs: + policies: {{s}} + all_projects: false + runner: + type: "constant" + times: 4 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 + {% endfor %} + NovaServers.suspend_and_resume_server: - args: diff --git a/rally/plugins/openstack/cleanup/resources.py b/rally/plugins/openstack/cleanup/resources.py index 5a967bba..0981510b 100644 --- a/rally/plugins/openstack/cleanup/resources.py +++ b/rally/plugins/openstack/cleanup/resources.py @@ -185,6 +185,14 @@ class NovaServer(base.ResourceManager): super(NovaServer, self).delete() +@base.resource("nova", "server_groups", order=next(_nova_order), + tenant_resource=True) +class NovaServerGroups(base.ResourceManager): + def list(self): + return [r for r in self._manager().list() + if utils.name_matches_object(r.name, nova_utils.NovaScenario)] + + @base.resource("nova", "floating_ips", order=next(_nova_order)) class NovaFloatingIPs(SynchronizedDeletion, base.ResourceManager): diff --git a/rally/plugins/openstack/scenarios/nova/server_groups.py b/rally/plugins/openstack/scenarios/nova/server_groups.py new file mode 100755 index 00000000..910206d6 --- /dev/null +++ b/rally/plugins/openstack/scenarios/nova/server_groups.py @@ -0,0 +1,55 @@ +# Copyright 2017: Inc. +# 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. + +from rally.common import logging +from rally import consts +from rally.plugins.openstack import scenario +from rally.plugins.openstack.scenarios.nova import utils +from rally.task import validation + + +"""Scenarios for Nova Group servers.""" + + +LOG = logging.getLogger(__name__) + + +@validation.required_services(consts.Service.NOVA) +@validation.required_openstack(users=True) +@scenario.configure(context={"cleanup": ["nova"]}, + name="NovaServerGroups.create_and_list_server_groups") +class CreateAndListServerGroups(utils.NovaScenario): + + def run(self, all_projects=False, kwargs=None): + """Create a server group, then list all server groups. + + Measure the "nova server-group-create" and "nova server-group-list" + command performance. + + :param all_projects: If True, display server groups from all + projects(Admin only) + :param kwargs: Server group name and policy + """ + kwargs["name"] = self.generate_random_name() + server_group = self._create_server_group(**kwargs) + msg = ("Server Groups isn't created") + self.assertTrue(server_group, err_msg=msg) + + server_groups_list = self._list_server_groups(all_projects) + msg = ("Server Group not included into list of server groups\n" + "Created server group: {}\n" + "list of server groups: {}").format(server_group, + server_groups_list) + self.assertIn(server_group, server_groups_list, err_msg=msg) diff --git a/rally/plugins/openstack/scenarios/nova/utils.py b/rally/plugins/openstack/scenarios/nova/utils.py index 4741dd94..07671ab7 100755 --- a/rally/plugins/openstack/scenarios/nova/utils.py +++ b/rally/plugins/openstack/scenarios/nova/utils.py @@ -456,6 +456,30 @@ class NovaScenario(scenario.OpenStackScenario): benchmark.nova_server_delete_poll_interval ) + @atomic.action_timer("nova.create_server_group") + def _create_server_group(self, **kwargs): + """Create (allocate) a server group. + + :param kwargs: Server group name and policy + + :returns: Nova server group + """ + return self.clients("nova").server_groups.create(**kwargs) + + @atomic.action_timer("nova.list_server_groups") + def _list_server_groups(self, all_projects=False): + """Get a list of all server groups. + + :param all_projects: If True, display server groups from all + projects(Admin only) + + :rtype: list of :class:`ServerGroup`. + """ + if all_projects: + return self.admin_clients("nova").server_groups.list(all_projects) + else: + return self.clients("nova").server_groups.list(all_projects) + @atomic.action_timer("nova.delete_image") def _delete_image(self, image): """Delete the given image. diff --git a/samples/tasks/scenarios/nova/create-and-list-server-groups.json b/samples/tasks/scenarios/nova/create-and-list-server-groups.json new file mode 100644 index 00000000..bdf06335 --- /dev/null +++ b/samples/tasks/scenarios/nova/create-and-list-server-groups.json @@ -0,0 +1,30 @@ +{ + "NovaServerGroups.create_and_list_server_groups": [ + { + "args": { + "kwargs": { + "policies": [ + "affinity" + ] + }, + "all_projects": false + }, + "runner": { + "type": "constant", + "times": 4, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/nova/create-and-list-server-groups.yaml b/samples/tasks/scenarios/nova/create-and-list-server-groups.yaml new file mode 100644 index 00000000..cc4dbe59 --- /dev/null +++ b/samples/tasks/scenarios/nova/create-and-list-server-groups.yaml @@ -0,0 +1,17 @@ + NovaServerGroups.create_and_list_server_groups: + - + args: + kwargs: + policies: ["affinity"] + all_projects: false + runner: + type: "constant" + times: 4 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 diff --git a/tests/unit/plugins/openstack/cleanup/test_resources.py b/tests/unit/plugins/openstack/cleanup/test_resources.py index 2215c83b..ac772ce7 100644 --- a/tests/unit/plugins/openstack/cleanup/test_resources.py +++ b/tests/unit/plugins/openstack/cleanup/test_resources.py @@ -190,6 +190,23 @@ class NovaAggregatesTestCase(test.TestCase): [mock.call(r.name, nutils.NovaScenario) for r in aggregates]) +class NovaServerGroupsTestCase(test.TestCase): + + @mock.patch("%s.base.ResourceManager._manager" % BASE) + @mock.patch("rally.common.utils.name_matches_object") + def test_list(self, mock_name_matches_object, + mock_resource_manager__manager): + server_groups = [mock.MagicMock(name="rally_foo1"), + mock.MagicMock(name="rally_foo2"), + mock.MagicMock(name="foo3")] + mock_name_matches_object.side_effect = [False, True, True] + mock_resource_manager__manager().list.return_value = server_groups + self.assertEqual(server_groups[1:], + resources.NovaServerGroups().list()) + mock_name_matches_object.assert_has_calls( + [mock.call(r.name, nutils.NovaScenario) for r in server_groups]) + + class NovaSecurityGroupTestCase(test.TestCase): @mock.patch("%s.base.ResourceManager._manager" % BASE) diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_server_groups.py b/tests/unit/plugins/openstack/scenarios/nova/test_server_groups.py new file mode 100755 index 00000000..7290bb4e --- /dev/null +++ b/tests/unit/plugins/openstack/scenarios/nova/test_server_groups.py @@ -0,0 +1,62 @@ +# Copyright 2017: Inc. +# 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 ddt +import mock + +from rally import exceptions as rally_exceptions +from rally.plugins.openstack.scenarios.nova import server_groups +from tests.unit import test + +SERVER_GROUPS_MODULE = "rally.plugins.openstack.scenarios.nova.server_groups" +NOVA_SERVER_GROUPS = SERVER_GROUPS_MODULE + ".NovaServerGroups" + + +@ddt.ddt +class NovaServerGroupsTestCase(test.ScenarioTestCase): + + def test_create_and_list_server_groups(self): + scenario = server_groups.CreateAndListServerGroups(self.context) + gen_name = mock.MagicMock() + scenario.generate_random_name = gen_name + all_projects = False + create_args = {"policies": ["fake_policy"]} + fake_server_group = mock.MagicMock() + scenario._create_server_group = mock.MagicMock() + scenario._list_server_groups = mock.MagicMock() + scenario._list_server_groups.return_value = [mock.MagicMock(), + fake_server_group, + mock.MagicMock()] + # Positive case + scenario._create_server_group.return_value = fake_server_group + scenario.run(kwargs=create_args) + scenario._create_server_group.assert_called_once_with(**create_args) + scenario._list_server_groups.assert_called_once_with(all_projects) + + # Negative case1: server group isn't created + scenario._create_server_group.return_value = None + self.assertRaises(rally_exceptions.RallyAssertionError, + scenario.run, + kwargs=create_args) + scenario._create_server_group.assert_called_with(**create_args) + + # Negative case2: server group not in the list of available server + # groups + scenario._create_server_group.return_value = mock.MagicMock() + self.assertRaises(rally_exceptions.RallyAssertionError, + scenario.run, + kwargs=create_args) + scenario._create_server_group.assert_called_with(**create_args) + scenario._list_server_groups.assert_called_with(all_projects) diff --git a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py index 1c09f03b..495c102b 100755 --- a/tests/unit/plugins/openstack/scenarios/nova/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/nova/test_utils.py @@ -806,6 +806,32 @@ class NovaScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(nova_scenario.atomic_actions(), "nova.create_keypair") + def test__create_server_group(self): + nova_scenario = utils.NovaScenario() + result = nova_scenario._create_server_group(fakeargs="fakeargs") + self.assertEqual( + self.clients("nova").server_groups.create.return_value, + result) + self.clients("nova").server_groups.create.assert_called_once_with( + fakeargs="fakeargs") + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.create_server_group") + + def test__list_server_groups(self): + nova_scenario = utils.NovaScenario() + result1 = nova_scenario._list_server_groups(all_projects=False) + result2 = nova_scenario._list_server_groups(all_projects=True) + self.assertEqual(self.clients("nova").server_groups.list.return_value, + result1) + admcli = self.admin_clients("nova") + self.assertEqual(admcli.server_groups.list.return_value, result2) + self.clients("nova").server_groups.list.assert_called_once_with( + False) + self.admin_clients("nova").server_groups.list.assert_called_once_with( + True) + self._test_atomic_action_timer(nova_scenario.atomic_actions(), + "nova.list_server_groups") + def test__delete_keypair(self): nova_scenario = utils.NovaScenario() nova_scenario._delete_keypair("fake_keypair")