improved error handling on server-side

This commit is contained in:
Devaev Maxim 2018-07-13 11:40:20 +00:00
parent 5664beda1b
commit 74fdc7464b
2 changed files with 24 additions and 16 deletions

View File

@ -23,27 +23,31 @@ class MassStorageError(Exception):
pass pass
class IsNotOperationalError(MassStorageError): class MassStorageOperationError(MassStorageError):
pass
class IsNotOperationalError(MassStorageOperationError):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("Missing path for mass-storage device") super().__init__("Missing path for mass-storage device")
class AlreadyConnectedToPcError(MassStorageError): class AlreadyConnectedToPcError(MassStorageOperationError):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("Mass-storage is already connected to Server") super().__init__("Mass-storage is already connected to Server")
class AlreadyConnectedToKvmError(MassStorageError): class AlreadyConnectedToKvmError(MassStorageOperationError):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("Mass-storage is already connected to KVM") super().__init__("Mass-storage is already connected to KVM")
class IsNotConnectedToKvmError(MassStorageError): class IsNotConnectedToKvmError(MassStorageOperationError):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("Mass-storage is not connected to KVM") super().__init__("Mass-storage is not connected to KVM")
class IsBusyError(MassStorageError): class IsBusyError(MassStorageOperationError):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("Mass-storage is busy (write in progress)") super().__init__("Mass-storage is busy (write in progress)")

View File

@ -17,7 +17,7 @@ from .hid import Hid
from .atx import Atx from .atx import Atx
from .msd import MassStorageError from .msd import MassStorageOperationError
from .msd import MassStorageDevice from .msd import MassStorageDevice
from .streamer import Streamer from .streamer import Streamer
@ -38,13 +38,17 @@ def _system_task(method: Callable) -> Callable:
return wrap return wrap
class _BadRequest(Exception):
pass
def _exceptions_as_400(msg: str, exceptions: List[Type[Exception]]) -> Callable: def _exceptions_as_400(msg: str, exceptions: List[Type[Exception]]) -> Callable:
def make_wrapper(method: Callable) -> Callable: def make_wrapper(method: Callable) -> Callable:
async def wrap(self: "Server", request: aiohttp.web.Request) -> aiohttp.web.Response: async def wrap(self: "Server", request: aiohttp.web.Request) -> aiohttp.web.Response:
try: try:
return (await method(self, request)) return (await method(self, request))
except tuple(exceptions) as err: # pylint: disable=catching-non-exception except tuple(exceptions) as err: # pylint: disable=catching-non-exception
get_logger().exception(msg) get_logger().error(msg)
return aiohttp.web.json_response({ return aiohttp.web.json_response({
"ok": False, "ok": False,
"result": { "result": {
@ -136,8 +140,8 @@ class Server: # pylint: disable=too-many-instance-attributes
if msg.type == aiohttp.web.WSMsgType.TEXT: if msg.type == aiohttp.web.WSMsgType.TEXT:
try: try:
event = json.loads(msg.data) event = json.loads(msg.data)
except Exception: except Exception as err:
logger.exception("Can't parse JSON event from websocket") logger.error("Can't parse JSON event from websocket: %s", err)
else: else:
if event.get("event_type") == "key": if event.get("event_type") == "key":
key = str(event.get("key", ""))[:64].strip() key = str(event.get("key", ""))[:64].strip()
@ -157,7 +161,7 @@ class Server: # pylint: disable=too-many-instance-attributes
async def __atx_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: async def __atx_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
return _json_200(self.__atx.get_state()) return _json_200(self.__atx.get_state())
@_exceptions_as_400("Click error", [RuntimeError]) @_exceptions_as_400("Click error", [_BadRequest])
async def __atx_click_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: async def __atx_click_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
button = request.query.get("button") button = request.query.get("button")
if button == "power": if button == "power":
@ -167,13 +171,13 @@ class Server: # pylint: disable=too-many-instance-attributes
elif button == "reset": elif button == "reset":
await self.__atx.click_reset() await self.__atx.click_reset()
else: else:
raise RuntimeError("Missing or invalid 'button=%s'" % (button)) raise _BadRequest("Missing or invalid 'button=%s'" % (button))
return _json_200({"clicked": button}) return _json_200({"clicked": button})
async def __msd_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: async def __msd_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
return _json_200(self.__msd.get_state()) return _json_200(self.__msd.get_state())
@_exceptions_as_400("Mass-storage error", [MassStorageError, RuntimeError]) @_exceptions_as_400("Mass-storage error", [MassStorageOperationError, _BadRequest])
async def __msd_connect_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: async def __msd_connect_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
to = request.query.get("to") to = request.query.get("to")
if to == "kvm": if to == "kvm":
@ -183,10 +187,10 @@ class Server: # pylint: disable=too-many-instance-attributes
await self.__msd.connect_to_pc() await self.__msd.connect_to_pc()
await self.__broadcast_event("msd_state", state="connected_to_server") # type: ignore await self.__broadcast_event("msd_state", state="connected_to_server") # type: ignore
else: else:
raise RuntimeError("Missing or invalid 'to=%s'" % (to)) raise _BadRequest("Missing or invalid 'to=%s'" % (to))
return _json_200(self.__msd.get_state()) return _json_200(self.__msd.get_state())
@_exceptions_as_400("Can't write data to mass-storage device", [MassStorageError, RuntimeError, OSError]) @_exceptions_as_400("Can't write data to mass-storage device", [MassStorageOperationError, _BadRequest])
async def __msd_write_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response: async def __msd_write_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
logger = get_logger(0) logger = get_logger(0)
reader = await request.multipart() reader = await request.multipart()
@ -194,12 +198,12 @@ class Server: # pylint: disable=too-many-instance-attributes
try: try:
field = await reader.next() field = await reader.next()
if not field or field.name != "image_name": if not field or field.name != "image_name":
raise RuntimeError("Missing 'image_name' field") raise _BadRequest("Missing 'image_name' field")
image_name = (await field.read()).decode("utf-8")[:256] image_name = (await field.read()).decode("utf-8")[:256]
field = await reader.next() field = await reader.next()
if not field or field.name != "image_data": if not field or field.name != "image_data":
raise RuntimeError("Missing 'image_data' field") raise _BadRequest("Missing 'image_data' field")
async with self.__msd: async with self.__msd:
await self.__broadcast_event("msd_state", state="busy") # type: ignore await self.__broadcast_event("msd_state", state="busy") # type: ignore