Source of the YABS board

These are the exact bytes the server runs. Compare the hash with your own sha256sum of the downloaded copy: if the two match, the page you read is the program you reviewed.
racks.php · 430 lines · 16.4 KiB · sha256 d8413b744e74a1202ca13cb6314123c8b88527e8ee774a521188210e994824d8 · download · board · README

<?php
// 3D hangar of the nodes that posted to the YABS board.
// One canvas, no dependency: the page ships its own isometric projection.
// Reads the board's SQLite database read-only and never writes to it.
// Deployed next to index.php in ~/www/public/yabs/.

declare(strict_types=1);
error_reporting(E_ALL & ~E_DEPRECATED);
ini_set('display_errors', '0');
date_default_timezone_set('Europe/Helsinki');

// Same database as index.php. Duplicated on purpose: the board is one file
// that must keep working if this page is deleted.
const DB_PATH = '/home/vasilves/yabs-data/yabs.sqlite';
const 
STEP = 1.7;      // floor spacing between racks, in scene units
const COLS = 4;        // racks per row inside a hall
const HALF_W = 0.55;   // half footprint of a rack, must match HALF in the script
const PAD = 0.55;      // free floor on every side of the racks in a hall

function h(?string $s): string
{
    return 
htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
}

function 
n($v, int $d = 2): string
{
    
// Dot for decimals, space for thousands, same notation as index.php.
    
return $v === null ? 'n/a' : number_format((float) $v, $d, '.', ' ');
}

