diff --git a/README.md b/README.md index 9521c3b78..cd84e99f3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The following fields may optionally be declared: - `subdevice_role`: Indicates that this is a `parent` or `child` device. (Default: None) For further detail on these attributes and those listed below, please reference the -[schema definition](tests/schema.json). +[schema definitions](tests/schema/). ### Component Definitions diff --git a/module-types/Juniper/EX9200-32XS.yaml b/module-types/Juniper/EX9200-32XS.yaml new file mode 100644 index 000000000..7a7a1c431 --- /dev/null +++ b/module-types/Juniper/EX9200-32XS.yaml @@ -0,0 +1,69 @@ +--- +manufacturer: Juniper +model: EX9200-32XS +comments: 32x10GE interfaces +interfaces: + - name: xe-{module}/0/0 + type: 10gbase-x-sfpp + - name: xe-{module}/0/1 + type: 10gbase-x-sfpp + - name: xe-{module}/0/2 + type: 10gbase-x-sfpp + - name: xe-{module}/0/3 + type: 10gbase-x-sfpp + - name: xe-{module}/0/4 + type: 10gbase-x-sfpp + - name: xe-{module}/0/5 + type: 10gbase-x-sfpp + - name: xe-{module}/0/6 + type: 10gbase-x-sfpp + - name: xe-{module}/0/7 + type: 10gbase-x-sfpp + - name: xe-{module}/1/0 + type: 10gbase-x-sfpp + - name: xe-{module}/1/1 + type: 10gbase-x-sfpp + - name: xe-{module}/1/2 + type: 10gbase-x-sfpp + - name: xe-{module}/1/3 + type: 10gbase-x-sfpp + - name: xe-{module}/1/4 + type: 10gbase-x-sfpp + - name: xe-{module}/1/5 + type: 10gbase-x-sfpp + - name: xe-{module}/1/6 + type: 10gbase-x-sfpp + - name: xe-{module}/1/7 + type: 10gbase-x-sfpp + - name: xe-{module}/2/0 + type: 10gbase-x-sfpp + - name: xe-{module}/2/1 + type: 10gbase-x-sfpp + - name: xe-{module}/2/2 + type: 10gbase-x-sfpp + - name: xe-{module}/2/3 + type: 10gbase-x-sfpp + - name: xe-{module}/2/4 + type: 10gbase-x-sfpp + - name: xe-{module}/2/5 + type: 10gbase-x-sfpp + - name: xe-{module}/2/6 + type: 10gbase-x-sfpp + - name: xe-{module}/2/7 + type: 10gbase-x-sfpp + - name: xe-{module}/3/0 + type: 10gbase-x-sfpp + - name: xe-{module}/3/1 + type: 10gbase-x-sfpp + - name: xe-{module}/3/2 + type: 10gbase-x-sfpp + - name: xe-{module}/3/3 + type: 10gbase-x-sfpp + - name: xe-{module}/3/4 + type: 10gbase-x-sfpp + - name: xe-{module}/3/5 + type: 10gbase-x-sfpp + - name: xe-{module}/3/6 + type: 10gbase-x-sfpp + - name: xe-{module}/3/7 + type: 10gbase-x-sfpp diff --git a/tests/schema.json b/schema/components.json similarity index 87% rename from tests/schema.json rename to schema/components.json index 75dd6d0e8..23b4ec25d 100644 --- a/tests/schema.json +++ b/schema/components.json @@ -1,111 +1,7 @@ { "type": "object", - "properties": { - "manufacturer": { - "type": "string" - }, - "model": { - "type": "string" - }, - "slug": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "part_number": { - "type": "string" - }, - "u_height": { - "type": "integer" - }, - "is_full_depth": { - "type": "boolean" - }, - "airflow": { - "type": "string", - "enum": [ - "front-to-rear", - "rear-to-front", - "left-to-right", - "right-to-left", - "side-to-rear", - "passive" - ] - }, - "subdevice_role": { - "type": "string", - "enum": [ - "parent", - "child" - ] - }, - "console-ports": { - "type": "array", - "items": { - "$ref": "#/definitions/console-port" - } - }, - "console-server-ports": { - "type": "array", - "items": { - "$ref": "#/definitions/console-server-port" - } - }, - "power-ports": { - "type": "array", - "items": { - "$ref": "#/definitions/power-port" - } - }, - "power-outlets": { - "type": "array", - "items": { - "$ref": "#/definitions/power-outlet" - } - }, - "interfaces": { - "type": "array", - "items": { - "$ref": "#/definitions/interface" - } - }, - "front-ports": { - "type": "array", - "items": { - "$ref": "#/definitions/front-port" - } - }, - "rear-ports": { - "type": "array", - "items": { - "$ref": "#/definitions/rear-port" - } - }, - "module-bays": { - "type": "array", - "items": { - "$ref": "#/definitions/module-bay" - } - }, - "device-bays": { - "type": "array", - "items": { - "$ref": "#/definitions/device-bay" - } - }, - "inventory-items": { - "type": "array", - "items": { - "$ref": "#/definitions/inventory-item" - } - }, - "comments": { - "type": "string" - } - }, - "required": ["manufacturer", "model", "slug"], "additionalProperties": false, - "definitions": { "console-port": { diff --git a/schema/devicetype.json b/schema/devicetype.json new file mode 100644 index 000000000..a7d994d91 --- /dev/null +++ b/schema/devicetype.json @@ -0,0 +1,107 @@ +{ + "type": "object", + "properties": { + "manufacturer": { + "type": "string" + }, + "model": { + "type": "string" + }, + "slug": { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "part_number": { + "type": "string" + }, + "u_height": { + "type": "integer" + }, + "is_full_depth": { + "type": "boolean" + }, + "airflow": { + "type": "string", + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "passive" + ] + }, + "subdevice_role": { + "type": "string", + "enum": [ + "parent", + "child" + ] + }, + "console-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/console-port" + } + }, + "console-server-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/console-server-port" + } + }, + "power-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/power-port" + } + }, + "power-outlets": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/power-outlet" + } + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/interface" + } + }, + "front-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/front-port" + } + }, + "rear-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/rear-port" + } + }, + "module-bays": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/module-bay" + } + }, + "device-bays": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/device-bay" + } + }, + "inventory-items": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/inventory-item" + } + }, + "comments": { + "type": "string" + } + }, + "required": ["manufacturer", "model", "slug"], + "additionalProperties": false +} diff --git a/schema/moduletype.json b/schema/moduletype.json new file mode 100644 index 000000000..4b56b37b0 --- /dev/null +++ b/schema/moduletype.json @@ -0,0 +1,61 @@ +{ + "type": "object", + "properties": { + "manufacturer": { + "type": "string" + }, + "model": { + "type": "string" + }, + "part_number": { + "type": "string" + }, + "console-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/console-port" + } + }, + "console-server-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/console-server-port" + } + }, + "power-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/power-port" + } + }, + "power-outlets": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/power-outlet" + } + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/interface" + } + }, + "front-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/front-port" + } + }, + "rear-ports": { + "type": "array", + "items": { + "$ref": "components.json#/definitions/rear-port" + } + }, + "comments": { + "type": "string" + } + }, + "required": ["manufacturer", "model"], + "additionalProperties": false +} diff --git a/tests/definitions_test.py b/tests/definitions_test.py index 0608a4dde..5146bce17 100644 --- a/tests/definitions_test.py +++ b/tests/definitions_test.py @@ -1,21 +1,41 @@ import glob import json +import os import pytest import yaml -from jsonschema import validate +from jsonschema import validate, RefResolver, Draft4Validator from jsonschema.exceptions import ValidationError +SCHEMAS = ( + ('device-types', 'devicetype.json'), + ('module-types', 'moduletype.json'), +) + + def _get_definition_files(): """ - Return a list of all definition files. + Return a list of all definition files within the specified path. """ - return [f for f in glob.glob("device-types/*/*", recursive=True)] + ret = [] + + for path, schema in SCHEMAS: + + # Initialize the schema + with open(f"schema/{schema}") as schema_file: + schema = json.loads(schema_file.read()) + + # Validate that the schema exists + assert schema, f"Schema definition for {path} is empty!" + + # Map each definition file to its schema + for f in glob.glob(f"{path}/*/*", recursive=True): + ret.append((f, schema)) + + return ret -# Initialize schema -with open("tests/schema.json") as schema_file: - schema = json.loads(schema_file.read()) +definition_files = _get_definition_files() def test_environment(): @@ -23,16 +43,13 @@ def test_environment(): Run basic sanity checks on the environment to ensure tests are running correctly. """ # Validate that definition files exist - assert _get_definition_files(), "No definition files found!" - - # Validate that the schema exists - assert schema, "Schema definition is empty!" + assert definition_files, "No definition files found!" -@pytest.mark.parametrize("file_path", _get_definition_files()) -def test_definition(file_path): +@pytest.mark.parametrize(('file_path', 'schema'), definition_files) +def test_definitions(file_path, schema): """ - Validate each DeviceType definition file using the provided JSON schema. + Validate each definition file using the provided JSON schema. """ # Check file extension assert file_path.split('.')[-1] in ('yaml', 'yml'), f"Invalid file extension: {file_path}" @@ -41,14 +58,14 @@ def test_definition(file_path): with open(file_path) as definition_file: content = definition_file.read() - # Check for trailing newline - assert content[-1] == '\n', "Missing trailing newline" + # Check for trailing newline + assert content.endswith('\n'), "Missing trailing newline" - # Load YAML data - definition = yaml.load(content, Loader=yaml.SafeLoader) + # Load YAML data from file + definition = yaml.load(content, Loader=yaml.SafeLoader) - # Run validation try: - validate(definition, schema=schema) + resolver = RefResolver(f'file://{os.getcwd()}/schema/devicetype.json', schema) + Draft4Validator(schema, resolver=resolver).validate(definition) except ValidationError as e: pytest.fail(f"{file_path} failed validation: {e}", False)