diff --git a/ironic/drivers/modules/agent_client.py b/ironic/drivers/modules/agent_client.py index 4c17403b96..feb4ebc88d 100644 --- a/ironic/drivers/modules/agent_client.py +++ b/ironic/drivers/modules/agent_client.py @@ -127,11 +127,18 @@ class AgentClient(object): LOG.error(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 ' 'result %(res)s, error %(error)s, HTTP status code %(code)d', {'node': node.uuid, 'method': method, 'res': result.get('command_result'), - 'error': result.get('command_error'), + 'error': error, 'code': response.status_code}) if response.status_code >= http_client.BAD_REQUEST: @@ -142,6 +149,14 @@ class AgentClient(object): raise exception.AgentAPIError(node=node.uuid, status=response.status_code, 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 diff --git a/ironic/tests/unit/drivers/modules/test_agent_client.py b/ironic/tests/unit/drivers/modules/test_agent_client.py index 65bb58172d..1bea741880 100644 --- a/ironic/tests/unit/drivers/modules/test_agent_client.py +++ b/ironic/tests/unit/drivers/modules/test_agent_client.py @@ -178,6 +178,27 @@ class TestAgentClient(base.TestCase): params={'wait': 'false'}, 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): with mock.patch.object(self.client.session, 'get', autospec=True) as mock_get: diff --git a/releasenotes/notes/consider_embedded_ipa_error_codes-c8fdfaa9e6a1ed06.yaml b/releasenotes/notes/consider_embedded_ipa_error_codes-c8fdfaa9e6a1ed06.yaml new file mode 100644 index 0000000000..0f4ee2d512 --- /dev/null +++ b/releasenotes/notes/consider_embedded_ipa_error_codes-c8fdfaa9e6a1ed06.yaml @@ -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.