$error = null;
$nodes = [];
try {
    
$db = new PDO('sqlite:' . DB_PATH, null, null, [
        
PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
    
// Latest run per node, plus how many runs that node ever posted.
    
$nodes = $db->query(
        
'SELECT r.*, (SELECT COUNT(*) FROM runs x WHERE x.node = r.node) AS n
           FROM runs r
           JOIN (SELECT node, MAX(tested_at) AS m FROM runs GROUP BY node) t
             ON t.node = r.node AND t.m = r.tested_at
          ORDER BY r.node'
    
)->fetchAll();
} catch (
Throwable $e) {
    
$error = $e->getMessage();
}

// PMSS puts the datacenter row in the first two letters of a hostname. Two
// rows share one datacenter, and nodes older than the scheme match no row.
const ROW_DC = ['le' => 'Lauttasaari', 'lt' => 'Lauttasaari',
                
'ke' => 'Kerava', 'kt' => 'Kerava', 'k2' => 'Kerava 2'];

// The hostname family ("le4", "le6", "lt5") is the hall. It comes from PMSS's
// naming, not from the database: no column here knows a datacenter.
$halls = [];
foreach (
$nodes as $row) {
    
$family = explode('-', (string) $row['node'])[0] ?: '?';
    
$halls[$family][] = $row;
}
ksort($halls);

$peak = 1.0;
foreach (
$nodes as $row) {
    
$peak = max($peak, (float) ($row['d1m'] ?? 0));
}

$racks = [];
$plates = [];
$offset = 0.0;
foreach (
$halls as $family => $list) {
    
// "3-229mighty" makes explode() return an int key, so cast before substr().
    
$dc = ROW_DC[substr((string) $family, 0, 2)] ?? null;   // datacenter row, if the name follows the scheme
    
$cols = max(1, min(COLS, count($list)));
    
$rows = (int) ceil(count($list) / $cols);
    foreach (
$list as $i => $row) {
        
$perf = $row['d1m'] === null ? null : (float) $row['d1m'];
        
$ratio = $perf === null ? 0.0 : min(1.0, $perf / $peak);
        
$parts = explode('-', (string) $row['node']);
        
$racks[] = [
            
'x'     => round($offset + ($i % $cols) * STEP, 3),
            
'z'     => round(intdiv($i, $cols) * STEP, 3),
            
'hh'    => round(0.5 + 1.7 * $ratio, 3),
            
't'     => round($ratio, 3),
            
'null'  => $perf === null,
            
'label' => end($parts),
            
'node'  => $row['node'],
            
'tip'   => sprintf(
                
"%s\n4k %s MB/s · %s IOPS\n64k %s · 512k %s · 1m %s MB/s\nsend %s · recv %s Mbit/s\n%s · %s cores · %s GiB RAM · %s GiB swap\n%s · %s · %s · GPU %s\n%d run(s), last %s",
                
$row['node'],
                
n($row['d4k']), n($row['d4k_iops'], 0),
                
n($row['d64k']), n($row['d512k']), n($row['d1m']),
                
n($row['send_mbit'], 0), n($row['recv_mbit'], 0),
                
$row['cpu'] ?? 'cpu?', $row['cores'] ?? '?', n($row['ram_gib'], 1), n($row['swap_gib'], 1),
                
$row['disk_gb'] === null ? 'disk?' : n((float) $row['disk_gb'] / 1000, 1) . ' TB',
                
$row['os'] ?? '?', $row['vm'] ?? '?', $row['gpu'] ?? 'none',
                (int) 
$row['n'],
                
$row['tested_at'] === null ? '?' : date('Y-m-d H:i T', (int) $row['tested_at'])
            ),
        ];
    }
    
// Symmetric margin, so every rack sits inside its slab. Offsetting from the
    // corner left the near rack 0.15 unit outside and 0.7 free on the far side.
    
$w = ($cols - 1) * STEP + 2 * (HALF_W + PAD);
    
$d = ($rows - 1) * STEP + 2 * (HALF_W + PAD);
    
$plates[] = [
        
'name'  => $dc === null ? (string) $family : $family . ' · ' . $dc,
        
'x'     => round($offset - HALF_W - PAD, 3),
        
'z'     => round(-HALF_W - PAD, 3),
        
'w'     => round($w, 3),
        
'd'     => round($d, 3),
    ];
    
$offset += $w + 1.5;
}

$scene = ['racks' => $racks, 'plates' => $plates, 'width' => round(max($offset, 4), 3), 'peak' => $peak, 'step' => STEP];
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>YABS hangar</title>
<link rel="icon" href="data:,">
<style>
  :root { color-scheme: dark; }
  * { box-sizing: border-box; }
  body { margin: 0; background: #0d1117; color: #c9d1d9;
         font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
  header { padding: 14px 18px 10px; border-bottom: 1px solid #21262d; }
  h1 { margin: 0 0 4px; font-size: 15px; font-weight: 600; letter-spacing: .04em; }
  .mut { color: #8b949e; }
  a { color: #58a6ff; text-decoration: none; }
  a:hover { text-decoration: underline; }
  #stage { position: relative; height: calc(100vh - 132px); min-height: 320px; cursor: grab; }
  #stage.drag { cursor: grabbing; }
  canvas { display: block; width: 100%; height: 100%; }
  #tip { position: absolute; left: 14px; top: 14px; max-width: 340px; padding: 10px 12px;
         background: #161b22ee; border: 1px solid #30363d; border-radius: 6px;
         white-space: pre-wrap; font-size: 12px; pointer-events: none; }
  #tip.empty { display: none; }
  footer { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; padding: 8px 18px;
           border-top: 1px solid #21262d; color: #8b949e; font-size: 12px; }
  button { font: inherit; color: #c9d1d9; background: #21262d; border: 1px solid #30363d;
           border-radius: 5px; padding: 3px 10px; cursor: pointer; }
  button:hover { background: #30363d; }
  .key { display: inline-flex; align-items: center; gap: 6px; }
  .sw { width: 34px; height: 8px; border-radius: 2px;
        background: linear-gradient(90deg, hsl(200 70% 55%), hsl(110 70% 50%), hsl(20 80% 55%)); }
</style>
</head>
<body>
<header>
  <h1>YABS hangar</h1>
  <div class="mut">
    One hall per hostname family, one rack per node, height and colour from the
    latest 1M fio throughput. Hover a rack for its numbers.
    <?php if ($racks): ?>
      <?= count($racks) ?> node(s), peak <?= n($peak) ?> MB/s.
    <?php endif; ?>
    <br>
    The first two letters of a PMSS hostname name a datacenter row:
    <code>le</code> and <code>lt</code> are Lauttasaari, <code>ke</code> and
    <code>kt</code> are Kerava, and a second Kerava DC will be <code>k2</code>.
    Older nodes predate the scheme and show their family alone.
  </div>
</header>

<div id="stage">
  <canvas id="hangar"></canvas>
  <div id="tip" class="empty"></div>
</div>

<footer>
  <button id="reset" type="button">reset view</button>
  <span class="key"><span class="sw"></span> 1M MB/s, slow to fast</span>
  <span class="mut">drag to orbit, wheel to zoom</span>
  <a href="./">board</a>
  <a href="README.md">README</a>
</footer>

<?php if ($error !== null): ?>
<script>document.getElementById('tip').className = '';
document.getElementById('tip').textContent = <?= json_encode('Database error: ' . $error, JSON_INVALID_UTF8_SUBSTITUTE) ?>;</script>
<?php elseif (!$racks): ?>
<script>document.getElementById('tip').className = '';
document.getElementById('tip').textContent = 'No runs yet. Install the probe, then reload.';</script>
<?php endif; ?>

<script>
// JSON_INVALID_UTF8_SUBSTITUTE: the text fields come from uploaded JSON, cut
// with substr(), so a split multibyte character would otherwise make
// json_encode return false and break this script for every visitor.
const scene = <?= json_encode($scene, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP | JSON_INVALID_UTF8_SUBSTITUTE) ?>;
const canvas = document.getElementById('hangar');
const ctx = canvas.getContext('2d');
const tip = document.getElementById('tip');
const stage = document.getElementById('stage');
const HALF = 0.55;                 // half width of a rack footprint
let yaw = -0.65, pitch = 0.55, zoom = 1, drag = null;
let boxes = [];                    // screen rect per rack, for hover

function resize() {
  const r = canvas.getBoundingClientRect();
  const dpr = window.devicePixelRatio || 1;
  canvas.width = Math.round(r.width * dpr);
  canvas.height = Math.round(r.height * dpr);
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  draw();
}

// Axonometric camera. y is up, x/z are the floor.
function projector(scale, ox, oy) {
  const cy = Math.cos(yaw), sy = Math.sin(yaw), sp = Math.sin(pitch), cp = Math.cos(pitch);
  const mid = scene.width / 2 - HALF;
  return (x, y, z) => {
    const X = (x - mid) * cy - z * sy;
    const Z = (x - mid) * sy + z * cy;
    return { x: ox + X * scale, y: oy + Z * scale * sp - y * scale * cp, d: Z };
  };
}

// Every corner the drawing can touch, so the fit does not depend on hand-tuned
// constants when halls or racks are added.
function extents(P) {
  const pts = [];
  for (const p of scene.plates) {
    for (const [x, z] of [[p.x, p.z], [p.x + p.w, p.z], [p.x + p.w, p.z + p.d], [p.x, p.z + p.d]]) {
      pts.push(P(x, 0, z));
    }
    pts.push(P(p.x, 0, p.z + p.d + 0.75));
  }
  for (const r of scene.racks) {
    pts.push(P(r.x, r.hh + 0.4, r.z));   // label above the rack
    pts.push(P(r.x, 0, r.z));
  }
  return {
    x0: Math.min(...pts.map(p => p.x)), x1: Math.max(...pts.map(p => p.x)),
    y0: Math.min(...pts.map(p => p.y)), y1: Math.max(...pts.map(p => p.y)),
  };
}

function quad(P, corners) {
  ctx.beginPath();
  corners.forEach((c, i) => {
    const p = P(c[0], c[1], c[2]);
    i ? ctx.lineTo(p.x, p.y) : ctx.moveTo(p.x, p.y);
  });
  ctx.closePath();
}

function draw() {
  const w = canvas.clientWidth, h = canvas.clientHeight;
  ctx.clearRect(0, 0, w, h);
  boxes = [];
  if (!scene.racks.length) return;

  const probe = extents(projector(1, 0, 0));
  const scale = Math.min((w - 80) / Math.max(1, probe.x1 - probe.x0),
                         (h - 90) / Math.max(1, probe.y1 - probe.y0)) * zoom;
  const P = projector(
    scale,
    w / 2 - (probe.x0 + probe.x1) / 2 * scale,
    h / 2 - (probe.y0 + probe.y1) / 2 * scale
  );

  // Halls: a floor slab, a light grid, and the family name on the near edge.
  for (const p of scene.plates) {
    quad(P, [[p.x, 0, p.z], [p.x + p.w, 0, p.z], [p.x + p.w, 0, p.z + p.d], [p.x, 0, p.z + p.d]]);
    ctx.fillStyle = '#11161e';
    ctx.fill();
    ctx.strokeStyle = '#1d242e';
    ctx.lineWidth = 1;
    ctx.stroke();
    ctx.strokeStyle = '#161d27';
    const seg = (ax, az, bx, bz) => {
      const a = P(ax, 0, az), b = P(bx, 0, bz);
      ctx.beginPath();
      ctx.moveTo(a.x, a.y);
      ctx.lineTo(b.x, b.y);
      ctx.stroke();
    };
    for (let gx = p.x + scene.step; gx < p.x + p.w; gx += scene.step) seg(gx, p.z, gx, p.z + p.d);
    for (let gz = p.z + scene.step; gz < p.z + p.d; gz += scene.step) seg(p.x, gz, p.x + p.w, gz);
    const lb = P(p.x + 0.15, 0, p.z + p.d + 0.55);
    ctx.fillStyle = '#7d8590';
    ctx.font = '600 13px ui-monospace, monospace';
    ctx.textAlign = 'left';
    ctx.fillText(p.name, lb.x, lb.y);
  }

  // Painter's algorithm over whole boxes: farthest rack drawn first.
  const order = scene.racks
    .map(r => ({ r, d: P(r.x, 0, r.z).d }))
    .sort((a, b) => a.d - b.d);

  for (const { r } of order) {
    const x = r.x, z = r.z, hgt = r.hh;
    const base = r.null ? 'hsl(210 8% 55%)' : `hsl(${Math.round(200 - 180 * r.t)} 70% 55%)`;

    // Contact shadow keeps the box sitting on the slab instead of floating.
    quad(P, [[x - HALF - .12, 0.001, z - HALF - .12], [x + HALF + .12, 0.001, z - HALF - .12],
             [x + HALF + .12, 0.001, z + HALF + .12], [x - HALF - .12, 0.001, z + HALF + .12]]);
    ctx.fillStyle = 'rgba(0,0,0,.45)';
    ctx.fill();

    const wall = (x1, z1, x2, z2, shade, front) => {
      const pts = [P(x1, 0, z1), P(x2, 0, z2), P(x2, hgt, z2), P(x1, hgt, z1)];
      return { pts, depth: pts.reduce((s, p) => s + p.d, 0) / 4, shade,
               a: [x1, z1], b: [x2, z2], front: !!front };
    };
    const walls = [
      wall(x - HALF, z - HALF, x + HALF, z - HALF, .55),          // north
      wall(x + HALF, z - HALF, x + HALF, z + HALF, .74),          // east
      wall(x + HALF, z + HALF, x - HALF, z + HALF, .92, true),    // south, the door
      wall(x - HALF, z + HALF, x - HALF, z - HALF, .66),          // west
    ].sort((a, b) => a.depth - b.depth);

    ctx.fillStyle = base;
    for (const f of walls) {
      ctx.beginPath();
      f.pts.forEach((pt, i) => i ? ctx.lineTo(pt.x, pt.y) : ctx.moveTo(pt.x, pt.y));
      ctx.closePath();
      ctx.fill();
      ctx.globalAlpha = 1 - f.shade;
      ctx.fillStyle = '#000';
      ctx.fill();
      ctx.globalAlpha = 1;
      ctx.fillStyle = base;

      // Vent slots and two status LEDs, so the box reads as a server and not a
      // bare cube. Drawn per face, in wall order, so nearer walls cover them.
      ctx.strokeStyle = 'rgba(0,0,0,.4)';
      ctx.lineWidth = 1;
      const along = (t, yv) => P(f.a[0] + (f.b[0] - f.a[0]) * t, yv,
                                 f.a[1] + (f.b[1] - f.a[1]) * t);
      for (let yv = 0.16; yv < hgt - 0.08; yv += 0.16) {
        const p1 = along(0.07, yv), p2 = along(0.93, yv);
        ctx.beginPath();
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.stroke();
      }
      if (f.front) {
        for (const [t, col] of [[0.15, '#7ee787'], [0.22, '#58a6ff']]) {
          const led = along(t, hgt - 0.13);
          ctx.fillStyle = col;
          ctx.beginPath();
          ctx.arc(led.x, led.y, Math.max(1.6, scale * 0.03), 0, Math.PI * 2);
          ctx.fill();
        }
        ctx.fillStyle = base;
      }
    }
    quad(P, [[x - HALF, hgt, z - HALF], [x + HALF, hgt, z - HALF],
             [x + HALF, hgt, z + HALF], [x - HALF, hgt, z + HALF]]);
    ctx.fill();
    ctx.fillStyle = 'rgba(255,255,255,.07)';
    ctx.fill();
    ctx.strokeStyle = '#0d1117';
    ctx.lineWidth = 1;
    ctx.stroke();

    // Halo first: the label can land on a bright top face of the rack behind.
    const lbl = P(x, hgt + 0.22, z);
    ctx.font = '600 13px ui-monospace, monospace';
    ctx.textAlign = 'center';
    ctx.lineJoin = 'round';
    if (scale > 26) {
      ctx.strokeStyle = '#0d1117';
      ctx.lineWidth = 3.5;
      ctx.strokeText(r.label, lbl.x, lbl.y);
      ctx.fillStyle = '#c9d1d9';
      ctx.fillText(r.label, lbl.x, lbl.y);
    }

    const all = walls.flatMap(f => f.pts).concat([lbl, P(x, 0, z)]);
    boxes.push({
      x0: Math.min(...all.map(p => p.x)), x1: Math.max(...all.map(p => p.x)),
      y0: Math.min(...all.map(p => p.y)), y1: Math.max(...all.map(p => p.y)),
      tip: r.tip,
    });
  }
}

stage.addEventListener('pointerdown', e => {
  drag = { x: e.clientX, y: e.clientY };
  stage.classList.add('drag');
  stage.setPointerCapture(e.pointerId);
});
stage.addEventListener('pointermove', e => {
  if (drag) {
    yaw += (e.clientX - drag.x) * 0.008;
    pitch = Math.max(0.15, Math.min(1.2, pitch + (e.clientY - drag.y) * 0.005));
    drag = { x: e.clientX, y: e.clientY };
    draw();
    return;
  }
  const r = canvas.getBoundingClientRect();
  const px = e.clientX - r.left, py = e.clientY - r.top;
  const hit = boxes.find(b => px >= b.x0 && px <= b.x1 && py >= b.y0 && py <= b.y1);
  if (hit) {
    tip.textContent = hit.tip;
    tip.className = '';
  } else {
    tip.className = 'empty';
  }
});
stage.addEventListener('pointerup', e => {
  drag = null;
  stage.classList.remove('drag');
  stage.releasePointerCapture(e.pointerId);
});
stage.addEventListener('pointercancel', () => { drag = null; stage.classList.remove('drag'); });
stage.addEventListener('wheel', e => {
  e.preventDefault();
  zoom = Math.max(0.35, Math.min(4, zoom * Math.exp(-e.deltaY * 0.0012)));
  draw();
}, { passive: false });
document.getElementById('reset').addEventListener('click', () => {
  yaw = -0.65; pitch = 0.55; zoom = 1;
  draw();
});
window.addEventListener('resize', resize);
resize();
</script>
</body>
</html>