Merge "Adds rescue_interface to base driver class"
This commit is contained in:
commit
b5563b4384
@ -227,6 +227,32 @@
|
|||||||
# "ironic.hardware.interfaces.raid" entrypoint. (string value)
|
# "ironic.hardware.interfaces.raid" entrypoint. (string value)
|
||||||
#default_raid_interface = <None>
|
#default_raid_interface = <None>
|
||||||
|
|
||||||
|
# Specify the list of rescue interfaces to load during service
|
||||||
|
# initialization. Missing rescue interfaces, or rescue
|
||||||
|
# interfaces which fail to initialize, will prevent the
|
||||||
|
# ironic-conductor service from starting. At least one rescue
|
||||||
|
# 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 rescue interfaces. A
|
||||||
|
# complete list of rescue interfaces present on your system
|
||||||
|
# may be found by enumerating the
|
||||||
|
# "ironic.hardware.interfaces.rescue" entrypoint. When setting
|
||||||
|
# this value, please make sure that every enabled hardware
|
||||||
|
# type will have the same set of enabled rescue interfaces on
|
||||||
|
# every ironic-conductor service. This option is part of
|
||||||
|
# rescue feature work, which is not currently exposed to
|
||||||
|
# users. (list value)
|
||||||
|
#enabled_rescue_interfaces = no-rescue
|
||||||
|
|
||||||
|
# Default rescue interface to be used for nodes that do not
|
||||||
|
# have rescue_interface field set. A complete list of rescue
|
||||||
|
# interfaces present on your system may be found by
|
||||||
|
# enumerating the "ironic.hardware.interfaces.rescue"
|
||||||
|
# entrypoint. This option is part of rescue feature work,
|
||||||
|
# which is not currently exposed to users. (string value)
|
||||||
|
#default_rescue_interface = <None>
|
||||||
|
|
||||||
# Specify the list of storage interfaces to load during
|
# Specify the list of storage interfaces to load during
|
||||||
# service initialization. Missing storage interfaces, or
|
# service initialization. Missing storage interfaces, or
|
||||||
# storage interfaces which fail to initialize, will prevent
|
# storage interfaces which fail to initialize, will prevent
|
||||||
|
@ -111,7 +111,7 @@ RELEASE_MAPPING = {
|
|||||||
'api': '1.36',
|
'api': '1.36',
|
||||||
'rpc': '1.42',
|
'rpc': '1.42',
|
||||||
'objects': {
|
'objects': {
|
||||||
'Node': ['1.21'],
|
'Node': ['1.22'],
|
||||||
'Conductor': ['1.2'],
|
'Conductor': ['1.2'],
|
||||||
'Chassis': ['1.3'],
|
'Chassis': ['1.3'],
|
||||||
'Port': ['1.7'],
|
'Port': ['1.7'],
|
||||||
|
@ -185,6 +185,28 @@ potentially due to invalid or incompatible information being defined for the
|
|||||||
node.
|
node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
RESCUE = 'rescue'
|
||||||
|
""" Node is in rescue mode. """
|
||||||
|
|
||||||
|
RESCUEFAIL = 'rescue failed'
|
||||||
|
""" Node rescue failed. """
|
||||||
|
|
||||||
|
RESCUEWAIT = 'rescue wait'
|
||||||
|
""" Node is waiting on an external callback.
|
||||||
|
|
||||||
|
This will be the node `provision_state` while the node is waiting for
|
||||||
|
the driver to finish rescuing the node.
|
||||||
|
"""
|
||||||
|
|
||||||
|
RESCUING = 'rescuing'
|
||||||
|
""" Node is in process of being rescued. """
|
||||||
|
|
||||||
|
UNRESCUEFAIL = 'unrescue failed'
|
||||||
|
""" Node unrescue failed. """
|
||||||
|
|
||||||
|
UNRESCUING = 'unrescuing'
|
||||||
|
""" Node is being restored from rescue mode (to active state). """
|
||||||
|
|
||||||
UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, CLEANFAIL, ERROR,
|
UPDATE_ALLOWED_STATES = (DEPLOYFAIL, INSPECTING, INSPECTFAIL, CLEANFAIL, ERROR,
|
||||||
VERIFYING, ADOPTFAIL)
|
VERIFYING, ADOPTFAIL)
|
||||||
"""Transitional states in which we allow updating a node."""
|
"""Transitional states in which we allow updating a node."""
|
||||||
|
@ -1555,6 +1555,11 @@ class ConductorManager(base_manager.BaseConductorManager):
|
|||||||
task.node.instance_info)
|
task.node.instance_info)
|
||||||
task.node.driver_internal_info['is_whole_disk_image'] = iwdi
|
task.node.driver_internal_info['is_whole_disk_image'] = iwdi
|
||||||
for iface_name in task.driver.non_vendor_interfaces:
|
for iface_name in task.driver.non_vendor_interfaces:
|
||||||
|
# TODO(stendulker): Remove this check in 'rescue' API patch
|
||||||
|
# Do not have to return the validation result for 'rescue'
|
||||||
|
# interface.
|
||||||
|
if iface_name == 'rescue':
|
||||||
|
continue
|
||||||
iface = getattr(task.driver, iface_name, None)
|
iface = getattr(task.driver, iface_name, None)
|
||||||
result = reason = None
|
result = reason = None
|
||||||
if iface:
|
if iface:
|
||||||
|
@ -52,6 +52,18 @@ _DEFAULT_IFACE_HELP = _('Default {0} interface to be used for nodes that '
|
|||||||
'be found by enumerating the '
|
'be found by enumerating the '
|
||||||
'"ironic.hardware.interfaces.{0}" entrypoint.')
|
'"ironic.hardware.interfaces.{0}" entrypoint.')
|
||||||
|
|
||||||
|
# TODO(stendulker) Remove this in rescue API patch.
|
||||||
|
_ENABLED_IFACE_HELP_FOR_RESCUE = (_ENABLED_IFACE_HELP +
|
||||||
|
_(' This option is part of rescue feature '
|
||||||
|
'work, which is not currently exposed to '
|
||||||
|
'users.'))
|
||||||
|
|
||||||
|
# TODO(stendulker) Remove this in rescue API patch.
|
||||||
|
_DEFAULT_IFACE_HELP_FOR_RESCUE = (_DEFAULT_IFACE_HELP +
|
||||||
|
_(' This option is part of rescue feature '
|
||||||
|
'work, which is not currently exposed to '
|
||||||
|
'users.'))
|
||||||
|
|
||||||
api_opts = [
|
api_opts = [
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'auth_strategy',
|
'auth_strategy',
|
||||||
@ -137,6 +149,11 @@ driver_opts = [
|
|||||||
help=_ENABLED_IFACE_HELP.format('raid')),
|
help=_ENABLED_IFACE_HELP.format('raid')),
|
||||||
cfg.StrOpt('default_raid_interface',
|
cfg.StrOpt('default_raid_interface',
|
||||||
help=_DEFAULT_IFACE_HELP.format('raid')),
|
help=_DEFAULT_IFACE_HELP.format('raid')),
|
||||||
|
cfg.ListOpt('enabled_rescue_interfaces',
|
||||||
|
default=['no-rescue'],
|
||||||
|
help=_ENABLED_IFACE_HELP_FOR_RESCUE.format('rescue')),
|
||||||
|
cfg.StrOpt('default_rescue_interface',
|
||||||
|
help=_DEFAULT_IFACE_HELP_FOR_RESCUE.format('rescue')),
|
||||||
cfg.ListOpt('enabled_storage_interfaces',
|
cfg.ListOpt('enabled_storage_interfaces',
|
||||||
default=['cinder', 'noop'],
|
default=['cinder', 'noop'],
|
||||||
help=_ENABLED_IFACE_HELP.format('storage')),
|
help=_ENABLED_IFACE_HELP.format('storage')),
|
||||||
|
@ -81,9 +81,6 @@ class BaseDriver(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
rescue = None
|
rescue = None
|
||||||
# NOTE(deva): hide rescue from the interface list in Icehouse
|
|
||||||
# because the API for this has not been created yet.
|
|
||||||
# standard_interfaces.append('rescue')
|
|
||||||
"""`Standard` attribute for accessing rescue features.
|
"""`Standard` attribute for accessing rescue features.
|
||||||
|
|
||||||
A reference to an instance of :class:RescueInterface.
|
A reference to an instance of :class:RescueInterface.
|
||||||
@ -170,7 +167,9 @@ class BareDriver(BaseDriver):
|
|||||||
|
|
||||||
A reference to an instance of :class:StorageInterface.
|
A reference to an instance of :class:StorageInterface.
|
||||||
"""
|
"""
|
||||||
standard_interfaces = BaseDriver.standard_interfaces + ('storage',)
|
|
||||||
|
standard_interfaces = (BaseDriver.standard_interfaces + ('rescue',
|
||||||
|
'storage',))
|
||||||
|
|
||||||
|
|
||||||
ALL_INTERFACES = set(BareDriver().all_interfaces)
|
ALL_INTERFACES = set(BareDriver().all_interfaces)
|
||||||
@ -562,6 +561,10 @@ class RescueInterface(BaseInterface):
|
|||||||
"""Boot the task's node into a rescue environment.
|
"""Boot the task's node into a rescue environment.
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:raises: InstanceRescueFailure if node validation or rescue operation
|
||||||
|
fails.
|
||||||
|
:returns: states.RESCUEWAIT if rescue is in progress asynchronously
|
||||||
|
or states.RESCUE if it is complete.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@ -569,8 +572,22 @@ class RescueInterface(BaseInterface):
|
|||||||
"""Tear down the rescue environment, and return to normal.
|
"""Tear down the rescue environment, and return to normal.
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:raises: InstanceUnrescueFailure if node validation or unrescue
|
||||||
|
operation fails.
|
||||||
|
:returns: states.ACTIVE if it is successful.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def clean_up(self, task):
|
||||||
|
"""Clean up the rescue environment for the task's node.
|
||||||
|
|
||||||
|
This is particularly useful for nodes where rescuing is asynchronous
|
||||||
|
and a timeout occurs.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Representation of a single vendor method metadata
|
# Representation of a single vendor method metadata
|
||||||
VendorMetadata = collections.namedtuple('VendorMetadata', ['method',
|
VendorMetadata = collections.namedtuple('VendorMetadata', ['method',
|
||||||
|
@ -66,6 +66,11 @@ class FakeHardware(hardware_type.AbstractHardwareType):
|
|||||||
"""List of classes of supported raid interfaces."""
|
"""List of classes of supported raid interfaces."""
|
||||||
return [fake.FakeRAID]
|
return [fake.FakeRAID]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_rescue_interfaces(self):
|
||||||
|
"""List of classes of supported rescue interfaces."""
|
||||||
|
return [fake.FakeRescue]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_storage_interfaces(self):
|
def supported_storage_interfaces(self):
|
||||||
"""List of classes of supported storage interfaces."""
|
"""List of classes of supported storage interfaces."""
|
||||||
|
@ -83,6 +83,11 @@ class AbstractHardwareType(object):
|
|||||||
"""List of supported raid interfaces."""
|
"""List of supported raid interfaces."""
|
||||||
return [noop.NoRAID]
|
return [noop.NoRAID]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_rescue_interfaces(self):
|
||||||
|
"""List of supported rescue interfaces."""
|
||||||
|
return [noop.NoRescue]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_storage_interfaces(self):
|
def supported_storage_interfaces(self):
|
||||||
"""List of supported storage interfaces."""
|
"""List of supported storage interfaces."""
|
||||||
|
@ -260,3 +260,19 @@ class FakeStorage(base.StorageInterface):
|
|||||||
|
|
||||||
def should_write_image(self, task):
|
def should_write_image(self, task):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class FakeRescue(base.RescueInterface):
|
||||||
|
"""Example implementation of a simple rescue interface."""
|
||||||
|
|
||||||
|
def get_properties(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def validate(self, task):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rescue(self, task):
|
||||||
|
return states.RESCUE
|
||||||
|
|
||||||
|
def unrescue(self, task):
|
||||||
|
return states.ACTIVE
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
from oslo_utils import versionutils
|
||||||
from oslo_versionedobjects import base as object_base
|
from oslo_versionedobjects import base as object_base
|
||||||
|
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
@ -55,7 +56,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
# power_interface, raid_interface, vendor_interface
|
# power_interface, raid_interface, vendor_interface
|
||||||
# Version 1.20: Type of network_interface changed to just nullable string
|
# Version 1.20: Type of network_interface changed to just nullable string
|
||||||
# Version 1.21: Add storage_interface field
|
# Version 1.21: Add storage_interface field
|
||||||
VERSION = '1.21'
|
# Version 1.22: Add rescue_interface field
|
||||||
|
VERSION = '1.22'
|
||||||
|
|
||||||
dbapi = db_api.get_instance()
|
dbapi = db_api.get_instance()
|
||||||
|
|
||||||
@ -123,6 +125,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
'network_interface': object_fields.StringField(nullable=True),
|
'network_interface': object_fields.StringField(nullable=True),
|
||||||
'power_interface': object_fields.StringField(nullable=True),
|
'power_interface': object_fields.StringField(nullable=True),
|
||||||
'raid_interface': object_fields.StringField(nullable=True),
|
'raid_interface': object_fields.StringField(nullable=True),
|
||||||
|
'rescue_interface': object_fields.StringField(nullable=True),
|
||||||
'storage_interface': object_fields.StringField(nullable=True),
|
'storage_interface': object_fields.StringField(nullable=True),
|
||||||
'vendor_interface': object_fields.StringField(nullable=True),
|
'vendor_interface': object_fields.StringField(nullable=True),
|
||||||
}
|
}
|
||||||
@ -415,6 +418,41 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
node = cls._from_db_object(context, cls(), db_node)
|
node = cls._from_db_object(context, cls(), db_node)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
def _convert_to_version(self, target_version,
|
||||||
|
remove_unavailable_fields=True):
|
||||||
|
"""Convert to the target version.
|
||||||
|
|
||||||
|
Convert the object to the target version. The target version may be
|
||||||
|
the same, older, or newer than the version of the object. This is
|
||||||
|
used for DB interactions as well as for serialization/deserialization.
|
||||||
|
|
||||||
|
Version 1.22: rescue_interface field was added. Its default value is
|
||||||
|
None. For versions prior to this, it should be set to None (or
|
||||||
|
removed).
|
||||||
|
|
||||||
|
:param target_version: the desired version of the object
|
||||||
|
:param remove_unavailable_fields: True to remove fields that are
|
||||||
|
unavailable in the target version; set this to True when
|
||||||
|
(de)serializing. False to set the unavailable fields to appropriate
|
||||||
|
values; set this to False for DB interactions.
|
||||||
|
"""
|
||||||
|
target_version = versionutils.convert_version_to_tuple(target_version)
|
||||||
|
# Convert the rescue_interface field.
|
||||||
|
rescue_iface_is_set = self.obj_attr_is_set('rescue_interface')
|
||||||
|
if target_version >= (1, 22):
|
||||||
|
# Target version supports rescue_interface.
|
||||||
|
if not rescue_iface_is_set:
|
||||||
|
# Set it to its default value if it is not set.
|
||||||
|
self.rescue_interface = None
|
||||||
|
elif rescue_iface_is_set:
|
||||||
|
# Target version does not support rescue_interface, and it is set.
|
||||||
|
if remove_unavailable_fields:
|
||||||
|
# (De)serialising: remove unavailable fields.
|
||||||
|
delattr(self, 'rescue_interface')
|
||||||
|
elif self.rescue_interface is not None:
|
||||||
|
# DB: set unavailable field to the default of None.
|
||||||
|
self.rescue_interface = None
|
||||||
|
|
||||||
|
|
||||||
@base.IronicObjectRegistry.register
|
@base.IronicObjectRegistry.register
|
||||||
class NodePayload(notification.NotificationPayloadBase):
|
class NodePayload(notification.NotificationPayloadBase):
|
||||||
@ -461,6 +499,11 @@ class NodePayload(notification.NotificationPayloadBase):
|
|||||||
'uuid': ('node', 'uuid')
|
'uuid': ('node', 'uuid')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO(stendulker): At a later point in time, once rescue_interface
|
||||||
|
# is able to be leveraged, we need to add the rescue_interface
|
||||||
|
# field to payload and increment the object versions for all objects
|
||||||
|
# that inherit the NodePayload object.
|
||||||
|
|
||||||
# Version 1.0: Initial version, based off of Node version 1.18.
|
# Version 1.0: Initial version, based off of Node version 1.18.
|
||||||
# Version 1.1: Type of network_interface changed to just nullable string
|
# Version 1.1: Type of network_interface changed to just nullable string
|
||||||
# similar to version 1.20 of Node.
|
# similar to version 1.20 of Node.
|
||||||
|
@ -208,7 +208,12 @@ class TestListDrivers(base.BaseApiTest):
|
|||||||
|
|
||||||
if use_dynamic:
|
if use_dynamic:
|
||||||
for iface in driver_base.ALL_INTERFACES:
|
for iface in driver_base.ALL_INTERFACES:
|
||||||
if storage_if or iface != 'storage':
|
# TODO(stendulker): Remove this check in 'rescue' API
|
||||||
|
# patch.
|
||||||
|
if iface == 'rescue':
|
||||||
|
self.assertNotIn('default_rescue_interface', data)
|
||||||
|
self.assertNotIn('enabled_rescue_interfaces', data)
|
||||||
|
elif storage_if or iface != 'storage':
|
||||||
self.assertIn('default_%s_interface' % iface, data)
|
self.assertIn('default_%s_interface' % iface, data)
|
||||||
self.assertIn('enabled_%s_interfaces' % iface, data)
|
self.assertIn('enabled_%s_interfaces' % iface, data)
|
||||||
self.assertIsNotNone(data['default_deploy_interface'])
|
self.assertIsNotNone(data['default_deploy_interface'])
|
||||||
|
@ -2174,17 +2174,36 @@ class TestPost(test_api_base.BaseApiTest):
|
|||||||
self.assertEqual('neutron', result['network_interface'])
|
self.assertEqual('neutron', result['network_interface'])
|
||||||
|
|
||||||
def test_create_node_specify_interfaces(self):
|
def test_create_node_specify_interfaces(self):
|
||||||
headers = {api_base.Version.string: '1.31'}
|
headers = {api_base.Version.string: '1.33'}
|
||||||
for field in api_utils.V31_FIELDS:
|
all_interface_fields = api_utils.V31_FIELDS + ['network_interface',
|
||||||
cfg.CONF.set_override('enabled_%ss' % field, ['fake'])
|
'rescue_interface',
|
||||||
for field in api_utils.V31_FIELDS:
|
'storage_interface']
|
||||||
|
for field in all_interface_fields:
|
||||||
|
if field == 'network_interface':
|
||||||
|
cfg.CONF.set_override('enabled_%ss' % field, ['flat'])
|
||||||
|
elif field == 'storage_interface':
|
||||||
|
cfg.CONF.set_override('enabled_%ss' % field, ['noop'])
|
||||||
|
else:
|
||||||
|
cfg.CONF.set_override('enabled_%ss' % field, ['fake'])
|
||||||
|
|
||||||
|
for field in all_interface_fields:
|
||||||
|
expected = 'fake'
|
||||||
|
if field == 'network_interface':
|
||||||
|
expected = 'flat'
|
||||||
|
elif field == 'storage_interface':
|
||||||
|
expected = 'noop'
|
||||||
|
elif field == 'rescue_interface':
|
||||||
|
# TODO(stendulker): Enable testing of rescue interface
|
||||||
|
# in its API patch.
|
||||||
|
continue
|
||||||
|
|
||||||
node = {
|
node = {
|
||||||
'uuid': uuidutils.generate_uuid(),
|
'uuid': uuidutils.generate_uuid(),
|
||||||
field: 'fake',
|
field: expected,
|
||||||
'driver': 'fake-hardware'
|
'driver': 'fake-hardware'
|
||||||
}
|
}
|
||||||
result = self._test_create_node(headers=headers, **node)
|
result = self._test_create_node(headers=headers, **node)
|
||||||
self.assertEqual('fake', result[field])
|
self.assertEqual(expected, result[field])
|
||||||
|
|
||||||
def test_create_node_specify_interfaces_bad_version(self):
|
def test_create_node_specify_interfaces_bad_version(self):
|
||||||
headers = {api_base.Version.string: '1.30'}
|
headers = {api_base.Version.string: '1.30'}
|
||||||
|
@ -97,7 +97,10 @@ class DriverLoadTestCase(db_base.DbTestCase):
|
|||||||
with task_manager.acquire(self.context, node.id) as task:
|
with task_manager.acquire(self.context, node.id) as task:
|
||||||
for iface in drivers_base.ALL_INTERFACES:
|
for iface in drivers_base.ALL_INTERFACES:
|
||||||
impl = getattr(task.driver, iface)
|
impl = getattr(task.driver, iface)
|
||||||
self.assertIsNotNone(impl)
|
if iface == 'rescue':
|
||||||
|
self.assertIsNone(impl)
|
||||||
|
else:
|
||||||
|
self.assertIsNotNone(impl)
|
||||||
|
|
||||||
@mock.patch.object(driver_factory, '_attach_interfaces_to_driver',
|
@mock.patch.object(driver_factory, '_attach_interfaces_to_driver',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@ -580,6 +583,11 @@ class TestFakeHardware(hardware_type.AbstractHardwareType):
|
|||||||
"""List of supported raid interfaces."""
|
"""List of supported raid interfaces."""
|
||||||
return [fake.FakeRAID]
|
return [fake.FakeRAID]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_rescue_interfaces(self):
|
||||||
|
"""List of supported rescue interfaces."""
|
||||||
|
return [fake.FakeRescue]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_vendor_interfaces(self):
|
def supported_vendor_interfaces(self):
|
||||||
"""List of supported rescue interfaces."""
|
"""List of supported rescue interfaces."""
|
||||||
@ -732,6 +740,25 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
|
|||||||
driver_factory.check_and_update_node_interfaces,
|
driver_factory.check_and_update_node_interfaces,
|
||||||
node)
|
node)
|
||||||
|
|
||||||
|
def test_none_rescue_interface(self):
|
||||||
|
node = obj_utils.get_test_node(self.context, driver='fake')
|
||||||
|
self.assertTrue(driver_factory.check_and_update_node_interfaces(node))
|
||||||
|
self.assertIsNone(node.rescue_interface)
|
||||||
|
|
||||||
|
def test_no_rescue_interface_default_from_conf(self):
|
||||||
|
self.config(enabled_rescue_interfaces=['fake'])
|
||||||
|
self.config(default_rescue_interface='fake')
|
||||||
|
node = obj_utils.get_test_node(self.context, driver='fake-hardware')
|
||||||
|
self.assertTrue(driver_factory.check_and_update_node_interfaces(node))
|
||||||
|
self.assertEqual('fake', node.rescue_interface)
|
||||||
|
|
||||||
|
def test_invalid_rescue_interface(self):
|
||||||
|
node = obj_utils.get_test_node(self.context, driver='fake-hardware',
|
||||||
|
rescue_interface='scoop')
|
||||||
|
self.assertRaises(exception.InterfaceNotFoundInEntrypoint,
|
||||||
|
driver_factory.check_and_update_node_interfaces,
|
||||||
|
node)
|
||||||
|
|
||||||
def test_no_raid_interface_no_default(self):
|
def test_no_raid_interface_no_default(self):
|
||||||
# NOTE(rloo): It doesn't seem possible to not have a default interface
|
# NOTE(rloo): It doesn't seem possible to not have a default interface
|
||||||
# for storage, so we'll test this case with raid.
|
# for storage, so we'll test this case with raid.
|
||||||
@ -753,6 +780,7 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
|
|||||||
'network': set(['noop']),
|
'network': set(['noop']),
|
||||||
'power': set(['fake']),
|
'power': set(['fake']),
|
||||||
'raid': set(['fake']),
|
'raid': set(['fake']),
|
||||||
|
'rescue': set(['fake']),
|
||||||
'storage': set([]),
|
'storage': set([]),
|
||||||
'vendor': set(['fake'])
|
'vendor': set(['fake'])
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,7 @@ class ServiceSetUpMixin(object):
|
|||||||
self.config(enabled_management_interfaces=['fake'])
|
self.config(enabled_management_interfaces=['fake'])
|
||||||
self.config(enabled_power_interfaces=['fake'])
|
self.config(enabled_power_interfaces=['fake'])
|
||||||
self.config(enabled_raid_interfaces=['fake', 'no-raid'])
|
self.config(enabled_raid_interfaces=['fake', 'no-raid'])
|
||||||
|
self.config(enabled_rescue_interfaces=['fake', 'no-rescue'])
|
||||||
self.config(enabled_vendor_interfaces=['fake', 'no-vendor'])
|
self.config(enabled_vendor_interfaces=['fake', 'no-vendor'])
|
||||||
|
|
||||||
self.service = manager.ConductorManager(self.hostname, 'test-topic')
|
self.service = manager.ConductorManager(self.hostname, 'test-topic')
|
||||||
|
@ -447,6 +447,7 @@ class TestBareDriver(base.TestCase):
|
|||||||
self.assertEqual(('deploy', 'power', 'network'),
|
self.assertEqual(('deploy', 'power', 'network'),
|
||||||
driver_base.BareDriver.core_interfaces)
|
driver_base.BareDriver.core_interfaces)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
('boot', 'console', 'inspect', 'management', 'raid', 'storage'),
|
('boot', 'console', 'inspect', 'management', 'raid',
|
||||||
|
'rescue', 'storage'),
|
||||||
driver_base.BareDriver.standard_interfaces
|
driver_base.BareDriver.standard_interfaces
|
||||||
)
|
)
|
||||||
|
@ -49,7 +49,6 @@ class FakeDriverTestCase(db_base.DbTestCase):
|
|||||||
self.assertIsInstance(self.driver.vendor, driver_base.VendorInterface)
|
self.assertIsInstance(self.driver.vendor, driver_base.VendorInterface)
|
||||||
self.assertIsInstance(self.driver.console,
|
self.assertIsInstance(self.driver.console,
|
||||||
driver_base.ConsoleInterface)
|
driver_base.ConsoleInterface)
|
||||||
self.assertIsNone(self.driver.rescue)
|
|
||||||
|
|
||||||
def test_get_properties(self):
|
def test_get_properties(self):
|
||||||
expected = ['A1', 'A2', 'B1', 'B2']
|
expected = ['A1', 'A2', 'B1', 'B2']
|
||||||
|
@ -260,3 +260,73 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
|
|
||||||
def test_payload_schemas(self):
|
def test_payload_schemas(self):
|
||||||
self._check_payload_schemas(objects.node, objects.Node.fields)
|
self._check_payload_schemas(objects.node, objects.Node.fields)
|
||||||
|
|
||||||
|
|
||||||
|
class TestConvertToVersion(db_base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestConvertToVersion, self).setUp()
|
||||||
|
self.ctxt = context.get_admin_context()
|
||||||
|
self.fake_node = db_utils.get_test_node(driver='fake-hardware')
|
||||||
|
|
||||||
|
def test_rescue_supported_missing(self):
|
||||||
|
# rescue_interface not set, should be set to default.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
delattr(node, 'rescue_interface')
|
||||||
|
node.obj_reset_changes()
|
||||||
|
|
||||||
|
node._convert_to_version("1.22")
|
||||||
|
|
||||||
|
self.assertIsNone(node.rescue_interface)
|
||||||
|
self.assertEqual({'rescue_interface': None},
|
||||||
|
node.obj_get_changes())
|
||||||
|
|
||||||
|
def test_rescue_supported_set(self):
|
||||||
|
# rescue_interface set, no change required.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
|
||||||
|
node.rescue_interface = 'fake'
|
||||||
|
node.obj_reset_changes()
|
||||||
|
node._convert_to_version("1.22")
|
||||||
|
self.assertEqual('fake', node.rescue_interface)
|
||||||
|
self.assertEqual({}, node.obj_get_changes())
|
||||||
|
|
||||||
|
def test_rescue_unsupported_missing(self):
|
||||||
|
# rescue_interface not set, no change required.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
|
||||||
|
delattr(node, 'rescue_interface')
|
||||||
|
node.obj_reset_changes()
|
||||||
|
node._convert_to_version("1.21")
|
||||||
|
self.assertNotIn('rescue_interface', node)
|
||||||
|
self.assertEqual({}, node.obj_get_changes())
|
||||||
|
|
||||||
|
def test_rescue_unsupported_set_remove(self):
|
||||||
|
# rescue_interface set, should be removed.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
|
||||||
|
node.rescue_interface = 'fake'
|
||||||
|
node.obj_reset_changes()
|
||||||
|
node._convert_to_version("1.21")
|
||||||
|
self.assertNotIn('rescue_interface', node)
|
||||||
|
self.assertEqual({}, node.obj_get_changes())
|
||||||
|
|
||||||
|
def test_rescue_unsupported_set_no_remove_non_default(self):
|
||||||
|
# rescue_interface set, should be set to default.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
|
||||||
|
node.rescue_interface = 'fake'
|
||||||
|
node.obj_reset_changes()
|
||||||
|
node._convert_to_version("1.21", False)
|
||||||
|
self.assertIsNone(node.rescue_interface)
|
||||||
|
self.assertEqual({'rescue_interface': None}, node.obj_get_changes())
|
||||||
|
|
||||||
|
def test_rescue_unsupported_set_no_remove_default(self):
|
||||||
|
# rescue_interface set, no change required.
|
||||||
|
node = objects.Node(self.context, **self.fake_node)
|
||||||
|
|
||||||
|
node.rescue_interface = None
|
||||||
|
node.obj_reset_changes()
|
||||||
|
node._convert_to_version("1.21", False)
|
||||||
|
self.assertIsNone(node.rescue_interface)
|
||||||
|
self.assertEqual({}, node.obj_get_changes())
|
||||||
|
@ -684,7 +684,7 @@ class TestObject(_LocalTest, _TestObject):
|
|||||||
# version bump. It is an MD5 hash of the object fields and remotable methods.
|
# version bump. It is an MD5 hash of the object fields and remotable methods.
|
||||||
# The fingerprint values should only be changed if there is a version bump.
|
# The fingerprint values should only be changed if there is a version bump.
|
||||||
expected_object_fingerprints = {
|
expected_object_fingerprints = {
|
||||||
'Node': '1.21-52674c214141cf3e09f8688bfed54577',
|
'Node': '1.22-f2c453dd0b42aec8d4833a69a9ac79f3',
|
||||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||||
'Port': '1.7-898a47921f4a1f53fcdddd4eeb179e0b',
|
'Port': '1.7-898a47921f4a1f53fcdddd4eeb179e0b',
|
||||||
|
@ -153,6 +153,7 @@ ironic.hardware.interfaces.raid =
|
|||||||
no-raid = ironic.drivers.modules.noop:NoRAID
|
no-raid = ironic.drivers.modules.noop:NoRAID
|
||||||
|
|
||||||
ironic.hardware.interfaces.rescue =
|
ironic.hardware.interfaces.rescue =
|
||||||
|
fake = ironic.drivers.modules.fake:FakeRescue
|
||||||
no-rescue = ironic.drivers.modules.noop:NoRescue
|
no-rescue = ironic.drivers.modules.noop:NoRescue
|
||||||
|
|
||||||
ironic.hardware.interfaces.storage =
|
ironic.hardware.interfaces.storage =
|
||||||
|
Loading…
Reference in New Issue
Block a user