remove processing of blacklist/whitelist/corelist extensions

This removes the facility in the API router to have limited lists of
extensions. From this point forward, what is in Nova is loaded, no
exceptions.

A number of unit tests and fakes have to be updated to pass after
this, as many unit tests were sending small lists of extensions to the
API router to simplify the responses they were getting back. This also
required stubbing out the request db cache in some cases, because
extensions later get content from there to avoid additional db
hits. As well as adjusting a few samples tests which now return more
data because all the extensions are always loaded.

There is much cleanup to happen after this, but this is the smallest
unit to get us over this boundary which will start letting us to
refold extensions back into the base views (and massively simplify the
API code).

This does not yet remove the config options because there is another
place those get used in servers.py and the testing fallout of that is
much bigger.

The reno will come with that patch.

Change-Id: I3e72f5e23ce39148f31dfdb76d18f403c6d04842
This commit is contained in:
Sean Dague 2016-06-08 15:27:19 -04:00
parent 43f69041e9
commit d8673cb256
11 changed files with 49 additions and 90 deletions

View File

@ -11,6 +11,12 @@
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/3e45fa2a-5204-466f-a684-c2a8e1c82d7f", "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/3e45fa2a-5204-466f-a684-c2a8e1c82d7f",
"rel": "bookmark" "rel": "bookmark"
} }
],
"OS-DCF:diskConfig": "AUTO",
"security_groups": [
{
"name": "default"
}
] ]
} }
} }

View File

@ -25,11 +25,8 @@ import stevedore
import webob.dec import webob.dec
import webob.exc import webob.exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi from nova.api.openstack import wsgi
import nova.conf import nova.conf
from nova import exception
from nova.i18n import _LC
from nova.i18n import _LE from nova.i18n import _LE
from nova.i18n import _LI from nova.i18n import _LI
from nova.i18n import _LW from nova.i18n import _LW
@ -245,40 +242,7 @@ class APIRouterV21(base_wsgi.Router):
# all extensions but eventually should be able to exclude # all extensions but eventually should be able to exclude
# based on a config file # based on a config file
def _check_load_extension(ext): def _check_load_extension(ext):
if (self.init_only is None or ext.obj.alias in
self.init_only) and isinstance(ext.obj,
extensions.V21APIExtensionBase):
# Check whitelist is either empty or if not then the extension
# is in the whitelist
if (not CONF.osapi_v21.extensions_whitelist or
ext.obj.alias in CONF.osapi_v21.extensions_whitelist):
# Check the extension is not in the blacklist
blacklist = CONF.osapi_v21.extensions_blacklist
if ext.obj.alias not in blacklist:
return self._register_extension(ext) return self._register_extension(ext)
return False
if (CONF.osapi_v21.extensions_blacklist or
CONF.osapi_v21.extensions_whitelist):
LOG.warning(
_LW('In the M release you must run all of the API. '
'The concept of API extensions will be removed from '
'the codebase to ensure there is a single Compute API.'))
self.init_only = init_only
LOG.debug("v21 API Extension Blacklist: %s",
CONF.osapi_v21.extensions_blacklist)
LOG.debug("v21 API Extension Whitelist: %s",
CONF.osapi_v21.extensions_whitelist)
in_blacklist_and_whitelist = set(
CONF.osapi_v21.extensions_whitelist).intersection(
CONF.osapi_v21.extensions_blacklist)
if len(in_blacklist_and_whitelist) != 0:
LOG.warning(_LW("Extensions in both blacklist and whitelist: %s"),
list(in_blacklist_and_whitelist))
self.api_extension_manager = stevedore.enabled.EnabledExtensionManager( self.api_extension_manager = stevedore.enabled.EnabledExtensionManager(
namespace=self.api_extension_namespace(), namespace=self.api_extension_namespace(),
@ -298,14 +262,6 @@ class APIRouterV21(base_wsgi.Router):
self._register_resources_check_inherits(mapper) self._register_resources_check_inherits(mapper)
self.api_extension_manager.map(self._register_controllers) self.api_extension_manager.map(self._register_controllers)
missing_core_extensions = self.get_missing_core_extensions(
self.loaded_extension_info.get_extensions().keys())
if not self.init_only and missing_core_extensions:
LOG.critical(_LC("Missing core API extensions: %s"),
missing_core_extensions)
raise exception.CoreAPIMissing(
missing_apis=missing_core_extensions)
LOG.info(_LI("Loaded extensions: %s"), LOG.info(_LI("Loaded extensions: %s"),
sorted(self.loaded_extension_info.get_extensions().keys())) sorted(self.loaded_extension_info.get_extensions().keys()))
super(APIRouterV21, self).__init__(mapper) super(APIRouterV21, self).__init__(mapper)

View File

@ -11,6 +11,12 @@
"href": "%(compute_endpoint)s/servers/%(uuid)s", "href": "%(compute_endpoint)s/servers/%(uuid)s",
"rel": "bookmark" "rel": "bookmark"
} }
],
"OS-DCF:diskConfig": "AUTO",
"security_groups": [
{
"name": "default"
}
] ]
} }
} }

