Commit 1f8588f6 authored by Iustin Pop's avatar Iustin Pop
Browse files

RAPI: format error messages as JSON

This patch changes the format of the HTTP error messages from text/html, which
is hard to parse from RAPI clients, to JSON which can be automatically parsed.

The error message is an object, which contains always three keys:
  - code, an integer with the error code
  - message, a short description
  - explain, holding (if available) a description of the error

In order to implement this, there is a bit of change to the http server
and executor classes. I've tested and the error handling still works
(but less optimal, no error message) in case the error formatting itself
raises an exception.

Reviewed-by: imsnah
parent 77e1d753
......@@ -36,6 +36,7 @@ from ganeti import daemon
from ganeti import ssconf
from ganeti import utils
from ganeti import luxi
from ganeti import serializer
from ganeti.rapi import connector
import ganeti.http.auth
......@@ -52,6 +53,24 @@ class RemoteApiRequestContext(object):
self.handler_access = None
class JsonErrorRequestExecutor(http.server.HttpServerRequestExecutor):
"""Custom Request Executor class that formats HTTP errors in JSON.
"""
error_content_type = "application/json"
def _FormatErrorMessage(self, values):
"""Formats the body of an error message.
@type values: dict
@param values: dictionary with keys code, message and explain.
@rtype: string
@return: the body of the message
"""
return serializer.DumpJson(values, indent=True)
class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
http.server.HttpServer):
"""REST Request Handler Class.
......@@ -233,7 +252,9 @@ def main():
try:
mainloop = daemon.Mainloop()
server = RemoteApiHttpServer(mainloop, "", options.port,
ssl_params=ssl_params, ssl_verify_peer=False)
ssl_params=ssl_params, ssl_verify_peer=False,
request_executor_class=
JsonErrorRequestExecutor)
server.Start()
try:
mainloop.Run()
......
......@@ -207,7 +207,7 @@ class _HttpClientToServerMessageReader(http.HttpMessageReader):
return http.HttpClientToServerStartLine(method, path, version)
class _HttpServerRequestExecutor(object):
class HttpServerRequestExecutor(object):
"""Implements server side of HTTP.
This class implements the server side of HTTP. It's based on code of
......@@ -405,8 +405,18 @@ class _HttpServerRequestExecutor(object):
headers[http.HTTP_CONTENT_TYPE] = self.error_content_type
self.response_msg.headers = headers
self.response_msg.body = self.error_message_format % values
self.response_msg.body = self._FormatErrorMessage(values)
def _FormatErrorMessage(self, values):
"""Formats the body of an error message.
@type values: dict
@param values: dictionary with keys code, message and explain.
@rtype: string
@return: the body of the message
"""
return self.error_message_format % values
class HttpServer(http.HttpBase):
"""Generic HTTP server class
......@@ -417,7 +427,8 @@ class HttpServer(http.HttpBase):
MAX_CHILDREN = 20
def __init__(self, mainloop, local_address, port,
ssl_params=None, ssl_verify_peer=False):
ssl_params=None, ssl_verify_peer=False,
request_executor_class=None):
"""Initializes the HTTP server
@type mainloop: ganeti.daemon.Mainloop
......@@ -431,10 +442,18 @@ class HttpServer(http.HttpBase):
@type ssl_verify_peer: bool
@param ssl_verify_peer: Whether to require client certificate
and compare it with our certificate
@type request_executor_class: class
@param request_executor_class: an class derived from the
HttpServerRequestExecutor class
"""
http.HttpBase.__init__(self)
if request_executor_class is None:
self.request_executor = HttpServerRequestExecutor
else:
self.request_executor = request_executor_class
self.mainloop = mainloop
self.local_address = local_address
self.port = port
......@@ -505,7 +524,7 @@ class HttpServer(http.HttpBase):
if pid == 0:
# Child process
try:
_HttpServerRequestExecutor(self, connection, client_addr)
self.request_executor(self, connection, client_addr)
except Exception:
logging.exception("Error while handling request from %s:%s",
client_addr[0], client_addr[1])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment