]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Added check on plugin.supported_extension_aliases
authorAmir Sadoughi <amir.sadoughi@gmail.com>
Thu, 5 Sep 2013 06:38:41 +0000 (01:38 -0500)
committerAmir Sadoughi <amir.sadoughi@gmail.com>
Tue, 19 Nov 2013 18:51:21 +0000 (12:51 -0600)
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

14 files changed:
neutron/api/extensions.py
neutron/common/exceptions.py
neutron/plugins/bigswitch/plugin.py
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
neutron/plugins/cisco/network_plugin.py
neutron/plugins/metaplugin/meta_neutron_plugin.py
neutron/plugins/midonet/plugin.py
neutron/plugins/nec/extensions/packetfilter.py
neutron/plugins/nec/extensions/router_provider.py
neutron/plugins/nec/nec_plugin.py
neutron/plugins/nicira/NeutronPlugin.py
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py
neutron/tests/unit/nicira/test_networkgw.py
neutron/tests/unit/test_extensions.py

index 5919d8396508672416e05128f38c86e4084fccc8..751c062adf9cce60b023ea59483c0bcc96eaa7b4 100644 (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]))
index df49df8580b746836b557f7ce116466b108026bc..a46e17dfcd0664ab55e930bd0aea5af2fdcfc158 100644 (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")
 
index b5e272e73188e8b4f91fbd9571b1db0e8e4806e0..ab2e7bf7c819807fc7cf4de62c3db7bd43382a8a 100644 (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
index 6a21d13150e635051d60f018c5c25eb0968d363f..1e695912df2cacbfa31bd8b3c6507e504f30d16b 100644 (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()
 
index f64822f51d18e71e5de7b0d98e72bcd55c8fd266..c7b0ea57f3452b8fd42c4dc307e691a3e74123ef 100644 (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()
 
index 6731d204bf1f5d60392193605891ef223a6cc967..fb4ad887768a9415e177f7fe012094f8fdcdb284 100644 (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',
-                                             'router', 'ext-gw-mode',
-                                             'extraroute']
+        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):
index ca2cb9c186769ab432c62ca00381ac7651f36854..230fe41cf5523259cfe2ee0966f27d532536a473 100644 (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):
index 52591a64b1f17d9aa1949149d1e3e6d14da08a1b..b487dc965c431229f5a365323e2185c660e8fc9f 100644 (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
index d893a4c18859c5547d4440efd5393053142ac8ae..102e2321842673d441a45adf412cc61c0258e8b6 100644 (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"
index 5d38a485f39db8ae66a629fc252d2c410405529a..8fbc59c95bcfa75988aca81d0a3bb41df20ec909 100644 (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()
index ab3319bc4bda281623b1a086993c81a4784e79ea..05081318ac295c1e62f3fc4c082edf61c4505af7 100644 (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,
index 59b2a1f99952a2f12c3e39aa54ec9de2ff306e47..298074c7dfc9992cfe0f56419e9af6ca50736244 100644 (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
index e8fcd32a0f20a4ebb3923be944c2efc86fc0aab7..db6fda6c7abbffcc05850c00d3b3345f47b0b0ba 100644 (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)
index b2ae1ec3b5508615029e63f19b9ddb87a0ccf3b4..4b0c639a63fbcf1c062d5c67744840fd79723b0e 100644 (file)
 
 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,15 +449,17 @@ 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}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
+        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"))
-        ext_mgr.add_extension(ext_stubs.StubExtension("e2"))
-        ext_mgr.add_extension(ext_stubs.StubExtension("e3"))
+            ext_mgr.add_extension(ext_stubs.StubExtension("e1"))
+            ext_mgr.add_extension(ext_stubs.StubExtension("e2"))
+            ext_mgr.add_extension(ext_stubs.StubExtension("e3"))
 
-        self.assertIn("e1", ext_mgr.extensions)
-        self.assertNotIn("e2", ext_mgr.extensions)
-        self.assertIn("e3", ext_mgr.extensions)
+            self.assertIn("e1", ext_mgr.extensions)
+            self.assertNotIn("e2", ext_mgr.extensions)
+            self.assertIn("e3", ext_mgr.extensions)
 
     def test_extensions_are_not_loaded_for_plugins_unaware_of_extensions(self):
         class ExtensionUnawarePlugin(object):
@@ -478,11 +482,13 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
             supported_extension_aliases = ["supported_extension"]
 
         plugin_info = {constants.CORE: PluginWithoutExpectedIface()}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
-        ext_mgr.add_extension(
-            ext_stubs.ExtensionExpectingPluginInterface("supported_extension"))
+        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"))
 
-        self.assertNotIn("e1", ext_mgr.extensions)
+            self.assertNotIn("e1", ext_mgr.extensions)
 
     def test_extensions_are_loaded_for_plugin_with_expected_interface(self):
 
@@ -494,11 +500,13 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
                 pass
 
         plugin_info = {constants.CORE: PluginWithExpectedInterface()}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
-        ext_mgr.add_extension(
-            ext_stubs.ExtensionExpectingPluginInterface("supported_extension"))
+        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"))
 
-        self.assertIn("supported_extension", ext_mgr.extensions)
+            self.assertIn("supported_extension", ext_mgr.extensions)
 
     def test_extensions_expecting_neutron_plugin_interface_are_loaded(self):
         class ExtensionForQuamtumPluginInterface(ext_stubs.StubExtension):
@@ -509,10 +517,13 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
             pass
         stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
         plugin_info = {constants.CORE: stub_plugin}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
-        ext_mgr.add_extension(ExtensionForQuamtumPluginInterface("e1"))
 
-        self.assertIn("e1", ext_mgr.extensions)
+        with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
+                        "check_if_plugin_extensions_loaded"):
+            ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
+            ext_mgr.add_extension(ExtensionForQuamtumPluginInterface("e1"))
+
+            self.assertIn("e1", ext_mgr.extensions)
 
     def test_extensions_without_need_for__plugin_interface_are_loaded(self):
         class ExtensionWithNoNeedForPluginInterface(ext_stubs.StubExtension):
@@ -525,10 +536,12 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
 
         stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
         plugin_info = {constants.CORE: stub_plugin}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
-        ext_mgr.add_extension(ExtensionWithNoNeedForPluginInterface("e1"))
+        with mock.patch("neutron.api.extensions.PluginAwareExtensionManager."
+                        "check_if_plugin_extensions_loaded"):
+            ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
+            ext_mgr.add_extension(ExtensionWithNoNeedForPluginInterface("e1"))
 
-        self.assertIn("e1", ext_mgr.extensions)
+            self.assertIn("e1", ext_mgr.extensions)
 
     def test_extension_loaded_for_non_core_plugin(self):
         class NonCorePluginExtenstion(ext_stubs.StubExtension):
@@ -537,10 +550,20 @@ class PluginAwareExtensionManagerTest(base.BaseTestCase):
 
         stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"])
         plugin_info = {constants.DUMMY: stub_plugin}
-        ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info)
-        ext_mgr.add_extension(NonCorePluginExtenstion("e1"))
+        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)
+            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):