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