From b14c06b5eda188a665ca37f884e9591cd8d47535 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Wed, 27 Jan 2016 08:23:48 -0800 Subject: [PATCH] Fail if required extensions are missing If the required extensions are missing, we currently log an error that is going to be practically ignored. That said, the unfulfilled requirement will most definitely going to lead to other failures, so we might as well fail fast. This patch also cleans up some dns-integration nonsense within the ML2 framework: the extension must not be declared statically as it's being loaded by the extension manager, and this fixes the lousy unit tests we have to live with. As for the db base plugin, some cleanup is still overdue, but it will have to be taken care of in a follow-up patch. Closes-bug: #1538623 Change-Id: Id50eeb52c5d209170042b48821a29af3421c2f5c --- doc/source/devref/effective_neutron.rst | 5 +++++ neutron/api/extensions.py | 3 ++- neutron/db/db_base_plugin_v2.py | 9 ++++++--- neutron/plugins/ml2/plugin.py | 2 +- neutron/tests/unit/api/test_extensions.py | 7 +++++++ neutron/tests/unit/db/test_agentschedulers_db.py | 3 +-- neutron/tests/unit/extension_stubs.py | 6 ++++++ .../fail-on-missing-extensions-bc332124b780875b.yaml | 5 +++++ 8 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml 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.