feat(rack): inline interactive elevation with links, tooltips, status borders
This commit is contained in:
parent
d5cfe9665c
commit
8d39fbcdf5
4 changed files with 362 additions and 18 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="1012" viewBox="0 0 616 1012" font-family="sans-serif" font-size="11">
|
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="1012" viewBox="0 0 616 1012" style="max-width:100%;height:auto" font-family="sans-serif" font-size="11">
|
||||||
<rect width="616" height="1012" fill="#ffffff"/>
|
<rect width="616" height="1012" fill="#ffffff"/>
|
||||||
<text x="12" y="28" font-size="16" font-weight="bold">Rack rack01</text>
|
<text x="12" y="28" font-size="16" font-weight="bold">Rack rack01</text>
|
||||||
<text x="178" y="34" text-anchor="middle" font-weight="bold">front</text>
|
<text x="178" y="34" text-anchor="middle" font-weight="bold">front</text>
|
||||||
|
|
@ -147,25 +147,55 @@
|
||||||
<text x="54" y="954" text-anchor="end" fill="#999">46</text>
|
<text x="54" y="954" text-anchor="end" fill="#999">46</text>
|
||||||
<text x="54" y="974" text-anchor="end" fill="#999">47</text>
|
<text x="54" y="974" text-anchor="end" fill="#999">47</text>
|
||||||
<text x="54" y="994" text-anchor="end" fill="#999">48</text>
|
<text x="54" y="994" text-anchor="end" fill="#999">48</text>
|
||||||
<rect x="59" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333"/>
|
<a href="/hardware/srv04/">
|
||||||
|
<title>srv04 · server · staging · cluster: — · U5–U6</title>
|
||||||
|
<rect x="59" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
<text x="178" y="144" text-anchor="middle" fill="#ffffff">srv04 (U5–U6)</text>
|
<text x="178" y="144" text-anchor="middle" fill="#ffffff">srv04 (U5–U6)</text>
|
||||||
<rect x="349" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/srv05/">
|
||||||
|
<title>srv05 · server · staging · cluster: — · U5–U6</title>
|
||||||
|
<rect x="349" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
<text x="468" y="144" text-anchor="middle" fill="#ffffff">srv05 (U5–U6)</text>
|
<text x="468" y="144" text-anchor="middle" fill="#ffffff">srv05 (U5–U6)</text>
|
||||||
<rect x="59" y="221" width="238" height="18" rx="3" fill="#59a14f" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/sw01/">
|
||||||
|
<title>sw01 · switch · in-use · cluster: — · U10</title>
|
||||||
|
<rect x="59" y="221" width="238" height="18" rx="3" fill="#59a14f" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="178" y="234" text-anchor="middle" fill="#ffffff">sw01 (U10)</text>
|
<text x="178" y="234" text-anchor="middle" fill="#ffffff">sw01 (U10)</text>
|
||||||
<rect x="59" y="501" width="238" height="18" rx="3" fill="#9c755f" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/pp01/">
|
||||||
|
<title>pp01 · patch-panel · in-use · cluster: — · U24</title>
|
||||||
|
<rect x="59" y="501" width="238" height="18" rx="3" fill="#9c755f" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="178" y="514" text-anchor="middle" fill="#ffffff">pp01 (U24)</text>
|
<text x="178" y="514" text-anchor="middle" fill="#ffffff">pp01 (U24)</text>
|
||||||
<rect x="12" y="40" width="16" height="960" fill="#e15759" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/pdu01/">
|
||||||
|
<title>pdu01 · pdu · in-use · cluster: — · 0U left</title>
|
||||||
|
<rect x="12" y="40" width="16" height="960" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="20" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 20 520)">pdu01</text>
|
<text x="20" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 20 520)">pdu01</text>
|
||||||
<rect x="588" y="40" width="16" height="960" fill="#e15759" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/pdu02/">
|
||||||
|
<title>pdu02 · pdu · in-use · cluster: — · 0U right</title>
|
||||||
|
<rect x="588" y="40" width="16" height="960" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
<text x="596" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 596 520)">pdu02</text>
|
<text x="596" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 596 520)">pdu02</text>
|
||||||
<rect x="59" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/srv01/">
|
||||||
|
<title>srv01 · server · staging · cluster: tappaas · shf01/front/slot 1</title>
|
||||||
|
<rect x="59" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
<text x="118" y="861" text-anchor="middle" fill="#ffffff">srv01</text>
|
<text x="118" y="861" text-anchor="middle" fill="#ffffff">srv01</text>
|
||||||
<rect x="179" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333"/>
|
</a>
|
||||||
|
<a href="/hardware/srv02/">
|
||||||
|
<title>srv02 · server · staging · cluster: tappaas · shf01/front/slot 2</title>
|
||||||
|
<rect x="179" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
<text x="238" y="861" text-anchor="middle" fill="#ffffff">srv02</text>
|
<text x="238" y="861" text-anchor="middle" fill="#ffffff">srv02</text>
|
||||||
<rect x="58" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
</a>
|
||||||
<rect x="349" y="761" width="238" height="192" rx="3" fill="#4c78a8" stroke="#333"/>
|
<a href="/hardware/srv03/">
|
||||||
|
<title>srv03 · server · staging · cluster: tappaas · shf01/rear/slot 1</title>
|
||||||
|
<rect x="349" y="761" width="238" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
<text x="468" y="861" text-anchor="middle" fill="#ffffff">srv03</text>
|
<text x="468" y="861" text-anchor="middle" fill="#ffffff">srv03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf01/">
|
||||||
|
<title>shf01 · shelf · in-use · cluster: tappaas · U37–U46</title>
|
||||||
|
<rect x="58" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
<rect x="348" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
<rect x="348" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
<text x="178" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
<text x="178" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
||||||
|
</a>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -4,7 +4,211 @@ _Auto-generated from `docs/hardware/*.md` (items with `rack: rack01`) — do not
|
||||||
|
|
||||||
## Elevation
|
## Elevation
|
||||||
|
|
||||||

