diff options
author | Dmitry Tantsur <divius.inside@gmail.com> | 2018-01-16 17:27:17 +0100 |
---|---|---|
committer | Dmitry Tantsur <divius.inside@gmail.com> | 2018-01-26 21:17:26 +0000 |
commit | cc6f7bc73e2b1c9dad9624f3bc9c4c6ac3d103de (patch) | |
tree | 016aa468cdbc15a9f31b5c73b5450f16e736aba6 /ironic/common/driver_factory.py | |
parent | 98570dc6addb4dbdac8cf394fcf29e6c640f1fff (diff) | |
download | ironic-cc6f7bc73e2b1c9dad9624f3bc9c4c6ac3d103de.tar.gz |
Automatically migrate nodes to hardware types
This change adds a new data migration: migrate_to_hardware_types.
It works by walking through known classic drivers, detecting matching
hardware types and interfaces and updates nodes accordingly.
Nodes that cannot be updated (e.g. matching hardware type is not
enabled) are skipped. A new migration option reset_unsupported_interfaces
can be set to True to allow resetting optional interfaces to their
no-op versions.
The example implementation are provided for the community supported
IPMI and SNMP drivers, as well as for fake drivers based on them.
Change-Id: I732b44f2ab1ef73f56b352415ffd9cdd8a0e232b
Partial-Bug: #1690185
Diffstat (limited to 'ironic/common/driver_factory.py')
-rw-r--r-- | ironic/common/driver_factory.py | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/ironic/common/driver_factory.py b/ironic/common/driver_factory.py index 531e23a29..a51e7bc54 100644 --- a/ironic/common/driver_factory.py +++ b/ironic/common/driver_factory.py @@ -17,6 +17,7 @@ import collections from oslo_concurrency import lockutils from oslo_log import log +import stevedore from stevedore import named from ironic.common import exception @@ -558,3 +559,100 @@ _INTERFACE_LOADERS = { # refactor them later to use _INTERFACE_LOADERS. NetworkInterfaceFactory = _INTERFACE_LOADERS['network'] StorageInterfaceFactory = _INTERFACE_LOADERS['storage'] + + +def calculate_migration_delta(driver_name, driver_class, + reset_unsupported_interfaces=False): + """Calculate an update for the given classic driver extension. + + This function calculates a database update required to convert a node + with a classic driver to hardware types and interfaces. + + This function is used in the data migrations and is not a part of the + public Python API. + + :param driver_name: the entry point name of the driver + :param driver_class: class of classic driver. + :param reset_unsupported_interfaces: if set to True, target interfaces + that are not enabled will be replaced with a no-<interface name>, + if possible. + :returns: Node fields requiring update as a dict (field -> new value). + None if a migration is not possible. + """ + # NOTE(dtantsur): provide defaults for optional interfaces + defaults = {'console': 'no-console', + 'inspect': 'no-inspect', + 'raid': 'no-raid', + 'rescue': 'no-rescue', + 'vendor': 'no-vendor'} + try: + hw_type, new_ifaces = driver_class.to_hardware_type() + except NotImplementedError: + LOG.warning('Skipping migrating nodes with driver %s, ' + 'migration not supported', driver_name) + return None + else: + ifaces = dict(defaults, **new_ifaces) + + if hw_type not in CONF.enabled_hardware_types: + LOG.warning('Skipping migrating nodes with driver %(drv)s: ' + 'hardware type %(hw_type)s is not enabled', + {'drv': driver_name, 'hw_type': hw_type}) + return None + + not_enabled = [] + delta = {'driver': hw_type} + for iface, value in ifaces.items(): + conf = 'enabled_%s_interfaces' % iface + if value not in getattr(CONF, conf): + not_enabled.append((iface, value)) + else: + delta['%s_interface' % iface] = value + + if not_enabled and reset_unsupported_interfaces: + still_not_enabled = [] + for iface, value in not_enabled: + try: + default = defaults[iface] + except KeyError: + still_not_enabled.append((iface, value)) + else: + conf = 'enabled_%s_interfaces' % iface + if default not in getattr(CONF, conf): + still_not_enabled.append((iface, value)) + else: + delta['%s_interface' % iface] = default + + not_enabled = still_not_enabled + + if not_enabled: + LOG.warning('Skipping migrating nodes with driver %(drv)s, ' + 'the following interfaces are not supported: ' + '%(ifaces)s', + {'drv': driver_name, + 'ifaces': ', '.join('%s_interface=%s' % tpl + for tpl in not_enabled)}) + return None + + return delta + + +def classic_drivers_to_migrate(): + """Get drivers requiring migration. + + This function is used in the data migrations and is not a part of the + public Python API. + + :returns: a dict mapping driver names to driver classes + """ + def failure_callback(mgr, ep, exc): + LOG.warning('Unable to load classic driver %(drv)s: %(err)s', + {'drv': ep.name, 'err': exc}) + + extension_manager = ( + stevedore.ExtensionManager( + 'ironic.drivers', + invoke_on_load=False, + on_load_failure_callback=failure_callback)) + + return {ext.name: ext.plugin for ext in extension_manager} |