Register/unregister hardware interfaces for conductors
This registers the intersection of supported and enabled interfaces for each hardware type enabled in the conductor at conductor startup, and unregisters them at conductor shutdown. Validation is left as a todo for now. Change-Id: I14e88bfc304de9414de008d1cc8568dda9115ecc Partial-Bug: #1524745
This commit is contained in:
parent
c1c86e81af
commit
b7e6b737d7
@ -30,48 +30,39 @@
|
||||
# developer documentation online. (list value)
|
||||
#enabled_drivers = pxe_ipmitool
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of hardware types to load during
|
||||
# service initialization. Missing hardware types, or hardware
|
||||
# types which fail to initialize, will prevent the conductor
|
||||
# service from starting. No hardware types are enabled by
|
||||
# default now, but in the future this option will default to a
|
||||
# recommended set of production-oriented hardware types. A
|
||||
# complete list of hardware types present on your system may
|
||||
# be found by enumerating the "ironic.hardware.types"
|
||||
# entrypoint. (list value)
|
||||
# Specify the list of hardware types to load during service
|
||||
# initialization. Missing hardware types, or hardware types
|
||||
# which fail to initialize, will prevent the conductor service
|
||||
# from starting. No hardware types are enabled by default now,
|
||||
# but in the future this option will default to a recommended
|
||||
# set of production-oriented hardware types. A complete list
|
||||
# of hardware types present on your system may be found by
|
||||
# enumerating the "ironic.hardware.types" entrypoint. (list
|
||||
# value)
|
||||
#enabled_hardware_types =
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. 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 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 enabled boot interfaces on
|
||||
# every ironic-conductor service. (list value)
|
||||
# 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
|
||||
# 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
|
||||
# enabled boot interfaces on every ironic-conductor service.
|
||||
# (list value)
|
||||
#enabled_boot_interfaces = pxe
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default boot interface to be used for nodes that do
|
||||
# not have boot_interface field set. A complete list of boot
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.boot"
|
||||
# entrypoint. (string value)
|
||||
# Default boot interface to be used for nodes that do not have
|
||||
# boot_interface field set. A complete list of boot interfaces
|
||||
# present on your system may be found by enumerating the
|
||||
# "ironic.hardware.interfaces.boot" entrypoint. (string value)
|
||||
#default_boot_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of console interfaces to load
|
||||
# during service initialization. Missing console interfaces,
|
||||
# or console interfaces which fail to initialize, will prevent
|
||||
# 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
|
||||
# interfaces. A complete list of console interfaces present on
|
||||
@ -82,19 +73,15 @@
|
||||
# interfaces on every ironic-conductor service. (list value)
|
||||
#enabled_console_interfaces = no-console
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default console interface to be used for nodes that
|
||||
# do not have console_interface field set. A complete list of
|
||||
# console interfaces present on your system may be found by
|
||||
# Default console interface to be used for nodes that do not
|
||||
# have console_interface field set. A complete list of console
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.console"
|
||||
# entrypoint. (string value)
|
||||
#default_console_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of deploy interfaces to load during
|
||||
# service initialization. Missing deploy interfaces, or deploy
|
||||
# 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.
|
||||
@ -106,20 +93,16 @@
|
||||
# every ironic-conductor service. (list value)
|
||||
#enabled_deploy_interfaces = iscsi,direct
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default deploy interface to be used for nodes that
|
||||
# do not have deploy_interface field set. A complete list of
|
||||
# deploy interfaces present on your system may be found by
|
||||
# Default deploy interface to be used for nodes that do not
|
||||
# have deploy_interface field set. A complete list of deploy
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.deploy"
|
||||
# entrypoint. (string value)
|
||||
#default_deploy_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of inspect interfaces to load
|
||||
# during service initialization. Missing inspect interfaces,
|
||||
# or inspect interfaces which fail to initialize, will prevent
|
||||
# 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
|
||||
# interfaces. A complete list of inspect interfaces present on
|
||||
@ -130,39 +113,31 @@
|
||||
# interfaces on every ironic-conductor service. (list value)
|
||||
#enabled_inspect_interfaces = no-inspect
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default inspect interface to be used for nodes that
|
||||
# do not have inspect_interface field set. A complete list of
|
||||
# inspect interfaces present on your system may be found by
|
||||
# Default inspect interface to be used for nodes that do not
|
||||
# have inspect_interface field set. A complete list of inspect
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.inspect"
|
||||
# entrypoint. (string value)
|
||||
#default_inspect_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. 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)
|
||||
# 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)
|
||||
#enabled_management_interfaces =
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default management interface to be used for nodes
|
||||
# that do not have management_interface field set. A complete
|
||||
# list of management interfaces present on your system may be
|
||||
# found by enumerating the
|
||||
# "ironic.hardware.interfaces.management" entrypoint. (string
|
||||
# value)
|
||||
# Default management interface to be used for nodes that do
|
||||
# not have management_interface field set. A complete list of
|
||||
# management interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.management"
|
||||
# entrypoint. (string value)
|
||||
#default_management_interface = <None>
|
||||
|
||||
# Specify the list of network interfaces to load during
|
||||
@ -185,10 +160,8 @@
|
||||
# entrypoint. (string value)
|
||||
#default_network_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of power interfaces to load during
|
||||
# service initialization. Missing power interfaces, or power
|
||||
# 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
|
||||
@ -200,37 +173,30 @@
|
||||
# every ironic-conductor service. (list value)
|
||||
#enabled_power_interfaces =
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default power interface to be used for nodes that do
|
||||
# not have power_interface field set. A complete list of power
|
||||
# Default power interface to be used for nodes that do not
|
||||
# have power_interface field set. A complete list of power
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.power"
|
||||
# entrypoint. (string value)
|
||||
#default_power_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. 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 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 enabled raid interfaces on
|
||||
# every ironic-conductor service. (list value)
|
||||
# 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
|
||||
# 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
|
||||
# enabled raid interfaces on every ironic-conductor service.
|
||||
# (list value)
|
||||
#enabled_raid_interfaces = agent,no-raid
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default raid interface to be used for nodes that do
|
||||
# not have raid_interface field set. A complete list of raid
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.raid"
|
||||
# entrypoint. (string value)
|
||||
# Default raid interface to be used for nodes that do not have
|
||||
# raid_interface field set. A complete list of raid interfaces
|
||||
# present on your system may be found by enumerating the
|
||||
# "ironic.hardware.interfaces.raid" entrypoint. (string value)
|
||||
#default_raid_interface = <None>
|
||||
|
||||
# Specify the list of storage interfaces to load during
|
||||
@ -253,10 +219,8 @@
|
||||
# entrypoint. (list value)
|
||||
#default_storage_interface = <None>
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Specify the list of vendor interfaces to load during
|
||||
# service initialization. Missing vendor interfaces, or vendor
|
||||
# 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.
|
||||
@ -268,11 +232,9 @@
|
||||
# every ironic-conductor service. (list value)
|
||||
#enabled_vendor_interfaces = no-vendor
|
||||
|
||||
# WARNING: This configuration option is part of the incomplete
|
||||
# driver composition work, changing it's setting has no
|
||||
# effect. Default vendor interface to be used for nodes that
|
||||
# do not have vendor_interface field set. A complete list of
|
||||
# vendor interfaces present on your system may be found by
|
||||
# Default vendor interface to be used for nodes that do not
|
||||
# have vendor_interface field set. A complete list of vendor
|
||||
# interfaces present on your system may be found by
|
||||
# enumerating the "ironic.hardware.interfaces.vendor"
|
||||
# entrypoint. (string value)
|
||||
#default_vendor_interface = <None>
|
||||
@ -292,8 +254,8 @@
|
||||
# hosts, and an exponent of the 2, there are 40 partitions in
|
||||
# the ring.A few thousand partitions should make rebalancing
|
||||
# smooth in most cases. The default is suitable for up to a
|
||||
# few hundred conductors. Too many partitions has a CPU
|
||||
# impact. (integer value)
|
||||
# few hundred conductors. Configuring for too many partitions
|
||||
# has a negative impact on CPU usage. (integer value)
|
||||
#hash_partition_exponent = 5
|
||||
|
||||
# [Experimental Feature] Number of hosts to map onto each hash
|
||||
@ -2042,6 +2004,22 @@
|
||||
# Reason: PKI token format is no longer supported.
|
||||
#hash_algorithms = md5
|
||||
|
||||
# A choice of roles that must be present in a service token.
|
||||
# Service tokens are allowed to request that an expired token
|
||||
# can be used and so this check should tightly control that
|
||||
# only actual services should be sending this token. Roles
|
||||
# here are applied as an ANY check so any role in this list
|
||||
# must be present. For backwards compatibility reasons this
|
||||
# currently only affects the allow_expired check. (list value)
|
||||
#service_token_roles = service
|
||||
|
||||
# For backwards compatibility reasons we must let valid
|
||||
# service tokens pass that don't pass the service_token_roles
|
||||
# check as valid. Setting this true will become the default in
|
||||
# a future release and should be enabled if possible. (boolean
|
||||
# value)
|
||||
#service_token_roles_required = false
|
||||
|
||||
# Authentication type to load (string value)
|
||||
# Deprecated group/name - [keystone_authtoken]/auth_plugin
|
||||
#auth_type = <None>
|
||||
|
@ -298,9 +298,8 @@ def get_driver(driver_name):
|
||||
raise exception.DriverNotFound(driver_name=driver_name)
|
||||
|
||||
|
||||
def drivers():
|
||||
"""Get all drivers as a dict name -> driver object."""
|
||||
factory = DriverFactory()
|
||||
def _get_all_drivers(factory):
|
||||
"""Get all drivers for `factory` as a dict name -> driver object."""
|
||||
# NOTE(jroll) I don't think this needs to be ordered, but
|
||||
# ConductorManager.init_host seems to depend on this behavior (or at
|
||||
# least the unit tests for it do), and it can't hurt much to keep it
|
||||
@ -309,6 +308,56 @@ def drivers():
|
||||
for name in factory.names)
|
||||
|
||||
|
||||
def drivers():
|
||||
"""Get all drivers.
|
||||
|
||||
:returns: Dictionary mapping driver name to driver object.
|
||||
"""
|
||||
return _get_all_drivers(DriverFactory())
|
||||
|
||||
|
||||
def hardware_types():
|
||||
"""Get all hardware types.
|
||||
|
||||
:returns: Dictionary mapping hardware type name to hardware type object.
|
||||
"""
|
||||
return _get_all_drivers(HardwareTypesFactory())
|
||||
|
||||
|
||||
def interfaces(interface_type):
|
||||
"""Get all interfaces for a given interface type.
|
||||
|
||||
:param interface_type: String, type of interface to fetch for.
|
||||
:returns: Dictionary mapping interface name to interface object.
|
||||
"""
|
||||
return _get_all_drivers(_INTERFACE_LOADERS[interface_type]())
|
||||
|
||||
|
||||
def enabled_supported_interfaces(hardware_type):
|
||||
"""Get usable interfaces for a given hardware type.
|
||||
|
||||
For a given hardware type, find the intersection of enabled and supported
|
||||
interfaces for each interface type. This is the set of interfaces that are
|
||||
usable for this hardware type.
|
||||
|
||||
:param hardware_type: The hardware type object to search.
|
||||
:returns: a dict mapping interface types to a list of enabled and supported
|
||||
interface names.
|
||||
"""
|
||||
mapping = dict()
|
||||
for interface_type in driver_base.ALL_INTERFACES:
|
||||
supported = set()
|
||||
enabled = set()
|
||||
supported_ifaces = getattr(hardware_type,
|
||||
'supported_%s_interfaces' % interface_type)
|
||||
for name, iface in interfaces(interface_type).items():
|
||||
enabled.add(name)
|
||||
if iface.__class__ in supported_ifaces:
|
||||
supported.add(name)
|
||||
mapping[interface_type] = enabled.intersection(supported)
|
||||
return mapping
|
||||
|
||||
|
||||
class BaseDriverFactory(object):
|
||||
"""Discover, load and manage the drivers available.
|
||||
|
||||
|
@ -429,6 +429,13 @@ class ConductorAlreadyRegistered(IronicException):
|
||||
_msg_fmt = _("Conductor %(conductor)s already registered.")
|
||||
|
||||
|
||||
class ConductorHardwareInterfacesAlreadyRegistered(IronicException):
|
||||
_msg_fmt = _("At least one of these (hardware type %(hardware_type)s, "
|
||||
"interface type %(interface_type)s, interfaces "
|
||||
"%(interfaces)s) combinations are already registered for "
|
||||
"this conductor.")
|
||||
|
||||
|
||||
class PowerStateFailure(InvalidState):
|
||||
_msg_fmt = _("Failed to set node power state to %(pstate)s.")
|
||||
|
||||
|
@ -148,6 +148,21 @@ class BaseConductorManager(object):
|
||||
self.conductor = objects.Conductor.register(
|
||||
admin_context, self.host, driver_names, update_existing=True)
|
||||
|
||||
# register hardware types and interfaces supported by this conductor
|
||||
# and validate them against other conductors
|
||||
try:
|
||||
self._register_and_validate_hardware_interfaces()
|
||||
except (exception.DriverLoadError, exception.DriverNotFound,
|
||||
exception.ConductorHardwareInterfacesAlreadyRegistered,
|
||||
exception.InterfaceNotFoundInEntrypoint):
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to register hardware types'))
|
||||
self.del_host()
|
||||
|
||||
# TODO(jroll) validate here that at least one driver OR
|
||||
# hardware type is loaded. If not, call del_host and raise
|
||||
# NoDriversLoaded.
|
||||
|
||||
# Start periodic tasks
|
||||
self._periodic_tasks_worker = self._executor.submit(
|
||||
self._periodic_tasks.start, allow_empty=True)
|
||||
@ -219,6 +234,28 @@ class BaseConductorManager(object):
|
||||
self._executor.shutdown(wait=True)
|
||||
self._started = False
|
||||
|
||||
def _register_and_validate_hardware_interfaces(self):
|
||||
# NOTE(jroll) may raise ConductorHardwareInterfacesAlreadyRegistered
|
||||
# or InterfaceNotFoundInEntrypoint,
|
||||
# we intentionally let this bubble up to the caller.
|
||||
|
||||
# first unregister, in case we have cruft laying around
|
||||
self.conductor.unregister_all_hardware_interfaces()
|
||||
|
||||
hardware_types = driver_factory.hardware_types()
|
||||
for ht_name, ht in hardware_types.items():
|
||||
interface_map = driver_factory.enabled_supported_interfaces(ht)
|
||||
for interface_type, interface_names in interface_map.items():
|
||||
default_interface = driver_factory.default_interface(
|
||||
ht, interface_type)
|
||||
self.conductor.register_hardware_interfaces(ht_name,
|
||||
interface_type,
|
||||
interface_names,
|
||||
default_interface)
|
||||
|
||||
# TODO(jroll) validate against other conductor, warn if different
|
||||
# how do we do this performantly? :|
|
||||
|
||||
def _collect_periodic_tasks(self, obj, args):
|
||||
"""Collect periodic tasks from a given object.
|
||||
|
||||
|
@ -27,9 +27,6 @@ from oslo_utils import netutils
|
||||
from ironic.common.i18n import _
|
||||
|
||||
|
||||
# TODO(dtantsur): remove the variants with warnings as soon as we support
|
||||
# actually creating nodes with hardware types.
|
||||
|
||||
_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 '
|
||||
@ -44,24 +41,12 @@ _ENABLED_IFACE_HELP = _('Specify the list of {0} interfaces to load during '
|
||||
'set of enabled {0} interfaces on every '
|
||||
'ironic-conductor service.')
|
||||
|
||||
_ENABLED_IFACE_HELP_WITH_WARNING = (
|
||||
_('WARNING: This configuration option is part of the incomplete driver '
|
||||
'composition work, changing it\'s setting has no effect. ') +
|
||||
_ENABLED_IFACE_HELP
|
||||
)
|
||||
|
||||
_DEFAULT_IFACE_HELP = _('Default {0} interface to be used for nodes that '
|
||||
'do not have {0}_interface field set. A complete '
|
||||
'list of {0} interfaces present on your system may '
|
||||
'be found by enumerating the '
|
||||
'"ironic.hardware.interfaces.{0}" entrypoint.')
|
||||
|
||||
_DEFAULT_IFACE_HELP_WITH_WARNING = (
|
||||
_('WARNING: This configuration option is part of the incomplete driver '
|
||||
'composition work, changing it\'s setting has no effect. ') +
|
||||
_DEFAULT_IFACE_HELP
|
||||
)
|
||||
|
||||
api_opts = [
|
||||
cfg.StrOpt(
|
||||
'auth_strategy',
|
||||
@ -95,10 +80,7 @@ driver_opts = [
|
||||
'developer documentation online.')),
|
||||
cfg.ListOpt('enabled_hardware_types',
|
||||
default=[],
|
||||
help=_('WARNING: This configuration option is part of the '
|
||||
'incomplete driver composition work, changing it\'s '
|
||||
'setting has no effect. '
|
||||
'Specify the list of hardware types to load during '
|
||||
help=_('Specify the list of hardware types to load during '
|
||||
'service initialization. Missing hardware types, or '
|
||||
'hardware types which fail to initialize, will prevent '
|
||||
'the conductor service from starting. No hardware '
|
||||
@ -110,29 +92,29 @@ driver_opts = [
|
||||
'"ironic.hardware.types" entrypoint.')),
|
||||
cfg.ListOpt('enabled_boot_interfaces',
|
||||
default=['pxe'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('boot')),
|
||||
help=_ENABLED_IFACE_HELP.format('boot')),
|
||||
cfg.StrOpt('default_boot_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('boot')),
|
||||
help=_DEFAULT_IFACE_HELP.format('boot')),
|
||||
cfg.ListOpt('enabled_console_interfaces',
|
||||
default=['no-console'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('console')),
|
||||
help=_ENABLED_IFACE_HELP.format('console')),
|
||||
cfg.StrOpt('default_console_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('console')),
|
||||
help=_DEFAULT_IFACE_HELP.format('console')),
|
||||
cfg.ListOpt('enabled_deploy_interfaces',
|
||||
default=['iscsi', 'direct'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('deploy')),
|
||||
help=_ENABLED_IFACE_HELP.format('deploy')),
|
||||
cfg.StrOpt('default_deploy_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('deploy')),
|
||||
help=_DEFAULT_IFACE_HELP.format('deploy')),
|
||||
cfg.ListOpt('enabled_inspect_interfaces',
|
||||
default=['no-inspect'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('inspect')),
|
||||
help=_ENABLED_IFACE_HELP.format('inspect')),
|
||||
cfg.StrOpt('default_inspect_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('inspect')),
|
||||
help=_DEFAULT_IFACE_HELP.format('inspect')),
|
||||
cfg.ListOpt('enabled_management_interfaces',
|
||||
default=[],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('management')),
|
||||
help=_ENABLED_IFACE_HELP.format('management')),
|
||||
cfg.StrOpt('default_management_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('management')),
|
||||
help=_DEFAULT_IFACE_HELP.format('management')),
|
||||
cfg.ListOpt('enabled_network_interfaces',
|
||||
default=['flat', 'noop'],
|
||||
help=_ENABLED_IFACE_HELP.format('network')),
|
||||
@ -140,14 +122,14 @@ driver_opts = [
|
||||
help=_DEFAULT_IFACE_HELP.format('network')),
|
||||
cfg.ListOpt('enabled_power_interfaces',
|
||||
default=[],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('power')),
|
||||
help=_ENABLED_IFACE_HELP.format('power')),
|
||||
cfg.StrOpt('default_power_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('power')),
|
||||
help=_DEFAULT_IFACE_HELP.format('power')),
|
||||
cfg.ListOpt('enabled_raid_interfaces',
|
||||
default=['agent', 'no-raid'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('raid')),
|
||||
help=_ENABLED_IFACE_HELP.format('raid')),
|
||||
cfg.StrOpt('default_raid_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('raid')),
|
||||
help=_DEFAULT_IFACE_HELP.format('raid')),
|
||||
cfg.ListOpt('enabled_storage_interfaces',
|
||||
default=['noop'],
|
||||
help=_ENABLED_IFACE_HELP.format('storage')),
|
||||
@ -155,9 +137,9 @@ driver_opts = [
|
||||
help=_DEFAULT_IFACE_HELP.format('storage')),
|
||||
cfg.ListOpt('enabled_vendor_interfaces',
|
||||
default=['no-vendor'],
|
||||
help=_ENABLED_IFACE_HELP_WITH_WARNING.format('vendor')),
|
||||
help=_ENABLED_IFACE_HELP.format('vendor')),
|
||||
cfg.StrOpt('default_vendor_interface',
|
||||
help=_DEFAULT_IFACE_HELP_WITH_WARNING.format('vendor')),
|
||||
help=_DEFAULT_IFACE_HELP.format('vendor')),
|
||||
]
|
||||
|
||||
exc_log_opts = [
|
||||
|
@ -537,6 +537,38 @@ class Connection(object):
|
||||
:returns: A list of conductor hostnames.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_conductor_hardware_interfaces(self, conductor_id):
|
||||
"""List all registered hardware interfaces for a conductor.
|
||||
|
||||
:param conductor_id: Database ID of conductor.
|
||||
:returns: List of ``ConductorHardwareInterfaces`` objects.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def register_conductor_hardware_interfaces(self, conductor_id,
|
||||
hardware_type, interface_type,
|
||||
interfaces, default_interface):
|
||||
"""Registers hardware interfaces for a conductor.
|
||||
|
||||
:param conductor_id: Database ID of conductor to register for.
|
||||
:param hardware_type: Name of hardware type for the interfaces.
|
||||
:param interface_type: Type of interfaces, e.g. 'deploy' or 'boot'.
|
||||
:param interfaces: List of interface names to register.
|
||||
:param default_interface: String, the default interface for this
|
||||
hardware type and interface type.
|
||||
:raises: ConductorHardwareInterfacesAlreadyRegistered if at least one
|
||||
of the interfaces in the combination of all parameters is
|
||||
already registered.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def unregister_conductor_hardware_interfaces(self, conductor_id):
|
||||
"""Unregisters all hardware interfaces for a conductor.
|
||||
|
||||
:param conductor_id: Database ID of conductor to unregister for.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def touch_node_provisioning(self, node_id):
|
||||
"""Mark the node's provisioning as running.
|
||||
|
@ -803,6 +803,38 @@ class Connection(api.Connection):
|
||||
.all())
|
||||
return [row['hostname'] for row in result]
|
||||
|
||||
def list_conductor_hardware_interfaces(self, conductor_id):
|
||||
query = (model_query(models.ConductorHardwareInterfaces)
|
||||
.filter_by(conductor_id=conductor_id))
|
||||
return query.all()
|
||||
|
||||
def register_conductor_hardware_interfaces(self, conductor_id,
|
||||
hardware_type, interface_type,
|
||||
interfaces, default_interface):
|
||||
with _session_for_write() as session:
|
||||
try:
|
||||
for iface in interfaces:
|
||||
conductor_hw_iface = models.ConductorHardwareInterfaces()
|
||||
conductor_hw_iface['conductor_id'] = conductor_id
|
||||
conductor_hw_iface['hardware_type'] = hardware_type
|
||||
conductor_hw_iface['interface_type'] = interface_type
|
||||
conductor_hw_iface['interface_name'] = iface
|
||||
is_default = (iface == default_interface)
|
||||
conductor_hw_iface['default'] = is_default
|
||||
session.add(conductor_hw_iface)
|
||||
session.flush()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.ConductorHardwareInterfacesAlreadyRegistered(
|
||||
hardware_type=hardware_type,
|
||||
interface_type=interface_type,
|
||||
interfaces=interfaces)
|
||||
|
||||
def unregister_conductor_hardware_interfaces(self, conductor_id):
|
||||
with _session_for_write():
|
||||
query = (model_query(models.ConductorHardwareInterfaces)
|
||||
.filter_by(conductor_id=conductor_id))
|
||||
query.delete()
|
||||
|
||||
def touch_node_provisioning(self, node_id):
|
||||
with _session_for_write():
|
||||
query = model_query(models.Node)
|
||||
|
@ -27,7 +27,9 @@ class Conductor(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add register() and unregister(), make the context parameter
|
||||
# to touch() optional.
|
||||
VERSION = '1.1'
|
||||
# Version 1.2: Add register_hardware_interfaces() and
|
||||
# unregister_all_hardware_interfaces()
|
||||
VERSION = '1.2'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
@ -115,4 +117,25 @@ class Conductor(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
# @object_base.remotable
|
||||
def unregister(self, context=None):
|
||||
"""Remove this conductor from the service registry."""
|
||||
self.unregister_all_hardware_interfaces()
|
||||
self.dbapi.unregister_conductor(self.hostname)
|
||||
|
||||
def register_hardware_interfaces(self, hardware_type, interface_type,
|
||||
interfaces, default_interface):
|
||||
"""Register hardware interfaces with the conductor.
|
||||
|
||||
:param hardware_type: Name of hardware type for the interfaces.
|
||||
:param interface_type: Type of interfaces, e.g. 'deploy' or 'boot'.
|
||||
:param interfaces: List of interface names to register.
|
||||
:param default_interface: String, the default interface for this
|
||||
hardware type and interface type.
|
||||
"""
|
||||
self.dbapi.register_conductor_hardware_interfaces(self.id,
|
||||
hardware_type,
|
||||
interface_type,
|
||||
interfaces,
|
||||
default_interface)
|
||||
|
||||
def unregister_all_hardware_interfaces(self):
|
||||
"""Unregister all hardware interfaces for this conductor."""
|
||||
self.dbapi.unregister_conductor_hardware_interfaces(self.id)
|
||||
|
@ -501,3 +501,30 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
|
||||
self.assertRaises(exception.InterfaceNotFoundInEntrypoint,
|
||||
driver_factory.check_and_update_node_interfaces,
|
||||
node)
|
||||
|
||||
def _test_enabled_supported_interfaces(self, enable_storage):
|
||||
ht = fake_hardware.FakeHardware()
|
||||
expected = {
|
||||
'boot': set(['fake']),
|
||||
'console': set(['fake']),
|
||||
'deploy': set(['fake']),
|
||||
'inspect': set(['fake']),
|
||||
'management': set(['fake']),
|
||||
'network': set(['noop']),
|
||||
'power': set(['fake']),
|
||||
'raid': set(['fake']),
|
||||
'storage': set([]),
|
||||
'vendor': set(['fake'])
|
||||
}
|
||||
if enable_storage:
|
||||
self.config(enabled_storage_interfaces=['fake'])
|
||||
expected['storage'] = set(['fake'])
|
||||
|
||||
mapping = driver_factory.enabled_supported_interfaces(ht)
|
||||
self.assertEqual(expected, mapping)
|
||||
|
||||
def test_enabled_supported_interfaces(self):
|
||||
self._test_enabled_supported_interfaces(False)
|
||||
|
||||
def test_enabled_supported_interfaces_non_default(self):
|
||||
self._test_enabled_supported_interfaces(True)
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
"""Test class for Ironic BaseConductorManager."""
|
||||
|
||||
import collections
|
||||
|
||||
import eventlet
|
||||
import futurist
|
||||
from futurist import periodics
|
||||
@ -26,6 +28,8 @@ from ironic.conductor import base_manager
|
||||
from ironic.conductor import manager
|
||||
from ironic.conductor import notification_utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import fake_hardware
|
||||
from ironic.drivers import generic
|
||||
from ironic import objects
|
||||
from ironic.objects import fields
|
||||
from ironic.tests import base as tests_base
|
||||
@ -160,6 +164,17 @@ class StartStopTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
self.service.init_host)
|
||||
self.assertTrue(log_mock.error.called)
|
||||
|
||||
@mock.patch.object(base_manager, 'LOG')
|
||||
@mock.patch.object(base_manager.BaseConductorManager,
|
||||
'_register_and_validate_hardware_interfaces')
|
||||
@mock.patch.object(base_manager.BaseConductorManager, 'del_host')
|
||||
def test_start_fails_hw_type_register(self, del_mock, reg_mock, log_mock):
|
||||
reg_mock.side_effect = exception.DriverNotFound('hw-type')
|
||||
self.assertRaises(exception.DriverNotFound,
|
||||
self.service.init_host)
|
||||
self.assertTrue(log_mock.error.called)
|
||||
del_mock.assert_called_once_with()
|
||||
|
||||
def test_prevent_double_start(self):
|
||||
self._start_service()
|
||||
self.assertRaisesRegex(RuntimeError, 'already running',
|
||||
@ -224,6 +239,61 @@ class ManagerSpawnWorkerTestCase(tests_base.TestCase):
|
||||
self.service._spawn_worker, 'fake')
|
||||
|
||||
|
||||
@mock.patch.object(objects.Conductor, 'unregister_all_hardware_interfaces',
|
||||
autospec=True)
|
||||
@mock.patch.object(objects.Conductor, 'register_hardware_interfaces',
|
||||
autospec=True)
|
||||
@mock.patch.object(driver_factory, 'default_interface', autospec=True)
|
||||
@mock.patch.object(driver_factory, 'hardware_types', autospec=True)
|
||||
@mock.patch.object(driver_factory, 'enabled_supported_interfaces',
|
||||
autospec=True)
|
||||
@mgr_utils.mock_record_keepalive
|
||||
class RegisterInterfacesTestCase(mgr_utils.ServiceSetUpMixin,
|
||||
tests_db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(RegisterInterfacesTestCase, self).setUp()
|
||||
self._start_service()
|
||||
|
||||
def test__register_and_validate_hardware_interfaces(self,
|
||||
esi_mock,
|
||||
ht_mock,
|
||||
default_mock,
|
||||
reg_mock,
|
||||
unreg_mock):
|
||||
# these must be same order as esi_mock side effect
|
||||
ht_mock.return_value = collections.OrderedDict((
|
||||
('fake-hardware', fake_hardware.FakeHardware()),
|
||||
('manual-management', generic.ManualManagementHardware),
|
||||
))
|
||||
esi_mock.side_effect = [
|
||||
collections.OrderedDict((
|
||||
('management', ['fake', 'noop']),
|
||||
('deploy', ['agent', 'iscsi']),
|
||||
)),
|
||||
collections.OrderedDict((
|
||||
('management', ['fake']),
|
||||
('deploy', ['agent', 'fake']),
|
||||
)),
|
||||
]
|
||||
default_mock.side_effect = ('fake', 'agent', 'fake', 'agent')
|
||||
expected_calls = [
|
||||
mock.call(mock.ANY, 'fake-hardware', 'management',
|
||||
['fake', 'noop'], 'fake'),
|
||||
mock.call(mock.ANY, 'fake-hardware', 'deploy', ['agent', 'iscsi'],
|
||||
'agent'),
|
||||
mock.call(mock.ANY, 'manual-management', 'management', ['fake'],
|
||||
'fake'),
|
||||
mock.call(mock.ANY, 'manual-management', 'deploy',
|
||||
['agent', 'fake'], 'agent'),
|
||||
]
|
||||
|
||||
self.service._register_and_validate_hardware_interfaces()
|
||||
|
||||
unreg_mock.assert_called_once_with(mock.ANY)
|
||||
# we're iterating over dicts, don't worry about order
|
||||
reg_mock.assert_has_calls(expected_calls)
|
||||
|
||||
|
||||
class StartConsolesTestCase(mgr_utils.ServiceSetUpMixin,
|
||||
tests_db_base.DbTestCase):
|
||||
@mock.patch.object(notification_utils, 'emit_console_notification')
|
||||
|
@ -44,6 +44,51 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
c = utils.get_test_conductor(**kwargs)
|
||||
return self.dbapi.register_conductor(c)
|
||||
|
||||
def test_register_conductor_hardware_interfaces(self):
|
||||
c = self._create_test_cdr()
|
||||
interfaces = ['direct', 'iscsi']
|
||||
self.dbapi.register_conductor_hardware_interfaces(c.id, 'generic',
|
||||
'deploy', interfaces,
|
||||
'iscsi')
|
||||
ifaces = self.dbapi.list_conductor_hardware_interfaces(c.id)
|
||||
ci1, ci2 = ifaces
|
||||
self.assertEqual(2, len(ifaces))
|
||||
self.assertEqual('generic', ci1.hardware_type)
|
||||
self.assertEqual('generic', ci2.hardware_type)
|
||||
self.assertEqual('deploy', ci1.interface_type)
|
||||
self.assertEqual('deploy', ci2.interface_type)
|
||||
self.assertEqual('direct', ci1.interface_name)
|
||||
self.assertEqual('iscsi', ci2.interface_name)
|
||||
self.assertFalse(ci1.default)
|
||||
self.assertTrue(ci2.default)
|
||||
|
||||
def test_register_conductor_hardware_interfaces_duplicate(self):
|
||||
c = self._create_test_cdr()
|
||||
interfaces = ['direct', 'iscsi']
|
||||
self.dbapi.register_conductor_hardware_interfaces(c.id, 'generic',
|
||||
'deploy', interfaces,
|
||||
'iscsi')
|
||||
ifaces = self.dbapi.list_conductor_hardware_interfaces(c.id)
|
||||
ci1, ci2 = ifaces
|
||||
self.assertEqual(2, len(ifaces))
|
||||
|
||||
# do it again for the duplicates
|
||||
self.assertRaises(
|
||||
exception.ConductorHardwareInterfacesAlreadyRegistered,
|
||||
self.dbapi.register_conductor_hardware_interfaces,
|
||||
c.id, 'generic', 'deploy', interfaces, 'iscsi')
|
||||
|
||||
def test_unregister_conductor_hardware_interfaces(self):
|
||||
c = self._create_test_cdr()
|
||||
interfaces = ['direct', 'iscsi']
|
||||
self.dbapi.register_conductor_hardware_interfaces(c.id, 'generic',
|
||||
'deploy', interfaces,
|
||||
'iscsi')
|
||||
self.dbapi.unregister_conductor_hardware_interfaces(c.id)
|
||||
|
||||
ifaces = self.dbapi.list_conductor_hardware_interfaces(c.id)
|
||||
self.assertEqual([], ifaces)
|
||||
|
||||
def test_get_conductor(self):
|
||||
c1 = self._create_test_cdr()
|
||||
c2 = self.dbapi.get_conductor(c1.hostname)
|
||||
|
@ -106,7 +106,8 @@ class TestConductorObject(base.DbTestCase):
|
||||
def test_register_update_existing_true(self):
|
||||
self._test_register(update_existing=True)
|
||||
|
||||
def test_unregister(self):
|
||||
@mock.patch.object(objects.Conductor, 'unregister_all_hardware_interfaces')
|
||||
def test_unregister(self, mock_unreg_ifaces):
|
||||
host = self.fake_conductor['hostname']
|
||||
with mock.patch.object(self.dbapi, 'get_conductor',
|
||||
autospec=True) as mock_get_cdr:
|
||||
@ -116,3 +117,31 @@ class TestConductorObject(base.DbTestCase):
|
||||
c = objects.Conductor.get_by_hostname(self.context, host)
|
||||
c.unregister()
|
||||
mock_unregister_cdr.assert_called_once_with(host)
|
||||
mock_unreg_ifaces.assert_called_once_with()
|
||||
|
||||
def test_register_hardware_interfaces(self):
|
||||
host = self.fake_conductor['hostname']
|
||||
self.config(default_deploy_interface='iscsi')
|
||||
with mock.patch.object(self.dbapi, 'get_conductor',
|
||||
autospec=True) as mock_get_cdr:
|
||||
with mock.patch.object(self.dbapi,
|
||||
'register_conductor_hardware_interfaces',
|
||||
autospec=True) as mock_register:
|
||||
mock_get_cdr.return_value = self.fake_conductor
|
||||
c = objects.Conductor.get_by_hostname(self.context, host)
|
||||
args = ('hardware-type', 'deploy', ['iscsi', 'direct'],
|
||||
'iscsi')
|
||||
c.register_hardware_interfaces(*args)
|
||||
mock_register.assert_called_once_with(c.id, *args)
|
||||
|
||||
def test_unregister_all_hardware_interfaces(self):
|
||||
host = self.fake_conductor['hostname']
|
||||
with mock.patch.object(self.dbapi, 'get_conductor',
|
||||
autospec=True) as mock_get_cdr:
|
||||
with mock.patch.object(self.dbapi,
|
||||
'unregister_conductor_hardware_interfaces',
|
||||
autospec=True) as mock_unregister:
|
||||
mock_get_cdr.return_value = self.fake_conductor
|
||||
c = objects.Conductor.get_by_hostname(self.context, host)
|
||||
c.unregister_all_hardware_interfaces()
|
||||
mock_unregister.assert_called_once_with(c.id)
|
||||
|
@ -409,7 +409,7 @@ expected_object_fingerprints = {
|
||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||
'Port': '1.6-609504503d68982a10f495659990084b',
|
||||
'Portgroup': '1.3-71923a81a86743b313b190f5c675e258',
|
||||
'Conductor': '1.1-5091f249719d4a465062a1b3dc7f860d',
|
||||
'Conductor': '1.2-5091f249719d4a465062a1b3dc7f860d',
|
||||
'EventType': '1.1-aa2ba1afd38553e3880c267404e8d370',
|
||||
'NotificationPublisher': '1.0-51a09397d6c0687771fb5be9a999605d',
|
||||
'NodePayload': '1.2-f4e7a1def3b2a5784863eeed46e3a25f',
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- Hardware interfaces are now loaded at conductor startup,
|
||||
and registered in the database. ``[default]/enabled_*_interfaces``
|
||||
and ``[default]/default_*_interfaces`` configuration options now have an
|
||||
effect on this.
|
||||
upgrade:
|
||||
- Hardware interfaces are now loaded at conductor startup,
|
||||
and registered in the database. ``[default]/enabled_*_interfaces``
|
||||
and ``[default]/default_*_interfaces`` configuration options now have an
|
||||
effect on this.
|
Loading…
x
Reference in New Issue
Block a user