Skip to content

Commit 57742db

Browse files
Add rack-types (#3026)
* feat: add front image * feat: startech 4post rack * feat: add digitus DN-19 07U-I-OD * feat: add digitus DN-19 07U-I-OD * Revert "feat: add front image" This reverts commit bdd2760. * fix: change form_factor * feat: add racktype schema * feat: add racktype schema * feat: upload generated known-racks.pickle * fix: change filename * feat: add rack type * refactor: change tests to work with racks * refactor: use the correct device class * fix: rename file correctly * Update racktype.json * fix: add missing desc_units schema property * fix: add missing desc_units value * chore: update readme with rack-types documentation --------- Co-authored-by: Harry <[email protected]>
1 parent a34ad44 commit 57742db

File tree

9 files changed

+225
-2
lines changed

9 files changed

+225
-2
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,48 @@ The following fields may **optionally** be declared:
103103
For further detail on these attributes and those listed below, please reference the
104104
[schema definitions](schema/) and the [Component Definitions](#component-definitions) below.
105105

106+
## Rack Type Definitions
107+
108+
Each definition **must** include at minimum the following fields:
109+
110+
- `manufacturer`: The name of the manufacturer which produces this rack type.
111+
- Type: String
112+
- `model`: The model number of the rack type. This must be unique per manufacturer.
113+
- Type: String
114+
- `slug`: A URL-friendly representation of the model number. Like the model number, this must be unique per
115+
manufacturer. All slugs should have the manufacturers name prepended to it with a dash, please see the example below.
116+
- Type: String
117+
- Pattern: `"^[-a-z0-9_]+$"`. Must match the following characters: `-`, Lowercase `a` to `z`, Numbers `0` to `9`.
118+
- `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.
119+
- Type: String
120+
- :test_tube: Example: `form_factor: 4-post-frame`
121+
- `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.
122+
- Type: Integer
123+
- :test_tube: Example: `width: 19`
124+
- `u_height`: The height of the rack type in rack units.
125+
- Type: Number
126+
- :test_tube: Example: `u_height: 42`
127+
- `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.
128+
- Type: Number
129+
- :test_tube: Example: `starting_unit: 1`
130+
131+
:test_tube: Example:
132+
133+
```yaml
134+
manufacturer: Startech
135+
model: 4 Post 42U
136+
slug: startech-4postrack42
137+
form_factor: 4-post-frame
138+
width: 19
139+
u_height: 42
140+
starting_unit: 1
141+
```
142+
143+
**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`.**
144+
145+
For further detail on these attributes and those listed below, please reference the
146+
[racktype schema definition](schema/racktype.json)
147+
106148
### Component Definitions
107149

108150
Valid component types are listed below. Each type of component must declare a list of the individual component templates
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
manufacturer: Digitus
3+
model: DN-19 07U-I-OD
4+
slug: digitus-dn-19-07u-i-od
5+
width: 19
6+
u_height: 7
7+
form_factor: wall-cabinet
8+
description: '[Datasheet](https://www.assmann.com/product-pdf/4016032360971?PL=de)'
9+
starting_unit: 1
10+
outer_width: 600
11+
outer_unit: mm
12+
mounting_depth: 450
13+
weight: 31
14+
max_weight: 100
15+
weight_unit: kg
16+
desc_units: false
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
manufacturer: Startech
3+
model: 4 Post 42U
4+
slug: startech-4postrack42
5+
width: 19
6+
u_height: 42
7+
form_factor: 4-post-frame
8+
description: Startech 4 Post 42U 19in rack with optional casters
9+
starting_unit: 1
10+
outer_width: 600
11+
outer_unit: mm
12+
# Adjustable depth, do we want the minimum or maximum depth?
13+
# Minimum adjusted depth
14+
mounting_depth: 560
15+
# Maximum adjusted depth
16+
# mounting_depth: 1017
17+
weight: 38.5
18+
# Different weights between stationary and on casters, which one?
19+
# Stationary
20+
# max_weight: 600
21+
# Rolling
22+
max_weight: 360
23+
weight_unit: kg
24+
desc_units: false

schema/racktype.json

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
{
2+
"type": "object",
3+
"$id": "urn:devicetype-library:rack-type",
4+
"$schema": "https://json-schema.org/draft/2020-12/schema",
5+
"properties": {
6+
"manufacturer": {
7+
"type": "string"
8+
},
9+
"model": {
10+
"type": "string"
11+
},
12+
"slug": {
13+
"type": "string",
14+
"pattern": "^[-a-z0-9_]+$"
15+
},
16+
"description": {
17+
"type": "string"
18+
},
19+
"form_factor": {
20+
"type": "string",
21+
"enum": [
22+
"wall-cabinet",
23+
"4-post-frame",
24+
"2-post-frame",
25+
"4-post-cabinet",
26+
"wall-frame",
27+
"wall-frame-vertical",
28+
"wall-cabinet-vertical"
29+
]
30+
},
31+
"width": {
32+
"type": "integer",
33+
"enum": [
34+
10,
35+
19,
36+
20,
37+
23
38+
]
39+
},
40+
"u_height": {
41+
"type": "number",
42+
"minimum": 0,
43+
"multipleOf": 1
44+
},
45+
"outer_width": {
46+
"type": "number",
47+
"minimum": 0,
48+
"multipleOf": 0.01
49+
},
50+
"outer_height": {
51+
"type": "number",
52+
"minimum": 0,
53+
"multipleOf": 0.01
54+
},
55+
"outer_depth": {
56+
"type": "number",
57+
"minimum": 0,
58+
"multipleOf": 0.01
59+
},
60+
"outer_unit": {
61+
"type": "string",
62+
"enum": [
63+
"mm",
64+
"in"
65+
]
66+
},
67+
"weight": {
68+
"type": "number",
69+
"minimum": 0,
70+
"multipleOf": 0.01
71+
},
72+
"max_weight": {
73+
"type": "number",
74+
"minimum": 0,
75+
"multipleOf": 0.01
76+
},
77+
"weight_unit": {
78+
"$ref": "urn:devicetype-library:generated-schema#/definitions/weight-unit"
79+
},
80+
"mounting_depth": {
81+
"type": "number",
82+
"minimum": 0,
83+
"multipleOf": 0.01
84+
},
85+
"starting_unit": {
86+
"type": "number",
87+
"minimum": 1,
88+
"multipleOf": 1
89+
},
90+
"desc_units": {
91+
"type": "boolean",
92+
"default": false
93+
},
94+
"comments": {
95+
"type": "string"
96+
}
97+
},
98+
"required": ["manufacturer", "model", "slug", "form_factor", "width", "u_height", "starting_unit"],
99+
"additionalProperties": false
100+
}

tests/definitions_test.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
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
22
import pickle_operations
33
from yaml_loader import DecimalSafeLoader
4-
from device_types import DeviceType, ModuleType, verify_filename, validate_components
4+
from device_types import DeviceType, ModuleType, RackType, verify_filename, validate_components
55
import decimal
66
import glob
77
import json
@@ -134,11 +134,13 @@ def test_environment():
134134
if USE_LOCAL_KNOWN_SLUGS:
135135
KNOWN_SLUGS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-slugs.pickle')
136136
KNOWN_MODULES = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-modules.pickle')
137+
KNOWN_RACKS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-racks.pickle')
137138
else:
138139
temp_dir = tempfile.TemporaryDirectory()
139140
repo = Repo.clone_from(url=NETBOX_DT_LIBRARY_URL, to_path=temp_dir.name)
140141
KNOWN_SLUGS = pickle_operations.read_pickle_data(f'{temp_dir.name}/tests/known-slugs.pickle')
141142
KNOWN_MODULES = pickle_operations.read_pickle_data(f'{temp_dir.name}/tests/known-modules.pickle')
143+
KNOWN_RACKS = pickle_operations.read_pickle_data(f'{ROOT_DIR}/tests/known-racks.pickle')
142144

143145
SCHEMA_REGISTRY = _generate_schema_registry()
144146

@@ -181,6 +183,12 @@ def test_definitions(file_path, schema, change_type):
181183
if "device-types" in file_path:
182184
# A device
183185
this_device = DeviceType(definition, file_path, change_type)
186+
elif "module-types" in file_path:
187+
# A module type
188+
this_device = ModuleType(definition, file_path, change_type)
189+
elif "rack-types" in file_path:
190+
# A rack type
191+
this_device = RackType(definition, file_path, change_type)
184192
else:
185193
# A module
186194
this_device = ModuleType(definition, file_path, change_type)

tests/device_types.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,28 @@ def _slugify_part_number(self):
179179
slugified = slugified[:-1]
180180
return slugified
181181

182+
class RackType:
183+
def __new__(cls, *args, **kwargs):
184+
return super().__new__(cls)
185+
186+
def __init__(self, definition, file_path, change_type):
187+
self.file_path = file_path
188+
self.isDevice = False
189+
self.definition = definition
190+
self.manufacturer = definition.get('manufacturer')
191+
self.model = definition.get('model')
192+
self._slug_model = self._slugify_model()
193+
self.change_type = change_type
194+
195+
def get_filepath(self):
196+
return self.file_path
197+
198+
def _slugify_model(self):
199+
slugified = self.model.casefold().replace(" ", "-").replace("sfp+", "sfpp").replace("poe+", "poep").replace("-+", "-plus").replace("+", "-plus-").replace("_", "-").replace("&", "-and-").replace("!", "").replace("/", "-").replace(",", "").replace("'", "").replace("*", "-")
200+
if slugified.endswith("-"):
201+
slugified = slugified[:-1]
202+
return slugified
203+
182204
def validate_component_names(component_names: (set or None)):
183205
if len(component_names) > 1:
184206
verify_name = list(component_names[0])
@@ -197,10 +219,17 @@ def validate_component_names(component_names: (set or None)):
197219
return False
198220
return True
199221

200-
def verify_filename(device: (DeviceType or ModuleType), KNOWN_MODULES: (set or None)):
222+
def verify_filename(device: (DeviceType or ModuleType or RackType), KNOWN_MODULES: (set or None)):
201223
head, tail = os.path.split(device.get_filepath())
202224
filename = tail.rsplit(".", 1)[0].casefold()
203225

226+
# Check if file is RackType
227+
if "rack-types" in device.file_path:
228+
if not filename == device._slug_model:
229+
device.failureMessage = f'{device.file_path} file name is invalid. Must be the model "{device._slug_model}"'
230+
return False
231+
return True
232+
204233
if not (filename == device._slug_model or filename == device._slug_part_number or filename == device.part_number.casefold()):
205234
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}"'
206235
return False

tests/generate-slug-list.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ def _generate_knowns(device_or_module):
107107

108108
_generate_knowns('module')
109109
pickle_operations.write_pickle_data(KNOWN_MODULES, f'{ROOT_DIR}/tests/known-modules.pickle')
110+
111+
_generate_knowns('rack')
112+
pickle_operations.write_pickle_data(KNOWN_MODULES, f'{ROOT_DIR}/tests/known-racks.pickle')

tests/known-racks.pickle

111 Bytes
Binary file not shown.

tests/test_configuration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
SCHEMAS = (
44
('device-types', 'devicetype.json'),
55
('module-types', 'moduletype.json'),
6+
('rack-types', 'racktype.json'),
67
)
78
SCHEMAS_BASEPATH = f"{os.getcwd()}/schema/"
89

0 commit comments

Comments
 (0)