diff --git a/confluent_server/aiohmi/redfish/command.py b/confluent_server/aiohmi/redfish/command.py index 7d1eb8b1..a9a32f8e 100644 --- a/confluent_server/aiohmi/redfish/command.py +++ b/confluent_server/aiohmi/redfish/command.py @@ -1268,10 +1268,23 @@ class Command(object): return fwlist = await self._do_web_request(await self.get_fwinventory()) fwurls = [x['@odata.id'] for x in fwlist.get('Members', [])] + wantcategory = category if category not in (None, 'all') else None + entries = [] async for res in self._do_bulk_requests(fwurls): - res = self._extract_fwinfo(res) + entries.append((self._fwcategory(res[0]), self._extract_fwinfo(res))) + categorised = any(x[0] for x in entries) + for fwcategory, res in entries: if res[0] is None: continue + if wantcategory: + if categorised: + if fwcategory != wantcategory: + continue + elif wantcategory != 'core': + # This platform does not say what any of its firmware + # belongs to, and an uncategorised inventory on a bmc is + # system firmware, so it can only answer for 'core' + continue yield res def _extract_fwinfo(self, inf): @@ -1577,32 +1590,48 @@ class Command(object): if 'Authorization' in self.wc.stdheaders: del self.wc.stdheaders['Authorization'] return + attached = False for vmurl in vmurls: vminfo = await self._do_web_request(vmurl, cache=False) - if vminfo.get('ConnectedVia', None) != 'NotConnected': + # ConnectedVia describes how the device is wired to the host rather + # than whether anything is in it, and some implementations report a + # permanent value there, so judge free by whether an image is loaded + if vminfo.get('Image', None) or vminfo.get('Inserted', False): continue inserturl = vminfo.get( 'Actions', {}).get( '#VirtualMedia.InsertMedia', {}).get('target', None) if inserturl: - await self._do_web_request(inserturl, {'Image': url}) - else: + try: + await self._do_web_request(inserturl, {'Image': url}) + attached = True + except (exc.RedfishError, exc.PyghmiException): + # Some implementations advertise the insert action without + # serving it, so fall back to setting the properties + inserturl = None + if not inserturl: try: await self._do_web_request(vmurl, {'Image': url, 'Inserted': True}, - 'PATCH') + 'PATCH', etag='*') except exc.RedfishError as re: if re.msgid.endswith(u'PropertyUnknown'): - await self._do_web_request(vmurl, {'Image': url}, 'PATCH') + await self._do_web_request(vmurl, {'Image': url}, 'PATCH', + etag='*') else: raise + attached = True break if suspendedxauth: self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken if 'Authorization' in self.wc.stdheaders: - del self.wc.stdheaders['Authorization'] + del self.wc.stdheaders['Authorization'] + if not attached: + raise exc.UnsupportedFunctionality( + 'No virtual media device on this platform was able to accept ' + 'the image') async for res in oem.list_media(self, cache=False): - pass + pass async def detach_remote_media(self): oem = await self.oem() diff --git a/confluent_server/aiohmi/redfish/oem/ami/megarac.py b/confluent_server/aiohmi/redfish/oem/ami/megarac.py index 7c69fdc8..a75c09d2 100644 --- a/confluent_server/aiohmi/redfish/oem/ami/megarac.py +++ b/confluent_server/aiohmi/redfish/oem/ami/megarac.py @@ -38,14 +38,25 @@ class OEMHandler(generic.OEMHandler): self._certverify = webclient.verifycallback return self + # Auxiliary power cycle, offered by the chassis on the megarac based systems + # that have an aux power domain to cycle + _auxresetaction = '#NvidiaChassis.AuxPowerReset' + async def reseat_bay(self, bay): if bay != -1: raise pygexc.UnsupportedFunctionality( 'This is not an enclosure manager') - - await self._do_web_request('/redfish/v1/Chassis/Chassis_0/Actions/Oem/NvidiaChassis.AuxPowerReset', { - "ResetType": "AuxPowerCycle" - }) + chassiscol = await self._do_web_request('/redfish/v1/Chassis') + for chassis in chassiscol.get('Members', []): + chassisinfo = await self._do_web_request(chassis['@odata.id']) + action = chassisinfo.get('Actions', {}).get('Oem', {}).get( + self._auxresetaction, {}).get('target', None) + if action: + await self._do_web_request(action, + {'ResetType': 'AuxPowerCycle'}) + return + raise pygexc.UnsupportedFunctionality( + 'Reseat is not supported on this platform') def format_messages(self, response): msgs = response.get('Messages', []) diff --git a/confluent_server/aiohmi/redfish/oem/generic.py b/confluent_server/aiohmi/redfish/oem/generic.py index ef5d4072..6564b1a4 100644 --- a/confluent_server/aiohmi/redfish/oem/generic.py +++ b/confluent_server/aiohmi/redfish/oem/generic.py @@ -713,14 +713,33 @@ class OEMHandler(object): # Blanking the username seems to be the convention # First, set a bogus password in case the implementation does honor # blank user, at least render such an account harmless + accinfo = await fishclient._account_url_info_by_id(uid) + if not accinfo: + raise Exception("No such account found") + accounturl = accinfo[0] + delerr = None + # Some implementations take longer to delete an account than they allow + # themselves, and report a timeout for a delete that is really underway + # or that would work on a second ask, so give the delete another go and + # then check whether the account is actually gone before concluding that + # this implementation cannot delete at all + for _ in range(3): + try: + await self._do_web_request(accounturl, method='DELETE') + return True + except Exception as de: + if delerr is None: + delerr = de + await asyncio.sleep(3) + if not await fishclient._account_url_info_by_id(uid): + return True try: - accinfo = await fishclient._account_url_info_by_id(uid) - if not accinfo: - raise Exception("No such account found") - await self._do_web_request(accinfo[0], method='DELETE') - except Exception: # fall back to old ipmi-like behavior for such implementations await fishclient.set_user_password(uid, base64.b64encode(os.urandom(15))) - await fishclient.set_user_name(uid, '') + await fishclient.set_user_name(uid, '') + except Exception: + # Report why deleting failed rather than why the fallback failed, + # since the delete is what was asked for + raise delerr return True async def set_bootdev(self, bootdev, persist=False, uefiboot=None,