Add DB API for Firmware and Object

Adds the following methods to DB API:

* create_firmware_component
* update_firmware_component
* get_firmware_component
* get_firmware_component_list

FirmwareComponent
* create | save | get

FirmwareComponentList
* get_by_node id | sync_firmware_components

Adds two exceptions:

* FirmwareComponentAlreadyExists
* FirmwareComponentNotFound

Tests for db and objects

Changes were required in models, the class name should match the
object name we will create

Story: 2010659
Task: 47977

Change-Id: Ie1e2a4150d4ee4521290737612780c02506f4a9e
This commit is contained in:
Iury Gregory Melo Ferreira 2023-05-11 19:30:28 -03:00
parent 76a920aed2
commit dad6724292
16 changed files with 711 additions and 8 deletions

View File

@ -874,3 +874,12 @@ class ConcurrentActionLimit(IronicException):
class SwiftObjectStillExists(IronicException):
_msg_fmt = _("Clean up failed for swift object %(obj)s during deletion"
" of node %(node)s.")
class FirmwareComponentAlreadyExists(Conflict):
_msg_fmt = _('A Firmware component %(name)s for node %(node)s'
' already exists.')
class FirmwareComponentNotFound(NotFound):
_msg_fmt = _("Node %(node)s doesn't have Firmware component %(name)s")

View File

@ -592,6 +592,7 @@ RELEASE_MAPPING = {
'TraitList': ['1.0'],
'VolumeConnector': ['1.0'],
'VolumeTarget': ['1.0'],
'FirmwareComponent': ['1.0'],
}
},
}

View File