View File

@ -52,6 +52,12 @@ class ConfigDriveTestV21(test.TestCase):
fakes.fake_instance_get()) fakes.fake_instance_get())
self.stub_out('nova.db.instance_get_by_uuid', self.stub_out('nova.db.instance_get_by_uuid',
fakes.fake_instance_get()) fakes.fake_instance_get())
# NOTE(sdague): because of the way extensions work, we have to
# also stub out the Request compute cache with a real compute
# object. Delete this once we remove all the gorp of
# extensions modifying the server objects.
self.stub_out('nova.api.openstack.wsgi.Request.get_db_instance',
fakes.fake_compute_get())
req = webob.Request.blank(self.base_url + uuids.sentinel) req = webob.Request.blank(self.base_url + uuids.sentinel)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
response = req.get_response(self.app) response = req.get_response(self.app)

View File

@ -70,13 +70,6 @@ class ExtensionLoadingTestCase(test.NoDBTestCase):
loaded_ext_info = extension_info.LoadedExtensionInfo() loaded_ext_info = extension_info.LoadedExtensionInfo()
self.assertFalse(loaded_ext_info._check_extension(fake_bad_extension)) self.assertFalse(loaded_ext_info._check_extension(fake_bad_extension))
def test_extensions_blacklist(self):
app = compute.APIRouterV21()
self.assertIn('os-hosts', app._loaded_extension_info.extensions)
CONF.set_override('extensions_blacklist', ['os-hosts'], 'osapi_v21')
app = compute.APIRouterV21()
self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
@mock.patch('nova.api.openstack.APIRouterV21._register_resources_list') @mock.patch('nova.api.openstack.APIRouterV21._register_resources_list')
def test_extensions_inherit(self, mock_register): def test_extensions_inherit(self, mock_register):
app = compute.APIRouterV21() app = compute.APIRouterV21()
@ -106,36 +99,6 @@ class ExtensionLoadingTestCase(test.NoDBTestCase):
app = compute.APIRouterV21() app = compute.APIRouterV21()
self.assertIn('os-hosts', app._loaded_extension_info.extensions) self.assertIn('os-hosts', app._loaded_extension_info.extensions)
def test_extensions_whitelist_block(self):
# NOTE(maurosr): just to avoid to get an exception raised for not
# loading all core api.
v21_core = openstack.API_V21_CORE_EXTENSIONS
openstack.API_V21_CORE_EXTENSIONS = set(['servers'])
self.addCleanup(self._set_v21_core, v21_core)
app = compute.APIRouterV21()
self.assertIn('os-hosts', app._loaded_extension_info.extensions)
CONF.set_override('extensions_whitelist', ['servers'], 'osapi_v21')
app = compute.APIRouterV21()
self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
def test_blacklist_overrides_whitelist(self):
# NOTE(maurosr): just to avoid to get an exception raised for not
# loading all core api.
v21_core = openstack.API_V21_CORE_EXTENSIONS
openstack.API_V21_CORE_EXTENSIONS = set(['servers'])
self.addCleanup(self._set_v21_core, v21_core)
app = compute.APIRouterV21()
self.assertIn('os-hosts', app._loaded_extension_info.extensions)
CONF.set_override('extensions_whitelist', ['servers', 'os-hosts'],
'osapi_v21')
CONF.set_override('extensions_blacklist', ['os-hosts'], 'osapi_v21')
app = compute.APIRouterV21()
self.assertNotIn('os-hosts', app._loaded_extension_info.extensions)
self.assertIn('servers', app._loaded_extension_info.extensions)
self.assertEqual(1, len(app._loaded_extension_info.extensions))
def test_get_missing_core_extensions(self): def test_get_missing_core_extensions(self):
v21_core = openstack.API_V21_CORE_EXTENSIONS v21_core = openstack.API_V21_CORE_EXTENSIONS
openstack.API_V21_CORE_EXTENSIONS = set(['core1', 'core2']) openstack.API_V21_CORE_EXTENSIONS = set(['core1', 'core2'])
@ -169,13 +132,6 @@ class ExtensionLoadingTestCase(test.NoDBTestCase):
# not be raised when creating an instance of compute.APIRouterV21 # not be raised when creating an instance of compute.APIRouterV21
compute.APIRouterV21() compute.APIRouterV21()
def test_core_extensions_missing(self):
self.stubs.Set(stevedore.enabled, 'EnabledExtensionManager',
fake_stevedore_enabled_extensions)
self.stubs.Set(extension_info, 'LoadedExtensionInfo',
fake_loaded_extension_info)
self.assertRaises(exception.CoreAPIMissing, compute.APIRouterV21)
def test_extensions_expected_error(self): def test_extensions_expected_error(self):
@extensions.expected_errors(404) @extensions.expected_errors(404)
def fake_func(): def fake_func():

