1
|
|
|
"""Utility functions.""" |
2
|
1 |
|
import functools |
3
|
1 |
|
from pathlib import Path |
4
|
|
|
|
5
|
1 |
|
from flask import request |
6
|
1 |
|
from openapi_core import create_spec |
7
|
1 |
|
from openapi_core.contrib.flask import FlaskOpenAPIRequest |
8
|
1 |
|
from openapi_core.validation.request.validators import RequestValidator |
9
|
1 |
|
from openapi_spec_validator import validate_spec |
10
|
1 |
|
from openapi_spec_validator.readers import read_from_filename |
11
|
1 |
|
from werkzeug.exceptions import BadRequest, UnsupportedMediaType |
12
|
|
|
|
13
|
1 |
|
from kytos.core import log |
14
|
1 |
|
from kytos.core.events import KytosEvent |
15
|
|
|
|
16
|
|
|
|
17
|
1 |
|
def emit_event(controller, name, context="kytos/mef_eline", **kwargs): |
18
|
|
|
"""Send an event when something happens with an EVC.""" |
19
|
1 |
|
event_name = f"{context}.{name}" |
20
|
1 |
|
event = KytosEvent(name=event_name, content=kwargs) |
21
|
1 |
|
controller.buffers.app.put(event) |
22
|
|
|
|
23
|
|
|
|
24
|
1 |
|
def notify_link_available_tags(controller, link, src_func=None): |
25
|
|
|
"""Notify link available tags.""" |
26
|
1 |
|
emit_event(controller, "link_available_tags", link=link, src_func=src_func) |
27
|
|
|
|
28
|
|
|
|
29
|
1 |
|
def compare_endpoint_trace(endpoint, vlan, trace): |
30
|
|
|
"""Compare and endpoint with a trace step.""" |
31
|
1 |
|
if vlan and "vlan" in trace: |
32
|
1 |
|
return ( |
33
|
|
|
endpoint.switch.dpid == trace["dpid"] |
34
|
|
|
and endpoint.port_number == trace["port"] |
35
|
|
|
and vlan == trace["vlan"] |
36
|
|
|
) |
37
|
|
|
return ( |
38
|
|
|
endpoint.switch.dpid == trace["dpid"] |
39
|
|
|
and endpoint.port_number == trace["port"] |
40
|
|
|
) |
41
|
|
|
|
42
|
|
|
|
43
|
1 |
|
def load_spec(): |
44
|
|
|
"""Validate openapi spec.""" |
45
|
1 |
|
napp_dir = Path(__file__).parent |
46
|
1 |
|
yml_file = napp_dir / "openapi.yml" |
47
|
1 |
|
spec_dict, _ = read_from_filename(yml_file) |
48
|
|
|
|
49
|
1 |
|
validate_spec(spec_dict) |
50
|
|
|
|
51
|
1 |
|
return create_spec(spec_dict) |
52
|
|
|
|
53
|
|
|
|
54
|
1 |
|
def validate(spec): |
55
|
|
|
"""Decorator to validate a REST endpoint input. |
56
|
|
|
|
57
|
|
|
Uses the schema defined in the openapi.yml file |
58
|
|
|
to validate. |
59
|
|
|
""" |
60
|
|
|
|
61
|
1 |
|
def validate_decorator(func): |
62
|
1 |
|
@functools.wraps(func) |
63
|
1 |
|
def wrapper_validate(*args, **kwargs): |
64
|
1 |
|
try: |
65
|
1 |
|
data = request.get_json() |
66
|
1 |
|
except BadRequest: |
67
|
1 |
|
result = "The request body is not a well-formed JSON." |
68
|
1 |
|
log.debug("create_circuit result %s %s", result, 400) |
69
|
1 |
|
raise BadRequest(result) from BadRequest |
70
|
1 |
|
if data is None: |
71
|
1 |
|
result = "The request body mimetype is not application/json." |
72
|
1 |
|
log.debug("update result %s %s", result, 415) |
73
|
1 |
|
raise UnsupportedMediaType(result) |
74
|
|
|
|
75
|
1 |
|
validator = RequestValidator(spec) |
76
|
1 |
|
openapi_request = FlaskOpenAPIRequest(request) |
77
|
1 |
|
result = validator.validate(openapi_request) |
78
|
1 |
|
if result.errors: |
79
|
1 |
|
error_response = ( |
80
|
|
|
"The request body contains invalid API data." |
81
|
|
|
) |
82
|
1 |
|
errors = result.errors[0] |
83
|
1 |
|
if hasattr(errors, "schema_errors"): |
84
|
1 |
|
schema_errors = errors.schema_errors[0] |
85
|
1 |
|
error_log = { |
86
|
|
|
"error_message": schema_errors.message, |
87
|
|
|
"error_validator": schema_errors.validator, |
88
|
|
|
"error_validator_value": schema_errors.validator_value, |
89
|
|
|
"error_path": list(schema_errors.path), |
90
|
|
|
"error_schema": schema_errors.schema, |
91
|
|
|
"error_schema_path": list(schema_errors.schema_path), |
92
|
|
|
} |
93
|
1 |
|
log.debug("Invalid request (API schema): %s", error_log) |
94
|
1 |
|
error_response += f" {schema_errors.message} for field" |
95
|
1 |
|
error_response += ( |
96
|
|
|
f" {'/'.join(map(str,schema_errors.path))}." |
97
|
|
|
) |
98
|
1 |
|
raise BadRequest(error_response) from BadRequest |
99
|
1 |
|
return func(*args, data=data, **kwargs) |
100
|
|
|
|
101
|
1 |
|
return wrapper_validate |
102
|
|
|
|
103
|
|
|
return validate_decorator |
104
|
|
|
|