mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-21 08:33:23 +00:00
Ask a platform which firmware image types it takes
Some will not take an image without being told which kind it is, and the only way to find out was to attempt an update and read the error, which writes to the bmc before it gets that far.
This commit is contained in:
@@ -56,7 +56,7 @@ components = ['all']
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: "
|
||||
"%prog <noderange> [list][updatestatus][update [--backup] "
|
||||
"%prog <noderange> [list][updatestatus][updatetypes][update [--backup] "
|
||||
"[--parameterfile <file>] <file>]|[<components>]")
|
||||
argparser.add_option('-b', '--backup', action='store_true',
|
||||
help='Target a backup bank rather than primary')
|
||||
@@ -71,6 +71,7 @@ argparser.add_option('-m', '--maxnodes', type='int',
|
||||
(options, args) = argparser.parse_args()
|
||||
upfile = None
|
||||
querystatus = False
|
||||
querytypes = False
|
||||
try:
|
||||
noderange = args[0]
|
||||
if len(args) > 1:
|
||||
@@ -82,6 +83,8 @@ try:
|
||||
comps = args[2:]
|
||||
elif args[1] == 'updatestatus':
|
||||
querystatus = True
|
||||
elif args[1] == 'updatetypes':
|
||||
querytypes = True
|
||||
else:
|
||||
comps = args[1:]
|
||||
components = []
|
||||
@@ -207,6 +210,17 @@ def show_firmware(session):
|
||||
','.join(components)))
|
||||
|
||||
|
||||
def show_update_types(session):
|
||||
global exitcode
|
||||
for res in session.read(
|
||||
'/noderange/{0}/inventory/firmware/updatetypes'.format(noderange)):
|
||||
exitcode |= client.printerror(res)
|
||||
for node in res.get('databynode', {}):
|
||||
types = res['databynode'][node].get('types', None)
|
||||
if types:
|
||||
print('{0}: {1}'.format(node, ','.join(types)))
|
||||
|
||||
|
||||
try:
|
||||
session = client.Command()
|
||||
if querystatus:
|
||||
@@ -217,6 +231,8 @@ try:
|
||||
currstat = res['databynode'][node].get('status', None)
|
||||
if currstat:
|
||||
print('{}: {}'.format(node, currstat))
|
||||
elif querytypes:
|
||||
show_update_types(session)
|
||||
elif upfile is None:
|
||||
show_firmware(session)
|
||||
else:
|
||||
|
||||
@@ -3,7 +3,7 @@ nodefirmware(8) -- Report firmware information on confluent nodes
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodefirmware <noderange> [list][updatestatus][update [--backup] [--parameterfile <file>] <file>]|[<components>]`
|
||||
`nodefirmware <noderange> [list][updatestatus][updatetypes][update [--backup] [--parameterfile <file>] <file>]|[<components>]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -20,6 +20,10 @@ the FPGA version where applicable).
|
||||
The updatestatus argument will describe the state of firmware updates on the
|
||||
nodes.
|
||||
|
||||
The updatetypes argument will name the kinds of firmware image the platform has
|
||||
to be told about, for use in a parameter file. Platforms that read the kind of
|
||||
firmware from the image itself have none to name and say so.
|
||||
|
||||
In the update form, it accepts a single file and attempts to update it using
|
||||
the out of band facilities. Firmware updates can end in one of three states:
|
||||
|
||||
@@ -57,13 +61,21 @@ holds, and refuse the update rather than guess. On an AMI MegaRAC that is an
|
||||
|
||||
{"OemParameters": {"ImageType": "BIOS"}}
|
||||
|
||||
Attempting the update without it names the types the platform accepts in the
|
||||
error, so there is no need to look them up first. The value is checked against
|
||||
the same list before anything is uploaded, so a name the platform does not accept
|
||||
is refused rather than sent.
|
||||
`nodefirmware <noderange> updatetypes` names the accepted types without
|
||||
attempting an update. The value is also checked against the same list before
|
||||
anything is uploaded, so a name the platform does not accept is refused rather
|
||||
than sent.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Ask what kinds of firmware image a node has to be told about:
|
||||
`# nodefirmware n1 updatetypes`
|
||||
`n1: BMC,BIOS,MB_CPLD,SCM_CPLD,BPB_CPLD,HPM_BMC,HPM_BIOS,HPM_SCP,HPM_BIOS2`
|
||||
|
||||
* Update the BIOS on a platform that has to be told:
|
||||
`# echo '{"OemParameters": {"ImageType": "BIOS"}}' > /tmp/bios.json`
|
||||
`# nodefirmware n1 update -p /tmp/bios.json /tmp/image.bin`
|
||||
|
||||
* Pull firmware from a node:
|
||||
`# nodefirmware r1`
|
||||
`r1: IMM: 3.70 (TCOO26H 2016-11-29T05:09:51)`
|
||||
|
||||
@@ -1811,6 +1811,15 @@ class Command(object):
|
||||
oem = await self.oem()
|
||||
return await oem.get_update_status()
|
||||
|
||||
async def get_update_types(self):
|
||||
"""The kinds of firmware image this platform has to be told about
|
||||
|
||||
Only some implementations will not take an image without being told
|
||||
which kind it is, and those are the ones with an answer here.
|
||||
"""
|
||||
oem = await self.oem()
|
||||
return await oem.get_update_types(self)
|
||||
|
||||
async def update_firmware(self, file, data=None, progress=None, bank=None, otherfields=()):
|
||||
"""Send file to BMC to perform firmware update
|
||||
|
||||
|
||||
@@ -108,14 +108,32 @@ class OEMHandler(generic.OEMHandler):
|
||||
'PreserveConfiguration': preserve,
|
||||
}}}, method='PATCH', etag='*')
|
||||
|
||||
# What the parameter naming the kind of firmware is called in the action
|
||||
# info. Builds differ, and the names it takes are the same vocabulary as
|
||||
# the OemParameters ImageType a multipart push wants.
|
||||
_imagetypeparams = ('ImageType', 'UpdateComponent')
|
||||
|
||||
async def _allowed_imagetypes(self):
|
||||
actinfo = await self._do_web_request(
|
||||
'/redfish/v1/UpdateService/SimpleUpdateActionInfo')
|
||||
try:
|
||||
actinfo = await self._do_web_request(
|
||||
'/redfish/v1/UpdateService/SimpleUpdateActionInfo')
|
||||
except pygexc.PyghmiException:
|
||||
# A build that does not publish the action info still wants an
|
||||
# image type, it just cannot say which names it takes
|
||||
return []
|
||||
for param in actinfo.get('Parameters', []):
|
||||
if param.get('Name', None) == 'ImageType':
|
||||
if param.get('Name', None) in self._imagetypeparams:
|
||||
return param.get('AllowableValues', [])
|
||||
return []
|
||||
|
||||
async def get_update_types(self, fishclient):
|
||||
allowed = await self._allowed_imagetypes()
|
||||
if not allowed:
|
||||
raise pygexc.UnsupportedFunctionality(
|
||||
'This bmc has to be told what kind of firmware an image holds '
|
||||
'but does not publish the names it accepts')
|
||||
return allowed
|
||||
|
||||
async def _checked_imagetype(self, imagetype, filename):
|
||||
"""Check the image type the parameter file gave against what is accepted.
|
||||
|
||||
|
||||
@@ -1525,6 +1525,16 @@ class OEMHandler(object):
|
||||
msgs = response.get('Messages', [])
|
||||
return ';'.join(self.format_message(x) for x in msgs)
|
||||
|
||||
async def get_update_types(self, fishclient):
|
||||
"""The kinds of firmware image this platform has to be told about.
|
||||
|
||||
A platform that reads the kind from the image has none to report, which
|
||||
is not the same as failing to answer.
|
||||
"""
|
||||
raise exc.UnsupportedFunctionality(
|
||||
'This platform does not take a firmware image type, it reads the '
|
||||
'kind of firmware from the image')
|
||||
|
||||
async def update_firmware(self, filename, data=None, progress=None, bank=None, otherfields=()):
|
||||
# disable cache to make sure we trigger the token renewal logic if needed
|
||||
usd, upurl, ismultipart = await self.retrieve_firmware_upload_url()
|
||||
|
||||
@@ -557,6 +557,10 @@ def _init_core():
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'null',
|
||||
}),
|
||||
'updatetypes': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'null',
|
||||
}),
|
||||
'updates': {
|
||||
'active': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
|
||||
@@ -604,6 +604,10 @@ class IpmiHandler:
|
||||
await self.handle_update()
|
||||
elif self.element[:3] == ['inventory', 'firmware', 'updatestatus']:
|
||||
await self.handle_update_status()
|
||||
elif self.element[:3] == ['inventory', 'firmware', 'updatetypes']:
|
||||
# Otherwise this falls through to the inventory handler and is read
|
||||
# as the name of a firmware component
|
||||
raise self._unsupported()
|
||||
elif self.element[0] == 'inventory':
|
||||
await self.handle_inventory()
|
||||
elif self.element == ['media', 'attach']:
|
||||
|
||||
@@ -477,6 +477,8 @@ class IpmiHandler:
|
||||
await self.handle_update()
|
||||
elif self.element[:3] == ['inventory', 'firmware', 'updatestatus']:
|
||||
await self.handle_update_status()
|
||||
elif self.element[:3] == ['inventory', 'firmware', 'updatetypes']:
|
||||
await self.handle_update_types()
|
||||
elif self.element[0] == 'inventory':
|
||||
await self.handle_inventory()
|
||||
elif self.element == ['media', 'attach']:
|
||||
@@ -923,6 +925,10 @@ class IpmiHandler:
|
||||
status = await self.ipmicmd.get_update_status()
|
||||
await self.output.put(msg.KeyValueData({'status': status}, self.node))
|
||||
|
||||
async def handle_update_types(self):
|
||||
await self.output.put(msg.KeyValueData(
|
||||
{'types': await self.ipmicmd.get_update_types()}, self.node))
|
||||
|
||||
async def handle_inventory(self):
|
||||
if self.element[1] == 'firmware':
|
||||
if len(self.element) == 3:
|
||||
|
||||
Reference in New Issue
Block a user