N3000 BMC and retimer firmware update

The update provides the support of updating BMC and retimer firmware
for the N3000 device.

Story: 2008965
Task: 42953

Change-Id: Id745bd642d906c917e50d54b02d5923ca6d277a7
Signed-off-by: Teresa Ho <teresa.ho@windriver.com>
This commit is contained in:
Teresa Ho 2021-07-30 12:16:54 -04:00
parent 8e82287b4a
commit d66269a6a1
13 changed files with 164 additions and 12 deletions

View File

@ -5594,6 +5594,7 @@ itemNotFound (404)
"description (Optional)", "plain", "xsd:string", "The description 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." "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." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"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." "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." "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."
@ -5612,6 +5613,7 @@ itemNotFound (404)
"description": null, "description": null,
"name": null, "name": null,
"image_version": null, "image_version": null,
"retimer_included": false,
"applied_labels": "applied_labels":
{ {
"key1": "value1", "key1": "value1",
@ -5690,6 +5692,7 @@ itemNotFound (404)
"description (Optional)", "plain", "xsd:string", "The description 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." "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." "applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"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." "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." "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."
@ -5708,6 +5711,7 @@ itemNotFound (404)
"description": null, "description": null,
"name": null, "name": null,
"image_version": null, "image_version": null,
"retimer_included": false,
"applied_labels": "applied_labels":
{ {
"key1": "value1", "key1": "value1",
@ -5746,6 +5750,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image." "name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description 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." "image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
**Response parameters** **Response parameters**
@ -5762,6 +5767,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image." "name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description 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." "image_version (Optional)", "plain", "xsd:string", "The version of the device 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." "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." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5780,6 +5786,7 @@ badMediaType (415)
"description": null, "description": null,
"name": null, "name": null,
"image_version": null, "image_version": null,
"retimer_included": false,
"applied_labels": null "applied_labels": null
} }
] ]
@ -5830,6 +5837,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image." "name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description 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." "image_version (Optional)", "plain", "xsd:string", "The version of the device 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." "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." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5848,6 +5856,7 @@ badMediaType (415)
"description": null, "description": null,
"name": null, "name": null,
"image_version": null, "image_version": null,
"retimer_included": false,
"applied_labels": "applied_labels":
{ {
"key1": "value1" "key1": "value1"
@ -5902,6 +5911,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image." "name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description 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." "image_version (Optional)", "plain", "xsd:string", "The version of the device 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." "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." "uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5920,6 +5930,7 @@ badMediaType (415)
"description": null, "description": null,
"name": null, "name": null,
"image_version": null, "image_version": null,
"retimer_included": false,
"applied_labels": null "applied_labels": null
} }
] ]

View File

@ -12,7 +12,7 @@ from cgtsclient import exc
CREATION_ATTRIBUTES = [ CREATION_ATTRIBUTES = [
'bitstream_type', 'pci_vendor', 'pci_device', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'uuid'] 'name', 'description', 'image_version', 'uuid', 'retimer_included']
class DeviceImage(base.Resource): class DeviceImage(base.Resource):

View File

