diff --git a/lower-constraints.txt b/lower-constraints.txt index 769dd7c87..f71395042 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -145,3 +145,4 @@ python-zaqarclient==1.2.0 tooz==1.58.0 zake==0.1.6 psutil==5.4.3 +jsonschema==2.6.0 diff --git a/requirements.txt b/requirements.txt index 2deac1fcb..df41497bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,4 +53,5 @@ futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD pytz>=2013.6 # MIT tenacity>=4.9.0 tooz>=1.58.0 # Apache-2.0 -psutil>=5.4.3 # BSD \ No newline at end of file +psutil>=5.4.3 # BSD +jsonschema>=2.6.0 # MIT diff --git a/vitrage/datasources/static/__init__.py b/vitrage/datasources/static/__init__.py index ca02539e5..3f2a456e2 100644 --- a/vitrage/datasources/static/__init__.py +++ b/vitrage/datasources/static/__init__.py @@ -45,6 +45,75 @@ OPTS = [ help='static data sources configuration directory')] +STATIC_SCHEMA = { + "type": "object", + "required": ["definitions"], + "properties": { + "metadata": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + }, + "description": { + "type": "string", + }, + } + }, + "definitions": { + "type": "object", + "required": ["entities", "relationships"], + "properties": { + "entities": { + "type": "array", + "items": { + "type": "object", + "required": ["static_id"], + "properties": { + "static_id": { + "type": "string", + }, + "type": { + "type": "string", + }, + "name": { + "type": "string", + }, + "id": { + "type": "string", + }, + "state": { + "type": "string", + }, + }, + }, + }, + "relationships": { + "type": "array", + "items": { + "type": "object", + "required": ["source", "target", + "relationship_type"], + "properties": { + "source": { + "type": "string", + }, + "target": { + "type": "string", + }, + "relationship_type": { + "type": "string", + }, + }, + }, + }, + } + }, + }, +} + + class StaticFields(object): """yaml fields for static definitions""" METADATA = 'metadata' diff --git a/vitrage/datasources/static/driver.py b/vitrage/datasources/static/driver.py index 4611a0eb7..24df94efe 100644 --- a/vitrage/datasources/static/driver.py +++ b/vitrage/datasources/static/driver.py @@ -13,6 +13,7 @@ # under the License. from itertools import chain +from jsonschema import validate from six.moves import reduce from oslo_log import log @@ -21,6 +22,7 @@ from vitrage.common.constants import DatasourceProperties as DSProps from vitrage.common.constants import GraphAction from vitrage.datasources.driver_base import DriverBase from vitrage.datasources.static import STATIC_DATASOURCE +from vitrage.datasources.static import STATIC_SCHEMA from vitrage.datasources.static import StaticFields from vitrage.utils import file as file_utils @@ -39,10 +41,15 @@ class StaticDriver(DriverBase): self.entities_cache = [] @staticmethod - def _is_valid_config(config): + def _is_valid_config(config, path): """check for validity of configuration""" - # TODO(yujunz) check with yaml schema or reuse template validation - return StaticFields.DEFINITIONS in config + try: + validate(config, STATIC_SCHEMA) + return True + except Exception: + msg = "Invalid config file: %s" % path + LOG.exception(msg) + return False @staticmethod def get_event_types(): @@ -103,9 +110,7 @@ class StaticDriver(DriverBase): def _get_entities_from_file(cls, path): config = file_utils.load_yaml_file(path) - if not cls._is_valid_config(config): - LOG.warning("Skipped invalid config (possible obsoleted): {}" - .format(path)) + if not cls._is_valid_config(config, path): return [] definitions = config[StaticFields.DEFINITIONS]