diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index 9b6acb09c4..13f44e9db5 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -43,9 +43,12 @@ # Specify the list of boot interfaces to load during service # initialization. Missing boot interfaces, or boot interfaces # which fail to initialize, will prevent the ironic-conductor -# service from starting. The default value is a recommended -# set of production-oriented boot interfaces. A complete list -# of boot interfaces present on your system may be found by +# service from starting. At least one boot interface that is +# supported by each enabled hardware type must be enabled +# here, or the ironic-conductor service will not start. Must +# not be an empty list. The default value is a recommended set +# of production-oriented boot interfaces. A complete list of +# boot interfaces present on your system may be found by # enumerating the "ironic.hardware.interfaces.boot" # entrypoint. When setting this value, please make sure that # every enabled hardware type will have the same set of @@ -62,8 +65,11 @@ # Specify the list of console interfaces to load during # service initialization. Missing console interfaces, or # console interfaces which fail to initialize, will prevent -# the ironic-conductor service from starting. The default -# value is a recommended set of production-oriented console +# the ironic-conductor service from starting. At least one +# console interface that is supported by each enabled hardware +# type must be enabled here, or the ironic-conductor service +# will not start. Must not be an empty list. The default value +# is a recommended set of production-oriented console # interfaces. A complete list of console interfaces present on # your system may be found by enumerating the # "ironic.hardware.interfaces.console" entrypoint. When @@ -82,9 +88,12 @@ # Specify the list of deploy interfaces to load during service # initialization. Missing deploy interfaces, or deploy # interfaces which fail to initialize, will prevent the -# ironic-conductor service from starting. The default value is -# a recommended set of production-oriented deploy interfaces. -# A complete list of deploy interfaces present on your system +# ironic-conductor service from starting. At least one deploy +# interface that is supported by each enabled hardware type +# must be enabled here, or the ironic-conductor service will +# not start. Must not be an empty list. The default value is a +# recommended set of production-oriented deploy interfaces. A +# complete list of deploy interfaces present on your system # may be found by enumerating the # "ironic.hardware.interfaces.deploy" entrypoint. When setting # this value, please make sure that every enabled hardware @@ -102,8 +111,11 @@ # Specify the list of inspect interfaces to load during # service initialization. Missing inspect interfaces, or # inspect interfaces which fail to initialize, will prevent -# the ironic-conductor service from starting. The default -# value is a recommended set of production-oriented inspect +# the ironic-conductor service from starting. At least one +# inspect interface that is supported by each enabled hardware +# type must be enabled here, or the ironic-conductor service +# will not start. Must not be an empty list. The default value +# is a recommended set of production-oriented inspect # interfaces. A complete list of inspect interfaces present on # your system may be found by enumerating the # "ironic.hardware.interfaces.inspect" entrypoint. When @@ -122,14 +134,18 @@ # Specify the list of management interfaces to load during # service initialization. Missing management interfaces, or # management interfaces which fail to initialize, will prevent -# the ironic-conductor service from starting. The default -# value is a recommended set of production-oriented management -# interfaces. A complete list of management interfaces present -# on your system may be found by enumerating the -# "ironic.hardware.interfaces.management" entrypoint. When -# setting this value, please make sure that every enabled -# hardware type will have the same set of enabled management -# interfaces on every ironic-conductor service. (list value) +# the ironic-conductor service from starting. At least one +# management interface that is supported by each enabled +# hardware type must be enabled here, or the ironic-conductor +# service will not start. Must not be an empty list. The +# default value is a recommended set of production-oriented +# management interfaces. A complete list of management +# interfaces present on your system may be found by +# enumerating the "ironic.hardware.interfaces.management" +# entrypoint. When setting this value, please make sure that +# every enabled hardware type will have the same set of +# enabled management interfaces on every ironic-conductor +# service. (list value) #enabled_management_interfaces = ipmitool # Default management interface to be used for nodes that do @@ -142,8 +158,11 @@ # Specify the list of network interfaces to load during # service initialization. Missing network interfaces, or # network interfaces which fail to initialize, will prevent -# the ironic-conductor service from starting. The default -# value is a recommended set of production-oriented network +# the ironic-conductor service from starting. At least one +# network interface that is supported by each enabled hardware +# type must be enabled here, or the ironic-conductor service +# will not start. Must not be an empty list. The default value +# is a recommended set of production-oriented network # interfaces. A complete list of network interfaces present on # your system may be found by enumerating the # "ironic.hardware.interfaces.network" entrypoint. When @@ -162,8 +181,11 @@ # Specify the list of power interfaces to load during service # initialization. Missing power interfaces, or power # interfaces which fail to initialize, will prevent the -# ironic-conductor service from starting. The default value is -# a recommended set of production-oriented power interfaces. A +# ironic-conductor service from starting. At least one power +# interface that is supported by each enabled hardware type +# must be enabled here, or the ironic-conductor service will +# not start. Must not be an empty list. The default value is a +# recommended set of production-oriented power interfaces. A # complete list of power interfaces present on your system may # be found by enumerating the # "ironic.hardware.interfaces.power" entrypoint. When setting @@ -182,9 +204,12 @@ # Specify the list of raid interfaces to load during service # initialization. Missing raid interfaces, or raid interfaces # which fail to initialize, will prevent the ironic-conductor -# service from starting. The default value is a recommended -# set of production-oriented raid interfaces. A complete list -# of raid interfaces present on your system may be found by +# service from starting. At least one raid interface that is +# supported by each enabled hardware type must be enabled +# here, or the ironic-conductor service will not start. Must +# not be an empty list. The default value is a recommended set +# of production-oriented raid interfaces. A complete list of +# raid interfaces present on your system may be found by # enumerating the "ironic.hardware.interfaces.raid" # entrypoint. When setting this value, please make sure that # every enabled hardware type will have the same set of @@ -201,8 +226,11 @@ # Specify the list of storage interfaces to load during # service initialization. Missing storage interfaces, or # storage interfaces which fail to initialize, will prevent -# the ironic-conductor service from starting. The default -# value is a recommended set of production-oriented storage +# the ironic-conductor service from starting. At least one +# storage interface that is supported by each enabled hardware +# type must be enabled here, or the ironic-conductor service +# will not start. Must not be an empty list. The default value +# is a recommended set of production-oriented storage # interfaces. A complete list of storage interfaces present on # your system may be found by enumerating the # "ironic.hardware.interfaces.storage" entrypoint. When @@ -221,9 +249,12 @@ # Specify the list of vendor interfaces to load during service # initialization. Missing vendor interfaces, or vendor # interfaces which fail to initialize, will prevent the -# ironic-conductor service from starting. The default value is -# a recommended set of production-oriented vendor interfaces. -# A complete list of vendor interfaces present on your system +# ironic-conductor service from starting. At least one vendor +# interface that is supported by each enabled hardware type +# must be enabled here, or the ironic-conductor service will +# not start. Must not be an empty list. The default value is a +# recommended set of production-oriented vendor interfaces. A +# complete list of vendor interfaces present on your system # may be found by enumerating the # "ironic.hardware.interfaces.vendor" entrypoint. When setting # this value, please make sure that every enabled hardware diff --git a/ironic/conductor/base_manager.py b/ironic/conductor/base_manager.py index b472b78fbb..2ad7175843 100644 --- a/ironic/conductor/base_manager.py +++ b/ironic/conductor/base_manager.py @@ -34,6 +34,7 @@ from ironic.conductor import notification_utils as notify_utils from ironic.conductor import task_manager from ironic.conf import CONF from ironic.db import api as dbapi +from ironic.drivers import base as driver_base from ironic import objects from ironic.objects import fields as obj_fields @@ -41,6 +42,28 @@ from ironic.objects import fields as obj_fields LOG = log.getLogger(__name__) +def _check_enabled_interfaces(): + """Sanity-check enabled_*_interfaces configs. + + We do this before we even bother to try to load up drivers. If we have any + dynamic drivers enabled, then we need interfaces enabled as well. + + :raises: ConfigInvalid if an enabled interfaces config option is empty. + """ + if CONF.enabled_hardware_types: + empty_confs = [] + iface_types = ['enabled_%s_interfaces' % i + for i in driver_base.ALL_INTERFACES] + for iface_type in iface_types: + conf_value = getattr(CONF, iface_type) + if not conf_value: + empty_confs.append(iface_type) + if empty_confs: + msg = (_('Configuration options %s cannot be an empty list.') % + ', '.join(empty_confs)) + raise exception.ConfigInvalid(error_msg=msg) + + class BaseConductorManager(object): def __init__(self, host, topic): @@ -83,6 +106,8 @@ class BaseConductorManager(object): self.ring_manager = hash_ring.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" + _check_enabled_interfaces() + # NOTE(deva): these calls may raise DriverLoadError or DriverNotFound # NOTE(vdrok): Instantiate network and storage interface factory on # startup so that all the interfaces are loaded at the very diff --git a/ironic/conf/default.py b/ironic/conf/default.py index fcd6ce1477..0861c1fe79 100644 --- a/ironic/conf/default.py +++ b/ironic/conf/default.py @@ -31,6 +31,10 @@ _ENABLED_IFACE_HELP = _('Specify the list of {0} interfaces to load during ' 'service initialization. Missing {0} interfaces, ' 'or {0} interfaces which fail to initialize, will ' 'prevent the ironic-conductor service from starting. ' + 'At least one {0} interface that is supported by each ' + 'enabled hardware type must be enabled here, or the ' + 'ironic-conductor service will not start. ' + 'Must not be an empty list. ' 'The default value is a recommended set of ' 'production-oriented {0} interfaces. A complete ' 'list of {0} interfaces present on your system may ' diff --git a/ironic/tests/unit/conductor/test_base_manager.py b/ironic/tests/unit/conductor/test_base_manager.py index aff8c8f9d3..be6aae298d 100644 --- a/ironic/tests/unit/conductor/test_base_manager.py +++ b/ironic/tests/unit/conductor/test_base_manager.py @@ -158,6 +158,17 @@ class StartStopTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase): self.assertTrue(mock_df.called) self.assertFalse(mock_reg.called) + def test_start_fails_on_no_enabled_interfaces(self): + self.config(enabled_boot_interfaces=[]) + self.assertRaisesRegex(exception.ConfigInvalid, + 'options enabled_boot_interfaces', + self.service.init_host) + + def test_starts_without_enabled_hardware_types(self): + self.config(enabled_hardware_types=[]) + self.config(enabled_boot_interfaces=[]) + self._start_service() + @mock.patch.object(base_manager, 'LOG') @mock.patch.object(driver_factory, 'HardwareTypesFactory') @mock.patch.object(driver_factory, 'DriverFactory') @@ -240,6 +251,23 @@ class StartStopTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase): self.assertTrue(wait_mock.called) +class CheckInterfacesTestCase(mgr_utils.ServiceSetUpMixin, + tests_db_base.DbTestCase): + def test__check_enabled_interfaces_success(self): + base_manager._check_enabled_interfaces() + + def test__check_enabled_interfaces_failure(self): + self.config(enabled_boot_interfaces=[]) + self.assertRaisesRegex(exception.ConfigInvalid, + 'options enabled_boot_interfaces', + base_manager._check_enabled_interfaces) + + def test__check_enabled_interfaces_skip_if_no_hw_types(self): + self.config(enabled_hardware_types=[]) + self.config(enabled_boot_interfaces=[]) + base_manager._check_enabled_interfaces() + + class KeepAliveTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase): def test__conductor_service_record_keepalive(self): self._start_service()