332 lines
10 KiB
Plaintext
332 lines
10 KiB
Plaintext
---
|
||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||
|
||
// ── Hardware data — edit this as your inventory grows ──────────
|
||
const hardware = {
|
||
compute: [
|
||
{
|
||
name: 'Proxmox Node 01',
|
||
model: 'Dell PowerEdge R730',
|
||
specs: ['2× Intel Xeon E5-2680v4 (28C/56T total)', '256GB DDR4 ECC LRDIMM', '2× 10GbE SFP+', 'iDRAC8 Enterprise', 'H730 RAID (passthrough)'],
|
||
role: 'Primary Proxmox hypervisor · VM host',
|
||
status: 'online',
|
||
location: 'homelab rack · U4',
|
||
},
|
||
{
|
||
name: 'Proxmox Node 02',
|
||
model: 'Dell PowerEdge R720',
|
||
specs: ['2× Intel Xeon E5-2670 (16C/32T total)', '128GB DDR3 ECC RDIMM', '2× 1GbE', 'iDRAC7 Enterprise'],
|
||
role: 'Secondary Proxmox node · backup workloads',
|
||
status: 'online',
|
||
location: 'homelab rack · U6',
|
||
},
|
||
{
|
||
name: 'Raspberry Pi 4 (8GB)',
|
||
model: 'Raspberry Pi 4 Model B',
|
||
specs: ['Broadcom BCM2711 (4× Cortex-A72)', '8GB LPDDR4', '2× USB 3.0, Gigabit Ethernet', '64GB Samsung Pro Endurance microSD'],
|
||
role: 'PXE boot server · ISC26 cluster deployment',
|
||
status: 'online',
|
||
location: 'desk · USB-C powered',
|
||
},
|
||
],
|
||
storage: [
|
||
{
|
||
name: 'ZFS NAS — zfs-oporto',
|
||
model: 'Custom Proxmox VM → ZFS passthrough',
|
||
specs: ['4× Seagate IronWolf 8TB (RAIDZ2)', '2× Samsung 870 EVO 1TB (SSD special class vdev)', 'ZFS 2.2 — RAIDZ expansion capable', '~20TB usable raw (RAIDZ2 overhead)'],
|
||
role: 'Primary NAS · NFS exports to K8s · Immich storage',
|
||
status: 'online',
|
||
location: 'pve-01 · RAIDZ2',
|
||
},
|
||
{
|
||
name: 'Longhorn Cluster Storage',
|
||
model: 'Distributed across k8s-worker1/2',
|
||
specs: ['2× 500GB NVMe (worker1)', '2× 500GB NVMe (worker2)', '3-replica default policy'],
|
||
role: 'Kubernetes persistent volumes · Longhorn CSI',
|
||
status: 'online',
|
||
location: 'k8s-worker1 / k8s-worker2',
|
||
},
|
||
],
|
||
networking: [
|
||
{
|
||
name: 'OPNsense Firewall',
|
||
model: 'Protectli VP2420',
|
||
specs: ['Intel Celeron J6413', '8GB DDR4', '4× 2.5GbE Intel i226', '64GB eMMC', 'OPNsense 26.1'],
|
||
role: 'Edge router · Firewall · WireGuard VPN · Unbound DNS · GeoIP blocks',
|
||
status: 'online',
|
||
location: 'homelab rack · U1',
|
||
},
|
||
{
|
||
name: 'Core Switch',
|
||
model: 'Cisco SG300-28PP',
|
||
specs: ['24× PoE+ 1GbE', '2× 1GbE combo SFP', 'Layer 3 managed', '375W PoE budget'],
|
||
role: 'Core VLAN switch · LAG to hypervisors',
|
||
status: 'online',
|
||
location: 'homelab rack · U2',
|
||
},
|
||
{
|
||
name: 'AP',
|
||
model: 'Ubiquiti U6-Lite',
|
||
specs: ['WiFi 6 (802.11ax)', '2.4 + 5GHz', 'PoE powered'],
|
||
role: 'Wireless · separate IoT SSID on VLAN50',
|
||
status: 'online',
|
||
location: 'ceiling mount',
|
||
},
|
||
],
|
||
work_hpc: [
|
||
{
|
||
name: 'ARCHER2 (EPCC)',
|
||
model: 'HPE Cray EX — AMD EPYC 7742',
|
||
specs: ['5,860 compute nodes', '2× AMD EPYC 7742 (128C/node)', '256GB DDR4 ECC/node', 'HPE Slingshot 11 100Gb/s HSN', '14.1 PiB Lustre storage'],
|
||
role: 'UK National HPC facility · sysadmin',
|
||
status: 'production',
|
||
location: 'EPCC · Edinburgh',
|
||
},
|
||
{
|
||
name: 'Cirrus (EPCC)',
|
||
model: 'SGI ICE XA / HPE ProLiant XL230a',
|
||
specs: ['280 compute nodes', '2× Intel Xeon E5-2695v4 (36C/node)', '256GB DDR4/node', 'Intel OmniPath HSN', 'Cirrus EX GPU partition: HPE Cray EX235n'],
|
||
role: 'EPCC Tier-2 HPC · sysadmin · GPU partition admin',
|
||
status: 'production',
|
||
location: 'EPCC · Edinburgh',
|
||
},
|
||
{
|
||
name: 'ISC26 Competition Cluster',
|
||
model: 'Atos BullSequana XH2000',
|
||
specs: ['1× BullSequana XD670 GPU node (4× NVIDIA H100 80GB)', '8× BullSequana XD2000 CPU nodes', 'NDR 400Gb/s InfiniBand fabric (Cornelis)', 'Raspberry Pi 4 PXE deployment server'],
|
||
role: 'ISC26 Student Cluster Competition — Hamburg 2026',
|
||
status: 'building',
|
||
location: 'EPCC · Edinburgh',
|
||
},
|
||
],
|
||
peripherals: [
|
||
{
|
||
name: 'Main Monitor',
|
||
model: 'LG 27UK850-W',
|
||
specs: ['27" 4K IPS', 'USB-C 60W PD', 'HDR400', 'USB-C + HDMI + DP'],
|
||
role: 'Primary display',
|
||
status: 'online',
|
||
},
|
||
{
|
||
name: '3D Printer',
|
||
model: 'Anycubic Kobra S1',
|
||
specs: ['FDM · 220×220×250mm build volume', 'Auto-levelling', 'Direct drive'],
|
||
role: 'Printing rack accessories, cable guides, custom mounts',
|
||
status: 'idle',
|
||
},
|
||
{
|
||
name: 'Laser Printer',
|
||
model: 'Kyocera ECOSYS M2135dn',
|
||
specs: ['A4 mono laser MFP', 'Duplex · LAN · ADF', '35ppm'],
|
||
role: 'Documents · printing homelab diagrams',
|
||
status: 'idle',
|
||
},
|
||
],
|
||
};
|
||
|
||
const categories = [
|
||
{ key: 'compute', label: 'Compute', icon: '▣' },
|
||
{ key: 'storage', label: 'Storage', icon: '◈' },
|
||
{ key: 'networking', label: 'Networking', icon: '⇅' },
|
||
{ key: 'work_hpc', label: 'Work / HPC', icon: '⬡' },
|
||
{ key: 'peripherals',label: 'Peripherals & Misc', icon: '◎' },
|
||
];
|
||
---
|
||
|
||
<BaseLayout title="Hardware" description="Full hardware inventory — homelab and work HPC systems">
|
||
<div class="hardware-page">
|
||
|
||
<!-- Hero -->
|
||
<div class="page-hero container">
|
||
<div class="accent-line"></div>
|
||
<h1>Hardware <span class="text-accent">Inventory</span></h1>
|
||
<p class="text-muted font-mono" style="font-size:0.88rem;margin-top:0.5rem">
|
||
Everything physical I touch, rack and bench · last updated 2025
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Jump nav -->
|
||
<div class="container jump-nav">
|
||
{categories.map(cat => (
|
||
<a href={`#${cat.key}`} class="jump-chip">
|
||
<span class="text-accent">{cat.icon}</span>
|
||
<span>{cat.label}</span>
|
||
</a>
|
||
))}
|
||
</div>
|
||
|
||
<!-- Category sections -->
|
||
{categories.map(cat => (
|
||
<section class="section container hw-section" id={cat.key}>
|
||
<div class="hw-section-header">
|
||
<span class="hw-icon text-accent">{cat.icon}</span>
|
||
<h2>{cat.label}</h2>
|
||
</div>
|
||
|
||
<div class="hw-grid">
|
||
{(hardware as any)[cat.key].map((item: any) => (
|
||
<div class="card hw-card">
|
||
<div class="hw-card-header">
|
||
<div>
|
||
<div class="hw-name font-display">{item.name}</div>
|
||
<div class="hw-model font-mono">{item.model}</div>
|
||
</div>
|
||
<span
|
||
class="hw-status tag"
|
||
style={
|
||
item.status === 'online' ? '' :
|
||
item.status === 'production' ? 'color:#00D2BE;border-color:#00D2BE;background:rgba(0,210,190,0.08)' :
|
||
item.status === 'building' ? 'color:#FFB800;border-color:#FFB800;background:rgba(255,184,0,0.08)' :
|
||
'color:var(--text-muted);border-color:var(--border-subtle);background:transparent'
|
||
}
|
||
>{item.status}</span>
|
||
</div>
|
||
|
||
<ul class="hw-specs">
|
||
{item.specs.map((s: string) => (
|
||
<li class="hw-spec">
|
||
<span class="spec-bullet text-accent">▸</span>
|
||
<span>{s}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
|
||
<div class="hw-footer">
|
||
<span class="hw-role text-muted">{item.role}</span>
|
||
{item.location && (
|
||
<span class="hw-location font-mono">{item.location}</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
))}
|
||
|
||
<!-- Total count footer -->
|
||
<div class="container hw-totals">
|
||
<div class="card" style="padding:1.5rem;text-align:center">
|
||
<p class="font-mono text-muted" style="font-size:0.8rem">
|
||
{Object.values(hardware).flat().length} items catalogued ·
|
||
<span class="text-accent">rack::log hardware registry</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</BaseLayout>
|
||
|
||
<style>
|
||
.hardware-page { padding-top: 8rem; padding-bottom: 4rem; }
|
||
|
||
.page-hero { margin-bottom: 2.5rem; }
|
||
|
||
/* Jump nav */
|
||
.jump-nav {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
margin-bottom: 4rem;
|
||
}
|
||
|
||
.jump-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
padding: 0.35rem 0.9rem;
|
||
border: 1px solid var(--border-subtle);
|
||
border-radius: 20px;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.78rem;
|
||
color: var(--text-muted);
|
||
background: var(--bg-surface);
|
||
transition: all var(--transition-fast);
|
||
}
|
||
.jump-chip:hover {
|
||
border-color: var(--border-glow);
|
||
color: var(--petronas-teal);
|
||
background: var(--petronas-glow);
|
||
}
|
||
|
||
/* Section header */
|
||
.hw-section { padding-top: 3rem; padding-bottom: 1rem; }
|
||
|
||
.hw-section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
margin-bottom: 1.75rem;
|
||
}
|
||
|
||
.hw-icon { font-size: 1.4rem; }
|
||
|
||
/* Cards */
|
||
.hw-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||
gap: 1.25rem;
|
||
}
|
||
|
||
.hw-card { display: flex; flex-direction: column; gap: 1rem; }
|
||
|
||
.hw-card-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.hw-name {
|
||
font-size: 1.05rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.hw-model {
|
||
font-size: 0.72rem;
|
||
color: var(--petronas-teal);
|
||
margin-top: 0.2rem;
|
||
}
|
||
|
||
.hw-status { white-space: nowrap; flex-shrink: 0; }
|
||
|
||
/* Specs list */
|
||
.hw-specs {
|
||
list-style: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.35rem;
|
||
flex: 1;
|
||
}
|
||
|
||
.hw-spec {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 0.5rem;
|
||
font-size: 0.83rem;
|
||
color: var(--text-secondary);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.spec-bullet { font-size: 0.6rem; flex-shrink: 0; margin-top: 0.15rem; }
|
||
|
||
/* Footer */
|
||
.hw-footer {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
padding-top: 0.75rem;
|
||
border-top: 1px solid var(--border-subtle);
|
||
}
|
||
|
||
.hw-role { font-size: 0.8rem; }
|
||
.hw-location { font-size: 0.68rem; color: var(--text-muted); }
|
||
|
||
/* Totals */
|
||
.hw-totals { margin-top: 4rem; }
|
||
|
||
@media (max-width: 600px) {
|
||
.hw-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|