diff --git a/doc/source/devref/effective_neutron.rst b/doc/source/devref/effective_neutron.rst index 9083680c7d9..273c78458d1 100644 --- a/doc/source/devref/effective_neutron.rst +++ b/doc/source/devref/effective_neutron.rst @@ -74,6 +74,11 @@ Document common pitfalls as well as good practices done during plugin developmen * When adding behavior to the L2 and L3 db base classes, do not assume that there is an agent on the other side of the message broker that interacts with the server. Plugins may not rely on `agents `_ at all. +* Be mindful of required capabilities when you develop plugin extensions. The + `Extension description `_ provides the ability to specify the list of required capabilities + for the extension you are developing. By declaring this list, the server will + not start up if the requirements are not met, thus avoiding leading the system + to experience undetermined behavior at runtime. Database interaction ~~~~~~~~~~~~~~~~~~~~ diff --git a/neutron/api/extensions.py b/neutron/api/extensions.py index e18c75f5ce7..081ffd72215 100644 --- a/neutron/api/extensions.py +++ b/neutron/api/extensions.py @@ -440,10 +440,11 @@ class ExtensionManager(object): # Exit loop as no progress was made break if exts_to_process: - # NOTE(salv-orlando): Consider whether this error should be fatal LOG.error(_LE("It was impossible to process the following " "extensions: %s because of missing requirements."), ','.join(exts_to_process.keys())) + raise exceptions.ExtensionsNotFound( + extensions=list(exts_to_process.keys())) # Extending extensions' attributes map. for ext in processed_exts.values(): diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index 02b941b8248..a93fa4fb64e 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -1172,7 +1172,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, status=p.get('status', constants.PORT_STATUS_ACTIVE), device_id=p['device_id'], device_owner=p['device_owner']) - if 'dns_name' in p: + if ('dns-integration' in self.supported_extension_aliases and + 'dns_name' in p): request_dns_name = self._get_request_dns_name(p) port_data['dns_name'] = request_dns_name @@ -1190,13 +1191,15 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, ips = self.ipam.allocate_ips_for_port_and_store(context, port, port_id) - if 'dns_name' in p: + if ('dns-integration' in self.supported_extension_aliases and + 'dns_name' in p): dns_assignment = [] if ips: dns_assignment = self._get_dns_names_for_port( context, ips, request_dns_name) - if 'dns_name' in p: + if ('dns-integration' in self.supported_extension_aliases and + 'dns_name' in p): db_port['dns_assignment'] = dns_assignment return self._make_port_dict(db_port, process_extensions=False) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index c80b864b6b8..e535140c78d 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -125,7 +125,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, "multi-provider", "allowed-address-pairs", "extra_dhcp_opt", "subnet_allocation", "net-mtu", "vlan-transparent", - "address-scope", "dns-integration", + "address-scope", "availability_zone", "network_availability_zone"] diff --git a/neutron/tests/unit/api/test_extensions.py b/neutron/tests/unit/api/test_extensions.py index 62d4ef99dc8..73257e8c689 100644 --- a/neutron/tests/unit/api/test_extensions.py +++ b/neutron/tests/unit/api/test_extensions.py @@ -502,6 +502,13 @@ class RequestExtensionTest(base.BaseTestCase): class ExtensionManagerTest(base.BaseTestCase): + def test_missing_required_extensions(self): + ext_mgr = extensions.ExtensionManager('') + attr_map = {} + ext_mgr.add_extension(ext_stubs.StubExtensionWithReqs('foo_alias')) + self.assertRaises(exceptions.ExtensionsNotFound, + ext_mgr.extend_resources, "2.0", attr_map) + def test_invalid_extensions_are_not_registered(self): class InvalidExtension(object): diff --git a/neutron/tests/unit/db/test_agentschedulers_db.py b/neutron/tests/unit/db/test_agentschedulers_db.py index c878422e68f..36343053a41 100644 --- a/neutron/tests/unit/db/test_agentschedulers_db.py +++ b/neutron/tests/unit/db/test_agentschedulers_db.py @@ -1400,8 +1400,7 @@ class OvsAgentSchedulerTestCase(OvsAgentSchedulerTestCaseBase): exc.HTTPNotFound.code) -class OvsDhcpAgentNotifierTestCase(test_l3.L3NatTestCaseMixin, - test_agent.AgentDBTestMixIn, +class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn, AgentSchedulerTestMixIn, test_plugin.NeutronDbPluginV2TestCase): plugin_str = 'neutron.plugins.ml2.plugin.Ml2Plugin' diff --git a/neutron/tests/unit/extension_stubs.py b/neutron/tests/unit/extension_stubs.py index 358769ef937..abfde11ee1a 100644 --- a/neutron/tests/unit/extension_stubs.py +++ b/neutron/tests/unit/extension_stubs.py @@ -37,6 +37,12 @@ class StubExtension(extensions.ExtensionDescriptor): return "" +class StubExtensionWithReqs(StubExtension): + + def get_required_extensions(self): + return ["foo"] + + class StubPlugin(object): def __init__(self, supported_extensions=None): diff --git a/releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml b/releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml new file mode 100644 index 00000000000..4de82b1c01e --- /dev/null +++ b/releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - The server will fail to start if any of the declared required + extensions, as needed by core and service plugins, are not + properly configured.