@ -13,7 +13,7 @@ def _print_device_image_show(obj):
'pci_vendor', 'pci_device', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'name', 'description', 'image_version',
'applied', 'applied_labels'] 'applied', 'applied_labels', 'retimer_included']
if isinstance(obj, dict): if isinstance(obj, dict):
data = [(f, obj.get(f, '')) for f in fields] 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', labels = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'name', 'description', 'image_version', 'retimer_included',
'applied', 'applied_labels'] 'applied', 'applied_labels']
fields = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', fields = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'name', 'description', 'image_version', 'retimer_included',
'applied', 'applied_labels'] 'applied', 'applied_labels']
device_images = cc.device_image.list() device_images = cc.device_image.list()
utils.print_list(device_images, fields, labels, sortby=1) utils.print_list(device_images, fields, labels, sortby=1)
@ -81,6 +81,9 @@ def do_device_image_list(cc, args):
@utils.arg('-u', '--uuid', @utils.arg('-u', '--uuid',
metavar='<uuid>', metavar='<uuid>',
help='UUID of the device image') help='UUID of the device image')
@utils.arg('--retimer-included',
metavar='<true/false>',
help='Retimer firmware included in BMC FW binary')
def do_device_image_upload(cc, args): def do_device_image_upload(cc, args):
"""Upload a device image.""" """Upload a device image."""
@ -90,7 +93,7 @@ def do_device_image_upload(cc, args):
field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version'] 'name', 'description', 'image_version', 'retimer_included']
# Prune input fields down to required/expected values # Prune input fields down to required/expected values
user_fields = dict((k, v) for (k, v) in vars(args).items() user_fields = dict((k, v) for (k, v) in vars(args).items()

View File

@ -81,6 +81,9 @@ class DeviceImage(base.APIBase):
image_version = wtypes.text image_version = wtypes.text
"The version of the device image" "The version of the device image"
retimer_included = bool
"Retimer firmware included in BMC firmware binary"
applied = bool applied = bool
"Represent current status: created or applied" "Represent current status: created or applied"
@ -109,7 +112,7 @@ class DeviceImage(base.APIBase):
['id', 'uuid', 'bitstream_type', 'pci_vendor', 'pci_device', ['id', 'uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'name', 'description', 'image_version',
'applied', 'applied_labels']) 'applied', 'applied_labels', 'retimer_included'])
# insert applied labels for this device image if they exist # insert applied labels for this device image if they exist
device_image = _get_applied_labels(device_image) device_image = _get_applied_labels(device_image)
@ -246,7 +249,7 @@ class DeviceImageController(rest.RestController):
field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device', field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id', 'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version'] 'name', 'description', 'image_version', 'retimer_included']
data = dict((k, v) for (k, v) in pecan.request.POST.items() data = dict((k, v) for (k, v) in pecan.request.POST.items()
if k in field_list and not (v is None)) if k in field_list and not (v is None))
msg = _validate_syntax(data) msg = _validate_syntax(data)
@ -431,6 +434,9 @@ def _validate_bitstream_type(dev_img):
elif (dev_img['bitstream_type'] == dconstants.BITSTREAM_TYPE_KEY_REVOCATION and elif (dev_img['bitstream_type'] == dconstants.BITSTREAM_TYPE_KEY_REVOCATION and
'revoke_key_id' not in dev_img): 'revoke_key_id' not in dev_img):
msg = _("revoke_key_id is required for key revocation bitstream type") msg = _("revoke_key_id is required for key revocation bitstream type")
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")
return msg return msg
@ -477,6 +483,10 @@ def _validate_syntax(device_image):
not cutils.is_uuid_like(device_image['uuid'])): not cutils.is_uuid_like(device_image['uuid'])):
msg = _("uuid must be a valid UUID") msg = _("uuid must be a valid UUID")
return msg 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")
return msg
msg = _validate_hexadecimal_fields(device_image) msg = _validate_hexadecimal_fields(device_image)
if not msg: if not msg:
msg = _validate_bitstream_type(device_image) msg = _validate_bitstream_type(device_image)

View File

@ -13705,7 +13705,8 @@ class ConductorManager(service.PeriodicService):
(host.hostname, pci_device.pciaddr, filename, device_image_state.id)) (host.hostname, pci_device.pciaddr, filename, device_image_state.id))
fpga_rpcapi = fpga_agent_rpcapi.AgentAPI() fpga_rpcapi = fpga_agent_rpcapi.AgentAPI()
fpga_rpcapi.host_device_update_image( fpga_rpcapi.host_device_update_image(
context, host.hostname, pci_device.pciaddr, filename, device_image_state.id) context, host.hostname, pci_device.pciaddr, filename, device_image_state.id,
device_image.retimer_included)
# We've kicked off a device image update, so exit the function. # We've kicked off a device image update, so exit the function.
return return
LOG.info("no more device images to process") LOG.info("no more device images to process")

View File

