mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-21 16:39:32 +00:00
Fix user deletion, media insertion, firmware categories and reseat
Deleting a user gave up after one attempt and reported why the fallback of blanking the name failed rather than why the delete did. MegaRAC reports a timeout for a delete that a second ask completes, so retry, check whether the account went away, and keep the original error. Attaching media judged a device free by ConnectedVia, which describes how the device is wired to the host rather than whether anything is in it. Every device on this bmc reports a fixed value there, so nothing was ever selected and the attach reported success having done nothing. Judge by whether an image is loaded, fall back to the properties when an advertised insert action is not served, and say so when no device would take the image. The firmware category was passed to the library and ignored, so core, adapters, disks and misc all returned the same full list. Classify by what each entry is related to, and answer only for core when a platform says nothing about where its firmware belongs. reseat_bay reached for a hardcoded Nvidia action on Chassis_0, so it failed with a not found for that url instead of saying reseat is unsupported.
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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', [])
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user