From 5806f21ce6cdee2fbbbd8b9507735a03880c0757 Mon Sep 17 00:00:00 2001 From: Kiran Date: Fri, 18 Sep 2015 22:06:45 +0530 Subject: [PATCH] Add Neutron Floating IP benchmark scenarios * Create and list floating IPs * Create and delete floating IPs * Ability to obtain network-id with network-name Change-Id: Iabd6b24fa6c1ee244b514ca90ee5d67a93443fbb --- rally-jobs/rally-neutron.yaml | 40 +++++++++ .../openstack/scenarios/neutron/network.py | 37 ++++++++ .../openstack/scenarios/neutron/utils.py | 57 +++++++++++- .../create_and_delete_floating_ips.json | 26 ++++++ .../create_and_delete_floating_ips.yaml | 17 ++++ .../neutron/create_and_list_floating_ips.json | 26 ++++++ .../neutron/create_and_list_floating_ips.yaml | 17 ++++ tests/unit/fakes.py | 24 ++++++ .../scenarios/neutron/test_network.py | 42 +++++++++ .../openstack/scenarios/neutron/test_utils.py | 86 +++++++++++++++++++ 10 files changed, 368 insertions(+), 4 deletions(-) create mode 100644 samples/tasks/scenarios/neutron/create_and_delete_floating_ips.json create mode 100644 samples/tasks/scenarios/neutron/create_and_delete_floating_ips.yaml create mode 100644 samples/tasks/scenarios/neutron/create_and_list_floating_ips.json create mode 100644 samples/tasks/scenarios/neutron/create_and_list_floating_ips.yaml diff --git a/rally-jobs/rally-neutron.yaml b/rally-jobs/rally-neutron.yaml index 6b48cd6503..ce5e73c4e0 100644 --- a/rally-jobs/rally-neutron.yaml +++ b/rally-jobs/rally-neutron.yaml @@ -44,6 +44,26 @@ failure_rate: max: 20 + NeutronNetworks.create_and_list_floating_ips: + - + args: + floating_network: "public" + floating_ip_args: {} + runner: + type: "constant" + times: {{smoke or 40}} + concurrency: {{smoke or 20}} + context: + users: + tenants: {{smoke or 5}} + users_per_tenant: {{smoke or 2}} + quotas: + neutron: + floatingip: -1 + sla: + failure_rate: + max: 0 + NeutronNetworks.create_and_list_routers: - args: @@ -383,6 +403,26 @@ failure_rate: max: 20 + NeutronNetworks.create_and_delete_floating_ips: + - + args: + floating_network: "public" + floating_ip_args: {} + runner: + type: "constant" + times: {{smoke or 40}} + concurrency: {{smoke or 20}} + context: + users: + tenants: {{smoke or 5}} + users_per_tenant: {{smoke or 2}} + quotas: + neutron: + floatingip: -1 + sla: + failure_rate: + max: 0 + NeutronNetworks.create_and_delete_routers: - args: diff --git a/rally/plugins/openstack/scenarios/neutron/network.py b/rally/plugins/openstack/scenarios/neutron/network.py index 140ce99dfb..ee92943db9 100644 --- a/rally/plugins/openstack/scenarios/neutron/network.py +++ b/rally/plugins/openstack/scenarios/neutron/network.py @@ -329,3 +329,40 @@ class NeutronNetworks(utils.NeutronScenario): for i in range(ports_per_network): port = self._create_port(network, port_create_args or {}) self._delete_port(port) + + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @validation.external_network_exists("floating_network") + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_list_floating_ips(self, floating_network=None, + floating_ip_args=None): + """Create and list floating IPs. + + Measure the "neutron floating-ip-create" and "neutron floating-ip-list" + commands performance. + + :param floating_network: str, external network for floating IP creation + :param floating_ip_args: dict, POST /floatingips request options + """ + floating_ip_args = floating_ip_args or {} + self._create_floatingip(floating_network, **floating_ip_args) + self._list_floating_ips() + + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @validation.external_network_exists("floating_network") + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_delete_floating_ips(self, floating_network=None, + floating_ip_args=None): + """Create and delete floating IPs. + + Measure the "neutron floating-ip-create" and "neutron + floating-ip-delete" commands performance. + + :param floating_network: str, external network for floating IP creation + :param floating_ip_args: dict, POST /floatingips request options + """ + floating_ip_args = floating_ip_args or {} + floating_ip = self._create_floatingip(floating_network, + **floating_ip_args) + self._delete_floating_ip(floating_ip["floatingip"]) diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index 0e58041fc9..3aaf93d630 100644 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -15,6 +15,7 @@ from rally.common.i18n import _ from rally.common import log as logging +from rally import exceptions from rally.plugins.openstack import scenario from rally.plugins.openstack.wrappers import network as network_wrapper from rally.task import atomic @@ -63,6 +64,20 @@ class NeutronScenario(scenario.OpenStackScenario): "id": list(resource.values())[0]["id"], "name": kwargs["name"]}) + def _get_network_id(self, network, **kwargs): + """Get Neutron network ID for the network name. + + param network: str, network name/id + param kwargs: dict, network options + returns: str, Neutron network-id + """ + networks = self._list_networks(atomic_action=False) + for net in networks: + if (net["name"] == network) or (net["id"] == network): + return net["id"] + msg = (_("Network %s not found.") % network) + raise exceptions.NotFoundException(message=msg) + @atomic.action_timer("neutron.create_network") def _create_network(self, network_create_args): """Create neutron network. @@ -74,10 +89,17 @@ class NeutronScenario(scenario.OpenStackScenario): return self.clients("neutron").create_network( {"network": network_create_args}) - @atomic.action_timer("neutron.list_networks") - def _list_networks(self): - """Return user networks list.""" - return self.clients("neutron").list_networks()["networks"] + def _list_networks(self, atomic_action=True, **kwargs): + """Return user networks list. + + :param atomic_action: True if this is an atomic action + :param kwargs: network list options + """ + if atomic_action: + with atomic.ActionTimer(self, "neutron.list_networks"): + return self.clients("neutron").list_networks( + **kwargs)["networks"] + return self.clients("neutron").list_networks(**kwargs)["networks"] @atomic.action_timer("neutron.update_network") def _update_network(self, network, network_update_args): @@ -390,3 +412,30 @@ class NeutronScenario(scenario.OpenStackScenario): self._warn_about_deprecated_name_kwarg(vip, vip_update_args) body = {"vip": vip_update_args} return self.clients("neutron").update_vip(vip["vip"]["id"], body) + + @atomic.action_timer("neutron.create_floating_ip") + def _create_floatingip(self, floating_network, **floating_ip_args): + """Create floating IP with floating_network. + + param: floating_network: str, external network to create floating IP + param: floating_ip_args: dict, POST /floatingips create options + returns: dict, neutron floating IP + """ + floating_network_id = self._get_network_id( + floating_network) + args = {"floating_network_id": floating_network_id} + args.update(floating_ip_args) + return self.clients("neutron").create_floatingip({"floatingip": args}) + + @atomic.action_timer("neutron.list_floating_ips") + def _list_floating_ips(self, **kwargs): + """Return floating IPs list.""" + return self.clients("neutron").list_floatingips(**kwargs) + + @atomic.action_timer("neutron.delete_floating_ip") + def _delete_floating_ip(self, floating_ip): + """Delete floating IP. + + :param: dict, floating IP object + """ + return self.clients("neutron").delete_floatingip(floating_ip["id"]) diff --git a/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.json b/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.json new file mode 100644 index 0000000000..42ab99c558 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.json @@ -0,0 +1,26 @@ +{ + "NeutronNetworks.create_and_delete_floating_ips": [ + { + "args": { + "floating_network": "public", + "floating_ip_args": {} + }, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 5 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 3 + }, + "quotas": { + "neutron": { + "floatingip": -1 + } + } + } + } + ] +} diff --git a/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.yaml b/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.yaml new file mode 100644 index 0000000000..e5f2c6e779 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create_and_delete_floating_ips.yaml @@ -0,0 +1,17 @@ +--- + NeutronNetworks.create_and_delete_floating_ips: + - + args: + floating_network: "public" + floating_ip_args: {} + runner: + type: "constant" + times: 10 + concurrency: 5 + context: + users: + tenants: 2 + users_per_tenant: 3 + quotas: + neutron: + floatingip: -1 diff --git a/samples/tasks/scenarios/neutron/create_and_list_floating_ips.json b/samples/tasks/scenarios/neutron/create_and_list_floating_ips.json new file mode 100644 index 0000000000..63b3a618c8 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create_and_list_floating_ips.json @@ -0,0 +1,26 @@ +{ + "NeutronNetworks.create_and_list_floating_ips": [ + { + "args": { + "floating_network": "public", + "floating_ip_args": {} + }, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 5 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 3 + }, + "quotas": { + "neutron": { + "floatingip": -1 + } + } + } + } + ] +} diff --git a/samples/tasks/scenarios/neutron/create_and_list_floating_ips.yaml b/samples/tasks/scenarios/neutron/create_and_list_floating_ips.yaml new file mode 100644 index 0000000000..f54083a387 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create_and_list_floating_ips.yaml @@ -0,0 +1,17 @@ +--- + NeutronNetworks.create_and_list_floating_ips: + - + args: + floating_network: "public" + floating_ip_args: {} + runner: + type: "constant" + times: 10 + concurrency: 5 + context: + users: + tenants: 2 + users_per_tenant: 3 + quotas: + neutron: + floatingip: -1 diff --git a/tests/unit/fakes.py b/tests/unit/fakes.py index fced5e99e6..c7b7aff26d 100644 --- a/tests/unit/fakes.py +++ b/tests/unit/fakes.py @@ -1036,6 +1036,7 @@ class FakeNeutronClient(object): self.__ports = {} self.__pools = {} self.__vips = {} + self.__fips = {} self.__tenant_id = kwargs.get("tenant_id", generate_uuid()) self.format = "json" @@ -1116,6 +1117,19 @@ class FakeNeutronClient(object): self.__vips[vip_id] = vip return {"vip": vip} + def create_floatingip(self, data): + fip = setup_dict(data["floatingip"], + required=["floating_network"], + defaults={"admin_state_up": True}) + if (fip["floating_network"] not in self.__nets): + raise neutron_exceptions.NeutronClientException + fip_id = generate_uuid() + + fip.update({"id": fip_id, + "tenant_id": self.__tenant_id}) + self.__fips[fip_id] = fip + return {"fip": fip} + def create_port(self, data): port = setup_dict(data["port"], required=["network_id"], @@ -1223,6 +1237,12 @@ class FakeNeutronClient(object): del self.__vips[vip_id] return "" + def delete_floatingip(self, fip_id): + if fip_id not in self.__fips: + raise neutron_exceptions.NeutronClientException + del self.__fips[fip_id] + return "" + def delete_port(self, port_id): if port_id not in self.__ports: raise neutron_exceptions.PortNotFoundClient @@ -1277,6 +1297,10 @@ class FakeNeutronClient(object): subnets = self._filter(self.__subnets.values(), search_opts) return {"subnets": subnets} + def list_floatingips(self, **search_opts): + fips = self._filter(self.__fips.values(), search_opts) + return {"floatingips": fips} + def remove_interface_router(self, router_id, data): subnet_id = data["subnet_id"] diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_network.py b/tests/unit/plugins/openstack/scenarios/neutron/test_network.py index 6daa81d6ee..392aae97fd 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_network.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_network.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ddt import mock from rally.plugins.openstack.scenarios.neutron import network @@ -22,6 +23,7 @@ NEUTRON_NETWORKS = ("rally.plugins.openstack.scenarios.neutron.network" ".NeutronNetworks") +@ddt.ddt class NeutronNetworksTestCase(test.ScenarioTestCase): @mock.patch(NEUTRON_NETWORKS + "._list_networks") @@ -627,3 +629,43 @@ class NeutronNetworksTestCase(test.ScenarioTestCase): self.assertEqual( mock__delete_port.mock_calls, [mock.call(mock__create_port.return_value)] * ports_per_network) + + @ddt.data( + {"floating_network": "ext-net"}, + {"floating_network": "ext-net", + "floating_ip_args": {"floating_ip_address": "1.1.1.1"}}, + ) + @ddt.unpack + def test_create_and_list_floating_ips(self, floating_network=None, + floating_ip_args=None): + scenario = network.NeutronNetworks() + floating_ip_args = floating_ip_args or {} + scenario._create_floatingip = mock.Mock() + scenario._list_floating_ips = mock.Mock() + scenario.create_and_list_floating_ips( + floating_network=floating_network, + floating_ip_args=floating_ip_args) + scenario._create_floatingip.assert_called_once_with( + floating_network, **floating_ip_args) + scenario._list_floating_ips.assert_called_once_with() + + @ddt.data( + {"floating_network": "ext-net"}, + {"floating_network": "ext-net", + "floating_ip_args": {"floating_ip_address": "1.1.1.1"}}, + ) + @ddt.unpack + def test_create_and_delete_floating_ips(self, floating_network=None, + floating_ip_args=None): + scenario = network.NeutronNetworks() + floating_ip_args = floating_ip_args or {} + fip = {"floatingip": {"id": "floating-ip-id"}} + scenario._create_floatingip = mock.Mock(return_value=fip) + scenario._delete_floating_ip = mock.Mock() + scenario.create_and_delete_floating_ips( + floating_network=floating_network, + floating_ip_args=floating_ip_args) + scenario._create_floatingip.assert_called_once_with( + floating_network, **floating_ip_args) + scenario._delete_floating_ip.assert_called_once_with( + scenario._create_floatingip.return_value["floatingip"]) diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py index 38102a295a..317362632c 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py @@ -16,6 +16,7 @@ import ddt import mock +from rally import exceptions from rally.plugins.openstack.scenarios.neutron import utils from tests.unit import test @@ -29,6 +30,38 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): super(NeutronScenarioTestCase, self).setUp() self.network = mock.Mock() + def test__get_network_id(self): + neutron_scenario = utils.NeutronScenario() + networks = [{"id": "foo-id", "name": "foo-network"}, + {"id": "bar-id", "name": "bar-network"}] + network_id = "foo-id" + + # Valid network-name + network = "foo-network" + neutron_scenario._list_networks = mock.Mock(return_value=networks) + resultant_network_id = neutron_scenario._get_network_id(network) + self.assertEqual(network_id, resultant_network_id) + neutron_scenario._list_networks.assert_called_once_with( + atomic_action=False) + + neutron_scenario._list_networks.reset_mock() + + # Valid network-id + network = "foo-id" + resultant_network_id = neutron_scenario._get_network_id(network) + self.assertEqual(network_id, resultant_network_id) + neutron_scenario._list_networks.assert_called_once_with( + atomic_action=False) + neutron_scenario._list_networks.reset_mock() + + # Invalid network-name + network = "absent-network" + self.assertRaises(exceptions.NotFoundException, + neutron_scenario._get_network_id, + network) + neutron_scenario._list_networks.assert_called_once_with( + atomic_action=False) + @mock.patch(NEUTRON_UTILS + "NeutronScenario._generate_random_name") def test_create_network(self, mock__generate_random_name): neutron_scenario = utils.NeutronScenario() @@ -50,6 +83,12 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): networks_list = [] networks_dict = {"networks": networks_list} self.clients("neutron").list_networks.return_value = networks_dict + + # without atomic action + return_networks_list = scenario._list_networks(atomic_action=False) + self.assertEqual(networks_list, return_networks_list) + + # with atomic action return_networks_list = scenario._list_networks() self.assertEqual(networks_list, return_networks_list) self._test_atomic_action_timer(scenario.atomic_actions(), @@ -431,6 +470,26 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): {"allocation_pools": []}, "10.10.10.0/24")] * subnets_per_network) + def test_list_floating_ips(self): + scenario = utils.NeutronScenario() + fips_list = [{"id": "floating-ip-id"}] + fips_dict = {"floatingips": fips_list} + self.clients("neutron").list_floatingips.return_value = fips_dict + self.assertEqual(scenario._list_floating_ips(), + self.clients("neutron").list_floatingips.return_value) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.list_floating_ips") + + def test_delete_floating_ip(self): + scenario = utils.NeutronScenario() + + fip = {"floatingip": {"id": "fake-id"}} + scenario._delete_floating_ip(fip["floatingip"]) + self.clients("neutron").delete_floatingip.assert_called_once_with( + fip["floatingip"]["id"]) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.delete_floating_ip") + def test_delete_v1_pool(self): scenario = utils.NeutronScenario() @@ -639,3 +698,30 @@ class NeutronLoadbalancerScenarioTestCase(test.ScenarioTestCase): self.assertEqual(resultant_vip, vip) self.clients("neutron").create_vip.assert_called_once_with( expected_vip_data) + + @ddt.data( + {}, + {"floating_ip_args": {}}, + {"floating_ip_args": {"floating_ip_address": "1.0.0.1"}}, + ) + @ddt.unpack + def test__create_floating_ip(self, floating_ip_args=None): + neutron_scenario = utils.NeutronScenario() + floating_network = "floating" + fip = {"floatingip": {"id": "fip-id"}} + network_id = "net-id" + floating_ip_args = floating_ip_args or {} + self.clients("neutron").create_floatingip.return_value = fip + mock_get_network_id = neutron_scenario._get_network_id = mock.Mock() + mock_get_network_id.return_value = network_id + args = {"floating_network_id": network_id} + args.update(floating_ip_args) + expected_fip_data = {"floatingip": args} + resultant_fip = neutron_scenario._create_floatingip( + floating_network, **floating_ip_args) + self.assertEqual(resultant_fip, fip) + self.clients("neutron").create_floatingip.assert_called_once_with( + expected_fip_data) + mock_get_network_id.assert_called_once_with(floating_network) + self._test_atomic_action_timer(neutron_scenario.atomic_actions(), + "neutron.create_floating_ip")