feat(rack): draw shelf-mounted tower heights in the elevation
Add an optional `chassis_u` field for shelf-mounted devices (their height in U where they stand on the shelf) and render it: - gen_rack draws each tower chassis_u U's tall, rising above the 1U shelf line; rail-mounted devices now paint on top so a PDU within a tower's span (e.g. pdu03 over srv05/06) stays visible - occupancy table shows each tower's real U-span (e.g. srv01 U37-U46) - validate_item checks chassis_u is a positive integer; absent chassis_u renders byte-identically to before - set chassis_u for srv01-07 (10/8/6/6/7/7/6U); document the field in the editing guide; regenerate rack01 artifacts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4dc975062a
commit
4153c8d1d9
14 changed files with 228 additions and 148 deletions
|
|
@ -118,12 +118,18 @@ Only files that declare a `rack:` appear in a rack elevation. The rack is 48U.
|
||||||
mounted_on: shf01 # an existing kind:shelf in the same rack
|
mounted_on: shf01 # an existing kind:shelf in the same rack
|
||||||
shelf_face: front # front | rear
|
shelf_face: front # front | rear
|
||||||
shelf_slot: 2 # integer ≥1
|
shelf_slot: 2 # integer ≥1
|
||||||
|
chassis_u: 6 # optional: device height in U (how tall it stands)
|
||||||
# no rack_u / u_height / rack_face
|
# no rack_u / u_height / rack_face
|
||||||
```
|
```
|
||||||
|
|
||||||
The shelf itself must be placed (have `rack_u` + `u_height`). Two devices
|
The shelf itself must be placed (have `rack_u` + `u_height`). Two devices
|
||||||
can't share the same `(shelf, face, slot)`.
|
can't share the same `(shelf, face, slot)`.
|
||||||
|
|
||||||
|
`chassis_u` is optional. Shelves are typically 1U trays; a device sitting on
|
||||||
|
one (e.g. a tower PC) stands `chassis_u` U's tall, rising above the shelf
|
||||||
|
line in the elevation without consuming those rack U's (so rail-mounted gear
|
||||||
|
may still occupy them). Omit it and the device just fills the shelf block.
|
||||||
|
|
||||||
### Power feeds
|
### Power feeds
|
||||||
|
|
||||||
A device draws power by listing feeds. Each feed must point at a real `kind:pdu`
|
A device draws power by listing feeds. Each feed must point at a real `kind:pdu`
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@ cluster: tappaas
|
||||||
|
|
||||||
- 1U full-depth tray at U46. Tower PCs stand on it and rise above U46; they are
|
- 1U full-depth tray at U46. Tower PCs stand on it and rise above U46; they are
|
||||||
not rail-mounted, so the U's above are not consumed in the rack model.
|
not rail-mounted, so the U's above are not consumed in the rack model.
|
||||||
- Front: srv01 (stands ~U37–U46), srv02 (~U39–U46).
|
- Front: srv01 (10U, U37–U46), srv02 (8U, U39–U46).
|
||||||
- Rear: srv03 (~U40–U46).
|
- Rear: srv03 (6U, U41–U46).
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@ rack_face: both
|
||||||
- 1U full-depth tray at U35. Tower PCs stand on it and rise above U35; they are
|
- 1U full-depth tray at U35. Tower PCs stand on it and rise above U35; they are
|
||||||
not rail-mounted, so the U's above are not consumed in the rack model
|
not rail-mounted, so the U's above are not consumed in the rack model
|
||||||
(e.g. pdu03 sits at U34, just above this shelf).
|
(e.g. pdu03 sits at U34, just above this shelf).
|
||||||
- Front: srv07 (stands ~U29–U35), srv04 (~U27–U35).
|
- Front: srv07 (6U, U30–U35), srv04 (6U, U30–U35).
|
||||||
- Rear: srv05 (~U27–U35), srv06 (~U27–U35).
|
- Rear: srv05 (7U, U29–U35), srv06 (7U, U29–U35).
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ rack: rack01
|
||||||
mounted_on: shf01
|
mounted_on: shf01
|
||||||
shelf_face: front
|
shelf_face: front
|
||||||
shelf_slot: 1
|
shelf_slot: 1
|
||||||
|
chassis_u: 10
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 1 }
|
- { pdu: pdu01, outlet: 1 }
|
||||||
links:
|
links:
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ rack: rack01
|
||||||
mounted_on: shf01
|
mounted_on: shf01
|
||||||
shelf_face: front
|
shelf_face: front
|
||||||
shelf_slot: 2
|
shelf_slot: 2
|
||||||
|
chassis_u: 8
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 2 }
|
- { pdu: pdu01, outlet: 2 }
|
||||||
links:
|
links:
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ rack: rack01
|
||||||
mounted_on: shf01
|
mounted_on: shf01
|
||||||
shelf_face: rear
|
shelf_face: rear
|
||||||
shelf_slot: 1
|
shelf_slot: 1
|
||||||
|
chassis_u: 6
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 3 }
|
- { pdu: pdu01, outlet: 3 }
|
||||||
links:
|
links:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ rack: rack01
|
||||||
mounted_on: shf02
|
mounted_on: shf02
|
||||||
shelf_face: front
|
shelf_face: front
|
||||||
shelf_slot: 2
|
shelf_slot: 2
|
||||||
|
chassis_u: 6
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 4 }
|
- { pdu: pdu01, outlet: 4 }
|
||||||
links:
|
links:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ rack: rack01
|
||||||
mounted_on: shf02
|
mounted_on: shf02
|
||||||
shelf_face: rear
|
shelf_face: rear
|
||||||
shelf_slot: 1
|
shelf_slot: 1
|
||||||
|
chassis_u: 7
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 5 }
|
- { pdu: pdu01, outlet: 5 }
|
||||||
links:
|
links:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ rack: rack01
|
||||||
mounted_on: shf02
|
mounted_on: shf02
|
||||||
shelf_face: rear
|
shelf_face: rear
|
||||||
shelf_slot: 2
|
shelf_slot: 2
|
||||||
|
chassis_u: 7
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 1 }
|
- { pdu: pdu01, outlet: 1 }
|
||||||
- { pdu: pdu02, outlet: 1 }
|
- { pdu: pdu02, outlet: 1 }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ rack: rack01
|
||||||
mounted_on: shf02
|
mounted_on: shf02
|
||||||
shelf_face: front
|
shelf_face: front
|
||||||
shelf_slot: 1
|
shelf_slot: 1
|
||||||
|
chassis_u: 6
|
||||||
power:
|
power:
|
||||||
- { pdu: pdu01, outlet: 1 }
|
- { pdu: pdu01, outlet: 1 }
|
||||||
- { pdu: pdu02, outlet: 1 }
|
- { pdu: pdu02, outlet: 1 }
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,65 @@
|
||||||
<text x="576" y="994" text-anchor="start" fill="#999">48</text>
|
<text x="576" y="994" text-anchor="start" fill="#999">48</text>
|
||||||
<rect x="42" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
<rect x="42" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
||||||
<rect x="332" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
<rect x="332" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
||||||
|
<a href="/hardware/srv01/">
|
||||||
|
<title>srv01 · server · in-use · cluster: tappaas · shf01/front/slot 1</title>
|
||||||
|
<rect x="43" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="102" y="861" text-anchor="middle" fill="#ffffff">srv01</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv02/">
|
||||||
|
<title>srv02 · server · staging · cluster: tappaas · shf01/front/slot 2</title>
|
||||||
|
<rect x="163" y="801" width="118" height="152" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="222" y="881" text-anchor="middle" fill="#ffffff">srv02</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv03/">
|
||||||
|
<title>srv03 · server · staging · cluster: tappaas · shf01/rear/slot 1</title>
|
||||||
|
<rect x="333" y="841" width="238" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="452" y="901" text-anchor="middle" fill="#ffffff">srv03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf01/">
|
||||||
|
<title>shf01 · shelf · in-use · cluster: tappaas · U46</title>
|
||||||
|
<rect x="42" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv07/">
|
||||||
|
<title>srv07 · server · staging · cluster: tappaas · shf02/front/slot 1</title>
|
||||||
|
<rect x="43" y="621" width="118" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="102" y="681" text-anchor="middle" fill="#ffffff">srv07</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv04/">
|
||||||
|
<title>srv04 · server · staging · cluster: — · shf02/front/slot 2</title>
|
||||||
|
<rect x="163" y="621" width="118" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="222" y="681" text-anchor="middle" fill="#ffffff">srv04</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv05/">
|
||||||
|
<title>srv05 · server · staging · cluster: — · shf02/rear/slot 1</title>
|
||||||
|
<rect x="333" y="601" width="118" height="132" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="392" y="671" text-anchor="middle" fill="#ffffff">srv05</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv06/">
|
||||||
|
<title>srv06 · server · staging · cluster: tappaas · shf02/rear/slot 2</title>
|
||||||
|
<rect x="453" y="601" width="118" height="132" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="512" y="671" text-anchor="middle" fill="#ffffff">srv06</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf02/">
|
||||||
|
<title>shf02 · shelf · in-use · cluster: — · U35</title>
|
||||||
|
<rect x="42" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="739" text-anchor="middle" fill="#333" font-size="9">shf02</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf03/">
|
||||||
|
<title>shf03 · shelf · in-use · cluster: — · U21</title>
|
||||||
|
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf04/">
|
||||||
|
<title>shf04 · shelf · in-use · cluster: — · U21</title>
|
||||||
|
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf04</text>
|
||||||
|
</a>
|
||||||
<a href="/hardware/pdu01/">
|
<a href="/hardware/pdu01/">
|
||||||
<title>pdu01 · pdu · in-use · cluster: — · U1</title>
|
<title>pdu01 · pdu · in-use · cluster: — · U1</title>
|
||||||
<rect x="333" y="41" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
<rect x="333" y="41" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
|
@ -272,65 +331,6 @@
|
||||||
<rect x="333" y="701" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
<rect x="333" y="701" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="452" y="714" text-anchor="middle" fill="#ffffff">pdu03 (U34)</text>
|
<text x="452" y="714" text-anchor="middle" fill="#ffffff">pdu03 (U34)</text>
|
||||||
</a>
|
</a>
|
||||||
<a href="/hardware/srv01/">
|
|
||||||
<title>srv01 · server · in-use · cluster: tappaas · shf01/front/slot 1</title>
|
|
||||||
<rect x="43" y="941" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5"/>
|
|
||||||
<text x="102" y="951" text-anchor="middle" fill="#ffffff">srv01</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv02/">
|
|
||||||
<title>srv02 · server · staging · cluster: tappaas · shf01/front/slot 2</title>
|
|
||||||
<rect x="163" y="941" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="222" y="951" text-anchor="middle" fill="#ffffff">srv02</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv03/">
|
|
||||||
<title>srv03 · server · staging · cluster: tappaas · shf01/rear/slot 1</title>
|
|
||||||
<rect x="333" y="941" width="238" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="452" y="951" text-anchor="middle" fill="#ffffff">srv03</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf01/">
|
|
||||||
<title>shf01 · shelf · in-use · cluster: tappaas · U46</title>
|
|
||||||
<rect x="42" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv07/">
|
|
||||||
<title>srv07 · server · staging · cluster: tappaas · shf02/front/slot 1</title>
|
|
||||||
<rect x="43" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="102" y="731" text-anchor="middle" fill="#ffffff">srv07</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv04/">
|
|
||||||
<title>srv04 · server · staging · cluster: — · shf02/front/slot 2</title>
|
|
||||||
<rect x="163" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="222" y="731" text-anchor="middle" fill="#ffffff">srv04</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv05/">
|
|
||||||
<title>srv05 · server · staging · cluster: — · shf02/rear/slot 1</title>
|
|
||||||
<rect x="333" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="392" y="731" text-anchor="middle" fill="#ffffff">srv05</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv06/">
|
|
||||||
<title>srv06 · server · staging · cluster: tappaas · shf02/rear/slot 2</title>
|
|
||||||
<rect x="453" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="512" y="731" text-anchor="middle" fill="#ffffff">srv06</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf02/">
|
|
||||||
<title>shf02 · shelf · in-use · cluster: — · U35</title>
|
|
||||||
<rect x="42" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="739" text-anchor="middle" fill="#333" font-size="9">shf02</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf03/">
|
|
||||||
<title>shf03 · shelf · in-use · cluster: — · U21</title>
|
|
||||||
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf03</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf04/">
|
|
||||||
<title>shf04 · shelf · in-use · cluster: — · U21</title>
|
|
||||||
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf04</text>
|
|
||||||
</a>
|
|
||||||
<text x="42" y="1020" font-weight="bold">Legend</text>
|
<text x="42" y="1020" font-weight="bold">Legend</text>
|
||||||
<rect x="42" y="1028" width="12" height="12" fill="#9c755f" stroke="#333"/>
|
<rect x="42" y="1028" width="12" height="12" fill="#9c755f" stroke="#333"/>
|
||||||
<text x="58" y="1038">patch-panel</text>
|
<text x="58" y="1038">patch-panel</text>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
|
@ -204,6 +204,65 @@ _Auto-generated from `docs/hardware/*.md` (items with `rack: rack01`) — do not
|
||||||
<text x="576" y="994" text-anchor="start" fill="#999">48</text>
|
<text x="576" y="994" text-anchor="start" fill="#999">48</text>
|
||||||
<rect x="42" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
<rect x="42" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
||||||
<rect x="332" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
<rect x="332" y="40" width="240" height="960" fill="none" stroke="#999" stroke-width="1.5"/>
|
||||||
|
<a href="/hardware/srv01/">
|
||||||
|
<title>srv01 · server · in-use · cluster: tappaas · shf01/front/slot 1</title>
|
||||||
|
<rect x="43" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="102" y="861" text-anchor="middle" fill="#ffffff">srv01</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv02/">
|
||||||
|
<title>srv02 · server · staging · cluster: tappaas · shf01/front/slot 2</title>
|
||||||
|
<rect x="163" y="801" width="118" height="152" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="222" y="881" text-anchor="middle" fill="#ffffff">srv02</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv03/">
|
||||||
|
<title>srv03 · server · staging · cluster: tappaas · shf01/rear/slot 1</title>
|
||||||
|
<rect x="333" y="841" width="238" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="452" y="901" text-anchor="middle" fill="#ffffff">srv03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf01/">
|
||||||
|
<title>shf01 · shelf · in-use · cluster: tappaas · U46</title>
|
||||||
|
<rect x="42" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv07/">
|
||||||
|
<title>srv07 · server · staging · cluster: tappaas · shf02/front/slot 1</title>
|
||||||
|
<rect x="43" y="621" width="118" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="102" y="681" text-anchor="middle" fill="#ffffff">srv07</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv04/">
|
||||||
|
<title>srv04 · server · staging · cluster: — · shf02/front/slot 2</title>
|
||||||
|
<rect x="163" y="621" width="118" height="112" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="222" y="681" text-anchor="middle" fill="#ffffff">srv04</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv05/">
|
||||||
|
<title>srv05 · server · staging · cluster: — · shf02/rear/slot 1</title>
|
||||||
|
<rect x="333" y="601" width="118" height="132" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="392" y="671" text-anchor="middle" fill="#ffffff">srv05</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv06/">
|
||||||
|
<title>srv06 · server · staging · cluster: tappaas · shf02/rear/slot 2</title>
|
||||||
|
<rect x="453" y="601" width="118" height="132" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="512" y="671" text-anchor="middle" fill="#ffffff">srv06</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf02/">
|
||||||
|
<title>shf02 · shelf · in-use · cluster: — · U35</title>
|
||||||
|
<rect x="42" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="739" text-anchor="middle" fill="#333" font-size="9">shf02</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf03/">
|
||||||
|
<title>shf03 · shelf · in-use · cluster: — · U21</title>
|
||||||
|
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf04/">
|
||||||
|
<title>shf04 · shelf · in-use · cluster: — · U21</title>
|
||||||
|
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf04</text>
|
||||||
|
</a>
|
||||||
<a href="/hardware/pdu01/">
|
<a href="/hardware/pdu01/">
|
||||||
<title>pdu01 · pdu · in-use · cluster: — · U1</title>
|
<title>pdu01 · pdu · in-use · cluster: — · U1</title>
|
||||||
<rect x="333" y="41" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
<rect x="333" y="41" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
|
@ -279,65 +338,6 @@ _Auto-generated from `docs/hardware/*.md` (items with `rack: rack01`) — do not
|
||||||
<rect x="333" y="701" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
<rect x="333" y="701" width="238" height="18" rx="3" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="452" y="714" text-anchor="middle" fill="#ffffff">pdu03 (U34)</text>
|
<text x="452" y="714" text-anchor="middle" fill="#ffffff">pdu03 (U34)</text>
|
||||||
</a>
|
</a>
|
||||||
<a href="/hardware/srv01/">
|
|
||||||
<title>srv01 · server · in-use · cluster: tappaas · shf01/front/slot 1</title>
|
|
||||||
<rect x="43" y="941" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5"/>
|
|
||||||
<text x="102" y="951" text-anchor="middle" fill="#ffffff">srv01</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv02/">
|
|
||||||
<title>srv02 · server · staging · cluster: tappaas · shf01/front/slot 2</title>
|
|
||||||
<rect x="163" y="941" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="222" y="951" text-anchor="middle" fill="#ffffff">srv02</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv03/">
|
|
||||||
<title>srv03 · server · staging · cluster: tappaas · shf01/rear/slot 1</title>
|
|
||||||
<rect x="333" y="941" width="238" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="452" y="951" text-anchor="middle" fill="#ffffff">srv03</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf01/">
|
|
||||||
<title>shf01 · shelf · in-use · cluster: tappaas · U46</title>
|
|
||||||
<rect x="42" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv07/">
|
|
||||||
<title>srv07 · server · staging · cluster: tappaas · shf02/front/slot 1</title>
|
|
||||||
<rect x="43" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="102" y="731" text-anchor="middle" fill="#ffffff">srv07</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv04/">
|
|
||||||
<title>srv04 · server · staging · cluster: — · shf02/front/slot 2</title>
|
|
||||||
<rect x="163" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="222" y="731" text-anchor="middle" fill="#ffffff">srv04</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv05/">
|
|
||||||
<title>srv05 · server · staging · cluster: — · shf02/rear/slot 1</title>
|
|
||||||
<rect x="333" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="392" y="731" text-anchor="middle" fill="#ffffff">srv05</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/srv06/">
|
|
||||||
<title>srv06 · server · staging · cluster: tappaas · shf02/rear/slot 2</title>
|
|
||||||
<rect x="453" y="721" width="118" height="12" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
|
||||||
<text x="512" y="731" text-anchor="middle" fill="#ffffff">srv06</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf02/">
|
|
||||||
<title>shf02 · shelf · in-use · cluster: — · U35</title>
|
|
||||||
<rect x="42" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="734" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="739" text-anchor="middle" fill="#333" font-size="9">shf02</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf03/">
|
|
||||||
<title>shf03 · shelf · in-use · cluster: — · U21</title>
|
|
||||||
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf03</text>
|
|
||||||
</a>
|
|
||||||
<a href="/hardware/shf04/">
|
|
||||||
<title>shf04 · shelf · in-use · cluster: — · U21</title>
|
|
||||||
<rect x="42" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<rect x="332" y="454" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
|
||||||
<text x="162" y="459" text-anchor="middle" fill="#333" font-size="9">shf04</text>
|
|
||||||
</a>
|
|
||||||
<text x="42" y="1020" font-weight="bold">Legend</text>
|
<text x="42" y="1020" font-weight="bold">Legend</text>
|
||||||
<rect x="42" y="1028" width="12" height="12" fill="#9c755f" stroke="#333"/>
|
<rect x="42" y="1028" width="12" height="12" fill="#9c755f" stroke="#333"/>
|
||||||
<text x="58" y="1038">patch-panel</text>
|
<text x="58" y="1038">patch-panel</text>
|
||||||
|
|
@ -499,11 +499,11 @@ flowchart LR
|
||||||
| U25 | [pp02](../../hardware/pp02.md) | patch-panel | front | in-use |
|
| U25 | [pp02](../../hardware/pp02.md) | patch-panel | front | in-use |
|
||||||
| U34 | [pdu03](../../hardware/pdu03.md) | pdu | rear | in-use |
|
| U34 | [pdu03](../../hardware/pdu03.md) | pdu | rear | in-use |
|
||||||
| U35 | [shf02](../../hardware/shf02.md) | shelf | both | in-use |
|
| U35 | [shf02](../../hardware/shf02.md) | shelf | both | in-use |
|
||||||
| U35 | [srv07](../../hardware/srv07.md) | server | front · shf02/1 | staging |
|
| U30–U35 | [srv07](../../hardware/srv07.md) | server | front · shf02/1 | staging |
|
||||||
| U35 | [srv04](../../hardware/srv04.md) | server | front · shf02/2 | staging |
|
| U30–U35 | [srv04](../../hardware/srv04.md) | server | front · shf02/2 | staging |
|
||||||
| U35 | [srv05](../../hardware/srv05.md) | server | rear · shf02/1 | staging |
|
| U29–U35 | [srv05](../../hardware/srv05.md) | server | rear · shf02/1 | staging |
|
||||||
| U35 | [srv06](../../hardware/srv06.md) | server | rear · shf02/2 | staging |
|
| U29–U35 | [srv06](../../hardware/srv06.md) | server | rear · shf02/2 | staging |
|
||||||
| U46 | [shf01](../../hardware/shf01.md) | shelf | both | in-use |
|
| U46 | [shf01](../../hardware/shf01.md) | shelf | both | in-use |
|
||||||
| U46 | [srv01](../../hardware/srv01.md) | server | front · shf01/1 | in-use |
|
| U37–U46 | [srv01](../../hardware/srv01.md) | server | front · shf01/1 | in-use |
|
||||||
| U46 | [srv02](../../hardware/srv02.md) | server | front · shf01/2 | staging |
|
| U39–U46 | [srv02](../../hardware/srv02.md) | server | front · shf01/2 | staging |
|
||||||
| U46 | [srv03](../../hardware/srv03.md) | server | rear · shf01/1 | staging |
|
| U41–U46 | [srv03](../../hardware/srv03.md) | server | rear · shf01/1 | staging |
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,14 @@ def validate_item(fm: dict) -> None:
|
||||||
f"{name}: 'shelf_slot' must be a whole number 1 or higher "
|
f"{name}: 'shelf_slot' must be a whole number 1 or higher "
|
||||||
f"(got {slot!r})."
|
f"(got {slot!r})."
|
||||||
)
|
)
|
||||||
|
if "chassis_u" in fm:
|
||||||
|
cu = fm.get("chassis_u")
|
||||||
|
if isinstance(cu, bool) or not isinstance(cu, int) or cu < 1:
|
||||||
|
raise SchemaError(
|
||||||
|
f"{name}: 'chassis_u' is the device's height in U where it "
|
||||||
|
f"stands on the shelf — it must be a whole number 1 or "
|
||||||
|
f"higher (got {cu!r})."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
face = fm.get("rack_face")
|
face = fm.get("rack_face")
|
||||||
if face not in FACES:
|
if face not in FACES:
|
||||||
|
|
@ -495,15 +503,6 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
)
|
)
|
||||||
p.append("</a>")
|
p.append("</a>")
|
||||||
|
|
||||||
for fm in items:
|
|
||||||
if fm.get("kind") == "shelf" or "mounted_on" in fm:
|
|
||||||
continue
|
|
||||||
face = fm.get("rack_face")
|
|
||||||
if face in ("front", "both"):
|
|
||||||
draw_device(fm, front_x)
|
|
||||||
if face in ("rear", "both"):
|
|
||||||
draw_device(fm, rear_x)
|
|
||||||
|
|
||||||
def draw_rail(fm: dict, x: int) -> None:
|
def draw_rail(fm: dict, x: int) -> None:
|
||||||
color = KIND_COLORS.get(fm.get("kind", ""), DEFAULT_COLOR)
|
color = KIND_COLORS.get(fm.get("kind", ""), DEFAULT_COLOR)
|
||||||
name = fm.get("hostname", "?")
|
name = fm.get("hostname", "?")
|
||||||
|
|
@ -522,11 +521,6 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
)
|
)
|
||||||
p.append("</a>")
|
p.append("</a>")
|
||||||
|
|
||||||
for idx, fm in enumerate(left_items):
|
|
||||||
draw_rail(fm, PAD + idx * RAIL_W)
|
|
||||||
for idx, fm in enumerate(right_items):
|
|
||||||
draw_rail(fm, right_gutter_x + LABEL_W + idx * RAIL_W)
|
|
||||||
|
|
||||||
SHELF_STRIP_H = 6
|
SHELF_STRIP_H = 6
|
||||||
shelves = [i for i in items if i.get("kind") == "shelf"]
|
shelves = [i for i in items if i.get("kind") == "shelf"]
|
||||||
mounted = [i for i in items if "mounted_on" in i]
|
mounted = [i for i in items if "mounted_on" in i]
|
||||||
|
|
@ -537,7 +531,6 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
y = u_y(u)
|
y = u_y(u)
|
||||||
block_h = h * U_H
|
block_h = h * U_H
|
||||||
strip_y = y + block_h - SHELF_STRIP_H
|
strip_y = y + block_h - SHELF_STRIP_H
|
||||||
avail_h = block_h - SHELF_STRIP_H
|
|
||||||
shelf_color = KIND_COLORS.get("shelf", DEFAULT_COLOR)
|
shelf_color = KIND_COLORS.get("shelf", DEFAULT_COLOR)
|
||||||
sname = fm.get("hostname", "?")
|
sname = fm.get("hostname", "?")
|
||||||
for col_x, sface in ((front_x, "front"), (rear_x, "rear")):
|
for col_x, sface in ((front_x, "front"), (rear_x, "rear")):
|
||||||
|
|
@ -554,15 +547,22 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
bw = (COL_W - idx * sub_w) if idx == n - 1 else sub_w
|
bw = (COL_W - idx * sub_w) if idx == n - 1 else sub_w
|
||||||
mcolor = KIND_COLORS.get(m.get("kind", ""), DEFAULT_COLOR)
|
mcolor = KIND_COLORS.get(m.get("kind", ""), DEFAULT_COLOR)
|
||||||
mname = m.get("hostname", "?")
|
mname = m.get("hostname", "?")
|
||||||
|
# The device stands on the shelf strip and rises chassis_u U's
|
||||||
|
# upward; without chassis_u it fills the shelf block (legacy).
|
||||||
|
dev_u = m.get("chassis_u")
|
||||||
|
if not isinstance(dev_u, int) or isinstance(dev_u, bool) or dev_u < 1:
|
||||||
|
dev_u = h
|
||||||
|
dev_h = dev_u * U_H - SHELF_STRIP_H
|
||||||
|
by = strip_y - dev_h
|
||||||
p.append(f'<a href="{_host_url(mname)}">')
|
p.append(f'<a href="{_host_url(mname)}">')
|
||||||
p.append(f"<title>{_tooltip(m)}</title>")
|
p.append(f"<title>{_tooltip(m)}</title>")
|
||||||
p.append(
|
p.append(
|
||||||
f'<rect x="{bx + 1}" y="{y + 1}" width="{bw - 2}" '
|
f'<rect x="{bx + 1}" y="{by + 1}" width="{bw - 2}" '
|
||||||
f'height="{avail_h - 2}" rx="3" fill="{mcolor}" '
|
f'height="{dev_h - 2}" rx="3" fill="{mcolor}" '
|
||||||
f"{_stroke_attrs(m.get('status'))}/>"
|
f"{_stroke_attrs(m.get('status'))}/>"
|
||||||
)
|
)
|
||||||
p.append(
|
p.append(
|
||||||
f'<text x="{bx + bw // 2}" y="{y + avail_h // 2 + 4}" '
|
f'<text x="{bx + bw // 2}" y="{by + dev_h // 2 + 4}" '
|
||||||
f'text-anchor="middle" fill="#ffffff">{_esc(mname)}</text>'
|
f'text-anchor="middle" fill="#ffffff">{_esc(mname)}</text>'
|
||||||
)
|
)
|
||||||
p.append("</a>")
|
p.append("</a>")
|
||||||
|
|
@ -579,9 +579,26 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
)
|
)
|
||||||
p.append("</a>")
|
p.append("</a>")
|
||||||
|
|
||||||
|
# Paint order (bottom → top): shelves and their towers first, then
|
||||||
|
# U-mounted devices (so a rail-mounted PDU stays visible over a tower),
|
||||||
|
# then 0U side rails.
|
||||||
for fm in sorted(shelves, key=lambda s: s.get("hostname", "")):
|
for fm in sorted(shelves, key=lambda s: s.get("hostname", "")):
|
||||||
draw_shelf(fm)
|
draw_shelf(fm)
|
||||||
|
|
||||||
|
for fm in items:
|
||||||
|
if fm.get("kind") == "shelf" or "mounted_on" in fm:
|
||||||
|
continue
|
||||||
|
face = fm.get("rack_face")
|
||||||
|
if face in ("front", "both"):
|
||||||
|
draw_device(fm, front_x)
|
||||||
|
if face in ("rear", "both"):
|
||||||
|
draw_device(fm, rear_x)
|
||||||
|
|
||||||
|
for idx, fm in enumerate(left_items):
|
||||||
|
draw_rail(fm, PAD + idx * RAIL_W)
|
||||||
|
for idx, fm in enumerate(right_items):
|
||||||
|
draw_rail(fm, right_gutter_x + LABEL_W + idx * RAIL_W)
|
||||||
|
|
||||||
legend_y = top + body_h + PAD + 8
|
legend_y = top + body_h + PAD + 8
|
||||||
p.append(
|
p.append(
|
||||||
f'<text x="{front_x}" y="{legend_y}" font-weight="bold">Legend</text>'
|
f'<text x="{front_x}" y="{legend_y}" font-weight="bold">Legend</text>'
|
||||||
|
|
@ -773,6 +790,12 @@ def render_page(rack: str, items: list[dict]) -> str:
|
||||||
if target and isinstance(target.get("rack_u"), int):
|
if target and isinstance(target.get("rack_u"), int):
|
||||||
su = target["rack_u"]
|
su = target["rack_u"]
|
||||||
sh = target["u_height"]
|
sh = target["u_height"]
|
||||||
|
cu = fm.get("chassis_u")
|
||||||
|
if isinstance(cu, int) and not isinstance(cu, bool) and cu >= 1:
|
||||||
|
base = su + sh - 1 # the shelf's bottom U; towers rise from it
|
||||||
|
top = base - cu + 1
|
||||||
|
urange = f"U{base}" if cu == 1 else f"U{top}–U{base}"
|
||||||
|
else:
|
||||||
urange = f"U{su}" if sh == 1 else f"U{su}–U{su + sh - 1}"
|
urange = f"U{su}" if sh == 1 else f"U{su}–U{su + sh - 1}"
|
||||||
else:
|
else:
|
||||||
urange = "—"
|
urange = "—"
|
||||||
|
|
|
||||||
|
|
@ -623,6 +623,50 @@ def test_render_svg_draws_shelf_and_occupants():
|
||||||
assert "shf01 (U37" not in svg
|
assert "shf01 (U37" not in svg
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_accepts_chassis_u():
|
||||||
|
gen_rack.validate_item(item(hostname="srv01", mounted_on="shf01",
|
||||||
|
shelf_face="front", shelf_slot=1, chassis_u=10))
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_rejects_bad_chassis_u():
|
||||||
|
with pytest.raises(gen_rack.SchemaError):
|
||||||
|
gen_rack.validate_item(item(hostname="srv01", mounted_on="shf01",
|
||||||
|
shelf_face="front", shelf_slot=1, chassis_u=0))
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_svg_tower_height_reflects_chassis_u():
|
||||||
|
items = [
|
||||||
|
shelf(hostname="shf01", rack_u=46, u_height=1),
|
||||||
|
item(hostname="srv01", mounted_on="shf01", shelf_face="front",
|
||||||
|
shelf_slot=1, chassis_u=10),
|
||||||
|
]
|
||||||
|
svg = gen_rack.render_svg("rack01", items)
|
||||||
|
# 10U tower drawn as 10*U_H - SHELF_STRIP_H - 2 px tall
|
||||||
|
assert 'height="192"' in svg
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_svg_paints_rail_device_over_shelf_tower():
|
||||||
|
# A PDU rail-mounted within a tower's span must be painted after the tower
|
||||||
|
# (later in the SVG = on top) so it stays visible.
|
||||||
|
items = [
|
||||||
|
shelf(hostname="shf02", rack_u=35, u_height=1),
|
||||||
|
item(hostname="srv05", mounted_on="shf02", shelf_face="rear",
|
||||||
|
shelf_slot=1, chassis_u=7),
|
||||||
|
item(hostname="pdu03", kind="pdu", rack_u=34, u_height=1,
|
||||||
|
rack_face="rear"),
|
||||||
|
]
|
||||||
|
svg = gen_rack.render_svg("rack01", items)
|
||||||
|
assert svg.index("srv05") < svg.index("pdu03")
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_page_mounted_shows_chassis_span():
|
||||||
|
items = [shelf(hostname="shf01", rack_u=46, u_height=1),
|
||||||
|
item(hostname="srv01", mounted_on="shf01", shelf_face="front",
|
||||||
|
shelf_slot=1, chassis_u=10)]
|
||||||
|
page = gen_rack.render_page("rack01", items)
|
||||||
|
assert "U37–U46" in page
|
||||||
|
|
||||||
|
|
||||||
def test_render_svg_shelf_is_deterministic():
|
def test_render_svg_shelf_is_deterministic():
|
||||||
base = [
|
base = [
|
||||||
shelf(),
|
shelf(),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue