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
This commit is contained in:
Trevor McCasland 2017-02-14 13:10:27 -06:00
parent 991ea0b923
commit 453e8064a5
4 changed files with 84 additions and 5 deletions

View File

@ -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.

View File

@ -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

View File

@ -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):

View File

@ -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