View File

@ -29,6 +29,8 @@ FAKE_FLAVORS = {
"vcpus": 1, "vcpus": 1,
"ephemeral_gb": 1, "ephemeral_gb": 1,
"disabled": False, "disabled": False,
"is_public": True,
"rxtx_factor": 1.0,
}, },
'flavor 2': { 'flavor 2': {
"flavorid": '2', "flavorid": '2',
@ -39,10 +41,16 @@ FAKE_FLAVORS = {
"vcpus": 1, "vcpus": 1,
"ephemeral_gb": 1, "ephemeral_gb": 1,
"disabled": True, "disabled": True,
"is_public": True,
"rxtx_factor": 1.0,
}, },
} }
def fake_get_db_flavor(req, flavorid):
return fake_flavor_get_by_flavor_id(flavorid)
def fake_flavor_get_by_flavor_id(flavorid, ctxt=None): def fake_flavor_get_by_flavor_id(flavorid, ctxt=None):
return FAKE_FLAVORS['flavor %s' % flavorid] return FAKE_FLAVORS['flavor %s' % flavorid]
@ -72,6 +80,8 @@ class FlavorDisabledTestV21(test.NoDBTestCase):
self.stubs.Set(flavors, self.stubs.Set(flavors,
"get_flavor_by_flavor_id", "get_flavor_by_flavor_id",
fake_flavor_get_by_flavor_id) fake_flavor_get_by_flavor_id)
self.stub_out('nova.api.openstack.wsgi.Request.get_db_flavor',
fake_get_db_flavor)
def _make_request(self, url): def _make_request(self, url):
req = webob.Request.blank(url) req = webob.Request.blank(url)

View File

