diff --git a/rally-jobs/rally-neutron-extensions.yaml b/rally-jobs/rally-neutron-extensions.yaml index 52111638..63a4435a 100644 --- a/rally-jobs/rally-neutron-extensions.yaml +++ b/rally-jobs/rally-neutron-extensions.yaml @@ -16,10 +16,36 @@ failure_rate: max: 0 + NeutronBGPVPN.create_and_list_bgpvpns: + - + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 3 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 + + NeutronBGPVPN.create_and_update_bgpvpns: + - + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 3 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 + NeutronBGPVPN.create_and_delete_bgpvpns: - - args: - bgpvpn_create_args: {} runner: type: "constant" times: 10 diff --git a/rally/plugins/openstack/cleanup/resources.py b/rally/plugins/openstack/cleanup/resources.py index 14c730a8..c571d272 100755 --- a/rally/plugins/openstack/cleanup/resources.py +++ b/rally/plugins/openstack/cleanup/resources.py @@ -307,9 +307,11 @@ class NeutronMixin(SynchronizedDeletion, base.ResourceManager): def list(self): resources = self._resource + "s" list_method = getattr(self._manager(), "list_%s" % resources) + result = list_method(tenant_id=self.tenant_uuid)[resources] + if self.tenant_uuid: + result = [r for r in result if r["tenant_id"] == self.tenant_uuid] - return filter(lambda r: r["tenant_id"] == self.tenant_uuid, - list_method(tenant_id=self.tenant_uuid)[resources]) + return result class NeutronLbaasV1Mixin(NeutronMixin): @@ -359,6 +361,15 @@ class NeutronV2Loadbalancer(NeutronLbaasV2Mixin): return False +@base.resource("neutron", "bgpvpn", order=next(_neutron_order), + admin_required=True, perform_for_admin_only=True) +class NeutronBgpvpn(NeutronMixin): + def list(self): + if self.supports_extension("bgpvpn"): + return self._manager().list_bgpvpns()["bgpvpns"] + return [] + + # NOTE(andreykurilin): There are scenarios which uses unified way for creating # and associating floating ips. They do not care about nova-net and neutron. # We should clean floating IPs for them, but hardcoding "neutron.floatingip" diff --git a/rally/plugins/openstack/scenarios/neutron/bgpvpn.py b/rally/plugins/openstack/scenarios/neutron/bgpvpn.py index cba88851..96f5d33e 100644 --- a/rally/plugins/openstack/scenarios/neutron/bgpvpn.py +++ b/rally/plugins/openstack/scenarios/neutron/bgpvpn.py @@ -18,21 +18,115 @@ from rally.task import validation """Scenarios for Neutron Networking-Bgpvpn.""" -@validation.restricted_parameters(["name"]) @validation.add("required_neutron_extensions", extensions=["bgpvpn"]) @validation.required_services(consts.Service.NEUTRON) -@validation.add("required_platform", platform="openstack", users=True) +@validation.add("required_platform", platform="openstack", admin=True) @scenario.configure(context={"admin_cleanup": ["neutron"]}, name="NeutronBGPVPN.create_and_delete_bgpvpns") class CreateAndDeleteBgpvpns(utils.NeutronScenario): - def run(self, bgpvpn_create_args=None): + def run(self, route_targets=None, import_targets=None, + export_targets=None, route_distinguishers=None, type="l3"): """Create bgpvpn and delete the bgpvpn. - Measure the "neutron bgpvpn-create" and bgpvpn-delete + Measure the "neutron bgpvpn-create" and neutron bgpvpn-delete command performance. - :param bgpvpn_create_args: dict, POST /v2.0/bgpvpn/bgpvpns request - options + + :param route_targets: Route Targets that will be both imported and + used for export + :param import_targets: Additional Route Targets that will be imported + :param export_targets: Additional Route Targets that will be used + for export. + :param route_distinguishers: List of route distinguisher strings + :param type: type of VPN and the technology behind it. + Acceptable formats: l2 and l3 """ - bgpvpn = self._create_bgpvpn(bgpvpn_create_args or {}) + bgpvpn = self._create_bgpvpn(route_targets=route_targets, + import_targets=import_targets, + export_targets=export_targets, + route_distinguishers=route_distinguishers, + type=type) self._delete_bgpvpn(bgpvpn) + + +@validation.add("required_neutron_extensions", extensions=["bgpvpn"]) +@validation.required_services(consts.Service.NEUTRON) +@validation.add("required_platform", platform="openstack", admin=True) +@scenario.configure(context={"admin_cleanup": ["neutron"]}, + name="NeutronBGPVPN.create_and_list_bgpvpns") +class CreateAndListBgpvpns(utils.NeutronScenario): + + def run(self, route_targets=None, import_targets=None, + export_targets=None, route_distinguishers=None, type="l3"): + """Create a bgpvpn and then list all bgpvpns + + Measure the "neutron bgpvpn-list" command performance. + + :param route_targets: Route Targets that will be both imported and + used for export + :param import_targets: Additional Route Targets that will be imported + :param export_targets: Additional Route Targets that will be used + for export. + :param route_distinguishers: List of route distinguisher strings + :param type: type of VPN and the technology behind it. + Acceptable formats: l2 and l3 + """ + bgpvpn = self._create_bgpvpn(route_targets=route_targets, + import_targets=import_targets, + export_targets=export_targets, + route_distinguishers=route_distinguishers, + type=type) + bgpvpns = self._list_bgpvpns() + self.assertIn(bgpvpn["bgpvpn"]["id"], [b["id"] for b in bgpvpns]) + + +@validation.add("required_neutron_extensions", extensions=["bgpvpn"]) +@validation.required_services(consts.Service.NEUTRON) +@validation.add("required_platform", platform="openstack", admin=True) +@scenario.configure(context={"admin_cleanup": ["neutron"]}, + name="NeutronBGPVPN.create_and_update_bgpvpns") +class CreateAndUpdateBgpvpns(utils.NeutronScenario): + + def run(self, update_name=False, route_targets=None, + import_targets=None, export_targets=None, + route_distinguishers=None, updated_route_targets=None, + updated_import_targets=None, updated_export_targets=None, + updated_route_distinguishers=None, bgpvpn_type="l3"): + """Create and Update bgpvpns + + Measure the "neutron bgpvpn-update" command performance. + + :param update_name: bool, whether or not to modify BGP VPN name + :param route_targets: Route Targets that will be both imported + and used for export + :param updated_route_targets: Updated Route Targets that will be both + imported and used for export + :param import_targets: Additional Route Targets that will be imported + :param updated_import_targets: Updated additional Route Targets that + will be imported + :param export_targets: additional Route Targets that will be used + for export. + :param updated_export_targets: Updated additional Route Targets that + will be used for export. + :param route_distinguishers: list of route distinguisher strings + :param updated_route_distinguishers: Updated list of route + distinguisher strings + :param bgpvpn_type: type of VPN and the technology behind it. + Acceptable formats: l2 and l3 + """ + create_bgpvpn_args = { + "route_targets": route_targets, + "import_targets": import_targets, + "export_targets": export_targets, + "route_distinguishers": route_distinguishers, + "type": bgpvpn_type + } + bgpvpn = self._create_bgpvpn(**create_bgpvpn_args) + update_bgpvpn_args = { + "update_name": update_name, + "route_targets": updated_route_targets, + "import_targets": updated_import_targets, + "export_targets": updated_export_targets, + "route_distinguishers": updated_route_distinguishers, + } + self._update_bgpvpn(bgpvpn, **update_bgpvpn_args) diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index aea9dc5e..799177ee 100755 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -44,9 +44,6 @@ class NeutronScenario(scenario.OpenStackScenario): HM_MAX_RETRIES = 3 HM_DELAY = 20 HM_TIMEOUT = 10 - # BGPVPN - BGPVPNS_PATH = "/bgpvpn/bgpvpns" - BGPVPN_PATH = "/bgpvpn/bgpvpns/%s" def _get_network_id(self, network, **kwargs): """Get Neutron network ID for the network name. @@ -712,23 +709,46 @@ class NeutronScenario(scenario.OpenStackScenario): **lb_list_args) @atomic.action_timer("neutron.create_bgpvpn") - def _create_bgpvpn(self, bgpvpn_create_args): + def _create_bgpvpn(self, **kwargs): """Create Bgpvpn resource (POST /bgpvpn/bgpvpn) - param: bgpvpn_create_args: dict bgpvpn options - returns: dict, bgpvpn resource details + :param kwargs: optional parameters to create BGP VPN + :returns dict, bgpvpn resource details """ - if "name" not in bgpvpn_create_args: - bgpvpn_create_args["name"] = self.generate_random_name() - return self.admin_clients("neutron").create_ext( - self.BGPVPNS_PATH, {"bgpvpn": bgpvpn_create_args}) + kwargs["name"] = self.generate_random_name() + return self.admin_clients("neutron").create_bgpvpn({"bgpvpn": kwargs}) @atomic.action_timer("neutron.delete_bgpvpn") def _delete_bgpvpn(self, bgpvpn): """Delete Bgpvpn resource.(DELETE /bgpvpn/bgpvpns/{id}) - param: bgpvpn: dict, bgpvpn - return: dict, bgpvpn + :param bgpvpn: dict, bgpvpn + :return dict, bgpvpn """ - return self.admin_clients("neutron").delete_ext( - self.BGPVPN_PATH, bgpvpn["bgpvpn"]["id"]) + return self.admin_clients("neutron").delete_bgpvpn( + bgpvpn["bgpvpn"]["id"]) + + @atomic.action_timer("neutron.list_bgpvpns") + def _list_bgpvpns(self, **kwargs): + """Return bgpvpns list. + + :param kwargs: dict, POST /bgpvpn/bgpvpns request options + :returns: bgpvpns list + """ + return self.admin_clients("neutron").list_bgpvpns( + True, **kwargs)["bgpvpns"] + + @atomic.action_timer("neutron.update_bgpvpn") + def _update_bgpvpn(self, bgpvpn, update_name=False, **kwargs): + """Update a bgpvpn. + + :param bgpvpn: dict, bgpvpn + :param update_name: update_name: bool, whether or not to modify + BGP VPN name + :param **kwargs: dict, PUT /bgpvpn/bgpvpns update options + :return dict, updated bgpvpn + """ + if update_name or "name" in kwargs: + kwargs["name"] = self.generate_random_name() + return self.admin_clients("neutron").update_bgpvpn( + bgpvpn["bgpvpn"]["id"], {"bgpvpn": kwargs}) diff --git a/requirements.txt b/requirements.txt index ce9fb6b7..0f8336d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ python-manilaclient>=1.12.0,<=1.14.0 # Apache Software License python-mistralclient>=2.0.0,<=3.0.0 # Apache Software License python-monascaclient>=1.1.0,<=1.5.0 # Apache Software License python-muranoclient>=0.8.2,<=0.12.0 # Apache License, Version 2.0 -python-neutronclient>=5.1.0,<=6.1.0 # Apache Software License +python-neutronclient==6.2.0 # Apache Software License python-novaclient==7.1.0 # Apache License, Version 2.0 python-saharaclient==1.1.0 # Apache License, Version 2.0 python-senlinclient>=1.1.0,<=1.2.0 # Apache Software License diff --git a/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.json b/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.json new file mode 100644 index 00000000..8d1491d2 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.json @@ -0,0 +1,23 @@ +{ + "NeutronBGPVPN.create_and_list_bgpvpns": [ + { + "args":{}, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 1, + "users_per_tenant": 1 + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} \ No newline at end of file diff --git a/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.yaml b/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.yaml new file mode 100644 index 00000000..cede19ba --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-list-bgpvpns.yaml @@ -0,0 +1,15 @@ +--- + NeutronBGPVPN.create_and_list_bgpvpns: + - + args: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 1 + users_per_tenant: 1 + sla: + failure_rate: + max: 0 \ No newline at end of file diff --git a/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.json b/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.json new file mode 100644 index 00000000..24227785 --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.json @@ -0,0 +1,19 @@ +{ + "NeutronBGPVPN.create_and_update_bgpvpns": [ + { + "args":{}, + "runner": { + "type": "constant", + "times": 10, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 1, + "users_per_tenant": 1 + + } + } + } + ] +} \ No newline at end of file diff --git a/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.yaml b/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.yaml new file mode 100644 index 00000000..c525305d --- /dev/null +++ b/samples/tasks/scenarios/neutron/create-and-update-bgpvpns.yaml @@ -0,0 +1,12 @@ +--- + NeutronBGPVPN.create_and_update_bgpvpns: + - + args: {} + runner: + type: "constant" + times: 10 + concurrency: 2 + context: + users: + tenants: 1 + users_per_tenant: 1 \ No newline at end of file diff --git a/tests/ci/osresources.py b/tests/ci/osresources.py index c004c2b8..22bf2af6 100755 --- a/tests/ci/osresources.py +++ b/tests/ci/osresources.py @@ -250,6 +250,10 @@ class Neutron(ResourceManager): if self.has_extension("lbaas"): return self.client.list_vips()["vips"] + def list_bgpvpns(self): + if self.has_extension("bgpvpn"): + return self.client.list_bgpvpns()["bgpvpns"] + class Glance(ResourceManager): diff --git a/tests/unit/plugins/openstack/cleanup/test_resources.py b/tests/unit/plugins/openstack/cleanup/test_resources.py index 9c23f1c9..90e68832 100755 --- a/tests/unit/plugins/openstack/cleanup/test_resources.py +++ b/tests/unit/plugins/openstack/cleanup/test_resources.py @@ -413,6 +413,38 @@ class NeutronV2LoadbalancerTestCase(test.TestCase): neutron_lb.id()) +class NeutronBgpvpnTestCase(test.TestCase): + + def get_neutron_bgpvpn_mixin(self, extensions=None): + if extensions is None: + extensions = [] + admin = mock.Mock() + neut = resources.NeutronBgpvpn(admin=admin) + neut._manager = mock.Mock() + neut._manager().list_extensions.return_value = { + "extensions": [{"alias": ext} for ext in extensions] + } + return neut + + def test_list_user(self): + neut = self.get_neutron_bgpvpn_mixin(extensions=["bgpvpn"]) + user_bgpvpns = {"bgpvpns": [{"tenant_id": "foo", "id": "bgpvpn_id"}]} + neut._manager().list_bgpvpns.return_value = user_bgpvpns + + bgpvpns_list = neut.list() + self.assertEqual("bgpvpn", neut._resource) + neut._manager().list_bgpvpns.assert_called_once_with() + self.assertEqual(bgpvpns_list, user_bgpvpns["bgpvpns"]) + + def test_list_admin(self): + neut = self.get_neutron_bgpvpn_mixin(extensions=["bgpvpn"]) + admin_bgpvpns = {"bgpvpns": [{"tenant_id": "foo", "id": "bgpvpn_id"}]} + neut._manager().list_bgpvpns.return_value = admin_bgpvpns + + self.assertEqual("bgpvpn", neut._resource) + self.assertEqual(neut.list(), admin_bgpvpns["bgpvpns"]) + + class NeutronFloatingIPTestCase(test.TestCase): def test_name(self): diff --git a/tests/unit/plugins/openstack/scenarios/neutron/test_bgpvpn.py b/tests/unit/plugins/openstack/scenarios/neutron/test_bgpvpn.py index f568f699..594d892f 100644 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_bgpvpn.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_bgpvpn.py @@ -24,19 +24,85 @@ class NeutronBgpvpnTestCase(test.TestCase): context = test.get_test_context() return context + def _get_bgpvpn_create_data(self): + return { + "route_targets": None, + "import_targets": None, + "export_targets": None, + "route_distinguishers": None} + + def _get_bgpvpn_update_data(self): + return { + "route_targets": None, + "import_targets": None, + "export_targets": None, + "route_distinguishers": None} + @ddt.data( {}, {"bgpvpn_create_args": None}, {"bgpvpn_create_args": {}}, - {"bgpvpn_create_args": {"name": "given-name"}}, ) @ddt.unpack def test_create_and_delete_bgpvpns(self, bgpvpn_create_args=None): scenario = bgpvpn.CreateAndDeleteBgpvpns(self._get_context()) bgpvpn_create_data = bgpvpn_create_args or {} + create_data = self._get_bgpvpn_create_data() + create_data.update(bgpvpn_create_data) scenario._create_bgpvpn = mock.Mock() scenario._delete_bgpvpn = mock.Mock() - scenario.run(bgpvpn_create_args=bgpvpn_create_data) - scenario._create_bgpvpn.assert_called_once_with(bgpvpn_create_data) + scenario.run(**create_data) + scenario._create_bgpvpn.assert_called_once_with( + type="l3", **create_data) scenario._delete_bgpvpn.assert_called_once_with( scenario._create_bgpvpn.return_value) + + @ddt.data( + {}, + {"bgpvpn_create_args": None}, + {"bgpvpn_create_args": {}}, + ) + @ddt.unpack + def test_create_and_list_bgpvpns(self, bgpvpn_create_args=None): + scenario = bgpvpn.CreateAndListBgpvpns(self._get_context()) + bgpvpn_create_data = bgpvpn_create_args or {} + create_data = self._get_bgpvpn_create_data() + create_data.update(bgpvpn_create_data) + bgpvpn_created = {"bgpvpn": {"id": 1, "name": "b1"}} + bgpvpn_listed = [{"id": 1}] + scenario._create_bgpvpn = mock.Mock(return_value=bgpvpn_created) + scenario._list_bgpvpns = mock.Mock(return_value=bgpvpn_listed) + scenario.run(**create_data) + scenario._create_bgpvpn.assert_called_once_with( + type="l3", **create_data) + scenario._list_bgpvpns.assert_called_once_with() + + @ddt.data( + {}, + {"bgpvpn_create_args": {}}, + {"bgpvpn_update_args": {}}, + {"bgpvpn_update_args": {"update_name": True}}, + {"bgpvpn_update_args": {"update_name": False}}, + ) + @ddt.unpack + def test_create_and_update_bgpvpns(self, bgpvpn_create_args=None, + bgpvpn_update_args=None): + scenario = bgpvpn.CreateAndUpdateBgpvpns(self._get_context()) + bgpvpn_create_data = bgpvpn_create_args or {} + bgpvpn_update_data = bgpvpn_update_args or {} + create_data = self._get_bgpvpn_create_data() + create_data.update(bgpvpn_create_data) + update_data = self._get_bgpvpn_update_data() + update_data.update(bgpvpn_update_data) + if "update_name" not in update_data: + update_data["update_name"] = False + bgpvpn_data = {} + bgpvpn_data.update(bgpvpn_create_data) + bgpvpn_data.update(bgpvpn_update_data) + scenario._create_bgpvpn = mock.Mock() + scenario._update_bgpvpn = mock.Mock() + scenario.run(**bgpvpn_data) + scenario._create_bgpvpn.assert_called_once_with( + type="l3", **create_data) + scenario._update_bgpvpn.assert_called_once_with( + scenario._create_bgpvpn.return_value, **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 2aab6d1d..595c1f71 100755 --- a/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/neutron/test_utils.py @@ -1032,37 +1032,60 @@ class NeutronScenarioTestCase(test.ScenarioTestCase): self._test_atomic_action_timer(self.scenario.atomic_actions(), "neutron.list_lbaasv2_loadbalancers") - @ddt.data( - {"bgpvpn_create_args": {}}, - {"bgpvpn_create_args": {"name": "given-name"}} - ) - @ddt.unpack - def test__create_bgpvpn(self, atomic_action=True, bgpvpn_create_args=None): - bgpvpn_create_args = bgpvpn_create_args or {} + def test__create_bgpvpn(self, atomic_action=True): bv = {"bgpvpn": {"id": "bgpvpn-id"}} - self.admin_clients("neutron").create_ext.return_value = bv - if bgpvpn_create_args.get("name") is None: - self.scenario.generate_random_name = mock.Mock( - return_value="random_name") - args = {"name": "random_name"} - args.update(bgpvpn_create_args) - expected_bv_data = {"bgpvpn": args} - resultant_bv = self.scenario._create_bgpvpn( - bgpvpn_create_args=bgpvpn_create_args) + self.admin_clients("neutron").create_bgpvpn.return_value = bv + self.scenario.generate_random_name = mock.Mock( + return_value="random_name") + expected_bv_data = {"bgpvpn": {"name": "random_name"}} + resultant_bv = self.scenario._create_bgpvpn() self.assertEqual(bv, resultant_bv) - self.admin_clients("neutron").create_ext.assert_called_once_with( - self.scenario.BGPVPNS_PATH, expected_bv_data) + self.admin_clients("neutron").create_bgpvpn.assert_called_once_with( + expected_bv_data) if atomic_action: self._test_atomic_action_timer(self.scenario.atomic_actions(), "neutron.create_bgpvpn") def test_delete_bgpvpn(self): bgpvpn_create_args = {} - bgpvpn = self.scenario._create_bgpvpn(bgpvpn_create_args) + bgpvpn = self.scenario._create_bgpvpn(**bgpvpn_create_args) self.scenario._delete_bgpvpn(bgpvpn) self._test_atomic_action_timer(self.scenario.atomic_actions(), "neutron.delete_bgpvpn") + def test__list_bgpvpns(self): + bgpvpns_list = [] + bgpvpns_dict = {"bgpvpns": bgpvpns_list} + self.admin_clients("neutron").list_bgpvpns.return_value = bgpvpns_dict + return_bgpvpns_list = self.scenario._list_bgpvpns() + self.assertEqual(bgpvpns_list, return_bgpvpns_list) + self._test_atomic_action_timer(self.scenario.atomic_actions(), + "neutron.list_bgpvpns") + + @ddt.data( + {}, + {"bgpvpn_update_args": {"update_name": True}}, + {"bgpvpn_update_args": {"update_name": False}}, + ) + @ddt.unpack + def test__update_bgpvpn(self, bgpvpn_update_args=None): + expected_bgpvpn = {"bgpvpn": {}} + bgpvpn_update_data = bgpvpn_update_args or {} + if bgpvpn_update_data.get("update_name"): + expected_bgpvpn = {"bgpvpn": {"name": "updated_name"}} + self.admin_clients( + "neutron").update_bgpvpn.return_value = expected_bgpvpn + self.scenario.generate_random_name = mock.Mock( + return_value="updated_name") + bgpvpn = {"bgpvpn": {"name": "bgpvpn-name", "id": "bgpvpn-id"}} + result_bgpvpn = self.scenario._update_bgpvpn(bgpvpn, + **bgpvpn_update_data) + self.admin_clients("neutron").update_bgpvpn.assert_called_once_with( + bgpvpn["bgpvpn"]["id"], expected_bgpvpn) + self.assertEqual(result_bgpvpn, expected_bgpvpn) + self._test_atomic_action_timer(self.scenario.atomic_actions(), + "neutron.update_bgpvpn") + class NeutronScenarioFunctionalTestCase(test.FakeClientsScenarioTestCase):