summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/services/exception.py
blob: c39209569dbd7c9d91cac57dc5936c1bce0195f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# -*- coding: utf-8 -*-

import json
import logging
from contextlib import contextmanager

import cephfs
import cherrypy
import rados
import rbd
from orchestrator import OrchestratorError

from ..exceptions import DashboardException, ViewCacheNoDataException
from ..rest_client import RequestException
from ..services.ceph_service import SendCommandError

logger = logging.getLogger('exception')


def serialize_dashboard_exception(e, include_http_status=False, task=None):
    """
    :type e: Exception
    :param include_http_status: Used for Tasks, where the HTTP status code is not available.
    """
    from ..tools import ViewCache
    if isinstance(e, ViewCacheNoDataException):
        return {'status': ViewCache.VALUE_NONE, 'value': None}

    out = dict(detail=str(e))
    try:
        out['code'] = e.code
    except AttributeError:
        pass
    component = getattr(e, 'component', None)
    out['component'] = component if component else None
    if include_http_status:
        out['status'] = getattr(e, 'status', 500)  # type: ignore
    if task:
        out['task'] = dict(name=task.name, metadata=task.metadata)  # type: ignore
    return out


# pylint: disable=broad-except
def dashboard_exception_handler(handler, *args, **kwargs):
    try:
        with handle_rados_error(component=None):  # make the None controller the fallback.
            return handler(*args, **kwargs)
    # pylint: disable=try-except-raise
    except (cherrypy.HTTPRedirect, cherrypy.NotFound, cherrypy.HTTPError):
        raise
    except (ViewCacheNoDataException, DashboardException) as error:
        logger.exception('Dashboard Exception')
        cherrypy.response.headers['Content-Type'] = 'application/json'
        cherrypy.response.status = getattr(error, 'status', 400)
        return json.dumps(serialize_dashboard_exception(error)).encode('utf-8')
    except Exception as error:
        logger.exception('Internal Server Error')
        raise error


@contextmanager
def handle_cephfs_error():
    try:
        yield
    except cephfs.OSError as e:
        raise DashboardException(e, component='cephfs') from e


@contextmanager
def handle_rbd_error():
    try:
        yield
    except rbd.OSError as e:
        raise DashboardException(e, component='rbd')
    except rbd.Error as e:
        raise DashboardException(e, component='rbd', code=e.__class__.__name__)


@contextmanager
def handle_rados_error(component):
    try:
        yield
    except rados.OSError as e:
        raise DashboardException(e, component=component)
    except rados.Error as e:
        raise DashboardException(e, component=component, code=e.__class__.__name__)


@contextmanager
def handle_send_command_error(component):
    try:
        yield
    except SendCommandError as e:
        raise DashboardException(e, component=component)


@contextmanager
def handle_orchestrator_error(component):
    try:
        yield
    except OrchestratorError as e:
        raise DashboardException(e, component=component)


@contextmanager
def handle_request_error(component):
    try:
        yield
    except RequestException as e:
        if e.content:
            content = json.loads(e.content)
            content_message = content.get('message')
            if content_message:
                raise DashboardException(
                    msg=content_message, component=component)
        raise DashboardException(e=e, component=component)


@contextmanager
def handle_error(component, http_status_code=None):
    try:
        yield
    except Exception as e:  # pylint: disable=broad-except
        raise DashboardException(e, component=component, http_status_code=http_status_code)


@contextmanager
def handle_custom_error(component, http_status_code=None, exceptions=()):
    try:
        yield
    except exceptions as e:
        raise DashboardException(e, component=component, http_status_code=http_status_code)