@ -29,6 +29,7 @@ FAKE_FLAVORS = {
"disabled": False, "disabled": False,
"ephemeral_gb": '20', "ephemeral_gb": '20',
"rxtx_factor": '1.0', "rxtx_factor": '1.0',
"is_public": True,
"vcpus": 1, "vcpus": 1,
}, },
'flavor 2': { 'flavor 2': {
@ -40,6 +41,7 @@ FAKE_FLAVORS = {
"ephemeral_gb": '25', "ephemeral_gb": '25',
"rxtx_factor": None, "rxtx_factor": None,
"disabled": False, "disabled": False,
"is_public": True,
"vcpus": 1, "vcpus": 1,
}, },
} }

View File

@ -29,6 +29,8 @@ FAKE_FLAVORS = {
"vcpus": 1, "vcpus": 1,
"ephemeral_gb": 1, "ephemeral_gb": 1,
"disabled": False, "disabled": False,
"is_public": True,
"rxtx_factor": 1.0,
}, },
'flavor 2': { 'flavor 2': {
"flavorid": '2', "flavorid": '2',
@ -39,6 +41,8 @@ FAKE_FLAVORS = {
"vcpus": 1, "vcpus": 1,
"ephemeral_gb": 1, "ephemeral_gb": 1,
"disabled": False, "disabled": False,
"is_public": True,
"rxtx_factor": 1.0,
}, },
} }

View File

@ -39,7 +39,9 @@ def fake_get_flavor_by_flavor_id(flavorid, ctxt=None):
'deleted_at': None, 'deleted_at': None,
'vcpu_weight': None, 'vcpu_weight': None,
'swap': 0, 'swap': 0,
'is_public': True,
'disabled': False, 'disabled': False,
'rxtx_factor': 1.0,
} }
@ -101,6 +103,8 @@ class FlavorExtraDataTestV21(test.NoDBTestCase):
'vcpus': 1, 'vcpus': 1,
'disk': 1, 'disk': 1,
'OS-FLV-EXT-DATA:ephemeral': 1, 'OS-FLV-EXT-DATA:ephemeral': 1,
'rxtx_factor': 1.0,
'os-flavor-access:is_public': True,
}, },
{ {
'id': '2', 'id': '2',
@ -109,6 +113,8 @@ class FlavorExtraDataTestV21(test.NoDBTestCase):
'vcpus': 1, 'vcpus': 1,
'disk': 1, 'disk': 1,
'OS-FLV-EXT-DATA:ephemeral': 1, 'OS-FLV-EXT-DATA:ephemeral': 1,
'rxtx_factor': 1.0,
'os-flavor-access:is_public': True,
}, },
] ]

View File

@ -285,6 +285,13 @@ class KeypairsTestV21(test.TestCase):
fakes.fake_instance_get()) fakes.fake_instance_get())
self.stub_out('nova.db.instance_get_by_uuid', self.stub_out('nova.db.instance_get_by_uuid',
fakes.fake_instance_get()) fakes.fake_instance_get())
# NOTE(sdague): because of the way extensions work, we have to
# also stub out the Request compute cache with a real compute
# object. Delete this once we remove all the gorp of
# extensions modifying the server objects.
self.stub_out('nova.api.openstack.wsgi.Request.get_db_instance',
fakes.fake_compute_get())
req = webob.Request.blank(self.base_url + '/servers/' + uuids.server) req = webob.Request.blank(self.base_url + '/servers/' + uuids.server)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
response = req.get_response(self.app_server) response = req.get_response(self.app_server)

View File

@ -65,7 +65,7 @@ def fake_wsgi(self, req):
def wsgi_app_v21(fake_auth_context=None, init_only=None, v2_compatible=False): def wsgi_app_v21(fake_auth_context=None, init_only=None, v2_compatible=False):
inner_app_v21 = compute.APIRouterV21(init_only) inner_app_v21 = compute.APIRouterV21()
if v2_compatible: if v2_compatible:
inner_app_v21 = openstack_api.LegacyV2CompatibleWrapper(inner_app_v21) inner_app_v21 = openstack_api.LegacyV2CompatibleWrapper(inner_app_v21)