From 0f0743686134898b8a4e6025dc2a1e981fc59fee Mon Sep 17 00:00:00 2001 From: Jim Rollenhagen Date: Tue, 6 Sep 2016 22:10:48 +0000 Subject: [PATCH] Log a warning for unsupported drivers and interfaces This iterates over all loaded drivers/interfaces at factory initialization time, and looks at an attribute 'supported'. If supported is not True, a message will be logged that the driver is unsupported, deprecated, and may be removed in a future release. This patch marks all drivers as supported, and subsequent patches will mark specific drivers as unsupported, based on their testing status. This also changes the NetworkInterface class to inherit from BaseInterface (to make this work right). That looks like a programming error when that interface was created. Change-Id: I6f9fa4f367bd2e14e5992d2e601bfdcf852ef3a4 Partial-Bug: #1526410 --- ironic/common/driver_factory.py | 13 ++++++- ironic/drivers/base.py | 19 +++++++++- .../tests/unit/common/test_driver_factory.py | 35 +++++++++++++++++-- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/ironic/common/driver_factory.py b/ironic/common/driver_factory.py index 1650a1a94a..a62741f73e 100644 --- a/ironic/common/driver_factory.py +++ b/ironic/common/driver_factory.py @@ -184,7 +184,8 @@ class BaseDriverFactory(object): cls._entrypoint_name, _check_func, invoke_on_load=True, - on_load_failure_callback=_catch_driver_not_found)) + on_load_failure_callback=_catch_driver_not_found, + propagate_map_exceptions=True)) # NOTE(deva): if we were unable to load any configured driver, perhaps # because it is not present on the system, raise an error. @@ -197,6 +198,10 @@ class BaseDriverFactory(object): raise exception.DriverNotFoundInEntrypoint( driver_name=names, entrypoint=cls._entrypoint_name) + # warn for any untested/unsupported/deprecated drivers or interfaces + cls._extension_manager.map(cls._extension_manager.names(), + _warn_if_unsupported) + LOG.info(_LI("Loaded the following drivers: %s"), cls._extension_manager.names()) @@ -206,6 +211,12 @@ class BaseDriverFactory(object): return self._extension_manager.names() +def _warn_if_unsupported(ext): + if not ext.obj.supported: + LOG.warning(_LW('Driver "%s" is UNSUPPORTED. It has been deprecated ' + 'and may be removed in a future release.'), ext.name) + + class DriverFactory(BaseDriverFactory): _entrypoint_name = 'ironic.drivers' _enabled_driver_list_config_option = 'enabled_drivers' diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index 63e44f94b6..0a45209a3c 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -48,6 +48,13 @@ class BaseDriver(object): the interfaces are appropriate. """ + supported = True + """Indicates if a driver is supported. + + This will be set to False for drivers which are untested in first- or + third-party CI, or in the proces of being deprecated. + """ + core_interfaces = [] standard_interfaces = [] @@ -166,6 +173,14 @@ class BareDriver(BaseDriver): class BaseInterface(object): """A base interface implementing common functions for Driver Interfaces.""" + + supported = True + """Indicates if an interface is supported. + + This will be set to False for interfaces which are untested in first- or + third-party CI, or in the proces of being deprecated. + """ + interface_type = 'base' def __new__(cls, *args, **kwargs): @@ -1021,9 +1036,11 @@ class RAIDInterface(BaseInterface): @six.add_metaclass(abc.ABCMeta) -class NetworkInterface(object): +class NetworkInterface(BaseInterface): """Base class for network interfaces.""" + interface_type = 'network' + def get_properties(self): """Return the properties of the interface. diff --git a/ironic/tests/unit/common/test_driver_factory.py b/ironic/tests/unit/common/test_driver_factory.py index e83d815553..4fd1e61dae 100644 --- a/ironic/tests/unit/common/test_driver_factory.py +++ b/ironic/tests/unit/common/test_driver_factory.py @@ -65,7 +65,7 @@ class DriverLoadTestCase(base.TestCase): with mock.patch.object(dispatch.NameDispatchExtensionManager, '__init__', self._fake_init_driver_err): driver_factory.DriverFactory._init_extension_manager() - self.assertEqual(2, mock_em.call_count) + self.assertEqual(3, mock_em.call_count) @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) def test_driver_duplicated_entry(self, mock_log): @@ -75,6 +75,33 @@ class DriverLoadTestCase(base.TestCase): ['fake'], driver_factory.DriverFactory._extension_manager.names()) self.assertTrue(mock_log.called) + @mock.patch.object(driver_factory, '_warn_if_unsupported') + def test_driver_init_checks_unsupported(self, mock_warn): + self.config(enabled_drivers=['fake']) + driver_factory.DriverFactory._init_extension_manager() + self.assertEqual( + ['fake'], driver_factory.DriverFactory._extension_manager.names()) + self.assertTrue(mock_warn.called) + + +class WarnUnsupportedDriversTestCase(base.TestCase): + @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) + def _test__warn_if_unsupported(self, supported, mock_log): + ext = mock.Mock() + ext.obj = mock.Mock() + ext.obj.supported = supported + driver_factory._warn_if_unsupported(ext) + if supported: + self.assertFalse(mock_log.called) + else: + self.assertTrue(mock_log.called) + + def test__warn_if_unsupported_with_supported(self): + self._test__warn_if_unsupported(True) + + def test__warn_if_unsupported_with_unsupported(self): + self._test__warn_if_unsupported(False) + class GetDriverTestCase(base.TestCase): def setUp(self): @@ -98,7 +125,8 @@ class NetworkInterfaceFactoryTestCase(db_base.DbTestCase): driver_factory.NetworkInterfaceFactory._extension_manager = None self.config(enabled_drivers=['fake']) - def test_build_driver_for_task(self): + @mock.patch.object(driver_factory, '_warn_if_unsupported') + def test_build_driver_for_task(self, mock_warn): # flat and noop network interfaces are enabled in base test case factory = driver_factory.NetworkInterfaceFactory node = obj_utils.create_test_node(self.context, driver='fake', @@ -112,6 +140,9 @@ class NetworkInterfaceFactoryTestCase(db_base.DbTestCase): factory._entrypoint_name) self.assertEqual(['flat', 'neutron', 'noop'], sorted(factory._enabled_driver_list)) + # NOTE(jroll) 4 checks, one for the driver we're building and + # one for each of the 3 network interfaces + self.assertEqual(4, mock_warn.call_count) def test_build_driver_for_task_default_is_none(self): # flat and noop network interfaces are enabled in base test case