diff --git a/shade/exc.py b/shade/exc.py index 1ccc437de..0091f838c 100644 --- a/shade/exc.py +++ b/shade/exc.py @@ -13,6 +13,7 @@ # limitations under the License. import sys +import json import munch from requests import exceptions as _rex @@ -140,6 +141,13 @@ def raise_from_response(response, error_message=None): except AttributeError: if response.reason: remote_error += " {reason}".format(reason=response.reason) + try: + json_resp = json.loads(details[detail_key]) + fault_string = json_resp.get('faultstring') + if fault_string: + remote_error += " {fault}".format(fault=fault_string) + except Exception: + pass _log_response_extras(response) diff --git a/shade/tests/unit/test_baremetal_node.py b/shade/tests/unit/test_baremetal_node.py index 9a8795745..df75f5c39 100644 --- a/shade/tests/unit/test_baremetal_node.py +++ b/shade/tests/unit/test_baremetal_node.py @@ -858,6 +858,46 @@ class TestBaremetalNode(base.IronicTestCase): self.assertEqual(available_node, return_value) self.assert_calls() + def test_node_set_provision_state_bad_request(self): + self.fake_baremetal_node['provision_state'] = 'enroll' + self.register_uris([ + dict( + method='PUT', + status_code=400, + json={'error': "{\"faultstring\": \"invalid state\"}"}, + uri=self.get_mock_url( + resource='nodes', + append=[self.fake_baremetal_node['uuid'], + 'states', 'provision'])), + ]) + self.assertRaisesRegexp( + exc.OpenStackCloudException, + '^Baremetal .* to dummy.*/states/provision invalid state$', + self.op_cloud.node_set_provision_state, + self.fake_baremetal_node['uuid'], + 'dummy') + self.assert_calls() + + def test_node_set_provision_state_bad_request_bad_json(self): + self.fake_baremetal_node['provision_state'] = 'enroll' + self.register_uris([ + dict( + method='PUT', + status_code=400, + json={'error': 'invalid json'}, + uri=self.get_mock_url( + resource='nodes', + append=[self.fake_baremetal_node['uuid'], + 'states', 'provision'])), + ]) + self.assertRaisesRegexp( + exc.OpenStackCloudException, + '^Baremetal .* to dummy.*/states/provision$', + self.op_cloud.node_set_provision_state, + self.fake_baremetal_node['uuid'], + 'dummy') + self.assert_calls() + def test_wait_for_baremetal_node_lock_locked(self): self.fake_baremetal_node['reservation'] = 'conductor0' unlocked_node = self.fake_baremetal_node.copy()