Merge "Power fault recovery: db and rpc implementation"
This commit is contained in:
commit
5f7d60a6c6
|
@ -103,7 +103,7 @@ RELEASE_MAPPING = {
|
|||
'api': '1.39',
|
||||
'rpc': '1.44',
|
||||
'objects': {
|
||||
'Node': ['1.24'],
|
||||
'Node': ['1.25'],
|
||||
'Conductor': ['1.2'],
|
||||
'Chassis': ['1.3'],
|
||||
'Port': ['1.8'],
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""add fault to node table
|
||||
|
||||
Revision ID: fb3f10dd262e
|
||||
Revises: 2d13bc3d6bba
|
||||
Create Date: 2018-03-23 14:10:52.142016
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fb3f10dd262e'
|
||||
down_revision = '2d13bc3d6bba'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('nodes', sa.Column('fault', sa.String(length=255),
|
||||
nullable=True))
|
|
@ -224,14 +224,15 @@ class Connection(api.Connection):
|
|||
'resource_class', 'provision_state', 'uuid', 'id',
|
||||
'chassis_uuid', 'associated', 'reserved',
|
||||
'reserved_by_any_of', 'provisioned_before',
|
||||
'inspection_started_before'}
|
||||
'inspection_started_before', 'fault'}
|
||||
unsupported_filters = set(filters).difference(supported_filters)
|
||||
if unsupported_filters:
|
||||
msg = _("SqlAlchemy API does not support "
|
||||
"filtering by %s") % ', '.join(unsupported_filters)
|
||||
raise ValueError(msg)
|
||||
for field in ['console_enabled', 'maintenance', 'driver',
|
||||
'resource_class', 'provision_state', 'uuid', 'id']:
|
||||
'resource_class', 'provision_state', 'uuid', 'id',
|
||||
'fault']:
|
||||
if field in filters:
|
||||
query = query.filter_by(**{field: filters[field]})
|
||||
if 'chassis_uuid' in filters:
|
||||
|
|
|
@ -165,6 +165,7 @@ class Node(Base):
|
|||
|
||||
maintenance = Column(Boolean, default=False)
|
||||
maintenance_reason = Column(Text, nullable=True)
|
||||
fault = Column(String(255), nullable=True)
|
||||
console_enabled = Column(Boolean, default=False)
|
||||
inspection_finished_at = Column(DateTime, nullable=True)
|
||||
inspection_started_at = Column(DateTime, nullable=True)
|
||||
|
|
|
@ -60,7 +60,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
# Version 1.22: Add rescue_interface field
|
||||
# Version 1.23: Add traits field
|
||||
# Version 1.24: Add bios_interface field
|
||||
VERSION = '1.24'
|
||||
# Version 1.25: Add fault field
|
||||
VERSION = '1.25'
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
|
@ -106,6 +107,7 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
|
||||
'maintenance': object_fields.BooleanField(),
|
||||
'maintenance_reason': object_fields.StringField(nullable=True),
|
||||
'fault': object_fields.StringField(nullable=True),
|
||||
'console_enabled': object_fields.BooleanField(),
|
||||
|
||||
# Any error from the most recent (last) asynchronous transaction
|
||||
|
@ -463,6 +465,18 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
node = cls._from_db_object(context, cls(), db_node)
|
||||
return node
|
||||
|
||||
def _convert_fault_field(self, target_version,
|
||||
remove_unavailable_fields=True):
|
||||
fault_is_set = self.obj_attr_is_set('fault')
|
||||
if target_version >= (1, 25):
|
||||
if not fault_is_set:
|
||||
self.fault = None
|
||||
elif fault_is_set:
|
||||
if remove_unavailable_fields:
|
||||
delattr(self, 'fault')
|
||||
elif self.fault is not None:
|
||||
self.fault = None
|
||||
|
||||
def _convert_to_version(self, target_version,
|
||||
remove_unavailable_fields=True):
|
||||
"""Convert to the target version.
|
||||
|
@ -480,6 +494,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
Version 1.24: bios_interface field was added. Its default value is
|
||||
None. For versions prior to this, it should be set to None (or
|
||||
removed).
|
||||
Version 1.25: fault field was added. For versions prior to
|
||||
this, it should be removed.
|
||||
|
||||
:param target_version: the desired version of the object
|
||||
:param remove_unavailable_fields: True to remove fields that are
|
||||
|
@ -530,6 +546,8 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||
# DB: set unavailable field to the default of None.
|
||||
self.bios_interface = None
|
||||
|
||||
self._convert_fault_field(target_version, remove_unavailable_fields)
|
||||
|
||||
|
||||
@base.IronicObjectRegistry.register
|
||||
class NodePayload(notification.NotificationPayloadBase):
|
||||
|
|
|
@ -110,6 +110,8 @@ def node_post_data(**kw):
|
|||
node.pop(name)
|
||||
if 'resource_class' not in kw:
|
||||
node.pop('resource_class')
|
||||
if 'fault' not in kw:
|
||||
node.pop('fault')
|
||||
|
||||
internal = node_controller.NodePatchType.internal_attrs()
|
||||
return remove_internal(node, internal)
|
||||
|
|
|
@ -712,6 +712,13 @@ class MigrationCheckersMixin(object):
|
|||
self.assertIsInstance(nodes.c.bios_interface.type,
|
||||
sqlalchemy.types.String)
|
||||
|
||||
def _check_fb3f10dd262e(self, engine, data):
|
||||
nodes_tbl = db_utils.get_table(engine, 'nodes')
|
||||
col_names = [column.name for column in nodes_tbl.c]
|
||||
self.assertIn('fault', col_names)
|
||||
self.assertIsInstance(nodes_tbl.c.fault.type,
|
||||
sqlalchemy.types.String)
|
||||
|
||||
def test_upgrade_and_version(self):
|
||||
with patch_with_engine(self.engine):
|
||||
self.migration_api.upgrade('head')
|
||||
|
|
|
@ -143,6 +143,7 @@ class DbNodeTestCase(base.DbTestCase):
|
|||
driver='driver-two',
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
maintenance=True,
|
||||
fault='boom',
|
||||
resource_class='foo')
|
||||
node3 = utils.create_test_node(
|
||||
driver='driver-one',
|
||||
|
@ -177,6 +178,12 @@ class DbNodeTestCase(base.DbTestCase):
|
|||
self.assertEqual(sorted([node1.id, node3.id]),
|
||||
sorted([r.id for r in res]))
|
||||
|
||||
res = self.dbapi.get_nodeinfo_list(filters={'fault': 'boom'})
|
||||
self.assertEqual([node2.id], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_nodeinfo_list(filters={'fault': 'moob'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_nodeinfo_list(filters={'resource_class': 'foo'})
|
||||
self.assertEqual([node2.id], [r.id for r in res])
|
||||
|
||||
|
@ -284,6 +291,7 @@ class DbNodeTestCase(base.DbTestCase):
|
|||
uuid=uuidutils.generate_uuid(),
|
||||
chassis_id=ch2['id'],
|
||||
maintenance=True,
|
||||
fault='boom',
|
||||
resource_class='foo')
|
||||
|
||||
res = self.dbapi.get_node_list(filters={'chassis_uuid': ch1['uuid']})
|
||||
|
@ -316,6 +324,12 @@ class DbNodeTestCase(base.DbTestCase):
|
|||
res = self.dbapi.get_node_list(filters={'maintenance': False})
|
||||
self.assertEqual([node1.id], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_nodeinfo_list(filters={'fault': 'boom'})
|
||||
self.assertEqual([node2.id], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_nodeinfo_list(filters={'fault': 'moob'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_node_list(filters={'resource_class': 'foo'})
|
||||
self.assertEqual([node2.id], [r.id for r in res])
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ def get_test_node(**kw):
|
|||
'reservation': kw.get('reservation'),
|
||||
'maintenance': kw.get('maintenance', False),
|
||||
'maintenance_reason': kw.get('maintenance_reason'),
|
||||
'fault': kw.get('fault'),
|
||||
'console_enabled': kw.get('console_enabled', False),
|
||||
'extra': kw.get('extra', {}),
|
||||
'updated_at': kw.get('updated_at'),
|
||||
|
|
|
@ -408,6 +408,7 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
|
||||
node.rescue_interface = 'fake'
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
node._convert_to_version("1.21", False)
|
||||
self.assertIsNone(node.rescue_interface)
|
||||
|
@ -420,6 +421,7 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||
|
||||
node.rescue_interface = None
|
||||
node.traits = None
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
node._convert_to_version("1.21", False)
|
||||
self.assertIsNone(node.rescue_interface)
|
||||
|
@ -466,6 +468,7 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||
# traits not set, should be set to default.
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
delattr(node, 'traits')
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.22", False)
|
||||
|
@ -478,18 +481,19 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
node.traits = objects.TraitList(self.ctxt)
|
||||
node.traits.obj_reset_changes()
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.22", False)
|
||||
|
||||
self.assertIsNone(node.traits)
|
||||
self.assertEqual({'traits': None},
|
||||
node.obj_get_changes())
|
||||
self.assertEqual({'traits': None}, node.obj_get_changes())
|
||||
|
||||
def test_trait_unsupported_set_no_remove_default(self):
|
||||
# traits set, no change required.
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
node.traits = None
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.22", False)
|
||||
|
@ -544,22 +548,76 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
|
||||
node.bios_interface = 'fake'
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
node._convert_to_version("1.23", False)
|
||||
self.assertIsNone(node.bios_interface)
|
||||
self.assertEqual({'bios_interface': None},
|
||||
node.obj_get_changes())
|
||||
self.assertEqual({'bios_interface': None}, node.obj_get_changes())
|
||||
|
||||
def test_bios_unsupported_set_no_remove_default(self):
|
||||
# bios_interface set, no change required.
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
|
||||
node.bios_interface = None
|
||||
node.fault = None
|
||||
node.obj_reset_changes()
|
||||
node._convert_to_version("1.23", False)
|
||||
self.assertIsNone(node.bios_interface)
|
||||
self.assertEqual({}, node.obj_get_changes())
|
||||
|
||||
def test_fault_supported_missing(self):
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
delattr(node, 'fault')
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.25")
|
||||
|
||||
self.assertIsNone(node.fault)
|
||||
self.assertEqual({'fault': None}, node.obj_get_changes())
|
||||
|
||||
def test_fault_supported_untouched(self):
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
node.maintenance = True
|
||||
node.fault = 'a fake fault'
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.25")
|
||||
|
||||
self.assertEqual('a fake fault', node.fault)
|
||||
self.assertEqual({}, node.obj_get_changes())
|
||||
|
||||
def test_fault_unsupported_missing(self):
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
delattr(node, 'fault')
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.24")
|
||||
|
||||
self.assertNotIn('fault', node)
|
||||
self.assertEqual({}, node.obj_get_changes())
|
||||
|
||||
def test_fault_unsupported_set_remove(self):
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
node.maintenance = True
|
||||
node.fault = 'some fake fault'
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.24")
|
||||
|
||||
self.assertNotIn('fault', node)
|
||||
self.assertEqual({}, node.obj_get_changes())
|
||||
|
||||
def test_fault_unsupported_set_remove_in_maintenance(self):
|
||||
node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
node.maintenance = True
|
||||
node.fault = 'some fake type'
|
||||
node.obj_reset_changes()
|
||||
|
||||
node._convert_to_version("1.24", False)
|
||||
|
||||
self.assertIsNone(node.fault)
|
||||
self.assertEqual({'fault': None}, node.obj_get_changes())
|
||||
|
||||
|
||||
class TestNodePayloads(db_base.DbTestCase):
|
||||
|
||||
|
|
|
@ -664,7 +664,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.24-7d3d504e5e0d2535b2390d558b27196a',
|
||||
'Node': '1.25-3a468b3e88d0a8fe7709f822fc654e4b',
|
||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||
'Port': '1.8-898a47921f4a1f53fcdddd4eeb179e0b',
|
||||
|
|
Loading…
Reference in New Issue