]>
Commit | Line | Data |
---|---|---|
a4b75251 TL |
1 | import collections |
2 | import json | |
3 | import logging | |
4 | import re | |
5 | from functools import wraps | |
6 | ||
7 | import cherrypy | |
8 | from ceph_argparse import ArgumentFormat # pylint: disable=import-error | |
9 | ||
10 | from ..exceptions import DashboardException | |
11 | from ..tools import getargspec | |
12 | ||
13 | logger = logging.getLogger(__name__) | |
14 | ||
15 | ||
16 | ENDPOINT_MAP = collections.defaultdict(list) # type: dict | |
17 | ||
18 | ||
19 | def _get_function_params(func): | |
20 | """ | |
21 | Retrieves the list of parameters declared in function. | |
22 | Each parameter is represented as dict with keys: | |
23 | * name (str): the name of the parameter | |
24 | * required (bool): whether the parameter is required or not | |
25 | * default (obj): the parameter's default value | |
26 | """ | |
27 | fspec = getargspec(func) | |
28 | ||
29 | func_params = [] | |
30 | nd = len(fspec.args) if not fspec.defaults else -len(fspec.defaults) | |
31 | for param in fspec.args[1:nd]: | |
32 | func_params.append({'name': param, 'required': True}) | |
33 | ||
34 | if fspec.defaults: | |
35 | for param, val in zip(fspec.args[nd:], fspec.defaults): | |
36 | func_params.append({ | |
37 | 'name': param, | |
38 | 'required': False, | |
39 | 'default': val | |
40 | }) | |
41 | ||
42 | return func_params | |
43 | ||
44 | ||
45 | def generate_controller_routes(endpoint, mapper, base_url): | |
46 | inst = endpoint.inst | |
47 | ctrl_class = endpoint.ctrl | |
48 | ||
49 | if endpoint.proxy: | |
50 | conditions = None | |
51 | else: | |
52 | conditions = dict(method=[endpoint.method]) | |
53 | ||
54 | # base_url can be empty or a URL path that starts with "/" | |
55 | # we will remove the trailing "/" if exists to help with the | |
56 | # concatenation with the endpoint url below | |
57 | if base_url.endswith("/"): | |
58 | base_url = base_url[:-1] | |
59 | ||
60 | endp_url = endpoint.url | |
61 | ||
62 | if endp_url.find("/", 1) == -1: | |
63 | parent_url = "{}{}".format(base_url, endp_url) | |
64 | else: | |
65 | parent_url = "{}{}".format(base_url, endp_url[:endp_url.find("/", 1)]) | |
66 | ||
67 | # parent_url might be of the form "/.../{...}" where "{...}" is a path parameter | |
68 | # we need to remove the path parameter definition | |
69 | parent_url = re.sub(r'(?:/\{[^}]+\})$', '', parent_url) | |
70 | if not parent_url: # root path case | |
71 | parent_url = "/" | |
72 | ||
73 | url = "{}{}".format(base_url, endp_url) | |
74 | ||
75 | logger.debug("Mapped [%s] to %s:%s restricted to %s", | |
76 | url, ctrl_class.__name__, endpoint.action, | |
77 | endpoint.method) | |
78 | ||
79 | ENDPOINT_MAP[endpoint.url].append(endpoint) | |
80 | ||
81 | name = ctrl_class.__name__ + ":" + endpoint.action | |
82 | mapper.connect(name, url, controller=inst, action=endpoint.action, | |
83 | conditions=conditions) | |
84 | ||
85 | # adding route with trailing slash | |
86 | name += "/" | |
87 | url += "/" | |
88 | mapper.connect(name, url, controller=inst, action=endpoint.action, | |
89 | conditions=conditions) | |
90 | ||
91 | return parent_url | |
92 | ||
93 | ||
94 | def json_error_page(status, message, traceback, version): | |
95 | cherrypy.response.headers['Content-Type'] = 'application/json' | |
96 | return json.dumps(dict(status=status, detail=message, traceback=traceback, | |
97 | version=version)) | |
98 | ||
99 | ||
100 | def allow_empty_body(func): # noqa: N802 | |
101 | """ | |
102 | The POST/PUT request methods decorated with ``@allow_empty_body`` | |
103 | are allowed to send empty request body. | |
104 | """ | |
105 | # pylint: disable=protected-access | |
106 | try: | |
107 | func._cp_config['tools.json_in.force'] = False | |
108 | except (AttributeError, KeyError): | |
109 | func._cp_config = {'tools.json_in.force': False} | |
110 | return func | |
111 | ||
112 | ||
113 | def validate_ceph_type(validations, component=''): | |
114 | def decorator(func): | |
115 | @wraps(func) | |
116 | def validate_args(*args, **kwargs): | |
117 | input_values = kwargs | |
118 | for key, ceph_type in validations: | |
119 | try: | |
120 | ceph_type.valid(input_values[key]) | |
121 | except ArgumentFormat as e: | |
122 | raise DashboardException(msg=e, | |
123 | code='ceph_type_not_valid', | |
124 | component=component) | |
125 | return func(*args, **kwargs) | |
126 | return validate_args | |
127 | return decorator |