diff --git a/rack-types/Digitus/dn-19-07u-i-od.yml b/rack-types/Digitus/dn-19-07u-i-od.yml new file mode 100644 index 0000000000..2fe9374788 --- /dev/null +++ b/rack-types/Digitus/dn-19-07u-i-od.yml @@ -0,0 +1,15 @@ +--- +manufacturer: Digitus +model: DN-19 07U-I-OD +slug: digitus-dn-19-07u-i-od +width: 19 +u_height: 7 +form_factor: wall-cabinet +description: '[Datasheet](https://www.assmann.com/product-pdf/4016032360971?PL=de)' +starting_unit: 1 +outer_width: 600 +outer_unit: mm +mounting_depth: 450 +weight: 31 +max_weight: 100 +weight_unit: kg diff --git a/rack-types/Startech/4-post-42u.yaml b/rack-types/Startech/4-post-42u.yaml new file mode 100644 index 0000000000..896e94f206 --- /dev/null +++ b/rack-types/Startech/4-post-42u.yaml @@ -0,0 +1,23 @@ +--- +manufacturer: Startech +model: 4 Post 42U +slug: startech-4postrack42 +width: 19 +u_height: 42 +form_factor: 4-post-frame +description: Startech 4 Post 42U 19in rack with optional casters +starting_unit: 1 +outer_width: 600 +outer_unit: mm +# Adjustable depth, do we want the minimum or maximum depth? +# Minimum adjusted depth +mounting_depth: 560 +# Maximum adjusted depth +# mounting_depth: 1017 +weight: 38.5 +# Different weights between stationary and on casters, which one? +# Stationary +# max_weight: 600 +# Rolling +max_weight: 360 +weight_unit: kg diff --git a/schema/racktype.json b/schema/racktype.json new file mode 100644 index 0000000000..11618e83f4 --- /dev/null +++ b/schema/racktype.json @@ -0,0 +1,102 @@ +{ + "type": "object", + "$id": "urn:devicetype-library:rack-type", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "manufacturer": { + "type": "string" + }, + "model": { + "type": "string" + }, + "slug": { + "type": "string", + "pattern": "^[-a-z0-9_]+$" + }, + "description": { + "type": "string" + }, + "form_factor": { + "type": "string", + "enum": [ + "wall-cabinet", + "4-post-frame", + "2-post-frame", + "4-post-cabinet", + "wall-frame", + "wall-frame-vertical", + "wall-cabinet-vertical" + ] + }, + "width": { + "type": "integer", + "enum": [ + 10, + 19, + 20, + 23 + ] + }, + "u_height": { + "type": "number", + "minimum": 0, + "multipleOf": 0.5 + }, + "outer_width": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "outer_height": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "outer_depth": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "outer_unit": { + "type": "string", + "enum": [ + "mm", + "in" + ] + }, + "weight": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "max_weight": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "weight_unit": { + "type": "string", + "enum": [ + "kg", + "g", + "lb", + "oz" + ] + }, + "mounting_depth": { + "type": "number", + "minimum": 0, + "multipleOf": 0.01 + }, + "starting_unit": { + "type": "number", + "minimum": 1, + "multipleOf": 1 + }, + "comments": { + "type": "string" + } + }, + "required": ["manufacturer", "model", "slug", "form_factor", "width", "u_height", "starting_unit"], + "additionalProperties": false +} diff --git a/tests/definitions_test.py b/tests/definitions_test.py index 78e4790218..51d26e6a54 100644 --- a/tests/definitions_test.py +++ b/tests/definitions_test.py @@ -1,7 +1,7 @@ from test_configuration import COMPONENT_TYPES, IMAGE_FILETYPES, SCHEMAS, SCHEMAS_BASEPATH, KNOWN_SLUGS, ROOT_DIR, USE_LOCAL_KNOWN_SLUGS, NETBOX_DT_LIBRARY_URL, KNOWN_MODULES, USE_UPSTREAM_DIFF, PRECOMMIT_ALL_SWITCHES import pickle_operations from yaml_loader import DecimalSafeLoader -from device_types import DeviceType, ModuleType, verify_filename, validate_components +from device_types import DeviceType, ModuleType, RackType, verify_filename, validate_components import decimal import glob import json @@ -134,11 +134,13 @@ def test_environment(): if USE_LOCAL_KNOWN_SLUGS: KNOWN_SLUGS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-slugs.pickle') KNOWN_MODULES = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-modules.pickle') + KNOWN_RACKS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-racks.pickle') else: temp_dir = tempfile.TemporaryDirectory() repo = Repo.clone_from(url=NETBOX_DT_LIBRARY_URL, to_path=temp_dir.name) KNOWN_SLUGS = pickle_operations.read_pickle_data(f'{temp_dir.name}/tests/known-slugs.pickle') KNOWN_MODULES = pickle_operations.read_pickle_data(f'{temp_dir.name}/tests/known-modules.pickle') + KNOWN_RACKS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-racks.pickle') SCHEMA_REGISTRY = _generate_schema_registry() @@ -181,6 +183,12 @@ def test_definitions(file_path, schema, change_type): if "device-types" in file_path: # A device this_device = DeviceType(definition, file_path, change_type) + elif "module-types" in file_path: + # A module type + this_device = ModuleType(definition, file_path, change_type) + elif "rack-types" in file_path: + # A rack type + this_device = RackType(definition, file_path, change_type) else: # A module this_device = ModuleType(definition, file_path, change_type) diff --git a/tests/device_types.py b/tests/device_types.py index b5dc3c4269..caeda1ea76 100644 --- a/tests/device_types.py +++ b/tests/device_types.py @@ -177,6 +177,28 @@ def _slugify_part_number(self): slugified = slugified[:-1] return slugified +class RackType: + def __new__(cls, *args, **kwargs): + return super().__new__(cls) + + def __init__(self, definition, file_path, change_type): + self.file_path = file_path + self.isDevice = False + self.definition = definition + self.manufacturer = definition.get('manufacturer') + self.model = definition.get('model') + self._slug_model = self._slugify_model() + self.change_type = change_type + + def get_filepath(self): + return self.file_path + + def _slugify_model(self): + slugified = self.model.casefold().replace(" ", "-").replace("sfp+", "sfpp").replace("poe+", "poep").replace("-+", "-plus").replace("+", "-plus-").replace("_", "-").replace("&", "-and-").replace("!", "").replace("/", "-").replace(",", "").replace("'", "").replace("*", "-") + if slugified.endswith("-"): + slugified = slugified[:-1] + return slugified + def validate_component_names(component_names: (set or None)): if len(component_names) > 1: verify_name = list(component_names[0]) @@ -195,10 +217,17 @@ def validate_component_names(component_names: (set or None)): return False return True -def verify_filename(device: (DeviceType or ModuleType), KNOWN_MODULES: (set or None)): +def verify_filename(device: (DeviceType or ModuleType or RackType), KNOWN_MODULES: (set or None)): head, tail = os.path.split(device.get_filepath()) filename = tail.rsplit(".", 1)[0].casefold() + # Check if file is RackType + if "rack-types" in device.file_path: + if not filename == device._slug_model: + device.failureMessage = f'{device.file_path} file name is invalid. Must be the model "{device._slug_model}"' + return False + return True + if not (filename == device._slug_model or filename == device._slug_part_number or filename == device.part_number.casefold()): device.failureMessage = f'{device.file_path} file name is invalid. Must be either the model "{device._slug_model}" or part_number "{device.part_number} / {device._slug_part_number}"' return False diff --git a/tests/generate-slug-list.py b/tests/generate-slug-list.py index 473d41ed91..61dc2c7cfa 100644 --- a/tests/generate-slug-list.py +++ b/tests/generate-slug-list.py @@ -107,3 +107,6 @@ def _generate_knowns(device_or_module): _generate_knowns('module') pickle_operations.write_pickle_data(KNOWN_MODULES, f'{ROOT_DIR}/tests/known-modules.pickle') + +_generate_knowns('rack') +pickle_operations.write_pickle_data(KNOWN_MODULES, f'{ROOT_DIR}/tests/known-racks.pickle') diff --git a/tests/known-racks.pickle b/tests/known-racks.pickle new file mode 100644 index 0000000000..94a0e59938 Binary files /dev/null and b/tests/known-racks.pickle differ diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 6102cc0e78..4c1e9bb3c0 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -3,6 +3,7 @@ SCHEMAS = ( ('device-types', 'devicetype.json'), ('module-types', 'moduletype.json'), + ('rack-types', 'racktype.json'), ) SCHEMAS_BASEPATH = f"{os.getcwd()}/schema/"