@ -0,0 +1,24 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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('retimer_included', Boolean, default=False))
def downgrade(migrate_engine):
# Downgrade is unsupported in this release.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -1524,6 +1524,7 @@ class DeviceImageFunctional(DeviceImageCommon, DeviceImage):
__tablename__ = 'device_images_functional' __tablename__ = 'device_images_functional'
bitstream_id = Column(String(255), nullable=True) bitstream_id = Column(String(255), nullable=True)
retimer_included = Column(Boolean, nullable=False, default=False)
__mapper_args__ = { __mapper_args__ = {
'polymorphic_identity': 'functional', 'polymorphic_identity': 'functional',

View File

@ -35,3 +35,6 @@ OPAE_IMG = "registry.local:9001/docker.io/starlingx/n3000-opae:stx.6.0-v1.0.1"
DOCKER_LOGIN_FLAG = "/var/run/docker_login_done" DOCKER_LOGIN_FLAG = "/var/run/docker_login_done"
N3000_RESET_FLAG = os.path.join(tsc.VOLATILE_PATH, ".sysinv_n3000_reset") N3000_RESET_FLAG = os.path.join(tsc.VOLATILE_PATH, ".sysinv_n3000_reset")
# This flag is set if the N3000 requires a second reset
N3000_RETIMER_FLAG = os.path.join(tsc.PLATFORM_CONF_PATH, ".sysinv_n3000_retimer")

View File

@ -551,7 +551,8 @@ class FpgaAgentManager(service.PeriodicService):
"this will likely cause problems.") "this will likely cause problems.")
pass pass
def device_update_image(self, context, pci_addr, filename, transaction_id): def device_update_image(self, context, pci_addr, filename, transaction_id,
retimer_included):
"""Write the device image to the device at the specified address. """Write the device image to the device at the specified address.
Transaction is the transaction ID as specified by sysinv-conductor. Transaction is the transaction ID as specified by sysinv-conductor.
@ -612,6 +613,9 @@ class FpgaAgentManager(service.PeriodicService):
os.remove(local_path) os.remove(local_path)
# start the watchdog service again # start the watchdog service again
start_watchdog() start_watchdog()
# If device image contains c827 retimer firmware, set the retimer flag
if retimer_included:
utils.touch(constants.N3000_RETIMER_FLAG)
except exception.SysinvException as exc: except exception.SysinvException as exc:
LOG.info("setting transaction id %s as failed" % transaction_id) LOG.info("setting transaction id %s as failed" % transaction_id)

View File

@ -20,6 +20,7 @@
import os import os
import shlex import shlex
from eventlet.green import subprocess from eventlet.green import subprocess
from glob import glob
from oslo_log import log from oslo_log import log
from sysinv.common import utils from sysinv.common import utils
@ -30,6 +31,18 @@ from sysinv.fpga_agent import constants
# Volatile flag file so we only reset the N3000s once after bootup. # Volatile flag file so we only reset the N3000s once after bootup.
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
SYSFS_DEVICE_PATH = "/sys/bus/pci/devices/"
FME_PATH = "/fpga/intel-fpga-dev.*/intel-fpga-fme.*/"
SPI_PATH = "spi-altera.*.auto/spi_master/spi*/spi*.*/"
# These are relative to SPI_PATH
EEPROM_LOAD_PATH = "pkvl/eeprom_load"
EEPROM_UPDATE_STATUS_PATH = "pkvl/eeprom_update_status"
# The value in eeprom_update_status must be 0x1111 to indicate successful
# update as documented in the Intel FPGA N3000 User Guide
EEPROM_UPDATE_SUCCESS = '0x1111'
def n3000_img_accessible(): def n3000_img_accessible():
cmd = 'docker image list "%s" --format "{{.Repository}}:{{.Tag}}"' % \ cmd = 'docker image list "%s" --format "{{.Repository}}:{{.Tag}}"' % \
@ -73,6 +86,52 @@ def reset_device_n3000(pci_addr):
raise exception.SysinvException(msg) raise exception.SysinvException(msg)
def get_n3000_sysfs_file(pattern):
"""Find a sysfs file related to the N3000.
The result should be an empty string if the file doesn't exist,
or a single line of text if it does.
"""
# Convert the pattern to a list of matching filenames
filenames = glob(pattern)
# If there are no matching files, return an empty string.
if len(filenames) == 0:
return ""
# If there's more than one filename, complain.
if len(filenames) > 1:
LOG.warn("Pattern %s gave %s matching filenames, using the first." %
(pattern, len(filenames)))
filename = filenames[0]
return filename
def update_device_n3000_retimer(pci_addr):
# Write 1 to the eeprom_load sysfs node of the card
eeprom_load_pattern = (SYSFS_DEVICE_PATH + pci_addr + FME_PATH +
SPI_PATH + EEPROM_LOAD_PATH)
try:
eeprom_load_file = get_n3000_sysfs_file(eeprom_load_pattern)
with open(eeprom_load_file, "w") as writer:
writer.write("1")
except Exception as e:
msg = "Failed to load retimer: %s" % str(e)
LOG.error(msg)
raise exception.SysinvException(msg)
# Check the eeprom_update_status node for completion
eeprom_update_status_pattern = (SYSFS_DEVICE_PATH + pci_addr + FME_PATH +
SPI_PATH + EEPROM_UPDATE_STATUS_PATH)
eeprom_update_status = get_n3000_sysfs_file(eeprom_update_status_pattern)
with open(eeprom_update_status, 'r') as reader:
status = reader.read()
if EEPROM_UPDATE_SUCCESS not in status:
LOG.error("Failed to update retimer, status=%s" % status)
def reset_n3000_fpgas(): def reset_n3000_fpgas():
if not os.path.exists(constants.N3000_RESET_FLAG): if not os.path.exists(constants.N3000_RESET_FLAG):
# Reset all N3000 FPGAs on the system. # Reset all N3000 FPGAs on the system.
@ -91,9 +150,23 @@ def reset_n3000_fpgas():
except Exception: except Exception:
got_exception = True got_exception = True
if not got_exception and os.path.exists(constants.N3000_RETIMER_FLAG):
# The retimer included flag is set, execute additional steps
fpga_addrs = get_n3000_devices()
for fpga_addr in fpga_addrs:
try:
LOG.info("Updating retimer")
update_device_n3000_retimer(fpga_addr)
LOG.info("Resetting N3000 second time")
reset_device_n3000(fpga_addr)
except Exception:
got_exception = True
LOG.info("Done resetting N3000 FPGAs.") LOG.info("Done resetting N3000 FPGAs.")
if not got_exception: if not got_exception:
utils.touch(constants.N3000_RESET_FLAG) utils.touch(constants.N3000_RESET_FLAG)
if os.path.exists(constants.N3000_RETIMER_FLAG):
os.remove(constants.N3000_RETIMER_FLAG)
return True return True
else: else:
return False return False

