From 4b1b5e170ae74c65fb09115120986d5d70b6dc6e Mon Sep 17 00:00:00 2001 From: Kiran Date: Wed, 26 Aug 2015 13:14:54 +0530 Subject: [PATCH] Add Neutron LBaaS V1 healthmonitor scenarios This patch adds the following Neutron Loadbalancer V1 Healthmonitor scenarios * Create and list healthmonitors * Create and delete healthmonitors * Create and update healthmonitors Change-Id: I5a6bda34446d8ec3e11b8f589bfc17b14f23ce4c --- rally-jobs/rally-neutron.yaml | 59 +++++++++++++++ .../context/quotas/neutron_quotas.py | 4 ++ .../scenarios/neutron/loadbalancer_v1.py | 64 +++++++++++++++++ .../openstack/scenarios/neutron/utils.py | 60 ++++++++++++++++ tests/unit/fakes.py | 43 +++++++++++ .../scenarios/neutron/test_loadbalancer_v1.py | 71 +++++++++++++++++++ .../openstack/scenarios/neutron/test_utils.py | 66 +++++++++++++++++ 7 files changed, 367 insertions(+) diff --git a/rally-jobs/rally-neutron.yaml b/rally-jobs/rally-neutron.yaml index ef64592b..e12594f1 100644 --- a/rally-jobs/rally-neutron.yaml +++ b/rally-jobs/rally-neutron.yaml @@ -255,6 +255,65 @@ failure_rate: max: 0 + NeutronLoadbalancerV1.create_and_list_healthmonitors: + - + args: + healthmonitor_create_args: {} + runner: + type: "constant" + times: {{smoke or 20}} + concurrency: {{smoke or 10}} + context: + users: + tenants: {{smoke or 5}} + users_per_tenant: {{smoke or 2}} + quotas: + neutron: + health_monitor: -1 + sla: + failure_rate: + max: 0 + + NeutronLoadbalancerV1.create_and_delete_healthmonitors: + - + args: + healthmonitor_create_args: {} + runner: + type: "constant" + times: {{smoke or 20}} + concurrency: {{smoke or 10}} + context: + users: + tenants: {{smoke or 5}} + users_per_tenant: {{smoke or 2}} + quotas: + neutron: + health_monitor: -1 + sla: + failure_rate: + max: 0 + + NeutronLoadbalancerV1.create_and_update_healthmonitors: + - + args: + healthmonitor_create_args: {} + healthmonitor_update_args: + admin_state_up: False + runner: + type: "constant" + times: {{smoke or 20}} + concurrency: {{smoke or 10}} + context: + users: + tenants: {{smoke or 5}} + users_per_tenant: {{smoke or 2}} + quotas: + neutron: + health_monitor: -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 0bdd38f6..0f006e70 100644 --- a/rally/plugins/openstack/context/quotas/neutron_quotas.py +++ b/rally/plugins/openstack/context/quotas/neutron_quotas.py @@ -60,6 +60,10 @@ class NeutronQuotas(object): "vip": { "type": "integer", "minimum": -1 + }, + "health_monitor": { + "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 daa7501c..ec55514b 100644 --- a/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py +++ b/rally/plugins/openstack/scenarios/neutron/loadbalancer_v1.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import random + from rally import consts from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.neutron import utils @@ -174,3 +176,65 @@ class NeutronLoadbalancerV1(utils.NeutronScenario): vips.append(self._create_v1_vip(pool, **vip_create_args)) for vip in vips: self._update_v1_vip(vip, **vip_update_args) + + @validation.required_neutron_extensions("lbaas") + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_list_healthmonitors(self, healthmonitor_create_args=None): + """Create healthmonitors(v1) and list healthmonitors(v1). + + Measure the "neutron lb-healthmonitor-list" command performance. This + scenario creates healthmonitors and lists them. + + :param healthmonitor_create_args: dict, POST /lb/healthmonitors request + options + """ + healthmonitor_create_args = healthmonitor_create_args or {} + self._create_v1_healthmonitor(**healthmonitor_create_args) + self._list_v1_healthmonitors() + + @validation.required_neutron_extensions("lbaas") + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_delete_healthmonitors(self, healthmonitor_create_args=None): + """Create a healthmonitor(v1) and delete healthmonitors(v1). + + Measure the "neutron lb-healthmonitor-create" and "neutron + lb-healthmonitor-delete" command performance. The scenario creates + healthmonitors and deletes those healthmonitors. + + :param healthmonitor_create_args: dict, POST /lb/healthmonitors request + options + """ + healthmonitor_create_args = healthmonitor_create_args or {} + healthmonitor = self._create_v1_healthmonitor( + **healthmonitor_create_args) + self._delete_v1_healthmonitor(healthmonitor["health_monitor"]) + + @validation.required_neutron_extensions("lbaas") + @validation.required_services(consts.Service.NEUTRON) + @validation.required_openstack(users=True) + @scenario.configure(context={"cleanup": ["neutron"]}) + def create_and_update_healthmonitors(self, + healthmonitor_create_args=None, + healthmonitor_update_args=None): + """Create a healthmonitor(v1) and update healthmonitors(v1). + + Measure the "neutron lb-healthmonitor-create" and "neutron + lb-healthmonitor-update" command performance. The scenario creates + healthmonitors and then updates them. + + :param healthmonitor_create_args: dict, POST /lb/healthmonitors request + options + :param healthmonitor_update_args: dict, POST /lb/healthmonitors update + options + """ + healthmonitor_create_args = healthmonitor_create_args or {} + healthmonitor_update_args = healthmonitor_update_args or { + "max_retries": random.choice(range(1, 10))} + healthmonitor = self._create_v1_healthmonitor( + **healthmonitor_create_args) + self._update_v1_healthmonitor(healthmonitor, + **healthmonitor_update_args) diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index 3aaf93d6..92d82e16 100644 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -33,6 +33,10 @@ class NeutronScenario(scenario.OpenStackScenario): LB_METHOD = "ROUND_ROBIN" LB_PROTOCOL = "HTTP" LB_PROTOCOL_PORT = 80 + HM_TYPE = "PING" + HM_MAX_RETRIES = 3 + HM_DELAY = 20 + HM_TIMEOUT = 10 def _warn_about_deprecated_name_kwarg(self, resource, kwargs): """Warn about use of a deprecated 'name' kwarg and replace it. @@ -439,3 +443,59 @@ class NeutronScenario(scenario.OpenStackScenario): :param: dict, floating IP object """ return self.clients("neutron").delete_floatingip(floating_ip["id"]) + + def _create_v1_healthmonitor(self, atomic_action=True, + **healthmonitor_create_args): + """Create LB healthmonitor. + + This atomic function creates healthmonitor with the provided + healthmonitor_create_args. + + :param atomic_action: True if this is an atomic action + :param healthmonitor_create_args: dict, POST /lb/healthmonitors + :returns: neutron healthmonitor dict + """ + args = {"type": self.HM_TYPE, + "delay": self.HM_DELAY, + "max_retries": self.HM_MAX_RETRIES, + "timeout": self.HM_TIMEOUT} + args.update(healthmonitor_create_args) + if atomic_action: + with atomic.ActionTimer(self, "neutron.create_healthmonitor"): + return self.clients("neutron").create_health_monitor( + {"health_monitor": args}) + return self.clients("neutron").create_health_monitor( + {"health_monitor": args}) + + @atomic.action_timer("neutron.list_healthmonitors") + def _list_v1_healthmonitors(self, **kwargs): + """List LB healthmonitors. + + This atomic function lists all helthmonitors. + + :param kwargs: optional parameters + :returns neutron lb healthmonitor list + """ + return self.clients("neutron").list_health_monitors(**kwargs) + + @atomic.action_timer("neutron.delete_healthmonitor") + def _delete_v1_healthmonitor(self, healthmonitor): + """Delete neutron healthmonitor. + + :param healthmonitor: neutron healthmonitor dict + """ + self.clients("neutron").delete_health_monitor(healthmonitor["id"]) + + @atomic.action_timer("neutron.update_healthmonitor") + def _update_v1_healthmonitor(self, healthmonitor, + **healthmonitor_update_args): + """Update neutron healthmonitor. + + :param healthmonitor: neutron lb healthmonitor dict + :param healthmonitor_update_args: POST /lb/healthmonitors + update options + :returns updated neutron lb healthmonitor dict + """ + body = {"health_monitor": healthmonitor_update_args} + return self.clients("neutron").update_health_monitor( + healthmonitor["health_monitor"]["id"], body) diff --git a/tests/unit/fakes.py b/tests/unit/fakes.py index 166f2628..b4335e6f 100644 --- a/tests/unit/fakes.py +++ b/tests/unit/fakes.py @@ -1037,6 +1037,7 @@ class FakeNeutronClient(object): self.__pools = {} self.__vips = {} self.__fips = {} + self.__healthmonitors = {} self.__tenant_id = kwargs.get("tenant_id", generate_uuid()) self.format = "json" @@ -1130,6 +1131,19 @@ class FakeNeutronClient(object): self.__fips[fip_id] = fip return {"fip": fip} + def create_health_monitor(self, data): + healthmonitor = setup_dict(data["healthmonitor"], + required=["type", "timeout", "delay", + "max_retries"], + defaults={"admin_state_up": True}) + healthmonitor_id = generate_uuid() + + healthmonitor.update({"id": healthmonitor_id, + "status": "PENDING_CREATE", + "tenant_id": self.__tenant_id}) + self.__healthmonitors[healthmonitor_id] = healthmonitor + return {"healthmonitor": healthmonitor} + def create_port(self, data): port = setup_dict(data["port"], required=["network_id"], @@ -1206,6 +1220,9 @@ class FakeNeutronClient(object): def update_vip(self, vip_id, data): self.update_resource(vip_id, self.__vips, data) + def update_health_monitor(self, healthmonitor_id, data): + self.update_resource(healthmonitor_id, self.__healthmonitors, data) + def update_subnet(self, subnet_id, data): self.update_resource(subnet_id, self.__subnets, data) @@ -1235,6 +1252,11 @@ class FakeNeutronClient(object): if vip_id not in self.__vips: raise neutron_exceptions.NeutronClientException del self.__vips[vip_id] + + def delete_health_monitor(self, healthmonitor_id): + if healthmonitor_id not in self.__healthmonitors: + raise neutron_exceptions.NeutronClientException + del self.__healthmonitors[healthmonitor_id] return "" def delete_floatingip(self, fip_id): @@ -1285,6 +1307,11 @@ class FakeNeutronClient(object): vips = self._filter(self.__vips.values(), search_opts) return {"vips": vips} + def list_health_monitors(self, **search_opts): + healthmonitors = self._filter( + self.__healthmonitors.values(), search_opts) + return {"healthmonitors": healthmonitors} + def list_ports(self, **search_opts): ports = self._filter(self.__ports.values(), search_opts) return {"ports": ports} @@ -1322,6 +1349,22 @@ class FakeNeutronClient(object): raise neutron_exceptions.NeutronClientException + def associate_health_monitor(self, pool_id, healthmonitor_id): + if pool_id not in self.__pools: + raise neutron_exceptions.NeutronClientException + if healthmonitor_id not in self.__healthmonitors: + raise neutron_exceptions.NeutronClientException + self.__pools[pool_id]["pool"]["healthmonitors"] = healthmonitor_id + return {"pool": self.__pools[pool_id]} + + def disassociate_health_monitor(self, pool_id, healthmonitor_id): + if pool_id not in self.__pools: + raise neutron_exceptions.NeutronClientException + if healthmonitor_id not in self.__healthmonitors: + raise neutron_exceptions.NeutronClientException + del self.__pools[pool_id]["pool"]["healthmonitors"][healthmonitor_id] + return "" + class FakeIronicClient(object): 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 c56506bf..93942c24 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_loadbalancer_v1.py @@ -250,3 +250,74 @@ class NeutronLoadbalancerv1TestCase(test.TestCase): [mock.call(pool, **vip_data) for pool in pools]) neutron_scenario._update_v1_vip.assert_has_calls( [mock.call(vip, **vip_update_data) for vip in vips]) + + @ddt.data( + {}, + {"healthmonitor_create_args": None}, + {"healthmonitor_create_args": {}}, + {"healthmonitor_create_args": {"name": "given-name"}}, + ) + @ddt.unpack + def test_create_and_list_healthmonitors(self, + healthmonitor_create_args=None): + neutron_scenario = loadbalancer_v1.NeutronLoadbalancerV1( + self._get_context()) + hm_data = healthmonitor_create_args or {} + neutron_scenario._create_v1_healthmonitor = mock.Mock() + neutron_scenario._list_v1_healthmonitors = mock.Mock() + neutron_scenario.create_and_list_healthmonitors( + healthmonitor_create_args=healthmonitor_create_args) + neutron_scenario._create_v1_healthmonitor.assert_called_once_with( + **hm_data) + neutron_scenario._list_v1_healthmonitors.assert_called_once_with() + + @ddt.data( + {}, + {"healthmonitor_create_args": None}, + {"healthmonitor_create_args": {}}, + {"healthmonitor_create_args": {"name": "given-name"}}, + ) + @ddt.unpack + def test_create_and_delete_healthmonitors(self, + healthmonitor_create_args=None): + neutron_scenario = loadbalancer_v1.NeutronLoadbalancerV1( + self._get_context()) + hm = {"health_monitor": {"id": "hm-id"}} + hm_data = healthmonitor_create_args or {} + neutron_scenario._create_v1_healthmonitor = mock.Mock(return_value=hm) + neutron_scenario._delete_v1_healthmonitor = mock.Mock() + neutron_scenario.create_and_delete_healthmonitors( + healthmonitor_create_args=healthmonitor_create_args) + neutron_scenario._create_v1_healthmonitor.assert_called_once_with( + **hm_data) + neutron_scenario._delete_v1_healthmonitor.assert_called_once_with( + neutron_scenario._create_v1_healthmonitor.return_value[ + "health_monitor"]) + + @ddt.data( + {}, + {"healthmonitor_create_args": None}, + {"healthmonitor_create_args": {}}, + {"healthmonitor_create_args": {"name": "given-name"}}, + ) + @ddt.unpack + def test_create_and_update_healthmonitors(self, + healthmonitor_create_args=None, + healthmonitor_update_args=None): + neutron_scenario = loadbalancer_v1.NeutronLoadbalancerV1( + self._get_context()) + mock_random = loadbalancer_v1.random = mock.Mock() + hm = {"healthmonitor": {"id": "hm-id"}} + hm_data = healthmonitor_create_args or {} + hm_update_data = healthmonitor_update_args or { + "max_retries": mock_random.choice.return_value} + neutron_scenario._create_v1_healthmonitor = mock.Mock(return_value=hm) + neutron_scenario._update_v1_healthmonitor = mock.Mock() + neutron_scenario.create_and_update_healthmonitors( + healthmonitor_create_args=healthmonitor_create_args, + healthmonitor_update_args=healthmonitor_update_args) + neutron_scenario._create_v1_healthmonitor.assert_called_once_with( + **hm_data) + neutron_scenario._update_v1_healthmonitor.assert_called_once_with( + neutron_scenario._create_v1_healthmonitor.return_value, + **hm_update_data) diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py index 7176eb84..8bed3291 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py @@ -725,3 +725,69 @@ class NeutronLoadbalancerScenarioTestCase(test.ScenarioTestCase): mock_get_network_id.assert_called_once_with(floating_network) self._test_atomic_action_timer(neutron_scenario.atomic_actions(), "neutron.create_floating_ip") + + @ddt.data( + {}, + {"healthmonitor_create_args": {}}, + {"healthmonitor_create_args": {"type": "TCP"}}, + {"atomic_action": False}, + {"atomic_action": False, + "healthmonitor_create_args": {"type": "TCP"}}, + {"healthmonitor_create_args": {}, + "atomic_action": False}, + ) + @ddt.unpack + def test__create_v1_healthmonitor(self, atomic_action=True, + healthmonitor_create_args=None): + neutron_scenario = utils.NeutronScenario() + hm = {"health_monitor": {"id": "hm-id"}} + healthmonitor_create_args = healthmonitor_create_args or {} + self.clients("neutron").create_health_monitor.return_value = hm + args = {"type": "PING", "delay": 20, + "timeout": 10, "max_retries": 3} + args.update(healthmonitor_create_args) + expected_hm_data = {"health_monitor": args} + resultant_hm = neutron_scenario._create_v1_healthmonitor( + atomic_action=atomic_action, + **healthmonitor_create_args) + self.assertEqual(resultant_hm, hm) + self.clients("neutron").create_health_monitor.assert_called_once_with( + expected_hm_data) + if atomic_action: + self._test_atomic_action_timer( + neutron_scenario.atomic_actions(), + "neutron.create_healthmonitor") + + def test_list_v1_healthmonitors(self): + scenario = utils.NeutronScenario() + hm_list = [] + hm_dict = {"health_monitors": hm_list} + self.clients("neutron").list_health_monitors.return_value = hm_dict + return_hm_dict = scenario._list_v1_healthmonitors() + self.assertEqual(hm_dict, return_hm_dict) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.list_healthmonitors") + + def test_delete_v1_healthmonitor(self): + scenario = utils.NeutronScenario() + healthmonitor = {"health_monitor": {"id": "fake-id"}} + scenario._delete_v1_healthmonitor(healthmonitor["health_monitor"]) + self.clients("neutron").delete_health_monitor.assert_called_once_with( + healthmonitor["health_monitor"]["id"]) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.delete_healthmonitor") + + def test_update_healthmonitor(self): + scenario = utils.NeutronScenario() + expected_hm = {"health_monitor": {"admin_state_up": False}} + mock_update = self.clients("neutron").update_health_monitor + mock_update.return_value = expected_hm + hm = {"health_monitor": {"id": "pool-id"}} + healthmonitor_update_args = {"admin_state_up": False} + result_hm = scenario._update_v1_healthmonitor( + hm, **healthmonitor_update_args) + self.assertEqual(result_hm, expected_hm) + mock_update.assert_called_once_with( + hm["health_monitor"]["id"], expected_hm) + self._test_atomic_action_timer(scenario.atomic_actions(), + "neutron.update_healthmonitor")