Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bdd2760
feat: add front image
bastianleicht Apr 18, 2025
ac14097
feat: startech 4post rack
bastianleicht Apr 18, 2025
6848b69
feat: add digitus DN-19 07U-I-OD
bastianleicht Apr 18, 2025
116b0c6
feat: add digitus DN-19 07U-I-OD
bastianleicht Apr 18, 2025
3ea3e03
Merge remote-tracking branch 'origin/rack-types' into rack-types
bastianleicht Apr 18, 2025
dd42e4f
Revert "feat: add front image"
bastianleicht Apr 18, 2025
7bfbe66
fix: change form_factor
bastianleicht Apr 18, 2025
47c164d
Merge branch 'master' into rack-types
bastianleicht Apr 28, 2025
64ab506
Merge branch 'master' into rack-types
bastianleicht May 5, 2025
bb2c3f9
Merge branch 'master' into rack-types
bastianleicht May 20, 2025
b770b68
feat: add racktype schema
bastianleicht May 20, 2025
8228fdd
feat: add racktype schema
bastianleicht May 20, 2025
ecdbf7d
feat: upload generated known-racks.pickle
bastianleicht May 20, 2025
37d5336
fix: change filename
bastianleicht May 21, 2025
a40167d
feat: add rack type
bastianleicht May 21, 2025
c63e6f8
refactor: change tests to work with racks
bastianleicht May 21, 2025
7b6274d
refactor: use the correct device class
bastianleicht May 21, 2025
344bac9
fix: rename file correctly
bastianleicht May 21, 2025
d751f52
Merge branch 'master' into rack-types
bastianleicht May 21, 2025
bc32f53
Merge branch 'master' into rack-types
bastianleicht May 22, 2025
6d24cac
Merge branch 'master' into rack-types
harryajc May 29, 2025
1a038ad
Merge branch 'master' into rack-types
bastianleicht Jun 3, 2025
13f0141
Merge branch 'master' into rack-types
bastianleicht Jun 6, 2025
d3cf31e
Merge branch 'master' into rack-types
bastianleicht Jun 13, 2025
64a4cbd
Update racktype.json
harryajc Aug 4, 2025
8028bf9
Merge branch 'master' into rack-types
bastianleicht Aug 5, 2025
cdc329f
Merge branch 'master' into rack-types
harryajc Aug 11, 2025
21ffc39
Merge branch 'netbox-community:master' into rack-types
bastianleicht Aug 11, 2025
89ea613
fix: add missing desc_units schema property
bastianleicht Aug 11, 2025
b9404ca
fix: add missing desc_units value
bastianleicht Aug 11, 2025
a7d0107
chore: update readme with rack-types documentation
bastianleicht Aug 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,48 @@ The following fields may **optionally** be declared:
For further detail on these attributes and those listed below, please reference the
[schema definitions](schema/) and the [Component Definitions](#component-definitions) below.

## Rack Type Definitions

Each definition **must** include at minimum the following fields:

- `manufacturer`: The name of the manufacturer which produces this rack type.
- Type: String
- `model`: The model number of the rack type. This must be unique per manufacturer.
- Type: String
- `slug`: A URL-friendly representation of the model number. Like the model number, this must be unique per
manufacturer. All slugs should have the manufacturers name prepended to it with a dash, please see the example below.
- Type: String
- Pattern: `"^[-a-z0-9_]+$"`. Must match the following characters: `-`, Lowercase `a` to `z`, Numbers `0` to `9`.
- `form_factor`: The form factor of the rack type. This is used to indicate the physical characteristics of the rack, such as whether it is a 4-post frame or a wall-cabinet etc.
- Type: String
- :test_tube: Example: `form_factor: 4-post-frame`
- `width`: The width of the rack type in zoll/inches. This is used to indicate the physical width of the rack, such as whether it is a 19" or 23" rack.
- Type: Integer
- :test_tube: Example: `width: 19`
- `u_height`: The height of the rack type in rack units.
- Type: Number
- :test_tube: Example: `u_height: 42`
- `starting_unit`: The unit number at which the rack starts. This is used to indicate the starting unit number of the rack, such as whether it starts at 1 or 42. The starting unit is normally defined from bottom to top, with the bottom unit being 1.
- Type: Number
- :test_tube: Example: `starting_unit: 1`

:test_tube: Example:

```yaml
manufacturer: Startech
model: 4 Post 42U
slug: startech-4postrack42
form_factor: 4-post-frame
width: 19
u_height: 42
starting_unit: 1
```

**Note: We are asking that all new racks also include the following optional fields: `outer_width`, `outer_height`, `outer_depth`, `outer_unit`, `weight`, `max_weight`, `weight_unit`, `mounting_depth`, and `desc_units`.**

For further detail on these attributes and those listed below, please reference the
[racktype schema definition](schema/racktype.json)

### Component Definitions

Valid component types are listed below. Each type of component must declare a list of the individual component templates
Expand Down
16 changes: 16 additions & 0 deletions rack-types/Digitus/dn-19-07u-i-od.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
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
desc_units: false
24 changes: 24 additions & 0 deletions rack-types/Startech/4-post-42u.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
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
desc_units: false
100 changes: 100 additions & 0 deletions schema/racktype.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"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": 1
},
"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": {
"$ref": "urn:devicetype-library:generated-schema#/definitions/weight-unit"
},
"mounting_depth": {
"type": "number",
"minimum": 0,
"multipleOf": 0.01
},
"starting_unit": {
"type": "number",
"minimum": 1,
"multipleOf": 1
},
"desc_units": {
"type": "boolean",
"default": false
},
"comments": {
"type": "string"
}
},
"required": ["manufacturer", "model", "slug", "form_factor", "width", "u_height", "starting_unit"],
"additionalProperties": false
}
10 changes: 9 additions & 1 deletion tests/definitions_test.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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)
Expand Down
31 changes: 30 additions & 1 deletion tests/device_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,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])
Expand All @@ -197,10 +219,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
Expand Down
3 changes: 3 additions & 0 deletions tests/generate-slug-list.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Binary file added tests/known-racks.pickle
Binary file not shown.
1 change: 1 addition & 0 deletions tests/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
SCHEMAS = (
('device-types', 'devicetype.json'),
('module-types', 'moduletype.json'),
('rack-types', 'racktype.json'),
)
SCHEMAS_BASEPATH = f"{os.getcwd()}/schema/"

Expand Down
Loading