Parse error object (in json format) returned by heat-api
With this fix, heatclient will display a clear error message when encounter an server-side error. Additionally, the corresponding traceback will be displayed when "--verbose" is set. Change-Id: I99b828465f61478a3c63fcf549d044a62523be1f
This commit is contained in:
parent
a49cf4c148
commit
2b83260feb
@ -12,6 +12,13 @@
|
||||
|
||||
import sys
|
||||
|
||||
verbose = 0
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
|
||||
class BaseException(Exception):
|
||||
"""An error occurred."""
|
||||
@ -38,6 +45,30 @@ class HTTPException(BaseException):
|
||||
"""Base exception for all HTTP-derived exceptions."""
|
||||
code = 'N/A'
|
||||
|
||||
def __init__(self, message=None):
|
||||
super(HTTPException, self).__init__(message)
|
||||
try:
|
||||
self.error = json.loads(message)
|
||||
if 'error' not in self.error:
|
||||
raise KeyError('Key "error" not exists')
|
||||
except KeyError:
|
||||
# NOTE(jianingy): If key 'error' happens not exist,
|
||||
# self.message becomes no sense. In this case, we
|
||||
# return doc of current exception class instead.
|
||||
self.error = {'error':
|
||||
{'message': self.__class__.__doc__}}
|
||||
except Exception:
|
||||
self.error = {'error':
|
||||
{'message': self.message or self.__class__.__doc__}}
|
||||
|
||||
def __str__(self):
|
||||
message = self.error['error'].get('message', 'Internal Error')
|
||||
if verbose:
|
||||
traceback = self.error['error'].get('traceback', '')
|
||||
return 'ERROR: %s\n%s' % (message, traceback)
|
||||
else:
|
||||
return 'ERROR: %s' % message
|
||||
|
||||
|
||||
class HTTPMultipleChoices(HTTPException):
|
||||
code = 300
|
||||
|
@ -236,11 +236,16 @@ class HeatShell(object):
|
||||
|
||||
httplib2.debuglevel = 1
|
||||
|
||||
def _setup_verbose(self, verbose):
|
||||
if verbose:
|
||||
exc.verbose = 1
|
||||
|
||||
def main(self, argv):
|
||||
# Parse args once to find version
|
||||
parser = self.get_base_parser()
|
||||
(options, args) = parser.parse_known_args(argv)
|
||||
self._setup_debugging(options.debug)
|
||||
self._setup_verbose(options.verbose)
|
||||
|
||||
# build available subcommands based on version
|
||||
api_version = options.heat_api_version
|
||||
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
|
||||
from heatclient import exc
|
||||
from heatclient.v1 import client as v1client
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
|
||||
@ -35,6 +36,34 @@ def script_heat_list():
|
||||
resp_dict))
|
||||
|
||||
|
||||
def script_heat_normal_error():
|
||||
resp_dict = {
|
||||
"explanation": "The resource could not be found.",
|
||||
"code": 404,
|
||||
"error": {
|
||||
"message": "The Stack (bad) could not be found.",
|
||||
"type": "StackNotFound",
|
||||
"traceback": "",
|
||||
},
|
||||
"title": "Not Found"
|
||||
}
|
||||
resp = FakeHTTPResponse(400,
|
||||
'The resource could not be found',
|
||||
{'content-type': 'application/json'},
|
||||
json.dumps(resp_dict))
|
||||
v1client.Client.json_request('GET', '/stacks/bad').AndRaise(
|
||||
exc.from_response(resp, json.dumps(resp_dict)))
|
||||
|
||||
|
||||
def script_heat_error(resp_string):
|
||||
resp = FakeHTTPResponse(400,
|
||||
'The resource could not be found',
|
||||
{'content-type': 'application/json'},
|
||||
resp_string)
|
||||
v1client.Client.json_request('GET', '/stacks/bad').AndRaise(
|
||||
exc.from_response(resp, resp_string))
|
||||
|
||||
|
||||
def fake_headers():
|
||||
return {'X-Auth-Token': 'abcd1234',
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -272,6 +272,108 @@ class ShellTest(TestCase):
|
||||
for r in required:
|
||||
self.assertRegexpMatches(list_text, r)
|
||||
|
||||
def test_parsable_error(self):
|
||||
message = "The Stack (bad) could not be found."
|
||||
resp_dict = {
|
||||
"explanation": "The resource could not be found.",
|
||||
"code": 404,
|
||||
"error": {
|
||||
"message": message,
|
||||
"type": "StackNotFound",
|
||||
"traceback": "",
|
||||
},
|
||||
"title": "Not Found"
|
||||
}
|
||||
|
||||
fakes.script_keystone_client()
|
||||
fakes.script_heat_error(json.dumps(resp_dict))
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
try:
|
||||
self.shell("stack-show bad")
|
||||
except exc.HTTPException as e:
|
||||
self.assertEqual(str(e), "ERROR: " + message)
|
||||
|
||||
def test_parsable_verbose(self):
|
||||
message = "The Stack (bad) could not be found."
|
||||
resp_dict = {
|
||||
"explanation": "The resource could not be found.",
|
||||
"code": 404,
|
||||
"error": {
|
||||
"message": message,
|
||||
"type": "StackNotFound",
|
||||
"traceback": "<TRACEBACK>",
|
||||
},
|
||||
"title": "Not Found"
|
||||
}
|
||||
|
||||
fakes.script_keystone_client()
|
||||
fakes.script_heat_error(json.dumps(resp_dict))
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
try:
|
||||
exc.verbose = 1
|
||||
self.shell("stack-show bad")
|
||||
except exc.HTTPException as e:
|
||||
expect = 'ERROR: The Stack (bad) could not be found.\n<TRACEBACK>'
|
||||
self.assertEqual(str(e), expect)
|
||||
|
||||
def test_parsable_malformed_error(self):
|
||||
invalid_json = "ERROR: {Invalid JSON Error."
|
||||
fakes.script_keystone_client()
|
||||
fakes.script_heat_error(invalid_json)
|
||||
self.m.ReplayAll()
|
||||
|
||||
try:
|
||||
self.shell("stack-show bad")
|
||||
except exc.HTTPException as e:
|
||||
self.assertEqual(str(e), "ERROR: " + invalid_json)
|
||||
|
||||
def test_parsable_malformed_error_missing_message(self):
|
||||
missing_message = {
|
||||
"explanation": "The resource could not be found.",
|
||||
"code": 404,
|
||||
"error": {
|
||||
"type": "StackNotFound",
|
||||
"traceback": "",
|
||||
},
|
||||
"title": "Not Found"
|
||||
}
|
||||
|
||||
fakes.script_keystone_client()
|
||||
fakes.script_heat_error(json.dumps(missing_message))
|
||||
self.m.ReplayAll()
|
||||
|
||||
try:
|
||||
self.shell("stack-show bad")
|
||||
except exc.HTTPException as e:
|
||||
self.assertEqual(str(e), "ERROR: Internal Error")
|
||||
|
||||
def test_parsable_malformed_error_missing_traceback(self):
|
||||
message = "The Stack (bad) could not be found."
|
||||
resp_dict = {
|
||||
"explanation": "The resource could not be found.",
|
||||
"code": 404,
|
||||
"error": {
|
||||
"message": message,
|
||||
"type": "StackNotFound",
|
||||
},
|
||||
"title": "Not Found"
|
||||
}
|
||||
|
||||
fakes.script_keystone_client()
|
||||
fakes.script_heat_error(json.dumps(resp_dict))
|
||||
self.m.ReplayAll()
|
||||
|
||||
try:
|
||||
exc.verbose = 1
|
||||
self.shell("stack-show bad")
|
||||
except exc.HTTPException as e:
|
||||
self.assertEqual(str(e),
|
||||
"ERROR: The Stack (bad) could not be found.\n")
|
||||
|
||||
def test_describe(self):
|
||||
fakes.script_keystone_client()
|
||||
resp_dict = {"stack": {
|
||||
|
Loading…
Reference in New Issue
Block a user