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)
|
||||
#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
|
||||
# service initialization. Missing storage interfaces, or
|
||||
# storage interfaces which fail to initialize, will prevent
|
||||
|
@ -111,7 +111,7 @@ RELEASE_MAPPING = {
|
||||
'api': '1.36',
|
||||
'rpc': '1.42',
|
||||
'objects': {
|
||||
'Node': ['1.21'],
|
||||
'Node': ['1.22'],
|
||||
'Conductor': ['1.2'],
|
||||
'Chassis': ['1.3'],
|
||||
'Port': ['1.7'],
|
||||
|
@ -185,6 +185,28 @@ potentially due to invalid or incompatible information being defined for the
|
||||
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,
|
||||
VERIFYING, ADOPTFAIL)
|
||||
"""Transitional states in which we allow updating a node."""
|
||||
|
@ -1555,6 +1555,11 @@ class ConductorManager(base_manager.BaseConductorManager):
|
||||
task.node.instance_info)
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = iwdi
|
||||
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)
|
||||
result = reason = None
|
||||
if iface:
|
||||
|
@ -52,6 +52,18 @@ _DEFAULT_IFACE_HELP = _('Default {0} interface to be used for nodes that '
|
||||
'be found by enumerating the '
|
||||
'"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 = [
|
||||
cfg.StrOpt(
|
||||
'auth_strategy',
|
||||
@ -137,6 +149,11 @@ driver_opts = [
|
||||
help=_ENABLED_IFACE_HELP.format('raid')),
|
||||
cfg.StrOpt('default_raid_interface',
|
||||
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',
|
||||
default=['cinder', 'noop'],
|
||||
help=_ENABLED_IFACE_HELP.format('storage')),
|
||||
|
@ -81,9 +81,6 @@ class BaseDriver(object):
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
A reference to an instance of :class:RescueInterface.
|
||||
@ -170,7 +167,9 @@ class BareDriver(BaseDriver):
|
||||
|
||||
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)
|
||||
@ -562,6 +561,10 @@ class RescueInterface(BaseInterface):
|
||||
"""Boot the task's node into a rescue environment.
|
||||
|
||||
: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
|
||||
@ -569,8 +572,22 @@ class RescueInterface(BaseInterface):
|
||||
"""Tear down the rescue environment, and return to normal.
|
||||
|
||||
: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
|
||||
VendorMetadata = collections.namedtuple('VendorMetadata', ['method',
|
||||
|
@ -66,6 +66,11 @@ class FakeHardware(hardware_type.AbstractHardwareType):
|
||||
"""List of classes of supported raid interfaces."""
|
||||
return [fake.FakeRAID]
|
||||
|
||||
@property
|
||||
def supported_rescue_interfaces(self):
|
||||
"""List of classes of supported rescue interfaces."""
|
||||
return [fake.FakeRescue]
|
||||
|
||||
@property
|
||||
def supported_storage_interfaces(self):
|
||||
"""List of classes of supported storage interfaces."""
|
||||
|
@ -83,6 +83,11 @@ class AbstractHardwareType(object):
|
||||
"""List of supported raid interfaces."""
|
||||
return [noop.NoRAID]
|
||||
|
||||
@property
|
||||
def supported_rescue_interfaces(self):
|
||||
"""List of supported rescue interfaces."""
|
||||
return [noop.NoRescue]
|
||||
|
||||
@property
|
||||
def supported_storage_interfaces(self):
|
||||
"""List of supported storage interfaces."""
|
||||
|
@ -260,3 +260,19 @@ class FakeStorage(base.StorageInterface):
|
||||
|
||||
def should_write_image(self, task):
|
||||
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 uuidutils
|
||||
from oslo_utils import versionutils
|
||||
from oslo_versionedobjects import base as object_base
|
||||
|
||||
from ironic.common import exception
|
||||
@ -55,7 +56,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
# power_interface, raid_interface, vendor_interface
|
||||
# Version 1.20: Type of network_interface changed to just nullable string
|
||||
# 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()
|
||||
|
||||
@ -123,6 +125,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
'network_interface': object_fields.StringField(nullable=True),
|
||||
'power_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),
|
||||
'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)
|
||||
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
|
||||
class NodePayload(notification.NotificationPayloadBase):
|
||||
@ -461,6 +499,11 @@ class NodePayload(notification.NotificationPayloadBase):
|
||||
'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.1: Type of network_interface changed to just nullable string
|
||||
# similar to version 1.20 of Node.
|
||||
|
@ -208,7 +208,12 @@ class TestListDrivers(base.BaseApiTest):
|
||||
|
||||
if use_dynamic:
|
||||
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('enabled_%s_interfaces' % iface, data)
|
||||
self.assertIsNotNone(data['default_deploy_interface'])
|
||||
|
@ -2174,17 +2174,36 @@ class TestPost(test_api_base.BaseApiTest):
|
||||
self.assertEqual('neutron', result['network_interface'])
|
||||
|
||||
def test_create_node_specify_interfaces(self):
|
||||
headers = {api_base.Version.string: '1.31'}
|
||||
for field in api_utils.V31_FIELDS:
|
||||
cfg.CONF.set_override('enabled_%ss' % field, ['fake'])
|
||||
for field in api_utils.V31_FIELDS:
|
||||
headers = {api_base.Version.string: '1.33'}
|
||||
all_interface_fields = api_utils.V31_FIELDS + ['network_interface',
|
||||
'rescue_interface',
|
||||
'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 = {
|
||||
'uuid': uuidutils.generate_uuid(),
|
||||
field: 'fake',
|
||||
field: expected,
|
||||
'driver': 'fake-hardware'
|
||||
}
|
||||
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):
|
||||
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:
|
||||
for iface in drivers_base.ALL_INTERFACES:
|
||||
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',
|
||||
autospec=True)
|
||||
@ -580,6 +583,11 @@ class TestFakeHardware(hardware_type.AbstractHardwareType):
|
||||
"""List of supported raid interfaces."""
|
||||
return [fake.FakeRAID]
|
||||
|
||||
@property
|
||||
def supported_rescue_interfaces(self):
|
||||
"""List of supported rescue interfaces."""
|
||||
return [fake.FakeRescue]
|
||||
|
||||
@property
|
||||
def supported_vendor_interfaces(self):
|
||||
"""List of supported rescue interfaces."""
|
||||
@ -732,6 +740,25 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
|
||||
driver_factory.check_and_update_node_interfaces,
|
||||
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):
|
||||
# NOTE(rloo): It doesn't seem possible to not have a default interface
|
||||
# for storage, so we'll test this case with raid.
|
||||
@ -753,6 +780,7 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
|
||||
'network': set(['noop']),
|
||||
'power': set(['fake']),
|
||||
'raid': set(['fake']),
|
||||
'rescue': set(['fake']),
|
||||
'storage': set([]),
|
||||
'vendor': set(['fake'])
|
||||
}
|
||||
|
@ -175,6 +175,7 @@ class ServiceSetUpMixin(object):
|
||||
self.config(enabled_management_interfaces=['fake'])
|
||||
self.config(enabled_power_interfaces=['fake'])
|
||||
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.service = manager.ConductorManager(self.hostname, 'test-topic')
|
||||
|
@ -447,6 +447,7 @@ class TestBareDriver(base.TestCase):
|
||||
self.assertEqual(('deploy', 'power', 'network'),
|
||||
driver_base.BareDriver.core_interfaces)
|
||||
self.assertEqual(
|
||||
('boot', 'console', 'inspect', 'management', 'raid', 'storage'),
|
||||
('boot', 'console', 'inspect', 'management', 'raid',
|
||||
'rescue', 'storage'),
|
||||
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.console,
|
||||
driver_base.ConsoleInterface)
|
||||
self.assertIsNone(self.driver.rescue)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = ['A1', 'A2', 'B1', 'B2']
|
||||
|
@ -260,3 +260,73 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
||||
|
||||
def test_payload_schemas(self):
|
||||
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.
|
||||
# The fingerprint values should only be changed if there is a version bump.
|
||||
expected_object_fingerprints = {
|
||||
'Node': '1.21-52674c214141cf3e09f8688bfed54577',
|
||||
'Node': '1.22-f2c453dd0b42aec8d4833a69a9ac79f3',
|
||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||
'Port': '1.7-898a47921f4a1f53fcdddd4eeb179e0b',
|
||||
|
Loading…
Reference in New Issue
Block a user