Init commit for standalone enodebd
Change-Id: I88eeef5135dd7ba8551ddd9fb6a0695f5325337b
diff --git a/eventd/event_validator.py b/eventd/event_validator.py
new file mode 100644
index 0000000..31b1636
--- /dev/null
+++ b/eventd/event_validator.py
@@ -0,0 +1,125 @@
+"""
+Copyright 2020 The Magma Authors.
+
+This source code is licensed under the BSD-style license found in the
+LICENSE file in the root directory of this source tree.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import json
+import logging
+from contextlib import closing
+from typing import Any, Dict
+
+import pkg_resources
+import yaml
+from bravado_core.spec import Spec
+from bravado_core.validate import validate_object as bravado_validate
+
+EVENT_REGISTRY = 'event_registry'
+SWAGGER_SPEC = 'swagger_spec'
+BRAVADO_SPEC = 'bravado_spec'
+MODULE = 'module'
+FILENAME = 'filename'
+DEFINITIONS = 'definitions'
+
+
+class EventValidator(object):
+ """
+ gRPC based server for EventD.
+ """
+
+ def __init__(self, config: Dict[str, Any]):
+ self.event_registry = config[EVENT_REGISTRY]
+ self.specs_by_filename = self._load_specs_from_registry()
+
+ def validate_event(self, raw_event: str, event_type: str) -> None:
+ """
+ Checks if an event is registered and validates it based on
+ a registered schema.
+ Args:
+ raw_event: The event to be validated, as a JSON-encoded string
+ event_type: The type of an event, which corresponds
+ to a generated model
+ Returns:
+ Does not return, but throws exceptions if validation fails.
+ """
+ event = json.loads(raw_event)
+
+ # Event not in registry
+ if event_type not in self.event_registry:
+ logging.debug(
+ 'Event type %s not among registered event types (%s)',
+ event_type, self.event_registry,
+ )
+ raise KeyError(
+ 'Event type {} not registered, '
+ 'please add it to the EventD config'.format(event_type),
+ )
+ filename = self.event_registry[event_type][FILENAME]
+ bravado_validate(
+ self.specs_by_filename[filename][BRAVADO_SPEC],
+ self.specs_by_filename[filename][SWAGGER_SPEC][event_type],
+ event,
+ )
+
+ def _load_specs_from_registry(self) -> Dict[str, Any]:
+ """
+ Loads all swagger definitions from the files specified in the
+ event registry.
+ """
+ specs_by_filename = {}
+ for event_type, info in self.event_registry.items():
+ filename = info[FILENAME]
+ if filename in specs_by_filename:
+ # Spec for this file is already registered
+ self._check_event_exists_in_spec(
+ specs_by_filename[filename][SWAGGER_SPEC],
+ filename,
+ event_type,
+ )
+ continue
+
+ module = '{}.swagger.specs'.format(info[MODULE])
+ if not pkg_resources.resource_exists(module, filename):
+ raise LookupError(
+ 'File {} not found under {}/swagger, please ensure that '
+ 'it exists'.format(filename, info[MODULE]),
+ )
+
+ stream = pkg_resources.resource_stream(module, filename)
+ with closing(stream) as spec_file:
+ swagger_spec = yaml.safe_load(spec_file)
+ self._check_event_exists_in_spec(
+ swagger_spec[DEFINITIONS], filename, event_type,
+ )
+
+ config = {'validate_swagger_spec': False}
+ bravado_spec = Spec.from_dict(swagger_spec, config=config)
+ specs_by_filename[filename] = {
+ SWAGGER_SPEC: swagger_spec[DEFINITIONS],
+ BRAVADO_SPEC: bravado_spec,
+ }
+
+ return specs_by_filename
+
+ @staticmethod
+ def _check_event_exists_in_spec(
+ swagger_definitions: Dict[str, Any],
+ filename: str,
+ event_type: str,
+ ):
+ """
+ Throw a KeyError if the event_type does not exist in swagger_definitions
+ """
+ if event_type not in swagger_definitions:
+ raise KeyError(
+ 'Event type {} is not defined in {}, '
+ 'please add the definition and re-generate '
+ 'swagger specifications'.format(event_type, filename),
+ )