diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index 33c9f6f8ef..14802f7531 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -26,14 +26,19 @@ # is provided in the creation request. (string value) #default_resource_class = -# Specify the list of drivers to load during service -# initialization. Missing drivers, or drivers which fail to -# initialize, will prevent the conductor service from +# DEPRECATED: Specify the list of drivers to load during +# service initialization. Missing drivers, or drivers which +# fail to initialize, will prevent the conductor service from # starting. The option default is a recommended set of # production-oriented drivers. A complete list of drivers # present on your system may be found by enumerating the # "ironic.drivers" entrypoint. An example may be found in the # developer documentation online. (list value) +# This option is deprecated for removal. +# Its value may be silently ignored in the future. +# Reason: Hardware types should be used instead of classic +# drivers. They are enabled via the enabled_hardware_types +# option. #enabled_drivers = pxe_ipmitool # Specify the list of hardware types to load during service diff --git a/ironic/conf/default.py b/ironic/conf/default.py index a5b476bb39..6611b96889 100644 --- a/ironic/conf/default.py +++ b/ironic/conf/default.py @@ -85,7 +85,11 @@ driver_opts = [ 'complete list of drivers present on your system may ' 'be found by enumerating the "ironic.drivers" ' 'entrypoint. An example may be found in the ' - 'developer documentation online.')), + 'developer documentation online.'), + deprecated_for_removal=True, + deprecated_reason=_('Hardware types should be used instead ' + 'of classic drivers. They are enabled ' + 'via the enabled_hardware_types option.')), cfg.ListOpt('enabled_hardware_types', default=['ipmi'], help=_('Specify the list of hardware types to load during ' diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index 0e7b2ffdf9..cfc284e1bc 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -49,11 +49,13 @@ class BaseDriver(object): the interfaces are appropriate. """ - supported = True + supported = False """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 process of being deprecated. + + All classic drivers are now deprecated, and thus unsupported. """ # NOTE(jlvillal): These should be tuples to help prevent child classes from diff --git a/ironic/tests/unit/common/test_driver_factory.py b/ironic/tests/unit/common/test_driver_factory.py index 12e7b2e673..12cb0dbe7c 100644 --- a/ironic/tests/unit/common/test_driver_factory.py +++ b/ironic/tests/unit/common/test_driver_factory.py @@ -31,7 +31,7 @@ from ironic.tests.unit.objects import utils as obj_utils class FakeEp(object): - name = 'fake' + name = 'fake-hardware' class DriverLoadTestCase(db_base.DbTestCase): @@ -45,52 +45,62 @@ class DriverLoadTestCase(db_base.DbTestCase): driver='aaa', reason='bbb')) def test_driver_load_error_if_driver_enabled(self): - self.config(enabled_drivers=['fake']) + self.config(enabled_hardware_types=['fake-hardware']) with mock.patch.object(named.NamedExtensionManager, '__init__', self._fake_init_driver_err): self.assertRaises( exception.DriverLoadError, - driver_factory.DriverFactory._init_extension_manager) + driver_factory.HardwareTypesFactory._init_extension_manager) def test_wrap_in_driver_load_error_if_driver_enabled(self): - self.config(enabled_drivers=['fake']) + self.config(enabled_hardware_types=['fake-hardware']) with mock.patch.object(named.NamedExtensionManager, '__init__', self._fake_init_name_err): self.assertRaises( exception.DriverLoadError, - driver_factory.DriverFactory._init_extension_manager) + driver_factory.HardwareTypesFactory._init_extension_manager) @mock.patch.object(named.NamedExtensionManager, 'names', autospec=True) def test_no_driver_load_error_if_driver_disabled(self, mock_em): - self.config(enabled_drivers=[]) + self.config(enabled_hardware_types=[]) with mock.patch.object(named.NamedExtensionManager, '__init__', self._fake_init_driver_err): - driver_factory.DriverFactory._init_extension_manager() + driver_factory.HardwareTypesFactory._init_extension_manager() self.assertEqual(1, mock_em.call_count) @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) def test_driver_duplicated_entry(self, mock_log): - self.config(enabled_drivers=['fake', 'fake']) - driver_factory.DriverFactory._init_extension_manager() + self.config(enabled_hardware_types=['fake-hardware', + 'fake-hardware']) + driver_factory.HardwareTypesFactory._init_extension_manager() self.assertEqual( - ['fake'], driver_factory.DriverFactory._extension_manager.names()) + ['fake-hardware'], + driver_factory.HardwareTypesFactory._extension_manager.names()) self.assertTrue(mock_log.called) @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) def test_driver_empty_entry(self, mock_log): - self.config(enabled_drivers=['fake', '']) - driver_factory.DriverFactory._init_extension_manager() + self.config(enabled_hardware_types=['fake-hardware', '']) + driver_factory.HardwareTypesFactory._init_extension_manager() self.assertEqual( - ['fake'], driver_factory.DriverFactory._extension_manager.names()) + ['fake-hardware'], + driver_factory.HardwareTypesFactory._extension_manager.names()) self.assertTrue(mock_log.called) @mock.patch.object(driver_factory, '_warn_if_unsupported', autospec=True) def test_driver_init_checks_unsupported(self, mock_warn): + self.config(enabled_hardware_types=['fake-hardware']) + driver_factory.HardwareTypesFactory._init_extension_manager() + self.assertEqual( + ['fake-hardware'], + driver_factory.HardwareTypesFactory._extension_manager.names()) + self.assertTrue(mock_warn.called) + + @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) + def test_classic_drivers_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) def test_build_driver_for_task(self): @@ -103,6 +113,7 @@ class DriverLoadTestCase(db_base.DbTestCase): else: self.assertIsNotNone(impl) + @mock.patch.object(drivers_base.BaseDriver, 'supported', True) @mock.patch.object(driver_factory, '_attach_interfaces_to_driver', autospec=True) @mock.patch.object(driver_factory.LOG, 'warning', autospec=True) diff --git a/releasenotes/notes/classic-drivers-deprecation-de464065187d4c14.yaml b/releasenotes/notes/classic-drivers-deprecation-de464065187d4c14.yaml new file mode 100644 index 0000000000..c2e433e747 --- /dev/null +++ b/releasenotes/notes/classic-drivers-deprecation-de464065187d4c14.yaml @@ -0,0 +1,22 @@ +--- +prelude: > + This release deprecates classic drivers in favor of hardware types. Please + check `the migration guide + `_ + for information on which hardware types and interfaces to enable before + upgrade and how to update the nodes. The ``ironic-dbsync + online_data_migrations`` command will handle the migration, if all + required hardware types and interfaces are enabled before the upgrade. +deprecations: + - | + The classic drivers, as well as the ``enabled_drivers`` configuration + option, are now deprecated and may be removed in the Rocky relese. + A deprecation warning will be logged for every loaded classic driver. + Check `the migration guide + `_ + for information on how to update your nodes. + + .. note:: + Check `the classic drivers future specification + `_ + for technical information behind this deprecation. diff --git a/releasenotes/notes/migrate_to_hardware_types-0c85c6707c4f296d.yaml b/releasenotes/notes/migrate_to_hardware_types-0c85c6707c4f296d.yaml index 5853f91609..c8eb2e1cd4 100644 --- a/releasenotes/notes/migrate_to_hardware_types-0c85c6707c4f296d.yaml +++ b/releasenotes/notes/migrate_to_hardware_types-0c85c6707c4f296d.yaml @@ -2,10 +2,9 @@ upgrade: - | Adds new data migration ``migrate_to_hardware_types`` that will try to - migrate nodes from classic drivers to hardware types on upgrade. Matching - hardware types and interfaces have to be provided on classic drivers - themselves. Nodes that cannot be migrated are skipped. This can primary - happen for three reasons: + migrate nodes from classic drivers to hardware types on upgrade. Nodes + that cannot be migrated are skipped. This may happen due to one of these + reasons: * migration is not implemented for the classic driver, * the matching hardware type is not enabled, @@ -21,3 +20,14 @@ upgrade: This migration can be repeated several times to migrate skipped nodes after the configuration is changed. +other: + - | + A classic driver implementation can now provide matching hardware type and + interfaces to enable automatic migration to hardware types. See `the + specification + `_ + for an explanation on how to do it. + + .. note:: + This feature will only be available until the classic drivers support + is removed (presumably in the Rocky release).