Added check on plugin.supported_extension_aliases

Added check to neutron.api.extensions.PluginAwareExtensionManager
which raises an exception when an alias in the plugin's
`supported_extension_aliases` list is not found in the set of loaded
extension aliases. If an alias is missing, it means the extension for
that alias has not been loaded, has not been found, and the file is
missing from paths listed in `oslo.config.CONF.api_extensions_path`.

This guards against a common class of bugs in plugins,
such as typographical errors in the `supported_extension_aliases`
property.

Plugin changes:

* bigswitch.plugin: Moves api_extensions_path override to plugin's
__init__ method, similar to other plugins.

* cisco.n1kv.n1kv_neutron_plugin: Removes "policy_profile_binding" and
"network_profile_binding" as they don't exist in Neutron currently.
Removed override of api_extensions_path as it is loaded through
cisco.network_plugin.

* cisco.network_plugin: Renames "Cisco Credential" to "credential".
Adds api_extension_path override to plugin's __init__ method.

* metaplugin.meta_neutron_plugin: Avoids alias of empty string when
cfg.CONF.META.supported_extension_aliases is an empty string.

* midonet.plugin: Fixes regression of 98e16a06 from 715b16ac.

* nec.nec_plugin: Extended override of api_extensions_path to append
nec extensions path to existing configured path.

* nicira.NeutronPlugin: Extended override of api_extensions_path to
append NXP_EXT_PATH to existing configured path.

Fixes: bug 1225080

Change-Id: Idcaade221d83c611fcbd87b503b2c8377d106962
This commit is contained in:
Amir Sadoughi 2013-09-05 01:38:41 -05:00
parent bc18247313
commit f4c8a18314
14 changed files with 100 additions and 65 deletions

View File

