#!/usr/bin/env node // Capacity planning worksheet (Phase 4 ยง25) โ€” computes recommended-concurrent-Labs from real // inputs, not a guessed marketing number. Inputs come from infra/environments/production/capacity.yml // (edit that file with real measured values from infra/scripts/loadtest/, then re-run this). // // Usage: node capacity-model.js [path-to-capacity.yml] const fs = require('fs'); const path = require('path'); // Reuses the Provisioner's existing js-yaml dependency (platform/provisioner/src/config.js // already parses YAML config this same way) rather than adding a new package.json just for // this one script. const yaml = require(path.join(__dirname, '../../platform/provisioner/node_modules/js-yaml')); const configPath = process.argv[2] || path.join(__dirname, '../environments/production/capacity.yml'); if (!fs.existsSync(configPath)) { console.error(`No capacity config at ${configPath} โ€” copy capacity.yml.example first and fill in real measured values.`); process.exit(1); } const cfg = yaml.load(fs.readFileSync(configPath, 'utf8')); function computeFor(resource, hostTotal, platformReserved, proxmoxOverheadPct, perLab, safetyMarginPct) { const usable = hostTotal * (1 - proxmoxOverheadPct / 100) - platformReserved; const withMargin = usable * (1 - safetyMarginPct / 100); return Math.floor(withMargin / perLab); } const cpuLimit = computeFor( 'CPU', cfg.host_cpu_cores, cfg.platform_reserved_cpu_cores, cfg.proxmox_overhead_pct, cfg.measured_lab_cpu_cores, cfg.safety_margin_pct ); const memLimit = computeFor( 'Memory', cfg.host_mem_mb, cfg.platform_reserved_mem_mb, cfg.proxmox_overhead_pct, cfg.measured_lab_mem_mb, cfg.safety_margin_pct ); const recommended = Math.min(cpuLimit, memLimit); const warningThreshold = Math.floor(recommended * 0.8); const hardAdmissionControl = recommended; // never provision past the more conservative of the two limits console.log('RootNode capacity model'); console.log('========================'); console.log(`Host: ${cfg.host_cpu_cores} cores, ${cfg.host_mem_mb} MB RAM`); console.log(`Platform-reserved: ${cfg.platform_reserved_cpu_cores} cores, ${cfg.platform_reserved_mem_mb} MB`); console.log(`Proxmox overhead: ${cfg.proxmox_overhead_pct}%`); console.log(`Measured per-Lab: ${cfg.measured_lab_cpu_cores} cores, ${cfg.measured_lab_mem_mb} MB`); console.log(`Safety margin: ${cfg.safety_margin_pct}%`); console.log(''); console.log(`CPU-limited concurrent Labs: ${cpuLimit}`); console.log(`Memory-limited concurrent Labs: ${memLimit}`); console.log(''); console.log(`RECOMMENDED concurrent Labs: ${recommended} (binding constraint: ${cpuLimit < memLimit ? 'CPU' : 'memory'})`); console.log(`WARNING threshold (80%): ${warningThreshold}`); console.log(`HARD admission-control threshold: ${hardAdmissionControl}`); console.log(''); if (cfg.measured_lab_cpu_cores === undefined || cfg._placeholder) { console.log('WARNING: this config still has placeholder/example values โ€” see infra/scripts/loadtest/ for how to measure real ones.'); }