|
<div class="rack-elevation">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="1012" viewBox="0 0 616 1012" style="max-width:100%;height:auto" font-family="sans-serif" font-size="11">
|
||||||
|
<rect width="616" height="1012" fill="#ffffff"/>
|
||||||
|
<text x="12" y="28" font-size="16" font-weight="bold">Rack rack01</text>
|
||||||
|
<text x="178" y="34" text-anchor="middle" font-weight="bold">front</text>
|
||||||
|
<rect x="58" y="40" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="60" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="80" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="100" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="120" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="140" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="160" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="180" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="200" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="220" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="240" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="260" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="280" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="300" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="320" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="340" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="360" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="380" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="400" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="420" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="440" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="460" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="480" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="500" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="520" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="540" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="560" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="580" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="600" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="620" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="640" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="660" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="680" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="700" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="720" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="740" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="760" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="780" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="800" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="820" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="840" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="860" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="880" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="900" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="920" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="940" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="960" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="58" y="980" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<text x="468" y="34" text-anchor="middle" font-weight="bold">rear</text>
|
||||||
|
<rect x="348" y="40" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="60" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="80" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="100" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="120" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="140" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="160" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="180" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="200" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="220" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="240" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="260" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="280" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="300" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="320" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="340" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="360" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="380" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="400" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="420" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="440" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="460" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="480" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="500" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="520" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="540" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="560" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="580" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="600" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="620" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="640" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="660" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="680" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="700" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="720" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="740" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="760" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="780" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="800" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="820" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="840" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="860" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="880" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="900" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="920" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="940" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="960" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<rect x="348" y="980" width="240" height="20" fill="#f5f5f5" stroke="#e0e0e0"/>
|
||||||
|
<text x="54" y="54" text-anchor="end" fill="#999">1</text>
|
||||||
|
<text x="54" y="74" text-anchor="end" fill="#999">2</text>
|
||||||
|
<text x="54" y="94" text-anchor="end" fill="#999">3</text>
|
||||||
|
<text x="54" y="114" text-anchor="end" fill="#999">4</text>
|
||||||
|
<text x="54" y="134" text-anchor="end" fill="#999">5</text>
|
||||||
|
<text x="54" y="154" text-anchor="end" fill="#999">6</text>
|
||||||
|
<text x="54" y="174" text-anchor="end" fill="#999">7</text>
|
||||||
|
<text x="54" y="194" text-anchor="end" fill="#999">8</text>
|
||||||
|
<text x="54" y="214" text-anchor="end" fill="#999">9</text>
|
||||||
|
<text x="54" y="234" text-anchor="end" fill="#999">10</text>
|
||||||
|
<text x="54" y="254" text-anchor="end" fill="#999">11</text>
|
||||||
|
<text x="54" y="274" text-anchor="end" fill="#999">12</text>
|
||||||
|
<text x="54" y="294" text-anchor="end" fill="#999">13</text>
|
||||||
|
<text x="54" y="314" text-anchor="end" fill="#999">14</text>
|
||||||
|
<text x="54" y="334" text-anchor="end" fill="#999">15</text>
|
||||||
|
<text x="54" y="354" text-anchor="end" fill="#999">16</text>
|
||||||
|
<text x="54" y="374" text-anchor="end" fill="#999">17</text>
|
||||||
|
<text x="54" y="394" text-anchor="end" fill="#999">18</text>
|
||||||
|
<text x="54" y="414" text-anchor="end" fill="#999">19</text>
|
||||||
|
<text x="54" y="434" text-anchor="end" fill="#999">20</text>
|
||||||
|
<text x="54" y="454" text-anchor="end" fill="#999">21</text>
|
||||||
|
<text x="54" y="474" text-anchor="end" fill="#999">22</text>
|
||||||
|
<text x="54" y="494" text-anchor="end" fill="#999">23</text>
|
||||||
|
<text x="54" y="514" text-anchor="end" fill="#999">24</text>
|
||||||
|
<text x="54" y="534" text-anchor="end" fill="#999">25</text>
|
||||||
|
<text x="54" y="554" text-anchor="end" fill="#999">26</text>
|
||||||
|
<text x="54" y="574" text-anchor="end" fill="#999">27</text>
|
||||||
|
<text x="54" y="594" text-anchor="end" fill="#999">28</text>
|
||||||
|
<text x="54" y="614" text-anchor="end" fill="#999">29</text>
|
||||||
|
<text x="54" y="634" text-anchor="end" fill="#999">30</text>
|
||||||
|
<text x="54" y="654" text-anchor="end" fill="#999">31</text>
|
||||||
|
<text x="54" y="674" text-anchor="end" fill="#999">32</text>
|
||||||
|
<text x="54" y="694" text-anchor="end" fill="#999">33</text>
|
||||||
|
<text x="54" y="714" text-anchor="end" fill="#999">34</text>
|
||||||
|
<text x="54" y="734" text-anchor="end" fill="#999">35</text>
|
||||||
|
<text x="54" y="754" text-anchor="end" fill="#999">36</text>
|
||||||
|
<text x="54" y="774" text-anchor="end" fill="#999">37</text>
|
||||||
|
<text x="54" y="794" text-anchor="end" fill="#999">38</text>
|
||||||
|
<text x="54" y="814" text-anchor="end" fill="#999">39</text>
|
||||||
|
<text x="54" y="834" text-anchor="end" fill="#999">40</text>
|
||||||
|
<text x="54" y="854" text-anchor="end" fill="#999">41</text>
|
||||||
|
<text x="54" y="874" text-anchor="end" fill="#999">42</text>
|
||||||
|
<text x="54" y="894" text-anchor="end" fill="#999">43</text>
|
||||||
|
<text x="54" y="914" text-anchor="end" fill="#999">44</text>
|
||||||
|
<text x="54" y="934" text-anchor="end" fill="#999">45</text>
|
||||||
|
<text x="54" y="954" text-anchor="end" fill="#999">46</text>
|
||||||
|
<text x="54" y="974" text-anchor="end" fill="#999">47</text>
|
||||||
|
<text x="54" y="994" text-anchor="end" fill="#999">48</text>
|
||||||
|
<a href="/hardware/srv04/">
|
||||||
|
<title>srv04 · server · staging · cluster: — · U5–U6</title>
|
||||||
|
<rect x="59" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="178" y="144" text-anchor="middle" fill="#ffffff">srv04 (U5–U6)</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv05/">
|
||||||
|
<title>srv05 · server · staging · cluster: — · U5–U6</title>
|
||||||
|
<rect x="349" y="121" width="238" height="38" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="468" y="144" text-anchor="middle" fill="#ffffff">srv05 (U5–U6)</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/sw01/">
|
||||||
|
<title>sw01 · switch · in-use · cluster: — · U10</title>
|
||||||
|
<rect x="59" y="221" width="238" height="18" rx="3" fill="#59a14f" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="178" y="234" text-anchor="middle" fill="#ffffff">sw01 (U10)</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/pp01/">
|
||||||
|
<title>pp01 · patch-panel · in-use · cluster: — · U24</title>
|
||||||
|
<rect x="59" y="501" width="238" height="18" rx="3" fill="#9c755f" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="178" y="514" text-anchor="middle" fill="#ffffff">pp01 (U24)</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/pdu01/">
|
||||||
|
<title>pdu01 · pdu · in-use · cluster: — · 0U left</title>
|
||||||
|
<rect x="12" y="40" width="16" height="960" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="20" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 20 520)">pdu01</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/pdu02/">
|
||||||
|
<title>pdu02 · pdu · in-use · cluster: — · 0U right</title>
|
||||||
|
<rect x="588" y="40" width="16" height="960" fill="#e15759" stroke="#333333" stroke-width="1.5"/>
|
||||||
|
<text x="596" y="520" text-anchor="middle" fill="#ffffff" transform="rotate(-90 596 520)">pdu02</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/srv01/">
|
||||||
|
<title>srv01 · server · staging · cluster: tappaas · shf01/front/slot 1</title>
|
||||||
|
<rect x="59" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="118" 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="179" y="761" width="118" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="238" y="861" 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="349" y="761" width="238" height="192" rx="3" fill="#4c78a8" stroke="#333333" stroke-width="1.5" stroke-dasharray="4 2"/>
|
||||||
|
<text x="468" y="861" text-anchor="middle" fill="#ffffff">srv03</text>
|
||||||
|
</a>
|
||||||
|
<a href="/hardware/shf01/">
|
||||||
|
<title>shf01 · shelf · in-use · cluster: tappaas · U37–U46</title>
|
||||||
|
<rect x="58" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<rect x="348" y="954" width="240" height="6" fill="#bab0ac" stroke="#333"/>
|
||||||
|
<text x="178" y="959" text-anchor="middle" fill="#333" font-size="9">shf01</text>
|
||||||
|
</a>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[Download SVG](rack01-elevation.svg)
|
||||||
|
|
||||||
## Power
|
## Power
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,54 @@ def _esc(s: object) -> str:
|
||||||
return str(s).replace("&", "&").replace("<", "<").replace(">", ">")
|
return str(s).replace("&", "&").replace("<", "<").replace(">", ">")
|
||||||
|
|
||||||
|
|
||||||
|
STATUS_STROKE: dict[str, tuple[str, float, str]] = {
|
||||||
|
"in-use": ("#333333", 1.5, ""),
|
||||||
|
"staging": ("#333333", 1.5, "4 2"),
|
||||||
|
"broken": ("#e15759", 3, ""),
|
||||||
|
"spare": ("#bbbbbb", 1.5, ""),
|
||||||
|
"donated": ("#bbbbbb", 1.5, ""),
|
||||||
|
}
|
||||||
|
DEFAULT_STATUS_STROKE: tuple[str, float, str] = ("#333333", 1.5, "")
|
||||||
|
|
||||||
|
|
||||||
|
def _status_stroke(status: object) -> tuple[str, float, str]:
|
||||||
|
return STATUS_STROKE.get(status, DEFAULT_STATUS_STROKE)
|
||||||
|
|
||||||
|
|
||||||
|
def _stroke_attrs(status: object) -> str:
|
||||||
|
stroke, sw, dash = _status_stroke(status)
|
||||||
|
dash_attr = f' stroke-dasharray="{dash}"' if dash else ""
|
||||||
|
return f'stroke="{stroke}" stroke-width="{sw}"{dash_attr}'
|
||||||
|
|
||||||
|
|
||||||
|
def _host_url(host: object) -> str:
|
||||||
|
return f"/hardware/{host}/"
|
||||||
|
|
||||||
|
|
||||||
|
def _placement(fm: dict) -> str:
|
||||||
|
if "mounted_on" in fm:
|
||||||
|
return (
|
||||||
|
f"{fm.get('mounted_on', '?')}/{fm.get('shelf_face', '')}/"
|
||||||
|
f"slot {fm.get('shelf_slot', '')}"
|
||||||
|
)
|
||||||
|
face = fm.get("rack_face")
|
||||||
|
if face in ZERO_U_FACES:
|
||||||
|
return f"0U {face}"
|
||||||
|
u = fm.get("rack_u")
|
||||||
|
h = fm.get("u_height")
|
||||||
|
if isinstance(u, int) and isinstance(h, int):
|
||||||
|
return f"U{u}" if h == 1 else f"U{u}–U{u + h - 1}"
|
||||||
|
return "?"
|
||||||
|
|
||||||
|
|
||||||
|
def _tooltip(fm: dict) -> str:
|
||||||
|
host = fm.get("hostname", "?")
|
||||||
|
return _esc(
|
||||||
|
f"{host} · {fm.get('kind', '')} · {fm.get('status', '')} · "
|
||||||
|
f"cluster: {fm.get('cluster', '—')} · {_placement(fm)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _sorted_items(items: list[dict]) -> list[dict]:
|
def _sorted_items(items: list[dict]) -> list[dict]:
|
||||||
"""Deterministic order: faced items by U then hostname, 0U items last."""
|
"""Deterministic order: faced items by U then hostname, 0U items last."""
|
||||||
return sorted(
|
return sorted(
|
||||||
|
|
@ -329,6 +377,7 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
p.append(
|
p.append(
|
||||||
f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" '
|
f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" '
|
||||||
f'height="{height}" viewBox="0 0 {width} {height}" '
|
f'height="{height}" viewBox="0 0 {width} {height}" '
|
||||||
|
f'style="max-width:100%;height:auto" '
|
||||||
f'font-family="sans-serif" font-size="11">'
|
f'font-family="sans-serif" font-size="11">'
|
||||||
)
|
)
|
||||||
p.append(f'<rect width="{width}" height="{height}" fill="#ffffff"/>')
|
p.append(f'<rect width="{width}" height="{height}" fill="#ffffff"/>')
|
||||||
|
|
@ -365,15 +414,19 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
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", "?")
|
||||||
urange = f"U{u}" if h == 1 else f"U{u}–U{u + h - 1}"
|
urange = f"U{u}" if h == 1 else f"U{u}–U{u + h - 1}"
|
||||||
|
p.append(f'<a href="{_host_url(name)}">')
|
||||||
|
p.append(f"<title>{_tooltip(fm)}</title>")
|
||||||
p.append(
|
p.append(
|
||||||
f'<rect x="{col_x + 1}" y="{y + 1}" width="{COL_W - 2}" '
|
f'<rect x="{col_x + 1}" y="{y + 1}" width="{COL_W - 2}" '
|
||||||
f'height="{box_h - 2}" rx="3" fill="{color}" stroke="#333"/>'
|
f'height="{box_h - 2}" rx="3" fill="{color}" '
|
||||||
|
f"{_stroke_attrs(fm.get('status'))}/>"
|
||||||
)
|
)
|
||||||
p.append(
|
p.append(
|
||||||
f'<text x="{col_x + COL_W // 2}" y="{y + box_h // 2 + 4}" '
|
f'<text x="{col_x + COL_W // 2}" y="{y + box_h // 2 + 4}" '
|
||||||
f'text-anchor="middle" fill="#ffffff">'
|
f'text-anchor="middle" fill="#ffffff">'
|
||||||
f'{_esc(name)} ({urange})</text>'
|
f"{_esc(name)} ({urange})</text>"
|
||||||
)
|
)
|
||||||
|
p.append("</a>")
|
||||||
|
|
||||||
for fm in items:
|
for fm in items:
|
||||||
if fm.get("kind") == "shelf" or "mounted_on" in fm:
|
if fm.get("kind") == "shelf" or "mounted_on" in fm:
|
||||||
|
|
@ -389,14 +442,17 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
name = fm.get("hostname", "?")
|
name = fm.get("hostname", "?")
|
||||||
cx = x + RAIL_W // 2
|
cx = x + RAIL_W // 2
|
||||||
cy = top + body_h // 2
|
cy = top + body_h // 2
|
||||||
|
p.append(f'<a href="{_host_url(name)}">')
|
||||||
|
p.append(f"<title>{_tooltip(fm)}</title>")
|
||||||
p.append(
|
p.append(
|
||||||
f'<rect x="{x}" y="{top}" width="{RAIL_W}" height="{body_h}" '
|
f'<rect x="{x}" y="{top}" width="{RAIL_W}" height="{body_h}" '
|
||||||
f'fill="{color}" stroke="#333"/>'
|
f"fill=\"{color}\" {_stroke_attrs(fm.get('status'))}/>"
|
||||||
)
|
)
|
||||||
p.append(
|
p.append(
|
||||||
f'<text x="{cx}" y="{cy}" text-anchor="middle" fill="#ffffff" '
|
f'<text x="{cx}" y="{cy}" text-anchor="middle" fill="#ffffff" '
|
||||||
f'transform="rotate(-90 {cx} {cy})">{_esc(name)}</text>'
|
f'transform="rotate(-90 {cx} {cy})">{_esc(name)}</text>'
|
||||||
)
|
)
|
||||||
|
p.append("</a>")
|
||||||
|
|
||||||
for idx, fm in enumerate(left_items):
|
for idx, fm in enumerate(left_items):
|
||||||
draw_rail(fm, PAD + idx * RAIL_W)
|
draw_rail(fm, PAD + idx * RAIL_W)
|
||||||
|
|
@ -430,14 +486,21 @@ 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", "?")
|
||||||
|
p.append(f'<a href="{_host_url(mname)}">')
|
||||||
|
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="{y + 1}" width="{bw - 2}" '
|
||||||
f'height="{avail_h - 2}" rx="3" fill="{mcolor}" stroke="#333"/>'
|
f'height="{avail_h - 2}" rx="3" fill="{mcolor}" '
|
||||||
|
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="{y + avail_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(f'<a href="{_host_url(sname)}">')
|
||||||
|
p.append(f"<title>{_tooltip(fm)}</title>")
|
||||||
|
for col_x in (front_x, rear_x):
|
||||||
p.append(
|
p.append(
|
||||||
f'<rect x="{col_x}" y="{strip_y}" width="{COL_W}" '
|
f'<rect x="{col_x}" y="{strip_y}" width="{COL_W}" '
|
||||||
f'height="{SHELF_STRIP_H}" fill="{shelf_color}" stroke="#333"/>'
|
f'height="{SHELF_STRIP_H}" fill="{shelf_color}" stroke="#333"/>'
|
||||||
|
|
@ -446,6 +509,7 @@ def render_svg(rack: str, items: list[dict]) -> str:
|
||||||
f'<text x="{front_x + COL_W // 2}" y="{strip_y + SHELF_STRIP_H - 1}" '
|
f'<text x="{front_x + COL_W // 2}" y="{strip_y + SHELF_STRIP_H - 1}" '
|
||||||
f'text-anchor="middle" fill="#333" font-size="9">{_esc(sname)}</text>'
|
f'text-anchor="middle" fill="#333" font-size="9">{_esc(sname)}</text>'
|
||||||
)
|
)
|
||||||
|
p.append("</a>")
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -559,7 +623,11 @@ def render_page(rack: str, items: list[dict]) -> str:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("## Elevation")
|
lines.append("## Elevation")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append(f"")
|
lines.append('<div class="rack-elevation">')
|
||||||
|
lines.append(render_svg(rack, items).rstrip())
|
||||||
|
lines.append("</div>")
|
||||||
|
lines.append("")
|
||||||
|
lines.append(f"[Download SVG]({rack}-elevation.svg)")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
power = render_power(rack, items)
|
power = render_power(rack, items)
|
||||||
if power:
|
if power:
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,9 @@ def test_render_page_has_banner_image_and_table():
|
||||||
items = [item(hostname="mf00", rack_u=1, u_height=2, rack_face="front")]
|
items = [item(hostname="mf00", rack_u=1, u_height=2, rack_face="front")]
|
||||||
page = gen_rack.render_page("rack01", items)
|
page = gen_rack.render_page("rack01", items)
|
||||||
assert "do not edit by hand" in page
|
assert "do not edit by hand" in page
|
||||||
assert "" in page
|
assert '<div class="rack-elevation">' in page
|
||||||
|
assert "<svg" in page
|
||||||
|
assert "[Download SVG](rack01-elevation.svg)" in page
|
||||||
assert "../../hardware/mf00.md" in page
|
assert "../../hardware/mf00.md" in page
|
||||||
assert "U1–U2" in page
|
assert "U1–U2" in page
|
||||||
|
|
||||||
|
|
@ -632,3 +634,43 @@ def test_render_page_lists_mounted_devices():
|
||||||
assert "../../hardware/srv01.md" in page
|
assert "../../hardware/srv01.md" in page
|
||||||
assert "front · shf01/1" in page
|
assert "front · shf01/1" in page
|
||||||
assert "U37–U46" in page # mounted device shows its shelf's U-range
|
assert "U37–U46" in page # mounted device shows its shelf's U-range
|
||||||
|
|
||||||
|
|
||||||
|
def test_svg_boxes_link_to_host_pages():
|
||||||
|
items = [item(hostname="srv04", rack_u=5, u_height=1, rack_face="front")]
|
||||||
|
svg = gen_rack.render_svg("rack01", items)
|
||||||
|
assert '<a href="/hardware/srv04/">' in svg
|
||||||
|
assert "<title>" in svg
|
||||||
|
|
||||||
|
|
||||||
|
def test_svg_status_border_styles():
|
||||||
|
staging = gen_rack.render_svg("rack01", [
|
||||||
|
item(hostname="a", rack_u=1, u_height=1, rack_face="front",
|
||||||
|
status="staging")])
|
||||||
|
broken = gen_rack.render_svg("rack01", [
|
||||||
|
item(hostname="b", rack_u=1, u_height=1, rack_face="front",
|
||||||
|
status="broken")])
|
||||||
|
assert 'stroke-dasharray="4 2"' in staging
|
||||||
|
assert 'stroke="#e15759"' in broken and 'stroke-width="3"' in broken
|
||||||
|
|
||||||
|
|
||||||
|
def test_svg_tooltip_has_cluster_and_placement():
|
||||||
|
items = [item(hostname="srv01", rack_u=1, u_height=1, rack_face="front",
|
||||||
|
status="staging", cluster="tappaas")]
|
||||||
|
svg = gen_rack.render_svg("rack01", items)
|
||||||
|
assert "cluster: tappaas" in svg
|
||||||
|
assert "U1" in svg
|
||||||
|
|
||||||
|
|
||||||
|
def test_svg_has_responsive_style():
|
||||||
|
svg = gen_rack.render_svg("rack01", [])
|
||||||
|
assert "max-width:100%" in svg
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_page_inlines_svg_with_download_link():
|
||||||
|
items = [item(hostname="srv04", rack_u=5, u_height=1, rack_face="front")]
|
||||||
|
page = gen_rack.render_page("rack01", items)
|
||||||
|
assert '<div class="rack-elevation">' in page
|
||||||
|
assert "<svg" in page
|
||||||
|
assert "[Download SVG](rack01-elevation.svg)" in page
|
||||||
|
assert "![Rack rack01 elevation]" not in page
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue