diff --git a/api-ref/source/api-ref-sysinv-v1-config.rst b/api-ref/source/api-ref-sysinv-v1-config.rst index 86ba3eff39..ee1e8408d1 100644 --- a/api-ref/source/api-ref-sysinv-v1-config.rst +++ b/api-ref/source/api-ref-sysinv-v1-config.rst @@ -5137,6 +5137,7 @@ itemNotFound (404) "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object." "links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage." @@ -5156,6 +5157,7 @@ itemNotFound (404) "description": null, "name": null, "image_version": null, + "bmc": false, "retimer_included": false, "applied_labels": { @@ -5235,6 +5237,7 @@ itemNotFound (404) "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object." "links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage." @@ -5254,6 +5257,7 @@ itemNotFound (404) "description": null, "name": null, "image_version": null, + "bmc": false, "retimer_included": false, "applied_labels": { @@ -5293,6 +5297,7 @@ badMediaType (415) "name (Optional)", "plain", "xsd:string", "The name of the device image." "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." **Response parameters** @@ -5310,6 +5315,7 @@ badMediaType (415) "name (Optional)", "plain", "xsd:string", "The name of the device image." "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object." @@ -5329,6 +5335,7 @@ badMediaType (415) "description": null, "name": null, "image_version": null, + "bmc": false, "retimer_included": false, "applied_labels": null } @@ -5380,6 +5387,7 @@ badMediaType (415) "name (Optional)", "plain", "xsd:string", "The name of the device image." "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object." @@ -5399,6 +5407,7 @@ badMediaType (415) "description": null, "name": null, "image_version": null, + "bmc": false, "retimer_included": false, "applied_labels": { @@ -5454,6 +5463,7 @@ badMediaType (415) "name (Optional)", "plain", "xsd:string", "The name of the device image." "description (Optional)", "plain", "xsd:string", "The description of the device image." "image_version (Optional)", "plain", "xsd:string", "The version of the device image." + "bmc (Optional)", "plain", "xsd:boolean", "This indicates whether it is a BMC functional image." "retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object." @@ -5473,6 +5483,7 @@ badMediaType (415) "description": null, "name": null, "image_version": null, + "bmc": false, "retimer_included": false, "applied_labels": null } diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image.py index 295e71c9b9..acad084e54 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2021 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -12,7 +12,7 @@ from cgtsclient import exc CREATION_ATTRIBUTES = [ 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', - 'name', 'description', 'image_version', 'uuid', 'retimer_included'] + 'name', 'description', 'image_version', 'uuid', 'bmc', 'retimer_included'] class DeviceImage(base.Resource): diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image_shell.py index 2a2d82755f..a4f60976ce 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/device_image_shell.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2021 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -13,7 +13,7 @@ def _print_device_image_show(obj): 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', 'name', 'description', 'image_version', - 'applied', 'applied_labels', 'retimer_included'] + 'applied', 'applied_labels', 'bmc', 'retimer_included'] if isinstance(obj, dict): data = [(f, obj.get(f, '')) for f in fields] @@ -37,11 +37,11 @@ def do_device_image_list(cc, args): labels = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', - 'name', 'description', 'image_version', 'retimer_included', + 'name', 'description', 'image_version', 'bmc', 'retimer_included', 'applied', 'applied_labels'] fields = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', - 'name', 'description', 'image_version', 'retimer_included', + 'name', 'description', 'image_version', 'bmc', 'retimer_included', 'applied', 'applied_labels'] device_images = cc.device_image.list() utils.print_list(device_images, fields, labels, sortby=1) @@ -81,6 +81,9 @@ def do_device_image_list(cc, args): @utils.arg('-u', '--uuid', metavar='', help='UUID of the device image') +@utils.arg('--bmc', + metavar='', + help='BMC image') @utils.arg('--retimer-included', metavar='', help='Retimer firmware included in BMC FW binary') @@ -93,7 +96,7 @@ def do_device_image_upload(cc, args): field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', - 'name', 'description', 'image_version', 'retimer_included'] + 'name', 'description', 'image_version', 'bmc', 'retimer_included'] # Prune input fields down to required/expected values user_fields = dict((k, v) for (k, v) in vars(args).items() diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py index c9bcd7ef85..986a8253a4 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py @@ -1,9 +1,10 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2021 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # +from distutils.util import strtobool import os import pecan from pecan import expose @@ -81,6 +82,9 @@ class DeviceImage(base.APIBase): image_version = wtypes.text "The version of the device image" + bmc = bool + "Represent the functional image is a BMC image" + retimer_included = bool "Retimer firmware included in BMC firmware binary" @@ -112,7 +116,7 @@ class DeviceImage(base.APIBase): ['id', 'uuid', 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', 'name', 'description', 'image_version', - 'applied', 'applied_labels', 'retimer_included']) + 'applied', 'applied_labels', 'bmc', 'retimer_included']) # insert applied labels for this device image if they exist device_image = _get_applied_labels(device_image) @@ -249,7 +253,7 @@ class DeviceImageController(rest.RestController): field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_id', 'key_signature', 'revoke_key_id', - 'name', 'description', 'image_version', 'retimer_included'] + 'name', 'description', 'image_version', 'bmc', 'retimer_included'] data = dict((k, v) for (k, v) in pecan.request.POST.items() if k in field_list and not (v is None)) msg = _validate_syntax(data) @@ -434,9 +438,21 @@ def _validate_bitstream_type(dev_img): elif (dev_img['bitstream_type'] == dconstants.BITSTREAM_TYPE_KEY_REVOCATION and 'revoke_key_id' not in dev_img): msg = _("revoke_key_id is required for key revocation bitstream type") + elif (dev_img['bitstream_type'] != dconstants.BITSTREAM_TYPE_FUNCTIONAL and + 'bmc' in dev_img.keys()): + msg = _("bmc option is only applicable to functional image") elif (dev_img['bitstream_type'] != dconstants.BITSTREAM_TYPE_FUNCTIONAL and 'retimer_included' in dev_img.keys()): msg = _("retimer_included option is only applicable to functional BMC image") + elif dev_img['bitstream_type'] == dconstants.BITSTREAM_TYPE_FUNCTIONAL: + bmc = False + retimer_included = False + if 'bmc' in dev_img.keys(): + bmc = bool(strtobool(dev_img['bmc'])) + if 'retimer_included' in dev_img.keys(): + retimer_included = bool(strtobool(dev_img['retimer_included'])) + if not bmc and retimer_included: + msg = _("retimer_included option is only applicable to functional BMC image") return msg @@ -483,6 +499,10 @@ def _validate_syntax(device_image): not cutils.is_uuid_like(device_image['uuid'])): msg = _("uuid must be a valid UUID") return msg + if ('bmc' in device_image.keys() and + not cutils.is_valid_boolstr(device_image['bmc'])): + msg = _("Parameter bmc must be a valid bool string") + return msg if ('retimer_included' in device_image.keys() and not cutils.is_valid_boolstr(device_image['retimer_included'])): msg = _("Parameter retimer_included must be a valid bool string") @@ -566,16 +586,27 @@ def process_device_image_apply(pcidevice_id, device_image, label_id=None): # return False for nothing to do response = False else: - # Remove the existing device_image_state record - pecan.request.dbapi.device_image_state_destroy(r.uuid) - # Remove the existing device image label if any - if label_id: - try: - img_lbl = pecan.request.dbapi.device_image_label_get_by_image_label( - img.id, label_id) - pecan.request.dbapi.device_image_label_destroy(img_lbl.uuid) - except exception.DeviceImageLabelNotFoundByKey: - pass + do_remove = True + # If the new image and the applied image are of a different kind (user/bmc), + # do not remove the image. + # If the new BMC image does not have retimer and an applied BMC image + # does have retimer, keep that applied device image state. + if device_image.bmc != img.bmc: + do_remove = False + if (device_image.bmc and + (not device_image.retimer_included and img.retimer_included)): + do_remove = False + if do_remove: + # Remove the existing device_image_state record + pecan.request.dbapi.device_image_state_destroy(r.uuid) + # Remove the existing device image label if any + if label_id: + try: + img_lbl = pecan.request.dbapi.device_image_label_get_by_image_label( + img.id, label_id) + pecan.request.dbapi.device_image_label_destroy(img_lbl.uuid) + except exception.DeviceImageLabelNotFoundByKey: + pass elif img.bitstream_type == dconstants.BITSTREAM_TYPE_KEY_REVOCATION: if img.id == device_image.id: if r.status in [dconstants.DEVICE_IMAGE_UPDATE_COMPLETED, diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/123_device_image_bmc.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/123_device_image_bmc.py new file mode 100644 index 0000000000..4db4b42ea3 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/123_device_image_bmc.py @@ -0,0 +1,23 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sqlalchemy import Boolean, Column, MetaData, Table + +ENGINE = 'InnoDB' +CHARSET = 'utf8' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + dev_img_functional = Table('device_images_functional', meta, autoload=True) + dev_img_functional.create_column(Column('bmc', Boolean, default=False)) + + +def downgrade(migrate_engine): + # Downgrade is unsupported in this release. + raise NotImplementedError('SysInv database downgrade is unsupported.') diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py index 1ddfbc3616..5c7864a38f 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py @@ -1593,6 +1593,7 @@ class DeviceImageFunctional(DeviceImageCommon, DeviceImage): __tablename__ = 'device_images_functional' bitstream_id = Column(String(255), nullable=True) + bmc = Column(Boolean, nullable=False, default=False) retimer_included = Column(Boolean, nullable=False, default=False) __mapper_args__ = { diff --git a/sysinv/sysinv/sysinv/sysinv/objects/device_image.py b/sysinv/sysinv/sysinv/sysinv/objects/device_image.py index e3c3d9b5cc..c3a20fc045 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/device_image.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/device_image.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2021 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -27,6 +27,7 @@ class DeviceImage(base.SysinvObject): 'image_version': utils.str_or_none, 'applied': utils.bool_or_none, 'capabilities': utils.dict_or_none, + 'bmc': utils.bool_or_none, 'retimer_included': utils.bool_or_none, } @@ -36,6 +37,7 @@ class DeviceImage(base.SysinvObject): 'name', 'description', 'image_version', + 'bmc', 'retimer_included'} @base.remotable_classmethod diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py index 51744a8359..e7350221fa 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2021 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -295,6 +295,25 @@ class TestPostDeviceImage(TestDeviceImage, dbbase.ControllerHostTestCase): self.assertIn("retimer_included option is only applicable to" " functional BMC image", str(result)) + def test_create_functional_image_non_bmc_with_retimer(self): + # Test creation of device image + bitstream_file = os.path.join(os.path.dirname(__file__), "data", + 'bitstream.bit') + data = { + 'bitstream_type': dconstants.BITSTREAM_TYPE_ROOT_KEY, + 'pci_vendor': fpga_constants.N3000_VENDOR, + 'pci_device': fpga_constants.N3000_DEVICE, + 'key_signature': '12345', + 'bmc': True, + } + upload_file = [('file', bitstream_file)] + result = self.post_with_files('/device_images', data, + upload_files=upload_file, + headers=self.API_HEADERS, + expect_errors=True) + self.assertIn("bmc option is only applicable to" + " functional image", str(result)) + def test_create_bitstream_type_invalid(self): # Test creation of device image bitstream_file = os.path.join(os.path.dirname(__file__), "data", @@ -362,6 +381,20 @@ class TestPatch(TestDeviceImage): pci_vendor='80ee', pci_device='beef', bitstream_id='6789') + self.device_image_bmc = dbutils.create_test_device_image( + bitstream_type=dconstants.BITSTREAM_TYPE_FUNCTIONAL, + pci_vendor='80ee', + pci_device='beef', + bitstream_id='0x2300011001030F', + bmc=True, + retimer_included=False) + self.device_image_bmc_retimer = dbutils.create_test_device_image( + bitstream_type=dconstants.BITSTREAM_TYPE_FUNCTIONAL, + pci_vendor='80ee', + pci_device='beef', + bitstream_id='0x2300011001030F', + bmc=True, + retimer_included=True) def test_device_image_apply_all_hosts(self): # Test applying device image to all hosts with fpga devices @@ -459,6 +492,101 @@ class TestPatch(TestDeviceImage): self.assertEqual(response.content_type, 'application/json') self.assertEqual(response.status_code, http_client.OK) + # Verify that an entry of image to device mapping is updated + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image2.id, self.pci_device.id) + self.assertEqual(self.device_image2.id, dev_img_state.image_id) + + def test_device_image_apply_functional_user_bmc(self): + # Test applying second device image with label + + # Assign label to a device + self.post_json('/device_labels', + [{'pcidevice_uuid': self.pci_device.uuid}, + {'key1': 'value1'}], + headers=self.API_HEADERS) + + # Apply the device user image + path = '/device_images/%s?action=apply' % self.device_image.uuid + response = self.patch_json(path, [{'key1': 'value1'}], + headers=self.API_HEADERS) + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.status_code, http_client.OK) + # Verify that an entry of image to device mapping is updated + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image.id, self.pci_device.id) + self.assertEqual(dconstants.DEVICE_IMAGE_UPDATE_PENDING, + dev_img_state.status) + + # Test 1: Apply a functional BMC device image + path = '/device_images/%s?action=apply' % self.device_image_bmc.uuid + response = self.patch_json(path, [{'key1': 'value1'}], + headers=self.API_HEADERS, + expect_errors=True) + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.status_code, http_client.OK) + # Verify that the entries for both images exist + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image.id, self.pci_device.id) + self.assertEqual(self.device_image.id, dev_img_state.image_id) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image_bmc.id, self.pci_device.id) + self.assertEqual(self.device_image_bmc.id, dev_img_state.image_id) + + # Test 2: Apply a functional BMC retimer device image + path = '/device_images/%s?action=apply' % self.device_image_bmc_retimer.uuid + response = self.patch_json(path, [{'key1': 'value1'}], + headers=self.API_HEADERS, + expect_errors=True) + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.status_code, http_client.OK) + # Verify that the old bmc image is replaced with new bmc image with retimer + state_list = self.dbapi.device_image_state_get_list() + self.assertEqual(len(state_list), 2) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image.id, self.pci_device.id) + self.assertEqual(self.device_image.id, dev_img_state.image_id) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image_bmc_retimer.id, self.pci_device.id) + self.assertEqual(self.device_image_bmc_retimer.id, dev_img_state.image_id) + + # Test 3: Apply a BMC image w/o retimer + path = '/device_images/%s?action=apply' % self.device_image_bmc.uuid + response = self.patch_json(path, [{'key1': 'value1'}], + headers=self.API_HEADERS, + expect_errors=True) + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.status_code, http_client.OK) + # Verify that all three states exist, the old BMC retimer image state exists + state_list = self.dbapi.device_image_state_get_list() + self.assertEqual(len(state_list), 3) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image.id, self.pci_device.id) + self.assertEqual(self.device_image.id, dev_img_state.image_id) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image_bmc_retimer.id, self.pci_device.id) + self.assertEqual(self.device_image_bmc_retimer.id, dev_img_state.image_id) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image_bmc.id, self.pci_device.id) + self.assertEqual(self.device_image_bmc.id, dev_img_state.image_id) + + # Test 4: Apply a BMC image with retimer + path = '/device_images/%s?action=apply' % self.device_image_bmc_retimer.uuid + response = self.patch_json(path, [{'key1': 'value1'}], + headers=self.API_HEADERS, + expect_errors=True) + self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.status_code, http_client.OK) + # Verify that state for BMC image without retimer is deleted + state_list = self.dbapi.device_image_state_get_list() + self.assertEqual(len(state_list), 2) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image.id, self.pci_device.id) + self.assertEqual(self.device_image.id, dev_img_state.image_id) + dev_img_state = self.dbapi.device_image_state_get_by_image_device( + self.device_image_bmc_retimer.id, self.pci_device.id) + self.assertEqual(self.device_image_bmc_retimer.id, dev_img_state.image_id) + def test_device_image_remove_all_hosts(self): # Test removing device image for all hosts with fpga devices # Remove the device image diff --git a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py index 9691689278..fec3ad7dd4 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py @@ -1609,6 +1609,8 @@ def get_test_device_image(**kw): 'name': kw.get('name'), 'description': kw.get('description'), 'version': kw.get('version'), + 'bmc': kw.get('bmc'), + 'retimer_included': kw.get('retimer_included'), } return device_image