@ -18,6 +18,7 @@
from abc import ABCMeta
import imp
import itertools
import os
from oslo.config import cfg
@ -578,6 +579,7 @@ class PluginAwareExtensionManager(ExtensionManager):
def __init__(self, path, plugins):
self.plugins = plugins
super(PluginAwareExtensionManager, self).__init__(path)
self.check_if_plugin_extensions_loaded()
def _check_extension(self, extension):
"""Check if an extension is supported by any plugin."""
@ -616,6 +618,16 @@ class PluginAwareExtensionManager(ExtensionManager):
NeutronManager.get_service_plugins())
return cls._instance
def check_if_plugin_extensions_loaded(self):
"""Check if an extension supported by a plugin has been loaded."""
plugin_extensions = set(itertools.chain.from_iterable([
getattr(plugin, "supported_extension_aliases", [])
for plugin in self.plugins.values()]))
missing_aliases = plugin_extensions - set(self.extensions)
if missing_aliases:
raise exceptions.ExtensionsNotFound(
extensions=list(missing_aliases))
class RequestExtension(object):
"""Extend requests and responses of core Neutron OpenStack API controllers.
@ -662,3 +674,9 @@ def get_extensions_path():
paths = ':'.join([cfg.CONF.api_extensions_path, paths])
return paths
def append_api_extensions_path(paths):
paths = [cfg.CONF.api_extensions_path] + paths
cfg.CONF.set_override('api_extensions_path',
':'.join([p for p in paths if p]))

View File

@ -261,6 +261,10 @@ class InvalidExtensionEnv(BadRequest):
message = _("Invalid extension environment: %(reason)s")
class ExtensionsNotFound(NotFound):
message = _("Extensions not found: %(extensions)s")
class InvalidContentType(NeutronException):
message = _("Invalid content type %(content_type)s")

View File

@ -48,11 +48,11 @@ import base64
import copy
import httplib
import json
import os
import socket
from oslo.config import cfg
from neutron.api import extensions as neutron_extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.common import constants as const
from neutron.common import exceptions
@ -77,17 +77,12 @@ from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging
from neutron.openstack.common import rpc
from neutron.plugins.bigswitch.db import porttracker_db
from neutron.plugins.bigswitch import extensions
from neutron.plugins.bigswitch import routerrule_db
from neutron.plugins.bigswitch.version import version_string_with_vcs
LOG = logging.getLogger(__name__)
# Include the BigSwitch Extensions path in the api_extensions
EXTENSIONS_PATH = os.path.join(os.path.dirname(__file__), 'extensions')
if not cfg.CONF.api_extensions_path:
cfg.CONF.set_override('api_extensions_path',
EXTENSIONS_PATH)
restproxy_opts = [
cfg.StrOpt('servers', default='localhost:8800',
help=_("A comma separated list of BigSwitch or Floodlight "
@ -450,6 +445,9 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
# init DB, proxy's persistent store defaults to in-memory sql-lite DB
db.configure_db()
# Include the BigSwitch Extensions path in the api_extensions
neutron_extensions.append_api_extensions_path(extensions.__path__)
# 'servers' is the list of network controller REST end-points
# (used in order specified till one suceeds, and it is sticky
# till next failure). Use 'server_auth' to encode api-key

View File

@ -21,8 +21,6 @@
import eventlet
from oslo.config import cfg as q_conf
from neutron.agent import securitygroups_rpc as sg_rpc
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
@ -148,8 +146,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# bulk operations.
__native_bulk_support = False
supported_extension_aliases = ["provider", "agent",
"policy_profile_binding",
"network_profile_binding",
"n1kv_profile", "network_profile",
"policy_profile", "external-net", "router",
"credential"]
@ -164,11 +160,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
n1kv_db_v2.initialize()
c_cred.Store.initialize()
self._initialize_network_ranges()
# If no api_extensions_path is provided set the following
if not q_conf.CONF.api_extensions_path:
q_conf.CONF.set_override(
'api_extensions_path',
'extensions:neutron/plugins/cisco/extensions')
self._setup_vsm()
self._setup_rpc()

View File

@ -22,6 +22,7 @@ import logging
from sqlalchemy import orm
import webob.exc as wexc
from neutron.api import extensions as neutron_extensions
from neutron.api.v2 import base
from neutron.common import exceptions as exc
from neutron.db import db_base_plugin_v2
@ -31,13 +32,14 @@ from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_exceptions as cexc
from neutron.plugins.cisco.common import config
from neutron.plugins.cisco.db import network_db_v2 as cdb
from neutron.plugins.cisco import extensions
LOG = logging.getLogger(__name__)
class PluginV2(db_base_plugin_v2.NeutronDbPluginV2):
"""Meta-Plugin with v2 API support for multiple sub-plugins."""
supported_extension_aliases = ["Cisco Credential", "Cisco qos"]
supported_extension_aliases = ["credential", "Cisco qos"]
_methods_to_delegate = ['create_network',
'delete_network', 'update_network', 'get_network',
'get_networks',
@ -81,6 +83,8 @@ class PluginV2(db_base_plugin_v2.NeutronDbPluginV2):
self.supported_extension_aliases.extend(
self._model.supported_extension_aliases)
neutron_extensions.append_api_extensions_path(extensions.__path__)
# Extend the fault map
self._extend_fault_map()

View File

@ -51,11 +51,12 @@ class MetaPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
def __init__(self, configfile=None):
LOG.debug(_("Start initializing metaplugin"))
self.supported_extension_aliases = \
cfg.CONF.META.supported_extension_aliases.split(',')
self.supported_extension_aliases += ['flavor', 'external-net',
self.supported_extension_aliases = ['flavor', 'external-net',
'router', 'ext-gw-mode',
'extraroute']
if cfg.CONF.META.supported_extension_aliases:
cfg_aliases = cfg.CONF.META.supported_extension_aliases.split(',')
self.supported_extension_aliases += cfg_aliases
# Ignore config option overapping
def _is_opt_registered(opts, opt):

View File

@ -204,7 +204,7 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
securitygroups_db.SecurityGroupDbMixin):
supported_extension_aliases = ['external-net', 'router', 'security-group',
'agent' 'dhcp_agent_scheduler', 'binding']
'agent', 'dhcp_agent_scheduler', 'binding']
__native_bulk_support = False
def __init__(self):

View File

@ -122,7 +122,6 @@ PACKET_FILTER_ATTR_MAP = {COLLECTION: PACKET_FILTER_ATTR_PARAMS}
class Packetfilter(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return ALIAS
@ -157,10 +156,6 @@ class Packetfilter(extensions.ExtensionDescriptor):
COLLECTION, resource, attr_map=PACKET_FILTER_ATTR_PARAMS)
return [pf_ext]
def update_attributes_map(self, attributes):
super(Packetfilter, self).update_attributes_map(
attributes, extension_attrs_map=PACKET_FILTER_ATTR_MAP)
def get_extended_resources(self, version):
if version == "2.0":
return PACKET_FILTER_ATTR_MAP

View File

@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron.api import extensions
from neutron.api.v2 import attributes
from neutron.openstack.common import log as logging
@ -33,7 +32,7 @@ ROUTER_PROVIDER_ATTRIBUTE = {
}
class Router_provider(extensions.ExtensionDescriptor):
class Router_provider(object):
@classmethod
def get_name(cls):
return "Router Provider"

View File

@ -17,6 +17,7 @@
# @author: Akihiro MOTOKI
from neutron.agent import securitygroups_rpc as sg_rpc
from neutron.api import extensions as neutron_extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.v2 import attributes as attrs
from neutron.common import constants as const
@ -46,6 +47,7 @@ from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import exceptions as nexc
from neutron.plugins.nec.db import api as ndb
from neutron.plugins.nec.db import router as rdb
from neutron.plugins.nec import extensions
from neutron.plugins.nec import nec_router
from neutron.plugins.nec import ofc_manager
from neutron.plugins.nec import packet_filter
@ -104,11 +106,8 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
self.ofc = ofc_manager.OFCManager()
self.base_binding_dict = self._get_base_binding_dict()
portbindings_base.register_port_dict_function()
# Set the plugin default extension path
# if no api_extensions_path is specified.
if not config.CONF.api_extensions_path:
config.CONF.set_override('api_extensions_path',
'neutron/plugins/nec/extensions')
neutron_extensions.append_api_extensions_path(extensions.__path__)
self.setup_rpc()
self.l3_rpc_notifier = nec_router.L3AgentNotifyAPI()

View File

@ -27,6 +27,7 @@ from oslo.config import cfg
from sqlalchemy.orm import exc as sa_exc
import webob.exc
from neutron.api import extensions as neutron_extensions
from neutron.api.v2 import attributes as attr
from neutron.api.v2 import base
from neutron.common import constants
@ -181,9 +182,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'default': self._nvp_delete_port}
}
# If no api_extensions_path is provided set the following
if not cfg.CONF.api_extensions_path:
cfg.CONF.set_override('api_extensions_path', NVP_EXT_PATH)
neutron_extensions.append_api_extensions_path([NVP_EXT_PATH])
self.nvp_opts = cfg.CONF.NVP
self.nvp_sync_opts = cfg.CONF.NVP_SYNC
self.cluster = create_nvp_cluster(cfg.CONF,

View File

@ -18,9 +18,9 @@
# @author: Abhishek Raut, Cisco Systems Inc.
from mock import patch
import os
from oslo.config import cfg
from neutron.api import extensions as neutron_extensions
from neutron.api.v2 import attributes
from neutron.common.test_lib import test_config
from neutron import context
@ -204,8 +204,7 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
n1kv_neutron_plugin.N1kvNeutronPluginV2._setup_vsm = _fake_setup_vsm
test_config['plugin_name_v2'] = self._plugin_name
cfg.CONF.set_override('api_extensions_path',
os.path.dirname(extensions.__file__))
neutron_extensions.append_api_extensions_path(extensions.__path__)
self.addCleanup(cfg.CONF.reset)
ext_mgr = NetworkProfileTestExtensionManager()
test_config['extension_manager'] = ext_mgr

View File

@ -32,6 +32,7 @@ from neutron.db import db_base_plugin_v2
from neutron import manager
from neutron.plugins.nicira.dbexts import nicira_networkgw_db
from neutron.plugins.nicira.extensions import nvp_networkgw as networkgw
from neutron.plugins.nicira.NeutronPlugin import NVP_EXT_PATH
from neutron import quota
from neutron.tests import base
from neutron.tests.unit import test_api_v2
@ -630,6 +631,10 @@ class TestNetworkGatewayPlugin(db_base_plugin_v2.NeutronDbPluginV2,
supported_extension_aliases = ["network-gateway"]
def __init__(self, **args):
super(TestNetworkGatewayPlugin, self).__init__(**args)
extensions.append_api_extensions_path([NVP_EXT_PATH])
def delete_port(self, context, id, nw_gw_port_check=True):
if nw_gw_port_check:
port = self._get_port(context, id)

View File

@ -17,12 +17,14 @@
import os
import mock
import routes
import webob
import webtest
from neutron.api import extensions
from neutron.common import config
from neutron.common import exceptions
from neutron.db import db_base_plugin_v2
from neutron.openstack.common import jsonutils
from neutron.openstack.common import log as logging
@ -447,6 +449,8 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
def test_unsupported_extensions_are_not_loaded(self):
stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1", "e3"])
plugin_info = {constants.CORE: stub_plugin}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(ext_stubs.StubExtension("e1"))
@ -478,9 +482,11 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
supported_extension_aliases = ["supported_extension"]
plugin_info = {constants.CORE: PluginWithoutExpectedIface()}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(
ext_stubs.ExtensionExpectingPluginInterface("supported_extension"))
ext_mgr.add_extension(ext_stubs.ExtensionExpectingPluginInterface(
"supported_extension"))
self.assertNotIn("e1", ext_mgr.extensions)
@ -494,9 +500,11 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
pass
plugin_info = {constants.CORE: PluginWithExpectedInterface()}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(
ext_stubs.ExtensionExpectingPluginInterface("supported_extension"))
ext_mgr.add_extension(ext_stubs.ExtensionExpectingPluginInterface(
"supported_extension"))
self.assertIn("supported_extension", ext_mgr.extensions)
@ -509,6 +517,9 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
pass
stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
plugin_info = {constants.CORE: stub_plugin}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(ExtensionForQuamtumPluginInterface("e1"))
@ -525,6 +536,8 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
plugin_info = {constants.CORE: stub_plugin}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(ExtensionWithNoNeedForPluginInterface("e1"))
@ -537,11 +550,21 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
plugin_info = {constants.DUMMY: stub_plugin}
with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
"check_if_plugin_extensions_loaded"):
ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
ext_mgr.add_extension(NonCorePluginExtenstion("e1"))
self.assertIn("e1", ext_mgr.extensions)
def test_unloaded_supported_extensions_raises_exception(self):
stub_plugin = ext_stubs.StubPlugin(
supported_extensions=["unloaded_extension"])
plugin_info = {constants.CORE: stub_plugin}
self.assertRaises(exceptions.ExtensionsNotFound,
extensions.PluginAwareExtensionManager,
'', plugin_info)
class ExtensionControllerTest(testlib_api.WebTestCase):