@ -1454,3 +1454,64 @@ class Connection(object, metaclass=abc.ABCMeta):
:returns: list of dicts containing shard names and count
"""
@abc.abstractclassmethod
def create_firmware_component(self, values):
"""Create a FirmwareComponent record for a given node.
:param values: a dictionary with the necessary information to create
a FirmwareComponent.
::
{
'component': String,
'initial_version': String,
'current_version': String,
'last_version_flashed': String
}
:returns: A FirmwareComponent object.
:raises: FirmwareComponentAlreadyExists if any of the component
records already exists.
"""
@abc.abstractclassmethod
def update_firmware_component(self, node_id, component, values):
"""Update a FirmwareComponent record.
:param node_id: The node id.
:param component: The component of the node to update.
:param values: A dictionary with the new information about the
FirmwareComponent.
::
{
'current_version': String,
'last_version_flashed': String
}
:returns: A FirmwareComponent object.
:raises: FirmwareComponentNotFound the component
is not found.
"""
@abc.abstractmethod
def get_firmware_component(self, node_id, name):
"""Retrieve Firmware Component.
:param node_id: The node id.
:param name: name of Firmware component.
:returns: The FirmwareComponent object.
:raises: NodeNotFound if the node is not found.
:raises: FirmwareComponentNotFound if the Firmware component
is not found.
"""
@abc.abstractclassmethod
def get_firmware_component_list(self, node_id):
"""Retrieve a list Firmware Components of a given node.
:param node_id: The node id.
:returns: A list of FirmwareComponent objects.
:raises: NodeNotFound if the node is not found.
"""

View File

@ -892,6 +892,11 @@ class Connection(api.Connection):
models.NodeInventory).filter_by(node_id=node_id)
inventory_query.delete()
# delete all firmware components attached to the node
firmware_component_query = session.query(
models.FirmwareComponent).filter_by(node_id=node_id)
firmware_component_query.delete()
query.delete()
def update_node(self, node_id, values):
@ -2721,3 +2726,103 @@ class Connection(api.Connection):
)
return shard_list
def create_firmware_component(self, values):
"""Create a FirmwareComponent record for a given node.
:param values: a dictionary with the necessary information to create
a FirmwareComponent.
::
{
'component': String,
'initial_version': String,
'current_version': String,
'last_version_flashed': String
}
:returns: A FirmwareComponent object.
:raises: FirmwareComponentAlreadyExists if any of the component
records already exists.
"""
fw_component = models.FirmwareComponent()
fw_component.update(values)
try:
with _session_for_write() as session:
session.add(fw_component)
session.flush()
except db_exc.DBDuplicateEntry:
raise exception.FirmwareComponentAlreadyExists(
name=values['component'], node=values['node_id'])
return fw_component
def update_firmware_component(self, node_id, component, values):
"""Update a FirmwareComponent record.
:param node_id: The node id.
:param component: The component of the node to update.
:param values: A dictionary with the new information about the
FirmwareComponent.
::
{
'current_version': String,
'last_version_flashed': String
}
:returns: A FirmwareComponent object.
:raises: FirmwareComponentNotFound the component
is not found.
"""
with _session_for_write() as session:
query = session.query(models.FirmwareComponent)
query = query.filter_by(node_id=node_id, component=component)
try:
ref = query.with_for_update().one()
except NoResultFound:
raise exception.FirmwareComponentNotFound(
node=node_id, name=component)
ref.update(values)
session.flush()
return ref
def get_firmware_component(self, node_id, name):
"""Retrieve Firmware Component.
:param node_id: The node id.
:param name: name of Firmware component.
:returns: The FirmwareComponent object.
:raises: NodeNotFound if the node is not found.
:raises: FirmwareComponentNotFound if the FirmwareComponent
is not found.
"""
with _session_for_read() as session:
self._check_node_exists(session, node_id)
query = session.query(models.FirmwareComponent).filter_by(
node_id=node_id, component=name)
try:
ref = query.one()
except NoResultFound:
raise exception.FirmwareComponentNotFound(
node=node_id, name=name)
return ref
def get_firmware_component_list(self, node_id):
"""Retrieve Firmware Components of a given node.
:param node_id: The node id.
:returns: A list of FirmwareComponent objects.
:raises: NodeNotFound if the node is not found.
"""
with _session_for_read() as session:
self._check_node_exists(session, node_id)
result = (session.query(models.FirmwareComponent)
.filter_by(node_id=node_id)
.all())
return result

View File

@ -499,7 +499,7 @@ class NodeInventory(Base):
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
class FirmwareInformation(Base):
class FirmwareComponent(Base):
"""Represents the firmware information of a bare metal node."""
__tablename__ = "firmware_information"
__table_args__ = (

View File

@ -30,6 +30,7 @@ def register_all():
__import__('ironic.objects.conductor')
__import__('ironic.objects.deploy_template')
__import__('ironic.objects.deployment')
__import__('ironic.objects.firmware')
__import__('ironic.objects.node')
__import__('ironic.objects.node_history')
__import__('ironic.objects.node_inventory')

161
ironic/objects/firmware.py Normal file
View File

@ -0,0 +1,161 @@
# coding=utf-8
#
#
# 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.
from oslo_versionedobjects import base as object_base
from ironic.db import api as dbapi
from ironic.objects import base
from ironic.objects import fields as object_fields
@base.IronicObjectRegistry.register
class FirmwareComponent(base.IronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = dbapi.get_instance()
fields = {
'id': object_fields.IntegerField(),
'node_id': object_fields.IntegerField(nullable=False),
'component': object_fields.StringField(nullable=False),
'initial_version': object_fields.StringField(nullable=False),
'current_version': object_fields.StringField(nullable=True),
'last_version_flashed': object_fields.StringField(nullable=True),
}
def create(self, context=None):
"""Create a Firmware record in the DB.
:param context: Security context.
:raises: NodeNotFound if the node is not found.
:raises: FirmwareComponentAlreadyExists if the record already exists.
"""
values = self.do_version_changes_for_db()
# Note(iurygregory): We ensure that when creating we will be setting
# initial_version to the current_version we got from the BMC.
values['initial_version'] = values['current_version']
db_fwcmp = self.dbapi.create_firmware_component(values)
self._from_db_object(self._context, self, db_fwcmp)
def save(self, context=None):
"""Save updates to this Firmware Component.
Updates will be made column by column based on the result of
self.what_changed()
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: FirmwareComponent(context)
:raises: NodeNotFound if the node id is not found.
:raises: FirmwareComponentNotFound if the component is not found.
"""
# NOTE(iurygregory): some fields shouldn't be updated, like
# 'initial_version', 'id', 'node_id', 'component'
# filter them out or raise an Error?
updates = self.do_version_changes_for_db()
up_fwcmp = self.dbapi.update_firmware_component(
self.node_id, self.component, updates)
self._from_db_object(self._context, self, up_fwcmp)
@classmethod
def get(cls, context, node_id, name):
"""Get a FirmwareComponent based on its node_id and name.
:param context: Security context.
:param node_id: The node id.
:param name: The Firmware Component name.
:raises: NodeNotFound if the node id is not found.
:raises: FirmwareComponentNotFound if the Firmware Component
name is not found.
:returns: A :class:'FirmwareComponent' object.
"""
db_fw_cmp = cls.dbapi.get_firmware_component(node_id, name)
fw_cmp = cls._from_db_object(context, cls(), db_fw_cmp)
return fw_cmp
@base.IronicObjectRegistry.register
class FirmwareComponentList(base.IronicObjectListBase, base.IronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = dbapi.get_instance()
fields = {
'objects': object_fields.ListOfObjectsField('FirmwareComponent'),
}
@classmethod
def get_by_node_id(cls, context, node_id):
"""Get FirmwareComponent based on node_id.
:param context: Security context.
:param node_id: The node id.
:raises: NodeNotFound if the node is not found.
:return: A list of FirmwareComponent objects.
"""
node_fw_components = cls.dbapi.get_firmware_component_list(node_id)
return object_base.obj_make_list(
context, cls(), FirmwareComponent, node_fw_components)
@classmethod
def sync_firmware_components(cls, context, node_id, components):
"""Returns a list of create/update components.
This method sync with the 'firmware_information' database table
and sorts three lists - create / update / unchanged components.
:param context: Security context.
:param node_id: The node id.
:param components: List of FirmwareComponents.
:returns: A 3-tuple of lists of Firmware Components to be created,
updated and unchanged.
"""
create_list = []
update_list = []
unchanged_list = []
current_components_dict = {}
current_components = cls.get_by_node_id(context, node_id)
for cmp in current_components:
current_components_dict[cmp.component] = {
'initial_version': cmp.initial_version,
'current_version': cmp.current_version,
'last_version_flashed': cmp.last_version_flashed,
}
for cmp in components:
if cmp['component'] in current_components_dict:
values = current_components_dict[cmp['component']]
cv_changed = cmp['current_version'] \
!= values.get('current_version')
lvf_changed = cmp['last_version_flashed'] \
!= values.get('last_version_flashed')
if cv_changed or lvf_changed:
update_list.append(cmp)
else:
unchanged_list.append(cmp)
else:
create_list.append(cmp)
return (create_list, update_list, unchanged_list)

View File

@ -101,7 +101,7 @@ class ReleaseMappingsTestCase(base.TestCase):
# NodeBase is also excluded as it is covered by Node.
exceptions = set(['NodeTag', 'ConductorHardwareInterfaces',
'NodeTrait', 'DeployTemplateStep',
'NodeBase', 'FirmwareInformation'])
'NodeBase'])
model_names -= exceptions
# NodeTrait maps to two objects
model_names |= set(['Trait', 'TraitList'])

View File

@ -1323,17 +1323,17 @@ class MigrationCheckersMixin(object):
insert_fw_cmp = fw_information.insert().values(fw_data)
connection.execute(insert_fw_cmp)
fw_cmp_stmt = sqlalchemy.select(
models.FirmwareInformation.initial_version
models.FirmwareComponent.initial_version
).where(
models.FirmwareInformation.node_id == data['id'],
models.FirmwareInformation.component == fw_data['component']
models.FirmwareComponent.node_id == data['id'],
models.FirmwareComponent.component == fw_data['component']
)
fw_component = connection.execute(fw_cmp_stmt).first()
self.assertEqual('v1.0.0', fw_component['initial_version'])
del_stmt = (
sqlalchemy.delete(
models.FirmwareInformation
).where(models.FirmwareInformation.node_id == data['id'])
models.FirmwareComponent
).where(models.FirmwareComponent.node_id == data['id'])
)
connection.execute(del_stmt)

View File

@ -0,0 +1,115 @@
# 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.
"""Tests for manipulating FirmwareComponent via the DB API"""
from ironic.common import exception
from ironic.tests.unit.db import base
from ironic.tests.unit.db import utils as db_utils
class DbFirmwareComponentTestCase(base.DbTestCase):
def setUp(self):
super(DbFirmwareComponentTestCase, self).setUp()
self.node = db_utils.create_test_node()
def test_create_firmware_component(self):
values = {'node_id': self.node.id}
fw_cmp = db_utils.create_test_firmware_component(**values)
self.assertCountEqual('bmc', fw_cmp.component)
self.assertCountEqual('v1.0.0', fw_cmp.initial_version)
def test_create_firmware_component_duplicate(self):
component = db_utils.get_test_firmware_component_list()[0]
component['node_id'] = self.node.id
self.dbapi.create_firmware_component(component)
self.assertRaises(exception.FirmwareComponentAlreadyExists,
self.dbapi.create_firmware_component,
component)
def test_get_firmware_component(self):
values = {'node_id': self.node.id}
db_utils.create_test_firmware_component(**values)
result = self.dbapi.get_firmware_component(self.node.id, 'bmc')
self.assertEqual(result.node_id, self.node.id)
self.assertEqual(result.component, 'bmc')
self.assertEqual(result.initial_version, 'v1.0.0')
def test_get_firmware_component_node_not_exist(self):
self.assertRaises(exception.NodeNotFound,
self.dbapi.get_firmware_component,
456, 'BIOS')
def test_get_firmware_component_setting_not_exist(self):
db_utils.create_test_firmware_component(node_id=self.node.id)
self.assertRaises(exception.FirmwareComponentNotFound,
self.dbapi.get_firmware_component,
self.node.id, 'bios_name')
def test_get_firmware_component_list(self):
db_utils.create_test_firmware_component(node_id=self.node.id)
result = self.dbapi.get_firmware_component_list(
node_id=self.node.id)
self.assertEqual(result[0]['node_id'], self.node.id)
self.assertEqual(result[0]['component'], 'bmc')
self.assertEqual(result[0]['initial_version'], 'v1.0.0')
self.assertEqual(result[0]['version'], '1.0')
self.assertEqual(len(result), 1)
def test_get_firmware_component_list_node_not_exist(self):
self.assertRaises(exception.NodeNotFound,
self.dbapi.get_firmware_component_list,
4040)
def test_update_firmware_components(self):
components = db_utils.get_test_firmware_component_list()
components[0].update({'node_id': self.node.id})
components[1].update({'node_id': self.node.id})
self.dbapi.create_firmware_component(components[0])
self.dbapi.create_firmware_component(components[1])
update_components = [
{'component': 'bmc', 'initial_version': 'v1.0.0',
'current_version': 'v1.3.0', 'last_version_flashed': 'v1.3.0'},
{'component': 'BIOS', 'initial_version': 'v1.5.0',
'current_version': 'v1.5.5', 'last_version_flashed': 'v1.5.5'}
]
bmc_values = update_components[0]
bios_values = update_components[1]
bmc = self.dbapi.update_firmware_component(
self.node.id, bmc_values['component'], bmc_values)
bios = self.dbapi.update_firmware_component(
self.node.id, bios_values['component'], bios_values)
self.assertCountEqual('v1.3.0', bmc.current_version)
self.assertCountEqual('v1.5.5', bios.current_version)
def test_update_firmware_component_not_exist(self):
values = db_utils.get_test_firmware_component_list()[0]
values['node_id'] = self.node.id
db_utils.create_test_firmware_component(**values)
values['component'] = 'nic'
self.assertRaises(exception.FirmwareComponentNotFound,
self.dbapi.update_firmware_component,
self.node.id, 'nic', values)
def test_delete_firmware_component_list(self):
values = db_utils.get_test_firmware_component_list()[0]
values['node_id'] = self.node.id
self.assertRaises(exception.FirmwareComponentNotFound,
self.dbapi.get_firmware_component,
self.node.id, 'bmc')
self.dbapi.create_firmware_component(values)
self.dbapi.destroy_node(self.node.id)
self.assertRaises(exception.NodeNotFound,
self.dbapi.get_firmware_component,
self.node.id, 'bmc')

View File

@ -798,6 +798,15 @@ class DbNodeTestCase(base.DbTestCase):
self.assertRaises(exception.NodeInventoryNotFound,
self.dbapi.get_node_inventory_by_node_id, node.id)
def test_firmware_component_list_after_destroying_a_node_by_uuid(self):
node = utils.create_test_node()
utils.create_test_firmware_component(node_id=node.id)
self.dbapi.destroy_node(node.uuid)
self.assertRaises(exception.NodeNotFound,
self.dbapi.get_firmware_component_list, node.id)
def test_update_node(self):
node = utils.create_test_node()

View File

@ -26,6 +26,7 @@ from ironic.objects import bios
from ironic.objects import chassis
from ironic.objects import conductor
from ironic.objects import deploy_template
from ironic.objects import firmware
from ironic.objects import node
from ironic.objects import node_history
from ironic.objects import node_inventory
@ -752,3 +753,43 @@ def create_test_inventory(**kw):
del inventory['id']
dbapi = db_api.get_instance()
return dbapi.create_node_inventory(inventory)
def create_test_firmware_component(**kw):
"""Create test Firmware Component entry in DB and return object.
Function to be used to create test FirmwareComponent object in the DB.
:param kw: kwargs with overriding values for firmware component
:returns: Test FirmwareComponent DB object
"""
fw_cmp_values = get_test_firmware_component(**kw)
if 'id' not in kw:
del fw_cmp_values['id']
dbapi = db_api.get_instance()
return dbapi.create_firmware_component(fw_cmp_values)
def get_test_firmware_component(**kw):
# NOTE(iurygregory): update version to get from the object in the object
# patch.
return {
'id': kw.get('id', 256),
'node_id': kw.get('node_id', 123),
'component': kw.get('component', 'bmc'),
'initial_version': kw.get('initial_version', 'v1.0.0'),
'current_version': kw.get('current_version', 'v1.0.0'),
'last_version_flashed': kw.get('last_version_flashed', None),
'version': kw.get('version', firmware.FirmwareComponent.VERSION),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
}
def get_test_firmware_component_list():
return [
{'component': 'bmc', 'initial_version': 'v1.0.0',
'current_version': 'v1.0.0', 'last_version_flashed': None},
{'component': 'BIOS', 'initial_version': 'v1.5.0',
'current_version': 'v1.5.0', 'last_version_flashed': None},
]

View File

@ -0,0 +1,166 @@
# 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.
import datetime
import types
from unittest import mock
from ironic.common import exception
from ironic import objects
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
class TestFirmwareComponentObject(db_base.DbTestCase,
obj_utils.SchemasTestMixIn):
def setUp(self):
super(TestFirmwareComponentObject, self).setUp()
self.firmware_component_dict = db_utils.get_test_firmware_component()
def test_get_firmware_component(self):
with mock.patch.object(self.dbapi, 'get_firmware_component',
autospec=True) as mock_get_component:
node_id = self.firmware_component_dict['node_id']
name = self.firmware_component_dict['component']
mock_get_component.return_value = self.firmware_component_dict
fw_component = objects.FirmwareComponent.get(
self.context, node_id, name)
mock_get_component.assert_called_once_with(node_id, name)
self.assertEqual(self.context, fw_component._context)
self.assertEqual(self.firmware_component_dict['node_id'],
fw_component.node_id)
self.assertEqual(self.firmware_component_dict['component'],
fw_component.component)
self.assertEqual(self.firmware_component_dict['initial_version'],
fw_component.initial_version)
self.assertEqual(self.firmware_component_dict['current_version'],
fw_component.current_version)
self.assertEqual(
self.firmware_component_dict['last_version_flashed'],
fw_component.last_version_flashed)
def test_get_firmware_component_does_not_exist(self):
fw_cmp_name = "does not exist"
with mock.patch.object(self.dbapi, 'get_firmware_component',
autospec=True) as mock_get_component:
not_found = exception.FirmwareComponentNotFound(
node=self.firmware_component_dict['node_id'],
name=fw_cmp_name)
mock_get_component.side_effect = not_found
self.assertRaises(exception.FirmwareComponentNotFound,
objects.FirmwareComponent.get, self.context,
self.firmware_component_dict['node_id'],
fw_cmp_name)
def test_get_firmware_component_node_does_not_exist(self):
with mock.patch.object(self.dbapi, 'get_firmware_component',
autospec=True) as mock_get_component:
mock_get_component.side_effect = exception.NodeNotFound(node=404)
self.assertRaises(exception.NodeNotFound,
objects.FirmwareComponent.get, self.context,
self.firmware_component_dict['node_id'],
self.firmware_component_dict['component'])
def test_create(self):
with mock.patch.object(self.dbapi, 'create_firmware_component',
autospec=True) as mock_db_create:
mock_db_create.return_value = self.firmware_component_dict
new_fw_component = objects.FirmwareComponent(
self.context, **self.firmware_component_dict)
new_fw_component.create()
mock_db_create.assert_called_once_with(
self.firmware_component_dict)
def test_save(self):
node_id = self.firmware_component_dict['node_id']
name = self.firmware_component_dict['component']
test_time = datetime.datetime(2000, 1, 1, 0, 0)
with mock.patch.object(self.dbapi, 'get_firmware_component',
autospec=True) as mock_get_fw:
mock_get_fw.return_value = self.firmware_component_dict
with mock.patch.object(self.dbapi, 'update_firmware_component',
autospec=True) as mock_update_fw:
mock_update_fw.return_value = (
db_utils.get_test_firmware_component(
current_version="v2.0.1",
last_version_flashed="v2.0.1",
updated_at=test_time)
)
fw_cmp = objects.FirmwareComponent.get(
self.context, node_id, name)
fw_cmp.current_version = "v2.0.1"
fw_cmp.last_version_flashed = "v2.0.1"
fw_cmp.save()
mock_get_fw.assert_called_once_with(node_id, name)
mock_update_fw.assert_called_once_with(
node_id, fw_cmp.component,
{'current_version': 'v2.0.1',
'last_version_flashed': 'v2.0.1',
'version': objects.FirmwareComponent.VERSION})
self.assertEqual(self.context, fw_cmp._context)
self.assertEqual("v2.0.1", fw_cmp.current_version)
self.assertEqual("v2.0.1", fw_cmp.last_version_flashed)
res_updated_at = (fw_cmp.updated_at).replace(tzinfo=None)
self.assertEqual(test_time, res_updated_at)
@mock.patch(
'ironic.objects.firmware.FirmwareComponentList.get_by_node_id',
spec_set=types.FunctionType)
def test_sync_firmware_components_create_and_update(self, mock_get):
node = obj_utils.create_test_node(self.context)
fw_obj = obj_utils.create_test_firmware_component(
self.context, node_id=node.id)
mock_get.return_value = [fw_obj]
components = db_utils.get_test_firmware_component_list()
components[0]['current_version'] = 'v2.0.0'
components[0]['last_version_flashed'] = 'v2.0.0'
create, update, unchanged = (
objects.FirmwareComponentList.sync_firmware_components(
self.context, node.id, components))
self.assertEqual(create, components[1:])
self.assertEqual(update, [components[0]])
self.assertEqual(unchanged, [])
@mock.patch(
'ironic.objects.firmware.FirmwareComponentList.get_by_node_id',
spec_set=types.FunctionType)
def test_sync_firmware_components_nochange(self, mock_get):
node = obj_utils.create_test_node(self.context)
fw_obj_1 = obj_utils.create_test_firmware_component(
self.context, node_id=node.id)
fw_obj_2 = obj_utils.create_test_firmware_component(
self.context, node_id=node.id, component='BIOS',
initial_version='v1.5.0', current_version='v1.5.0')
mock_get.return_value = [fw_obj_1, fw_obj_2]
components = db_utils.get_test_firmware_component_list()
create, update, unchanged = (
objects.FirmwareComponentList.sync_firmware_components(
self.context, node.id, components))
self.assertEqual(create, [])
self.assertEqual(update, [])
self.assertEqual(unchanged, components)

View File

@ -35,7 +35,7 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
super(TestNodeObject, self).setUp()
self.ctxt = context.get_admin_context()
self.fake_node = db_utils.get_test_node()
self.node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
self.node = obj_utils.get_test_node(self.context, **self.fake_node)
def test_as_dict_insecure(self):
self.node.driver_info['ipmi_password'] = 'fake'

View File

@ -722,6 +722,8 @@ expected_object_fingerprints = {
'Deployment': '1.0-ff10ae028c5968f1596131d85d7f5f9d',
'NodeHistory': '1.0-9b576c6481071e7f7eac97317fa29418',
'NodeInventory': '1.0-97692fec24e20ab02022b9db54e8f539',
'FirmwareComponent': '1.0-0e0720dab959e20247bbcfd5f28958c5',
'FirmwareComponentList': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
}

View File

@ -392,3 +392,35 @@ def create_test_inventory(ctxt, node, **kw):
setattr(inv, key, value)
inv.create()
return inv
def get_test_firmware_component(ctxt, **kw):
"""Return a FirmwareComponent object with appropriate attributes.
NOTE: The object leaves the attributes marked as changed, such
that a create() could be used to commit it to the DB.
"""
kw['object_type'] = 'firmware'
get_db_fw_checked = check_keyword_arguments(
db_utils.get_test_firmware_component)
db_fw_cmp = get_db_fw_checked(**kw)
if 'id' not in kw:
del db_fw_cmp['id']
fw_cmp = objects.FirmwareComponent(ctxt)
for key in db_fw_cmp:
setattr(fw_cmp, key, db_fw_cmp[key])
return fw_cmp
def create_test_firmware_component(ctxt, **kw):
"""Create and return a test Firmware Component object.
Create a Firmware Component in the DB and return a FirmwareComponent
object with appropriate attributes.
"""
fw_cmp = get_test_firmware_component(ctxt, **kw)
fw_cmp.create()
return fw_cmp