From 453e8064a531197ca26b020474049afff40ba969 Mon Sep 17 00:00:00 2001 From: Trevor McCasland Date: Tue, 14 Feb 2017 13:10:27 -0600 Subject: [PATCH] Exit on failure to load mechanism drivers By using the on_missing_entrypoints_callback and on_load_failure_callback options[1] we can call a handler for the missing driver error properly. As the bug states, I logged it as a critical failure and terminated the neutron server. I used SystemExit, I wasn't sure if there was a more graceful way of exiting. [1] http://docs.openstack.org/developer/stevedore/managers.html Change-Id: Id18afd159d0b0ada0cc36964dd9c1ebe7a1cd94b Closes-Bug: #1659290 --- neutron/plugins/ml2/managers.py | 27 ++++++++++--- .../plugins/ml2/drivers/mech_faulty_agent.py | 38 +++++++++++++++++++ .../tests/unit/plugins/ml2/test_managers.py | 23 +++++++++++ setup.cfg | 1 + 4 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 5bc3b2873f7..e585a3391bb 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -24,7 +24,7 @@ from oslo_utils import excutils import six import stevedore -from neutron._i18n import _, _LE, _LI, _LW +from neutron._i18n import _, _LC, _LE, _LI, _LW from neutron.db import api as db_api from neutron.db import segments_db from neutron.extensions import external_net @@ -344,10 +344,14 @@ class MechanismManager(stevedore.named.NamedExtensionManager): LOG.info(_LI("Configured mechanism driver names: %s"), cfg.CONF.ml2.mechanism_drivers) - super(MechanismManager, self).__init__('neutron.ml2.mechanism_drivers', - cfg.CONF.ml2.mechanism_drivers, - invoke_on_load=True, - name_order=True) + super(MechanismManager, self).__init__( + 'neutron.ml2.mechanism_drivers', + cfg.CONF.ml2.mechanism_drivers, + invoke_on_load=True, + name_order=True, + on_missing_entrypoints_callback=self._driver_not_found, + on_load_failure_callback=self._driver_not_loaded + ) LOG.info(_LI("Loaded mechanism driver names: %s"), self.names()) self._register_mechanisms() self.host_filtering_supported = self.is_host_filtering_supported() @@ -355,6 +359,19 @@ class MechanismManager(stevedore.named.NamedExtensionManager): LOG.info(_LI("No mechanism drivers provide segment reachability " "information for agent scheduling.")) + def _driver_not_found(self, names): + msg = (_("The following mechanism drivers were not found: %s") + % names) + LOG.critical(msg) + raise SystemExit(msg) + + def _driver_not_loaded(self, manager, entrypoint, exception): + LOG.critical(_LC("The '%(entrypoint)s' entrypoint could not be" + " loaded for the following reason: '%(reason)s'."), + {'entrypoint': entrypoint, + 'reason': exception}) + raise SystemExit(str(exception)) + def _register_mechanisms(self): """Register all mechanism drivers. diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py b/neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py new file mode 100644 index 00000000000..18bb532de38 --- /dev/null +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py @@ -0,0 +1,38 @@ +# All Rights Reserved. +# +# Based on openvswitch mechanism driver. +# +# Copyright (c) 2013 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron.plugins.ml2.drivers import mech_agent + + +class FaultyAgentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): + """ML2 mechanism driver for testing of handlers for faulty drivers + + The purpose of this class is to test the ml2 plugin manager handlers for + on_load_failure_callback parameter provided by the + stevedore.named.NamedExtensionManager class. + """ + + def __init__(self): + raise Exception("Using a faulty driver for testing purposes.") + + def get_allowed_network_types(self, agent): + pass + + def get_mappings(self, agent): + pass diff --git a/neutron/tests/unit/plugins/ml2/test_managers.py b/neutron/tests/unit/plugins/ml2/test_managers.py index 7fc63aa2cc9..7381ff460cb 100644 --- a/neutron/tests/unit/plugins/ml2/test_managers.py +++ b/neutron/tests/unit/plugins/ml2/test_managers.py @@ -42,6 +42,29 @@ class TestManagers(base.BaseTestCase): self.assertTrue(manager._check_driver_to_bind( 'fake_driver', segments_to_bind, binding_levels)) + @mock.patch.object(managers.LOG, 'critical') + @mock.patch.object(managers.MechanismManager, '_driver_not_loaded') + def test__driver_not_found(self, mock_not_loaded, mock_log): + config.cfg.CONF.set_override('mechanism_drivers', ['invalidmech'], + group='ml2') + self.assertRaises(SystemExit, managers.MechanismManager) + mock_not_loaded.assert_not_called() + mock_log.assert_called_once_with("The following mechanism drivers " + "were not found: %s" + % set(['invalidmech'])) + + @mock.patch.object(managers.LOG, 'critical') + @mock.patch.object(managers.MechanismManager, '_driver_not_found') + def test__driver_not_loaded(self, mock_not_found, mock_log): + config.cfg.CONF.set_override('mechanism_drivers', ['faulty_agent'], + group='ml2') + self.assertRaises(SystemExit, managers.MechanismManager) + mock_log.assert_called_once_with(u"The '%(entrypoint)s' entrypoint " + "could not be loaded for the " + "following reason: '%(reason)s'.", + {'entrypoint': mock.ANY, + 'reason': mock.ANY}) + class TestMechManager(base.BaseTestCase): def setUp(self): diff --git a/setup.cfg b/setup.cfg index f423344a2eb..1bc9c928065 100644 --- a/setup.cfg +++ b/setup.cfg @@ -98,6 +98,7 @@ neutron.ml2.mechanism_drivers = l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver sriovnicswitch = neutron.plugins.ml2.drivers.mech_sriov.mech_driver.mech_driver:SriovNicSwitchMechanismDriver fake_agent = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:FakeAgentMechanismDriver + faulty_agent = neutron.tests.unit.plugins.ml2.drivers.mech_faulty_agent:FaultyAgentMechanismDriver neutron.ml2.extension_drivers = test = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestExtensionDriver testdb = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestDBExtensionDriver