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, **kwargs): |
18
|
|
|
"""Send an event when something happens with an EVC.""" |
19
|
1 |
|
event_name = f"kytos/mef_eline.{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): |
25
|
|
|
"""Notify link available tags.""" |
26
|
1 |
|
emit_event(controller, "link_available_tags", link=link) |
27
|
|
|
|
28
|
|
|
|
29
|
1 |
|
def compare_endpoint_trace(endpoint, vlan, trace): |
30
|
|
|
"""Compare and endpoint with a trace step.""" |
31
|
1 |
|
return ( |
32
|
|
|
endpoint.switch.dpid == trace["dpid"] |
33
|
|
|
and endpoint.port_number == trace["port"] |
34
|
|
|
and vlan == trace["vlan"] |
35
|
|
|
) |
36
|
|
|
|
37
|
|
|
|
38
|
1 |
|
def load_spec(): |
39
|
|
|
"""Validate openapi spec.""" |
40
|
1 |
|
napp_dir = Path(__file__).parent |
41
|
1 |
|
yml_file = napp_dir / "openapi.yml" |
42
|
1 |
|
spec_dict, _ = read_from_filename(yml_file) |
43
|
|
|
|
44
|
1 |
|
validate_spec(spec_dict) |
45
|
|
|
|
46
|
1 |
|
return create_spec(spec_dict) |
47
|
|
|
|
48
|
|
|
|
49
|
1 |
|
def validate(spec): |
50
|
|
|
"""Decorator to validate a REST endpoint input. |
51
|
|
|
|
52
|
|
|
Uses the schema defined in the openapi.yml file |
53
|
|
|
to validate. |
54
|
|
|
""" |
55
|
|
|
|
56
|
1 |
|
def validate_decorator(func): |
57
|
1 |
|
@functools.wraps(func) |
58
|
|
|
def wrapper_validate(*args, **kwargs): |
59
|
1 |
|
try: |
60
|
1 |
|
data = request.get_json() |
61
|
1 |
|
except BadRequest: |
62
|
1 |
|
result = "The request body is not a well-formed JSON." |
63
|
1 |
|
log.debug("create_circuit result %s %s", result, 400) |
64
|
1 |
|
raise BadRequest(result) from BadRequest |
65
|
1 |
|
if data is None: |
66
|
1 |
|
result = "The request body mimetype is not application/json." |
67
|
1 |
|
log.debug("update result %s %s", result, 415) |
68
|
1 |
|
raise UnsupportedMediaType(result) |
69
|
|
|
|
70
|
1 |
|
validator = RequestValidator(spec) |
71
|
1 |
|
openapi_request = FlaskOpenAPIRequest(request) |
72
|
1 |
|
result = validator.validate(openapi_request) |
73
|
1 |
|
if result.errors: |
74
|
|
|
errors = result.errors[0] |
75
|
|
|
if hasattr(errors, "schema_errors"): |
76
|
|
|
schema_errors = errors.schema_errors[0] |
77
|
|
|
error_log = { |
78
|
|
|
"error_message": schema_errors.message, |
79
|
|
|
"error_validator": schema_errors.validator, |
80
|
|
|
"error_validator_value": schema_errors.validator_value, |
81
|
|
|
"error_path": list(schema_errors.path), |
82
|
|
|
"error_schema": schema_errors.schema, |
83
|
|
|
"error_schema_path": list(schema_errors.schema_path), |
84
|
|
|
} |
85
|
|
|
log.debug("error response: %s", error_log) |
86
|
|
|
error_response = f"{schema_errors.message} for field" |
87
|
|
|
error_response += ( |
88
|
|
|
f" {'/'.join(map(str,schema_errors.path))}." |
89
|
|
|
) |
90
|
|
|
else: |
91
|
|
|
error_response = ( |
92
|
|
|
"The request body mimetype is not application/json." |
93
|
|
|
) |
94
|
|
|
raise BadRequest(error_response) from BadRequest |
95
|
1 |
|
return func(*args, data=data, **kwargs) |
96
|
|
|
|
97
|
1 |
|
return wrapper_validate |
98
|
|
|
|
99
|
|
|
return validate_decorator |
100
|
|
|
|