#!/usr/bin/env ts-node /** * Script to initialize the four topology VLANs for the datacenter network * * VLANs to create: * - VLAN 10: MGMT (10.10.10.0/24, gateway 10.10.10.1) * - VLAN 20: VM-LAN (10.20.0.0/22, gateway 10.20.0.1) * - VLAN 30: CEPH-PUBLIC (10.30.0.0/24, gateway 10.30.0.1) * - VLAN 99: OOB-MGMT (10.99.99.0/24, gateway 10.99.99.1) * * Usage: * ts-node scripts/init-topology-vlans.ts * or * node dist/scripts/init-topology-vlans.js */ import { createVlan } from '../src/services/vlanService'; import prisma from '../src/lib/db'; import logger from '../src/lib/logger'; import { OmadaVlanCreate } from '../src/types/omada'; interface TopologyVlan { name: string; vlanId: number; subnet: string; gateway: string; description: string; } const TOPOLOGY_VLANS: TopologyVlan[] = [ { name: 'MGMT', vlanId: 10, subnet: '10.10.10.0/24', gateway: '10.10.10.1', description: 'Proxmox host management, SSH, APIs', }, { name: 'VM-LAN', vlanId: 20, subnet: '10.20.0.0/22', gateway: '10.20.0.1', description: 'Default VM / container network', }, { name: 'CEPH-PUBLIC', vlanId: 30, subnet: '10.30.0.0/24', gateway: '10.30.0.1', description: 'Ceph "public" network (client/public side)', }, { name: 'OOB-MGMT', vlanId: 99, subnet: '10.99.99.0/24', gateway: '10.99.99.1', description: 'iDRAC/IPMI, PDUs, router mgmt, console', }, ]; async function initializeTopologyVlans(siteIdOrName: string): Promise { try { logger.info('Starting topology VLAN initialization', { siteIdOrName }); // Find site in database const site = await prisma.site.findFirst({ where: { OR: [ { id: siteIdOrName }, { omadaSiteId: siteIdOrName }, { name: siteIdOrName }, ], }, }); if (!site) { throw new Error(`Site not found: ${siteIdOrName}`); } logger.info('Found site', { siteId: site.id, siteName: site.name, omadaSiteId: site.omadaSiteId }); const results = []; for (const vlanConfig of TOPOLOGY_VLANS) { try { // Check if VLAN already exists in database const existingVlan = await prisma.vlan.findFirst({ where: { siteId: site.id, vlanId: vlanConfig.vlanId, }, }); if (existingVlan) { logger.warn('VLAN already exists in database, skipping', { vlanId: vlanConfig.vlanId, vlanName: vlanConfig.name, existingVlanId: existingVlan.id, }); results.push({ vlan: vlanConfig.name, status: 'skipped', reason: 'Already exists in database', }); continue; } // Create VLAN via Omada API const omadaVlanConfig: OmadaVlanCreate = { name: vlanConfig.name, vlanId: vlanConfig.vlanId, subnet: vlanConfig.subnet, gateway: vlanConfig.gateway, dhcpEnabled: false, // Disable DHCP by default, can be enabled later if needed description: vlanConfig.description, enabled: true, }; logger.info('Creating VLAN', { siteId: site.omadaSiteId, vlanConfig: omadaVlanConfig, }); const omadaVlan = await createVlan(site.omadaSiteId, omadaVlanConfig); // Store in database const dbVlan = await prisma.vlan.create({ data: { omadaVlanId: omadaVlan.id, siteId: site.id, name: omadaVlan.name, vlanId: vlanConfig.vlanId, subnet: vlanConfig.subnet, gateway: vlanConfig.gateway, dhcpEnabled: false, description: vlanConfig.description, }, }); logger.info('VLAN created successfully', { vlanId: vlanConfig.vlanId, vlanName: vlanConfig.name, dbVlanId: dbVlan.id, }); results.push({ vlan: vlanConfig.name, status: 'created', vlanId: vlanConfig.vlanId, dbVlanId: dbVlan.id, }); // Log audit await prisma.auditLog.create({ data: { action: 'create_vlan', targetType: 'site', targetId: site.id, details: { siteName: site.name, vlanName: vlanConfig.name, vlanId: vlanConfig.vlanId, script: 'init-topology-vlans', }, }, }); } catch (error) { logger.error('Error creating VLAN', { vlanName: vlanConfig.name, vlanId: vlanConfig.vlanId, error: error instanceof Error ? error.message : String(error), }); results.push({ vlan: vlanConfig.name, status: 'error', error: error instanceof Error ? error.message : String(error), }); } } // Print summary console.log('\n=== Topology VLAN Initialization Summary ==='); console.log(`Site: ${site.name} (${site.omadaSiteId})`); console.log('\nResults:'); results.forEach((result) => { if (result.status === 'created') { console.log(` ✓ ${result.vlan} (VLAN ${result.vlanId}) - Created`); } else if (result.status === 'skipped') { console.log(` ⊘ ${result.vlan} - Skipped (${result.reason})`); } else { console.log(` ✗ ${result.vlan} - Error: ${result.error}`); } }); const successCount = results.filter((r) => r.status === 'created').length; const skippedCount = results.filter((r) => r.status === 'skipped').length; const errorCount = results.filter((r) => r.status === 'error').length; console.log(`\nSummary: ${successCount} created, ${skippedCount} skipped, ${errorCount} errors`); if (errorCount > 0) { process.exit(1); } } catch (error) { logger.error('Fatal error initializing topology VLANs', { error: error instanceof Error ? error.message : String(error), }); console.error('Error:', error instanceof Error ? error.message : String(error)); process.exit(1); } finally { await prisma.$disconnect(); } } // Main execution const siteIdOrName = process.argv[2]; if (!siteIdOrName) { console.error('Usage: ts-node scripts/init-topology-vlans.ts '); console.error('\nExample:'); console.error(' ts-node scripts/init-topology-vlans.ts "Default"'); console.error(' ts-node scripts/init-topology-vlans.ts '); process.exit(1); } initializeTopologyVlans(siteIdOrName).catch((error) => { console.error('Unhandled error:', error); process.exit(1); });