View File

@ -52,11 +52,12 @@ class AgentAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
default_version=self.RPC_API_VERSION) default_version=self.RPC_API_VERSION)
def host_device_update_image(self, context, hostname, pci_addr, def host_device_update_image(self, context, hostname, pci_addr,
filename, transaction_id): filename, transaction_id, retimer_included):
LOG.info("sending device_update_image to host %s" % hostname) LOG.info("sending device_update_image to host %s" % hostname)
topic = '%s.%s' % (self.topic, hostname) topic = '%s.%s' % (self.topic, hostname)
return self.cast(context, return self.cast(context,
self.make_msg('device_update_image', self.make_msg('device_update_image',
pci_addr=pci_addr, filename=filename, pci_addr=pci_addr, filename=filename,
transaction_id=transaction_id), transaction_id=transaction_id,
retimer_included=retimer_included),
topic=topic) topic=topic)

View File

@ -27,6 +27,7 @@ class DeviceImage(base.SysinvObject):
'image_version': utils.str_or_none, 'image_version': utils.str_or_none,
'applied': utils.bool_or_none, 'applied': utils.bool_or_none,
'capabilities': utils.dict_or_none, 'capabilities': utils.dict_or_none,
'retimer_included': utils.bool_or_none,
} }
_optional_fields = {'bitstream_id', _optional_fields = {'bitstream_id',
@ -34,7 +35,8 @@ class DeviceImage(base.SysinvObject):
'revoke_key_id', 'revoke_key_id',
'name', 'name',
'description', 'description',
'image_version'} 'image_version',
'retimer_included'}
@base.remotable_classmethod @base.remotable_classmethod
def get_by_uuid(cls, context, uuid): def get_by_uuid(cls, context, uuid):

View File

@ -276,6 +276,25 @@ class TestPostDeviceImage(TestDeviceImage, dbbase.ControllerHostTestCase):
self.assertIn("revoke_key_id is required for key revocation bitstream" self.assertIn("revoke_key_id is required for key revocation bitstream"
" type", str(result)) " type", str(result))
def test_create_non_functional_image_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_KEY_REVOCATION,
'pci_vendor': fpga_constants.N3000_VENDOR,
'pci_device': fpga_constants.N3000_DEVICE,
'revoke_key_id': '12345',
'retimer_included': 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("retimer_included option is only applicable to"
" functional BMC image", str(result))
def test_create_bitstream_type_invalid(self): def test_create_bitstream_type_invalid(self):
# Test creation of device image # Test creation of device image
bitstream_file = os.path.join(os.path.dirname(__file__), "data", bitstream_file = os.path.join(os.path.dirname(__file__), "data",