diff --git a/rally-jobs/rally-neutron.yaml b/rally-jobs/rally-neutron.yaml index 486e302d..811cb75c 100644 --- a/rally-jobs/rally-neutron.yaml +++ b/rally-jobs/rally-neutron.yaml @@ -163,6 +163,29 @@ failure_rate: max: 0 + NeutronLoadbalancerV1.create_and_list_vips: + - + args: + vip_create_args: {} + runner: + type: "constant" + times: 20 + concurrency: 10 + context: + users: + tenants: 5 + users_per_tenant: 4 + network: {} + quotas: + neutron: + network: -1 + subnet: -1 + pool: -1 + vip: -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 ff91bc0a..0bdd38f6 100644 --- a/rally/plugins/openstack/context/quotas/neutron_quotas.py +++ b/rally/plugins/openstack/context/quotas/neutron_quotas.py @@ -56,6 +56,10 @@ class NeutronQuotas(object): "pool": { "type": "integer", "minimum": -1 + }, + "vip": { + "type": "integer", + "minimum": -1 } } } diff --git a/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py b/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py index 56a14a15..b11abbca 100644 --- a/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py +++ b/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py @@ -13,6 +13,7 @@ from rally import consts from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.neutron import utils +from rally.task import atomic from rally.task import validation @@ -85,3 +86,30 @@ class NeutronLoadbalancerV1(utils.NeutronScenario): pools = self._create_v1_pools(networks, **pool_create_args) for pool in pools: self._update_v1_pool(pool, **pool_update_args) + + @validation.restricted_parameters(["pool_id", "subnet_id"], + subdict="vip_create_args") + @validation.required_neutron_extensions("lbaas") + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(admin=True) + @validation.required_contexts("network") + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_list_vips(self, pool_create_args=None, + vip_create_args=None): + """Create a vip(v1) and then list vips(v1). + + Measure the "neutron lb-vip-create" and "neutron lb-vip-list" command + performance. The scenario creates a vip for every pool created and + then lists vips. + + :param vip_create_args: dict, POST /lb/vips request options + :param pool_create_args: dict, POST /lb/pools request options + """ + vip_create_args = vip_create_args or {} + pool_create_args = pool_create_args or {} + networks = self.context.get("tenant", {}).get("networks", []) + pools = self._create_v1_pools(networks, **pool_create_args) + with atomic.ActionTimer(self, "neutron.create_%s_vips" % len(pools)): + for pool in pools: + self._create_v1_vip(pool, **vip_create_args) + self._list_v1_vips() diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index 247a4d0a..8bf3a800 100644 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -31,6 +31,7 @@ class NeutronScenario(scenario.OpenStackScenario): # TODO(rkiran): modify in case LBaaS-v2 requires LB_METHOD = "ROUND_ROBIN" LB_PROTOCOL = "HTTP" + LB_PROTOCOL_PORT = 80 def _warn_about_deprecated_name_kwarg(self, resource, kwargs): """Warn about use of a deprecated 'name' kwarg and replace it. @@ -347,3 +348,23 @@ class NeutronScenario(scenario.OpenStackScenario): self._warn_about_deprecated_name_kwarg(pool, pool_update_args) body = {"pool": pool_update_args} return self.clients("neutron").update_pool(pool["pool"]["id"], body) + + def _create_v1_vip(self, pool, **vip_create_args): + """Create VIP(v1) + + :parm pool: dict, neutron lb-pool + :parm vip_create_args: dict, POST /lb/vips request options + :returns: dict, neutron lb vip + """ + args = {"protocol": self.LB_PROTOCOL, + "protocol_port": self.LB_PROTOCOL_PORT, + "name": self._generate_random_name("rally_vip_"), + "pool_id": pool["pool"]["id"], + "subnet_id": pool["pool"]["subnet_id"]} + args.update(vip_create_args) + return self.clients("neutron").create_vip({"vip": args}) + + @atomic.action_timer("neutron.list_vips") + def _list_v1_vips(self, **kwargs): + """Return user lb vip list(v1).""" + return self.clients("neutron").list_vips(**kwargs) diff --git a/tests/unit/fakes.py b/tests/unit/fakes.py index f5bbc339..72f8e5df 100644 --- a/tests/unit/fakes.py +++ b/tests/unit/fakes.py @@ -1035,6 +1035,7 @@ class FakeNeutronClient(object): self.__routers = {} self.__ports = {} self.__pools = {} + self.__vips = {} self.__tenant_id = kwargs.get("tenant_id", generate_uuid()) self.format = "json" @@ -1098,6 +1099,23 @@ class FakeNeutronClient(object): self.__pools[pool_id] = pool return {"pool": pool} + def create_vip(self, data): + vip = setup_dict(data["vip"], + required=["protocol_port", "protocol", "subnet_id", + "pool_id"], + defaults={"name": generate_name("vip_"), + "admin_state_up": True}) + if (vip["subnet_id"] not in self.__subnets) or (vip["pool_id"] not in + self.__pools): + raise neutron_exceptions.NeutronClientException + vip_id = generate_uuid() + + vip.update({"id": vip_id, + "status": "PENDING_CREATE", + "tenant_id": self.__tenant_id}) + self.__vips[vip_id] = vip + return {"vip": vip} + def create_port(self, data): port = setup_dict(data["port"], required=["network_id"], @@ -1234,6 +1252,10 @@ class FakeNeutronClient(object): pools = self._filter(self.__pools.values(), search_opts) return {"pools": pools} + def list_vips(self, **search_opts): + vips = self._filter(self.__vips.values(), search_opts) + return {"vips": vips} + 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 index 48005ed1..06c7412b 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py @@ -125,3 +125,36 @@ class NeutronLoadbalancerv1TestCase(test.TestCase): for pool in pools: neutron_scenario._update_v1_pool.assert_called_once_with( pool, **pool_update_args) + + @ddt.data( + {}, + {"vip_create_args": None}, + {"vip_create_args": {}}, + {"vip_create_args": {"name": "given-vip-name"}}, + {"pool_create_args": None}, + {"pool_create_args": {}}, + {"pool_create_args": {"name": "given-pool-name"}}, + ) + @ddt.unpack + def test_create_and_list_vips(self, pool_create_args=None, + vip_create_args=None): + neutron_scenario = loadbalancer_v1.NeutronLoadbalancerV1( + self._get_context()) + pools = [{ + "pool": { + "id": "pool-id" + } + }] + vip_data = vip_create_args or {} + pool_data = pool_create_args or {} + networks = self._get_context()["tenant"]["networks"] + neutron_scenario._create_v1_pools = mock.Mock(return_value=pools) + neutron_scenario._create_v1_vip = mock.Mock() + neutron_scenario._list_v1_vips = mock.Mock() + neutron_scenario.create_and_list_vips( + pool_create_args=pool_create_args, vip_create_args=vip_create_args) + neutron_scenario._create_v1_pools.assert_called_once_with( + networks, **pool_data) + neutron_scenario._create_v1_vip.assert_has_calls( + [mock.call(pool, **vip_data) for pool in pools]) + neutron_scenario._list_v1_vips.assert_called_once_with() diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py index 7e7bfb69..6bbf6a05 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py @@ -476,6 +476,16 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(scenario.atomic_actions(), "neutron.list_pools") + def test_list_v1_vips(self): + scenario = utils.NeutronScenario() + vips_list = [] + vips_dict = {"vips": vips_list} + self.clients("neutron").list_vips.return_value = vips_dict + return_vips_dict = scenario._list_v1_vips() + self.assertEqual(vips_dict, return_vips_dict) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.list_vips") + class NeutronScenarioFunctionalTestCase(test.FakeClientsScenarioTestCase): @@ -572,3 +582,29 @@ class NeutronLoadbalancerScenarioTestCase(test.ScenarioTestCase): if atomic_action: self._test_atomic_action_timer( neutron_scenario.atomic_actions(), "neutron.create_pool") + + @ddt.data( + {}, + {"vip_create_args": {}}, + {"vip_create_args": {"name": "given-name"}}, + ) + @ddt.unpack + def test__create_v1_vip(self, vip_create_args=None): + neutron_scenario = utils.NeutronScenario() + vip = {"vip": {"id": "vip-id"}} + pool = {"pool": {"id": "pool-id", "subnet_id": "subnet-id"}} + vip_create_args = vip_create_args or {} + if vip_create_args.get("name") is None: + neutron_scenario._generate_random_name = mock.Mock( + return_value="random_name") + self.clients("neutron").create_vip.return_value = vip + args = {"protocol_port": 80, "protocol": "HTTP", "name": "random_name", + "subnet_id": pool["pool"]["subnet_id"], + "pool_id": pool["pool"]["id"]} + args.update(vip_create_args) + expected_vip_data = {"vip": args} + resultant_vip = neutron_scenario._create_v1_vip( + pool, **vip_create_args) + self.assertEqual(resultant_vip, vip) + self.clients("neutron").create_vip.assert_called_once_with( + expected_vip_data)