2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-05 20:47:57 +00:00

Report an error that carries no message of its own

nodereseat printed "Error: " and nothing else against a bmc that refused the
credentials. The redfish plugin reports it properly, but the message it emits
re-raises TargetEndpointBadCredentials with no arguments when a single node is
addressed, and the enclosure plugin renders that with str(e), which is empty.

Both hardwaremanagement plugins already had a helper for exactly this, one copy
each. Keep one in confluent.exceptions instead, teach it to fall back to the
description a confluent exception carries by class before falling back to the
exception name, and use it in the enclosure plugin too.

get_error_body had the mirror image of the same bug, joining the class
description and the message unconditionally and so answering "Bad Credentials -"
with a separator and nothing after it. The apierrorstr property beside it
already gets this right, so use it.
This commit is contained in:
Markus Hilger
2026-08-13 23:50:32 +02:00
parent 7724a18c43
commit f6ba3802bc
4 changed files with 19 additions and 29 deletions
+14 -2
View File
@@ -19,6 +19,19 @@ import base64
import json
import msgpack
def exc_text(theexc):
"""Render an exception for a user, even when it carries no message.
An error with no text at all tells the user nothing, so fall back to the
description a confluent exception carries by class, and then to the name of
the exception.
"""
text = str(theexc)
if text and text != 'None':
return text
return getattr(theexc, '_apierrorstr', None) or type(theexc).__name__
def deserialize_exc(msg):
excd = msgpack.unpackb(msg, raw=False)
if excd[0] == 'Exception':
@@ -35,8 +48,7 @@ class ConfluentException(Exception):
_apierrorstr = 'Unexpected Error'
def get_error_body(self):
errstr = ' - '.join((self._apierrorstr, str(self)))
return json.dumps({'error': errstr })
return json.dumps({'error': self.apierrorstr})
def serialize(self):
return msgpack.packb([self.__class__.__name__, [str(self)]],
@@ -29,11 +29,11 @@ async def reseat_bays(encmgr, bays, configmanager, rspq):
inputdata={'reseat': encbay}):
await rspq.put(rsp)
except pygexc.UnsupportedFunctionality as uf:
await rspq.put(msg.ConfluentNodeError(node, str(uf)))
await rspq.put(msg.ConfluentNodeError(node, exc.exc_text(uf)))
except exc.TargetEndpointUnreachable as uf:
await rspq.put(msg.ConfluentNodeError(node, str(uf)))
await rspq.put(msg.ConfluentNodeError(node, exc.exc_text(uf)))
except Exception as e:
await rspq.put(msg.ConfluentNodeError(node, str(e)))
await rspq.put(msg.ConfluentNodeError(node, exc.exc_text(e)))
finally:
await rspq.put(None)
@@ -15,6 +15,7 @@
import asyncio
import confluent.exceptions as exc
from confluent.exceptions import exc_text
import confluent.firmwaremanager as firmwaremanager
import confluent.interface.console as conapi
import confluent.messages as msg
@@ -284,18 +285,6 @@ def _donothing(data):
pass
def exc_text(theexc):
"""Render an exception for a user, even when it carries no message.
An error with no text at all tells the user nothing, so name the exception
when that is all we have.
"""
text = str(theexc)
if not text or text == 'None':
return type(theexc).__name__
return text
class IpmiConsole(conapi.Console):
configattributes = frozenset(_configattributes)
bmctonodemapping = {}
@@ -17,6 +17,7 @@ import json
import asyncio
import confluent.vinzmanager as vinzmanager
import confluent.exceptions as exc
from confluent.exceptions import exc_text
import confluent.firmwaremanager as firmwaremanager
import confluent.messages as msg
import confluent.util as util
@@ -272,18 +273,6 @@ def _donothing(data):
pass
def exc_text(theexc):
"""Render an exception for a user, even when it carries no message.
An error with no text at all tells the user nothing, so name the exception
when that is all we have.
"""
text = str(theexc)
if not text or text == 'None':
return type(theexc).__name__
return text
async def perform_requests(operator, nodes, element, cfg, inputdata, realop):
cryptit = cfg.decrypt
cfg.decrypt = True