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:
parent
43f69041e9
commit
d8673cb256
@ -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"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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():
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user