blob: 2243a2052a2dc632b5d845725c8ebd015e7a5edf [file] [log] [blame]
Wei-Yu Chenad55cb82022-02-15 20:07:01 +08001# SPDX-FileCopyrightText: 2020 The Magma Authors.
2# SPDX-FileCopyrightText: 2022 Open Networking Foundation <support@opennetworking.org>
3#
4# SPDX-License-Identifier: BSD-3-Clause
Wei-Yu Chen49950b92021-11-08 19:19:18 +08005
6import json
7import logging
8from contextlib import closing
9from typing import Any, Dict
10
11import pkg_resources
12import yaml
13from bravado_core.spec import Spec
14from bravado_core.validate import validate_object as bravado_validate
15
16EVENT_REGISTRY = 'event_registry'
17SWAGGER_SPEC = 'swagger_spec'
18BRAVADO_SPEC = 'bravado_spec'
19MODULE = 'module'
20FILENAME = 'filename'
21DEFINITIONS = 'definitions'
22
23
24class EventValidator(object):
25 """
26 gRPC based server for EventD.
27 """
28
29 def __init__(self, config: Dict[str, Any]):
30 self.event_registry = config[EVENT_REGISTRY]
31 self.specs_by_filename = self._load_specs_from_registry()
32
33 def validate_event(self, raw_event: str, event_type: str) -> None:
34 """
35 Checks if an event is registered and validates it based on
36 a registered schema.
37 Args:
38 raw_event: The event to be validated, as a JSON-encoded string
39 event_type: The type of an event, which corresponds
40 to a generated model
41 Returns:
42 Does not return, but throws exceptions if validation fails.
43 """
44 event = json.loads(raw_event)
45
46 # Event not in registry
47 if event_type not in self.event_registry:
48 logging.debug(
49 'Event type %s not among registered event types (%s)',
50 event_type, self.event_registry,
51 )
52 raise KeyError(
53 'Event type {} not registered, '
54 'please add it to the EventD config'.format(event_type),
55 )
56 filename = self.event_registry[event_type][FILENAME]
57 bravado_validate(
58 self.specs_by_filename[filename][BRAVADO_SPEC],
59 self.specs_by_filename[filename][SWAGGER_SPEC][event_type],
60 event,
61 )
62
63 def _load_specs_from_registry(self) -> Dict[str, Any]:
64 """
65 Loads all swagger definitions from the files specified in the
66 event registry.
67 """
68 specs_by_filename = {}
69 for event_type, info in self.event_registry.items():
70 filename = info[FILENAME]
71 if filename in specs_by_filename:
72 # Spec for this file is already registered
73 self._check_event_exists_in_spec(
74 specs_by_filename[filename][SWAGGER_SPEC],
75 filename,
76 event_type,
77 )
78 continue
79
80 module = '{}.swagger.specs'.format(info[MODULE])
81 if not pkg_resources.resource_exists(module, filename):
82 raise LookupError(
83 'File {} not found under {}/swagger, please ensure that '
84 'it exists'.format(filename, info[MODULE]),
85 )
86
87 stream = pkg_resources.resource_stream(module, filename)
88 with closing(stream) as spec_file:
89 swagger_spec = yaml.safe_load(spec_file)
90 self._check_event_exists_in_spec(
91 swagger_spec[DEFINITIONS], filename, event_type,
92 )
93
94 config = {'validate_swagger_spec': False}
95 bravado_spec = Spec.from_dict(swagger_spec, config=config)
96 specs_by_filename[filename] = {
97 SWAGGER_SPEC: swagger_spec[DEFINITIONS],
98 BRAVADO_SPEC: bravado_spec,
99 }
100
101 return specs_by_filename
102
103 @staticmethod
104 def _check_event_exists_in_spec(
105 swagger_definitions: Dict[str, Any],
106 filename: str,
107 event_type: str,
108 ):
109 """
110 Throw a KeyError if the event_type does not exist in swagger_definitions
111 """
112 if event_type not in swagger_definitions:
113 raise KeyError(
114 'Event type {} is not defined in {}, '
115 'please add the definition and re-generate '
116 'swagger specifications'.format(event_type, filename),
117 )