From bc9e8d06600a4cecdf43afe36fc74035d2c213a5 Mon Sep 17 00:00:00 2001 From: Maplalabs Date: Fri, 22 May 2015 15:43:36 +0530 Subject: [PATCH] Add Neutron Loadbalancer v1 create and list pool Enable benchmarking of the Neutron LoadBalancer v1 pool-create and pool-list APIs. Due to the different API scheme and confusing naming conventions in Neutron (LB is v1 API and LBaaS for v2 API) the appropriate code is explicitly marked with 'v1' since this only implements the API v1 benchmarking. Change-Id: If51f6286be49724c0bc4ae343815704f01153e97 --- rally-jobs/rally-neutron.yaml | 22 ++++++++ .../context/quotas/neutron_quotas.py | 4 ++ .../scenarios/neutron/loadbalancer_v1.py | 38 ++++++++++++++ .../openstack/scenarios/neutron/utils.py | 22 ++++++++ tests/unit/fakes.py | 20 ++++++++ .../scenarios/neutron/test_loadbalancer_v1.py | 45 +++++++++++++++++ .../openstack/scenarios/neutron/test_utils.py | 50 +++++++++++++++++++ 7 files changed, 201 insertions(+) create mode 100644 rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py create mode 100644 tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py diff --git a/rally-jobs/rally-neutron.yaml b/rally-jobs/rally-neutron.yaml index 3960ce69..0249d0c2 100644 --- a/rally-jobs/rally-neutron.yaml +++ b/rally-jobs/rally-neutron.yaml @@ -91,6 +91,28 @@ failure_rate: max: 20 + NeutronLoadbalancerV1.create_and_list_pools: + - + args: + pool_create_args: {} + runner: + type: "constant" + times: 20 + concurrency: 10 + context: + users: + tenants: 1 + users_per_tenant: 1 + network: {} + quotas: + neutron: + network: -1 + subnet: -1 + pool: -1 + sla: + failure_rate: + max: 0 + NeutronNetworks.create_and_update_networks: - args: diff --git a/rally/plugins/openstack/context/quotas/neutron_quotas.py b/rally/plugins/openstack/context/quotas/neutron_quotas.py index 67728099..ff91bc0a 100644 --- a/rally/plugins/openstack/context/quotas/neutron_quotas.py +++ b/rally/plugins/openstack/context/quotas/neutron_quotas.py @@ -52,6 +52,10 @@ class NeutronQuotas(object): "security_group_rule": { "type": "integer", "minimum": -1 + }, + "pool": { + "type": "integer", + "minimum": -1 } } } diff --git a/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py b/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py new file mode 100644 index 00000000..a0de8426 --- /dev/null +++ b/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py @@ -0,0 +1,38 @@ +# 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.benchmark.scenarios import base +from rally.benchmark import validation +from rally import consts +from rally.plugins.openstack.scenarios.neutron import utils + + +class NeutronLoadbalancerV1(utils.NeutronScenario): + """Benchmark scenarios for Neutron Loadbalancer v1.""" + + @validation.restricted_parameters("subnet_id", subdict="pool_create_args") + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @validation.required_contexts("network") + @base.scenario(context={"cleanup": ["neutron"]}) + def create_and_list_pools(self, pool_create_args=None): + """Create a pool(v1) and then list pools(v1). + + Measure the "neutron lb-pool-list" command performance. + The scenario creates a pool for every subnet and then lists pools. + + :param pool_create_args: dict, POST /lb/pools request options + """ + for net in self.context.get("tenant", {}).get("networks", []): + for subnet_id in net["subnets"]: + self._create_v1_pool(subnet_id, **pool_create_args) + self._list_v1_pools() diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index 0fe7fab7..ef5f5951 100644 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -28,6 +28,9 @@ class NeutronScenario(base.Scenario): RESOURCE_NAME_PREFIX = "rally_net_" RESOURCE_NAME_LENGTH = 16 SUBNET_IP_VERSION = 4 + # TODO(rkiran): modify in case LBaaS-v2 requires + LB_METHOD = "ROUND_ROBIN" + LB_PROTOCOL = "HTTP" @base.atomic_action_timer("neutron.create_network") def _create_network(self, network_create_args): @@ -287,3 +290,22 @@ class NeutronScenario(base.Scenario): """ self.clients("neutron").remove_interface_router( router["id"], {"subnet_id": subnet["id"]}) + + @base.atomic_action_timer("neutron.create_pool") + def _create_v1_pool(self, subnet_id, **pool_create_args): + """Create pool(v1) + + :parm subnet_id: str, neutron subnet-id + :parm pool_create_args: dict, POST /lb/pools request options + :returns: obj, neutron lb pool + """ + args = {"lb_method": self.LB_METHOD, "protocol": self.LB_PROTOCOL, + "name": self._generate_random_name("rally_pool_"), + "subnet_id": subnet_id} + args.update(pool_create_args) + return self.clients("neutron").create_pool({"pool": args}) + + @base.atomic_action_timer("neutron.list_pools") + def _list_v1_pools(self, **kwargs): + """Return user lb pool list(v1).""" + return self.clients("neutron").list_pools() diff --git a/tests/unit/fakes.py b/tests/unit/fakes.py index 779eebbe..d88d1a11 100644 --- a/tests/unit/fakes.py +++ b/tests/unit/fakes.py @@ -1064,6 +1064,7 @@ class FakeNeutronClient(object): self.__subnets = {} self.__routers = {} self.__ports = {} + self.__pools = {} self.__tenant_id = kwargs.get("tenant_id", generate_uuid()) self.format = "json" @@ -1112,6 +1113,21 @@ class FakeNeutronClient(object): self.__networks[network_id] = network return {"network": network} + def create_pool(self, data): + pool = setup_dict(data["pool"], + required=["lb_method", "protocol", "subnet_id"], + defaults={"name": generate_name("pool_"), + "admin_state_up": True}) + if pool["subnet_id"] not in self.__subnets: + raise neutron_exceptions.NeutronClientException + pool_id = generate_uuid() + + pool.update({"id": pool_id, + "status": "PENDING_CREATE", + "tenant_id": self.__tenant_id}) + self.__pools[pool_id] = pool + return {"pool": pool} + def create_port(self, data): port = setup_dict(data["port"], required=["network_id"], @@ -1238,6 +1254,10 @@ class FakeNeutronClient(object): nets = self._filter(self.__networks.values(), search_opts) return {"networks": nets} + def list_pools(self, **search_opts): + pools = self._filter(self.__pools.values(), search_opts) + return {"pools": pools} + def list_ports(self, **search_opts): ports = self._filter(self.__ports.values(), search_opts) return {"ports": ports} diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py b/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py new file mode 100644 index 00000000..c88997bb --- /dev/null +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py @@ -0,0 +1,45 @@ +# 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.neutron import loadbalancer_v1 +from tests.unit import test + + +class NeutronLoadbalancerv1TestCase(test.TestCase): + + def _get_context(self): + return { + "user": {"id": "fake_user", "tenant_id": "fake_tenant"}, + "tenant": {"id": "fake_tenant", + "networks": [{"id": "fake_net", + "subnets": ["fake_subnet"]}]}} + + def _validate_scenario(self, pool_create_args): + neutron_scenario = loadbalancer_v1.NeutronLoadbalancerV1( + self._get_context()) + neutron_scenario._create_v1_pool = mock.Mock() + neutron_scenario._list_v1_pools = mock.Mock() + neutron_scenario.create_and_list_pools( + pool_create_args=pool_create_args) + for net in self._get_context()["tenant"]["networks"]: + for subnet_id in net["subnets"]: + neutron_scenario._create_v1_pool.assert_called_once_with( + subnet_id, **pool_create_args) + neutron_scenario._list_v1_pools.assert_called_once_with() + + def test_create_and_list_pools_default(self): + self._validate_scenario(pool_create_args={}) + + def test_create_and_list_pools_explicit(self): + self._validate_scenario(pool_create_args={"name": "given-name"}) diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py index 4ab5449e..032bdcb7 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py @@ -391,6 +391,56 @@ class NeutronScenarioTestCase(test.ClientsTestCase): {"allocation_pools": []}, "10.10.10.0/24")] * subnets_per_network) + def test_create_v1_pool_explicit(self): + neutron_scenario = utils.NeutronScenario() + lb_method = "LEAST_CONNECTIONS" + subnet = "fake-id" + pool = mock.Mock() + self.clients("neutron").create_pool.return_value = pool + # Explicit options + pool_data = {"lb_method": lb_method, "name": "explicit-name"} + args = {"lb_method": "ROUND_ROBIN", "protocol": "HTTP", + "name": "random_name", "subnet_id": subnet} + args.update(pool_data) + expected_pool_data = {"pool": args} + pool = neutron_scenario._create_v1_pool( + subnet_id=subnet, **pool_data) + self.clients("neutron").create_pool.assert_called_once_with( + expected_pool_data) + self._test_atomic_action_timer( + neutron_scenario.atomic_actions(), "neutron.create_pool") + + @mock.patch(NEUTRON_UTILS + "NeutronScenario._generate_random_name") + def test_create_v1_pool_default(self, mock_random_name): + neutron_scenario = utils.NeutronScenario() + random_name = "random_name" + subnet = "fake-id" + pool = mock.Mock() + self.clients("neutron").create_pool.return_value = pool + mock_random_name.return_value = random_name + # Random pool name + pool_data = {} + args = {"lb_method": "ROUND_ROBIN", "protocol": "HTTP", + "name": "random_name", "subnet_id": subnet} + args.update(pool_data) + expected_pool_data = {"pool": args} + pool = neutron_scenario._create_v1_pool( + subnet_id=subnet, **pool_data) + self.clients("neutron").create_pool.assert_called_once_with( + expected_pool_data) + self._test_atomic_action_timer( + neutron_scenario.atomic_actions(), "neutron.create_pool") + + def test_list_v1_pools(self): + scenario = utils.NeutronScenario() + pools_list = [] + pools_dict = {"pools": pools_list} + self.clients("neutron").list_pools.return_value = pools_dict + return_pools_dict = scenario._list_v1_pools() + self.assertEqual(pools_dict, return_pools_dict) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.list_pools") + class NeutronScenarioFunctionalTestCase(test.FakeClientsTestCase):