Fix agent_client handling of embedded errors

The agent_client may get a general "command_error" field
returned to it upon commands and the agent may not properly
sinal that the command failed because we are reliant upon
the same data elsewhere in the ironic/agent interaction.

This resulted in the case where the embedded error signaled that
the error was method compatability as opposed to the actual method
command error.

This could cause higher level failures and prevent fallback logic
from detecting that we could try a different command.

We now consider the error type, and raise the appropriate exception
to signal that the issue may be an API compatability issue.

Change-Id: Ia2f63bd853632e1d7138901cf23fde1e261fc4d6
This commit is contained in:
Julia Kreger 2020-04-22 08:42:29 -07:00
parent b493191371
commit bfeef067aa
3 changed files with 46 additions and 1 deletions

View File

@ -127,11 +127,18 @@ class AgentClient(object):
LOG.error(msg) LOG.error(msg)
raise exception.IronicException(msg) raise exception.IronicException(msg)
error = result.get('command_error')
exc_type = None
if error:
# if an error, we should see if a type field exists. This type
# field may signal an exception that is compatability based.
exc_type = error.get('type')
LOG.debug('Agent command %(method)s for node %(node)s returned ' LOG.debug('Agent command %(method)s for node %(node)s returned '
'result %(res)s, error %(error)s, HTTP status code %(code)d', 'result %(res)s, error %(error)s, HTTP status code %(code)d',
{'node': node.uuid, 'method': method, {'node': node.uuid, 'method': method,
'res': result.get('command_result'), 'res': result.get('command_result'),
'error': result.get('command_error'), 'error': error,
'code': response.status_code}) 'code': response.status_code})
if response.status_code >= http_client.BAD_REQUEST: if response.status_code >= http_client.BAD_REQUEST:
@ -142,6 +149,14 @@ class AgentClient(object):
raise exception.AgentAPIError(node=node.uuid, raise exception.AgentAPIError(node=node.uuid,
status=response.status_code, status=response.status_code,
error=result.get('faultstring')) error=result.get('faultstring'))
if exc_type == 'TypeError':
LOG.error('Agent command %(method)s for node %(node)s failed. '
'Internal %(exc_type)s error detected: Error %(error)s',
{'method': method, 'node': node.uuid,
'exc_type': exc_type, 'error': error})
raise exception.AgentAPIError(node=node.uuid,
status=error.get('code'),
error=result.get('faultstring'))
return result return result

View File

@ -178,6 +178,27 @@ class TestAgentClient(base.TestCase):
params={'wait': 'false'}, params={'wait': 'false'},
timeout=60) timeout=60)
def test__command_error_code_okay_error_typeerror_embedded(self):
response_text = ('{"faultstring": "you dun goofd", '
'"command_error": {"type": "TypeError"}}')
self.client.session.post.return_value = MockResponse(
response_text)
method = 'standby.run_image'
image_info = {'image_id': 'test_image'}
params = {'image_info': image_info}
url = self.client._get_command_url(self.node)
body = self.client._get_command_body(method, params)
self.assertRaises(exception.AgentAPIError,
self.client._command,
self.node, method, params)
self.client.session.post.assert_called_once_with(
url,
data=body,
params={'wait': 'false'},
timeout=60)
def test_get_commands_status(self): def test_get_commands_status(self):
with mock.patch.object(self.client.session, 'get', with mock.patch.object(self.client.session, 'get',
autospec=True) as mock_get: autospec=True) as mock_get:

View File

@ -0,0 +1,9 @@
---
fixes:
- |
Fixes an issue in the ``ironic-python-agent`` client code
where a command exception may not be captured in the interaction
with the agent rest API. The client code would return the resulting
error message and a static error code. We now look with-in the error
to detect if the error may be a compatability error to raise the
appropriate exception for fallback logic to engage.