diff --git a/nova/tests/functional/api_sample_tests/api_sample_base.py b/nova/tests/functional/api_sample_tests/api_sample_base.py index a5f2ec923a0f..45408cf265ce 100644 --- a/nova/tests/functional/api_sample_tests/api_sample_base.py +++ b/nova/tests/functional/api_sample_tests/api_sample_base.py @@ -17,9 +17,7 @@ import os from oslo_config import cfg import testscenarios -from nova.api import openstack from nova.api.openstack import API_V21_CORE_EXTENSIONS # noqa -from nova.api.openstack import compute from nova import test from nova.tests.functional import api_paste_fixture from nova.tests.functional import api_samples_test_base @@ -28,15 +26,64 @@ from nova.tests.unit import fake_utils CONF = cfg.CONF +# API samples heavily uses testscenarios. This allows us to use the +# same tests, with slight variations in configuration to ensure our +# various ways of calling the API are compatible. Testscenarios works +# through the class level ``scenarios`` variable. It is an array of +# tuples where the first value in each tuple is an arbitrary name for +# the scenario (should be unique), and the second item is a dictionary +# of attributes to change in the class for the test. +# +# By default we're running scenarios for 3 situations +# +# - Hitting the default /v2 endpoint +# +# - Hitting the default /v2.1 endpoint +# +# - Hitting the /v2 but fixing the paste pipeline so that it uses the +# legacy v2 code. This requires a fixture. +# +# Things we need to set: +# +# - _api_version - what version of the API we should be hitting +# +# - request_api_version - what API microversion should be used +# +# - _additional_fixtures - any additional fixtures need +# +# - _legacy_v2_code - True/False if we are using the legacy v2 code +# stack. Sadly, a few tests really care about this. +# +# NOTE(sdague): if you want to build a test that only tests specific +# microversions, then replace the ``scenarios`` class variable in that +# test class with something like: +# +# [("v2_11", {'_api_version': 'v2.1', 'request_api_version', '2.11'})] + class ApiSampleTestBaseV21(testscenarios.WithScenarios, api_samples_test_base.ApiSampleTestBase): _api_version = 'v2' + # any additional fixtures needed for this scenario + _additional_fixtures = [] sample_dir = None extra_extensions_to_load = None - scenarios = [('v2', {'_test': 'v2'}), - ('v2_1', {'_test': 'v2.1'}), - ('v2_1_compatible', {'_test': 'v2.1_compatible'})] + _legacy_v2_code = False + + scenarios = [ + # test v2 with the v2.1 compatibility stack + ('v2', { + '_api_version': 'v2'}), + # test v2.1 base microversion + ('v2_1', { + '_api_version': 'v2.1'}), + # test v2 with the v2 legacy code + ('v2legacy', { + '_api_version': 'v2', + '_legacy_v2_code': True, + '_additional_fixtures': [ + api_paste_fixture.ApiPasteLegacyV2Fixture]}) + ] def setUp(self): self.flags(use_ipv6=False, @@ -55,24 +102,19 @@ class ApiSampleTestBaseV21(testscenarios.WithScenarios, CONF.set_override('extensions_whitelist', whitelist, 'osapi_v21') - expected_middleware = [] - if (not hasattr(self, '_test') or (self._test == 'v2.1')): - # NOTE(gmann): we should run v21 tests on /v2.1 but then we need - # two sets of sample files as api version (v2 or v2.1) is being - # added in response's link/namespace etc - # override /v2 in compatibility mode with v2.1 - self.useFixture(api_paste_fixture.ApiPasteV21Fixture()) - expected_middleware = [compute.APIRouterV21] - elif self._test == 'v2.1_compatible': - expected_middleware = [openstack.LegacyV2CompatibleWrapper, - compute.APIRouterV21] - elif (self._test == 'v2' and self._api_version == 'v2'): - # override /v2 in compatibility mode with v2 legacy - self.useFixture(api_paste_fixture.ApiPasteLegacyV2Fixture()) + + # load any additional fixtures specified by the scenario + for fix in self._additional_fixtures: + self.useFixture(fix()) + + # super class call is delayed here so that we have the right + # paste and conf before loading all the services, as we can't + # change these later. super(ApiSampleTestBaseV21, self).setUp() + self.useFixture(test.SampleNetworks(host=self.network.host)) fake_network.stub_compute_with_ips(self.stubs) fake_utils.stub_out_utils_spawn_n(self.stubs) + + # this is used to generate sample docs self.generate_samples = os.getenv('GENERATE_SAMPLES') is not None - if expected_middleware: - self._check_api_endpoint('/v2', expected_middleware) diff --git a/nova/tests/functional/api_sample_tests/test_access_ips.py b/nova/tests/functional/api_sample_tests/test_access_ips.py index a25f257f7af9..1acab202ab48 100644 --- a/nova/tests/functional/api_sample_tests/test_access_ips.py +++ b/nova/tests/functional/api_sample_tests/test_access_ips.py @@ -39,8 +39,8 @@ class AccessIPsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): return f def _servers_post(self, subs): - response = self._do_post('servers', 'server-post-req', subs) subs.update(self._get_regexes()) + response = self._do_post('servers', 'server-post-req', subs) return self._verify_response('server-post-resp', subs, response, 202) def test_servers_post(self): diff --git a/nova/tests/functional/api_sample_tests/test_extension_info.py b/nova/tests/functional/api_sample_tests/test_extension_info.py index 4992da00482e..282960396c8f 100644 --- a/nova/tests/functional/api_sample_tests/test_extension_info.py +++ b/nova/tests/functional/api_sample_tests/test_extension_info.py @@ -38,11 +38,15 @@ class ExtensionInfoAllSamplesJsonTest(api_sample_base.ApiSampleTestBaseV21): soft_auth.side_effect = fake_soft_extension_authorizer response = self._do_get('extensions') subs = self._get_regexes() + # The full extension list is one of the places that things are + # different between the API versions and the legacy vs. new + # stack. We default to the v2.1 case. template = 'extensions-list-resp' - if self._test == 'v2': - template = 'extensions-list-resp-v2' - if self._test == 'v2.1_compatible': + if self._api_version == 'v2': template = 'extensions-list-resp-v21-compatible' + if self._api_version == 'v2' and self._legacy_v2_code: + template = 'extensions-list-resp-v2' + self._verify_response(template, subs, response, 200) diff --git a/nova/tests/functional/api_sample_tests/test_fixed_ips.py b/nova/tests/functional/api_sample_tests/test_fixed_ips.py index a949ab6c88c4..133fec56b950 100644 --- a/nova/tests/functional/api_sample_tests/test_fixed_ips.py +++ b/nova/tests/functional/api_sample_tests/test_fixed_ips.py @@ -121,7 +121,7 @@ class FixedIpV24Test(FixedIpTest): # NOTE(gmann): microversion tests do not need to run for v2 API # so defining scenarios only for v2.4 which will run the original tests # by appending '(v2_4)' in test_id. - scenarios = [('v2_4', {})] + scenarios = [('v2_4', {'_api_version': 'v2.1'})] def test_get_fixed_ip(self): self._test_get_fixed_ip(reserved=False) diff --git a/nova/tests/functional/api_sample_tests/test_keypairs.py b/nova/tests/functional/api_sample_tests/test_keypairs.py index 71f8f1e08e35..7e6b3f425d28 100644 --- a/nova/tests/functional/api_sample_tests/test_keypairs.py +++ b/nova/tests/functional/api_sample_tests/test_keypairs.py @@ -115,7 +115,7 @@ class KeyPairsV22SampleJsonTest(KeyPairsSampleJsonTest): # NOTE(gmann): microversion tests do not need to run for v2 API # so defining scenarios only for v2.2 which will run the original tests # by appending '(v2_2)' in test_id. - scenarios = [('v2_2', {})] + scenarios = [('v2_2', {'_api_version': 'v2.1'})] def test_keypairs_post(self): # NOTE(claudiub): overrides the method with the same name in @@ -174,7 +174,7 @@ class KeyPairsV210SampleJsonTest(KeyPairsSampleJsonTest): request_api_version = '2.10' expected_post_status_code = 201 expected_delete_status_code = 204 - scenarios = [('v2_10', {})] + scenarios = [('v2_10', {'_api_version': 'v2.1'})] def test_keypair_create_for_user(self): subs = { diff --git a/nova/tests/functional/api_sample_tests/test_limits.py b/nova/tests/functional/api_sample_tests/test_limits.py index b8ffd2551777..09fd396d1d6f 100644 --- a/nova/tests/functional/api_sample_tests/test_limits.py +++ b/nova/tests/functional/api_sample_tests/test_limits.py @@ -31,7 +31,7 @@ class LimitsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): # NOTE(gmann): We have to separate the template files between V2 # and V2.1 as the response are different. self.template = 'limit-get-resp' - if(self._test == "v2"): + if self._legacy_v2_code: self.template = 'v2-limit-get-resp' def _get_flags(self): diff --git a/nova/tests/functional/api_sample_tests/test_remote_consoles.py b/nova/tests/functional/api_sample_tests/test_remote_consoles.py index a0af836423cd..e6bffdf91b8d 100644 --- a/nova/tests/functional/api_sample_tests/test_remote_consoles.py +++ b/nova/tests/functional/api_sample_tests/test_remote_consoles.py @@ -89,7 +89,7 @@ class ConsolesV26SampleJsonTests(test_servers.ServersSampleBase): # NOTE(gmann): microversion tests do not need to run for v2 API # so defining scenarios only for v2.6 which will run the original tests # by appending '(v2_6)' in test_id. - scenarios = [('v2_6', {})] + scenarios = [('v2_6', {'_api_version': 'v2.1'})] def setUp(self): super(ConsolesV26SampleJsonTests, self).setUp() @@ -110,7 +110,7 @@ class ConsolesV26SampleJsonTests(test_servers.ServersSampleBase): class ConsolesV28SampleJsonTests(test_servers.ServersSampleBase): extension_name = "os-remote-consoles" request_api_version = '2.8' - scenarios = [('v2_8', {})] + scenarios = [('v2_8', {'_api_version': 'v2.1'})] _api_version = 'v2' def setUp(self): diff --git a/nova/tests/functional/api_sample_tests/test_servers.py b/nova/tests/functional/api_sample_tests/test_servers.py index 31269f1a49c8..9154a25e424a 100644 --- a/nova/tests/functional/api_sample_tests/test_servers.py +++ b/nova/tests/functional/api_sample_tests/test_servers.py @@ -116,7 +116,7 @@ class ServersSampleJson29Test(ServersSampleJsonTest): # NOTE(gmann): microversion tests do not need to run for v2 API # so defining scenarios only for v2.9 which will run the original tests # by appending '(v2_9)' in test_id. - scenarios = [('v2_9', {})] + scenarios = [('v2_9', {'_api_version': 'v2.1'})] class ServerSortKeysJsonTests(ServersSampleBase): diff --git a/nova/tests/functional/api_sample_tests/test_services.py b/nova/tests/functional/api_sample_tests/test_services.py index a52ece200dfe..1b0786141ebf 100644 --- a/nova/tests/functional/api_sample_tests/test_services.py +++ b/nova/tests/functional/api_sample_tests/test_services.py @@ -110,8 +110,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV21): class ServicesV211JsonTest(ServicesJsonTest): request_api_version = '2.11' # NOTE(gryf): There is no need to run those tests on v2 API. Only - # scenarios for v2_9 will be run. - scenarios = [('v2_11', {})] + # scenarios for v2_11 will be run. + scenarios = [('v2_11', {'_api_version': 'v2.1'})] def test_services_list(self): """Return a list of all agent builds.""" diff --git a/nova/tests/functional/api_sample_tests/test_used_limits.py b/nova/tests/functional/api_sample_tests/test_used_limits.py index 3c16f9ad487f..67833851ee70 100644 --- a/nova/tests/functional/api_sample_tests/test_used_limits.py +++ b/nova/tests/functional/api_sample_tests/test_used_limits.py @@ -32,20 +32,24 @@ class UsedLimitsSamplesJsonTest(api_sample_base.ApiSampleTestBaseV21): # NOTE(park): We have to separate the template files between V2 # and V2.1 as the response are different. self.template = 'usedlimits-get-resp' - if(self._test == "v2"): + if self._legacy_v2_code: self.template = 'v2-usedlimits-get-resp' def _get_flags(self): f = super(UsedLimitsSamplesJsonTest, self)._get_flags() - f['osapi_compute_extension'] = CONF.osapi_compute_extension[:] - f['osapi_compute_extension'].append("nova.api.openstack.compute." - "legacy_v2.contrib.server_group_quotas." - "Server_group_quotas") - f['osapi_compute_extension'].append("nova.api.openstack.compute." - "legacy_v2.contrib.used_limits.Used_limits") - f['osapi_compute_extension'].append("nova.api.openstack.compute." - "legacy_v2.contrib.used_limits_for_admin." - "Used_limits_for_admin") + if self._legacy_v2_code: + f['osapi_compute_extension'] = CONF.osapi_compute_extension[:] + f['osapi_compute_extension'].append( + "nova.api.openstack.compute." + "legacy_v2.contrib.server_group_quotas." + "Server_group_quotas") + f['osapi_compute_extension'].append( + "nova.api.openstack.compute." + "legacy_v2.contrib.used_limits.Used_limits") + f['osapi_compute_extension'].append( + "nova.api.openstack.compute." + "legacy_v2.contrib.used_limits_for_admin." + "Used_limits_for_admin") return f def test_get_used_limits(self): diff --git a/nova/tests/functional/api_sample_tests/test_virtual_interfaces.py b/nova/tests/functional/api_sample_tests/test_virtual_interfaces.py index a246774e7a08..337dd62345c2 100644 --- a/nova/tests/functional/api_sample_tests/test_virtual_interfaces.py +++ b/nova/tests/functional/api_sample_tests/test_virtual_interfaces.py @@ -28,8 +28,7 @@ class VirtualInterfacesJsonTest(test_servers.ServersSampleBase): def setUp(self): super(VirtualInterfacesJsonTest, self).setUp() self.template = 'vifs-list-resp' - if (hasattr(self, '_test') and self._test in ('v2', - 'v2.1_compatible')): + if self._api_version == 'v2': self.template = 'vifs-list-resp-v2' def _get_flags(self): @@ -58,4 +57,4 @@ class VirtualInterfacesJsonV212Test(VirtualInterfacesJsonTest): # NOTE(gmann): microversion tests do not need to run for v2 API # so defining scenarios only for v2.12 which will run the original tests # by appending '(v2_12)' in test_id. - scenarios = [('v2_12', {})] + scenarios = [('v2_12', {'_api_version': 'v2.1'})] diff --git a/nova/tests/functional/api_samples_test_base.py b/nova/tests/functional/api_samples_test_base.py index 147006307781..0b68f8042fed 100644 --- a/nova/tests/functional/api_samples_test_base.py +++ b/nova/tests/functional/api_samples_test_base.py @@ -247,6 +247,13 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase): """ return subs + def _update_links(self, sample_data): + """Process sample data and update version specific links.""" + url_re = self._get_host() + "/v(2|2\.1)" + new_url = self._get_host() + "/" + self._api_version + updated_data = re.sub(url_re, new_url, sample_data) + return updated_data + def _verify_response(self, name, subs, response, exp_code): self.assertEqual(exp_code, response.status_code) response_data = response.content @@ -267,6 +274,7 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase): with file(self._get_sample(name, self.request_api_version)) as sample: sample_data = sample.read() + sample_data = self._update_links(sample_data) try: template_data = self._objectify(template_data) diff --git a/nova/tests/functional/wsgi/test_secgroup.py b/nova/tests/functional/wsgi/test_secgroup.py index 3783a9f738df..e4666332b2a1 100644 --- a/nova/tests/functional/wsgi/test_secgroup.py +++ b/nova/tests/functional/wsgi/test_secgroup.py @@ -17,7 +17,6 @@ import testscenarios from nova import test from nova.tests import fixtures as nova_fixtures -from nova.tests.functional import api_paste_fixture import nova.tests.unit.image.fake from nova.tests.unit import policy_fixture @@ -40,15 +39,16 @@ class SecgroupsFullstack(testscenarios.WithScenarios, test.TestCase): # test across the scenarios listed below setting the attributres # in the dictionary on ``self`` for each scenario. scenarios = [ - ('v2', {'_test': 'v2'}), # regular base scenario, test the v2 api - ('v2_1', {'_test': 'v2.1'}) # test v2.1 api on the v2 api endpoint + ('v2', { + '_api_version': 'v2'}), + # test v2.1 base microversion + ('v2_1', { + '_api_version': 'v2.1'}), ] def setUp(self): super(SecgroupsFullstack, self).setUp() self.useFixture(policy_fixture.RealPolicyFixture()) - if self._test == 'v2.1': - self.useFixture(api_paste_fixture.ApiPasteV21Fixture()) api_fixture = self.useFixture(nova_fixtures.OSAPIFixture()) self.api = api_fixture.api