Rack: ConnectX-3 SFP+ cards in the ZimaBoards' PCIe slots; correct NIC mapping
Lint / JS (eslint) (push) Successful in 10s
Lint / Python (flake8) (push) Successful in 51s
Lint / Notify on failure (push) Skipped
Lint / Deploy (push) Skipped
Lint / JS (eslint) (pull_request) Successful in 8s
Lint / Python (flake8) (pull_request) Successful in 50s
Lint / Notify on failure (pull_request) Skipped
Lint / Deploy (pull_request) Skipped
Security / Python Security (bandit) (pull_request) Successful in 1m7s
Security / Python Security (bandit) (push) Successful in 1m8s
Test / Python Tests (pytest) (push) Successful in 1m38s
Test / Python Tests (pytest) (pull_request) Successful in 1m38s

From the rack photo, each ZimaBoard has a single-port ConnectX-3 lying flat
in its side PCIe slot with a DAC attached. Front drawing "ZimaBoard 832 +
ConnectX-3" draws the card beside the board at scale: PCB at board height,
heatsink, SFP+ cage with the DAC and pull tab, and a Gandalf link mark for
the card's interface.

The jacks were lit in NIC name order, which put the ConnectX-3 (enp1s0, PCI
bus 1) on the left RJ45. The onboard ports are enp2s0 (left) and enp3s0
(right); the PCIe card is enp1s0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-11 00:35:25 -05:00
co-authored by Claude Opus 5.5
parent a9de175d90
commit 486a93c5a0
4 changed files with 67 additions and 7 deletions
+5 -3
View File
@@ -150,9 +150,11 @@ The rack front, drawn from the rack units set on cutsheet items, top first.
and facing.
- **Shelves to scale**: a shelf draws its contents left to right by **Position on shelf**, at true scale
(a 19" panel is 482.6 mm). Gear with a drawing, such as the IceWhale ZimaBoard 832 (traced from
IceWhale's labelled photo of its port end), shows its real ports: each RJ45's link light follows the
host's physical NICs in name order (first NIC on the left jack), and the status glow follows the
host. Anything else is a labelled box.
IceWhale's labelled photo of its port end), shows its real ports: the onboard RJ45 link lights follow
enp2s0 (left) and enp3s0 (right), and the status glow follows the host. The "ZimaBoard 832 +
ConnectX-3" drawing adds the single-port SFP+ card lying flat in the side PCIe slot, as in the rack,
with a DAC attached; its round dot is Gandalf's link mark for enp1s0 (the slot is PCI bus 1), not an
LED on the card. Anything else is a labelled box.
- **Racks**: admins name a rack and set its height with **Rack settings** on the page, and add more with
**+ Rack** (a tab per rack). Renaming a rack moves its items; an empty rack can be deleted. Each item
has a rack (empty means the first). Until a rack is saved, racks come from config:
+1
View File
@@ -38,6 +38,7 @@ CHASSIS = {
'sliger-cx4712': 'Sliger CX4712 4U (10-bay)',
'rosewill-rsv-l4500u': 'Rosewill RSV-L4500U 4U (15-bay)',
'zimaboard-832': 'IceWhale ZimaBoard 832 (shelf gear)',
'zimaboard-832-cx3': 'IceWhale ZimaBoard 832 + ConnectX-3 SFP+ in PCIe (shelf gear)',
'server-1u': 'Generic 1U server',
'server-2u': 'Generic 2U server',
'server-4u': 'Generic 4U server',
+49 -4
View File
@@ -172,10 +172,18 @@ function zimaUsb(x, y) {
${Array.from({ length: 5 }, (_, i) => `<rect class="zb-usb-pin" x="${x + 50 + i * 24}" y="${y + 46}" width="10" height="4"/>`).join('')}`;
}
// Which NIC is which on a ZimaBoard: the onboard Realtek GbE ports enumerate on PCI buses 2 and 3
// (enp2s0 left jack, enp3s0 right), and a card in the PCIe slot on bus 1 (enp1s0, enp1s0d1).
const ZIMA_NICS = { rj45: ['enp2s0', 'enp3s0'], slot: /^enp1s0/ };
function zimaNic(ctx, name) {
const n = (ctx.interfaces || []).find(i => i.name === name);
return n ? n.up : ctx.status === 'up';
}
function zimaFront(ctx) {
// ctx: { status, interfaces: [{name, up}] } - one RJ45 per NIC, left to right
const nics = ctx.interfaces || [];
const link = i => (nics[i] ? nics[i].up : ctx.status === 'up');
// ctx: { status, interfaces: [{name, up}] }
const link = i => zimaNic(ctx, ZIMA_NICS.rj45[i]);
const on = ctx.status === 'up' || ctx.status === 'degraded';
const pads = [60, 105, 420, 470, 640, 700, 790, 835]
.map(x => `<rect class="zb-pad" x="${x}" y="309" width="12" height="7"/>`).join('');
@@ -199,10 +207,47 @@ function zimaFront(ctx) {
<circle class="zb-dc-pin" cx="744" cy="242" r="8"/>`;
}
// A Mellanox ConnectX-3 single-port SFP+ card (MCX311A) in the ZimaBoard's side PCIe slot, lying flat
// beside the board as in the rack photo (wiki.lotusguild.org/zimaboards.jpg): its front edge is about
// 54 mm (572 px at the ZimaBoard photo's 10.59 px/mm), PCB at the board's PCB height, heatsink on the
// chip, and the SFP+ cage toward the board with a DAC plugged in and its pull tab hanging down.
// The round dot on the cage is Gandalf's link mark (like the cutsheet marks), not an LED on the card.
const CX3_PX = 572;
function cx3Card(up) {
return `
<rect class="cx-part" x="40" y="314" width="70" height="22" rx="2"/>
<rect class="cx-part" x="150" y="314" width="40" height="16" rx="2"/>
<rect class="cx-part" x="250" y="314" width="90" height="26" rx="2"/>
<rect class="cx-pcb" x="0" y="296" width="${CX3_PX}" height="18" rx="2"/>
<rect class="cx-pcb-edge" x="0" y="296" width="${CX3_PX}" height="4"/>
<rect class="cx-heatsink" x="110" y="232" width="210" height="64" rx="3"/>
${Array.from({ length: 9 }, (_, i) => `<rect class="cx-fin" x="${120 + i * 22}" y="232" width="9" height="64"/>`).join('')}
<rect class="cx-cage" x="380" y="190" width="150" height="106" rx="4"/>
<rect class="cx-cage-lip" x="380" y="190" width="150" height="10" rx="3"/>
<rect class="cx-plug" x="392" y="206" width="126" height="80" rx="5"/>
<rect class="cx-boot" x="410" y="220" width="90" height="52" rx="10"/>
<circle class="cx-cable" cx="455" cy="246" r="20"/>
<path class="cx-tab" d="M441 286 V392 Q441 404 455 404 Q469 404 469 392 V286"/>
<circle class="cx-link" cx="520" cy="200" r="13" style="fill:${up ? '#3ad16b' : '#e0312b'}"><title>Gandalf: SFP+ link ${up ? 'up' : 'down'}</title></circle>`;
}
function zimaCx3Front(ctx) {
return `${cx3Card(zimaNic(ctx, (ctx.interfaces || []).map(i => i.name).find(n => ZIMA_NICS.slot.test(n)) || 'enp1s0'))}
<g transform="translate(${CX3_PX} 0)">${zimaFront(ctx)}</g>`;
}
const ZIMA_SPECS = 'Intel Celeron N3450, 8GB LPDDR4, 32GB eMMC, 2x GbE, 2x USB 3.0, 2x SATA, PCIe 2.0 x4, 6W';
const SHELF_GEAR = {
'zimaboard-832': {
label: 'IceWhale ZimaBoard 832', mm: ZIMA.mm, width: ZIMA.width, height: ZIMA.height, draw: zimaFront,
specs: 'Intel Celeron N3450, 8GB LPDDR4, 32GB eMMC, 2x GbE, 2x USB 3.0, 2x SATA, PCIe 2.0 x4, 6W',
specs: ZIMA_SPECS,
},
'zimaboard-832-cx3': {
label: 'IceWhale ZimaBoard 832 + ConnectX-3 SFP+', mm: ZIMA.mm * (ZIMA.width + CX3_PX) / ZIMA.width,
width: ZIMA.width + CX3_PX, height: ZIMA.height, draw: zimaCx3Front,
specs: `${ZIMA_SPECS}; Mellanox ConnectX-3 10G SFP+ (enp1s0) in the PCIe slot, DAC attached`,
},
};
+12
View File
@@ -1617,3 +1617,15 @@ body.fp-marks-off .fp-svg .fp-mark { display: none; }
.zb-dc-face { fill: #2b2b2b; }
.zb-dc-ring { fill: #4b4b4b; }
.zb-dc-pin { fill: #c9a43a; }
.cx-pcb { fill: #1d6b43; }
.cx-pcb-edge { fill: #2f8a5a; }
.cx-part { fill: #26292d; }
.cx-heatsink { fill: #18191b; }
.cx-fin { fill: #2c2e31; }
.cx-cage { fill: #c8cbcf; stroke: #85898f; stroke-width: 3; }
.cx-cage-lip { fill: #dde0e3; }
.cx-plug { fill: #2b2e33; }
.cx-boot { fill: #1b1d20; }
.cx-cable { fill: #0e0f10; }
.cx-tab { fill: none; stroke: #c9ccd0; stroke-width: 9; stroke-linejoin: round; }
.cx-link { stroke: #101418; stroke-width: 3; }