# =====================================================================
# D-123 MODEL B (2026-07-16) -- vr1-dc0 INNER SUBSTRATE, created inside vvr1-dc0.
# Run this root AFTER: (1) the outer opentofu/ root has booted + sized vvr1-dc0
# (expose_nested_virt=true), and (2) the bootstrap gate (site-headend-install.sh
# node-host mode) has installed libvirtd + the inner pool + kvm nested=1 + the
# OPNsense base image on vvr1-dc0. The provider dials vvr1-dc0 over the D-124 transit
# (R-5: remote qemu+ssh from Office1). All module bodies are UNCHANGED from the outer
# root -- only the provider they run against moved (vcloud -> vvr1-dc0). See
# docs/model-a-fallback-plan.md for the Model A revert.
# =====================================================================

provider "libvirt" {
  # qemu+ssh to vvr1-dc0 over the transit. Key auth + known_hosts verified out of band
  # (NO no_verify). The IP is MEASURED after the outer apply (hard rule 2).
  # keyfile + sshauth are REQUIRED URI params (measured 2026-07-20, first real dial):
  # the provider's Go ssh transport ignores ~/.ssh/config and default identities
  # ("attempted methods [none]"). known_hosts: ALL key types must be present --
  # a single accept-new ED25519 entry fails the handshake with "knownhosts: key
  # mismatch" (seed via ssh-keyscan, fingerprint cross-checked against the
  # accept-new-trusted entry).
  uri = "qemu+ssh://${var.vvr1_dc0_ssh_user}@${var.vvr1_dc0_transit_ip}/system?keyfile=${var.vvr1_dc0_ssh_keyfile}&sshauth=privkey"
}

# Inner storage pool (backs the node + edge disks) -- created inside vvr1-dc0 at the
# path the bootstrap step provisioned.
module "inner_storage" {
  source      = "../modules/dc-storage-pool"
  dc_name     = "vr1-dc0-inner"
  target_path = var.inner_pool_path
}

# The six vr1-dc0 planes (D-101 layout, unchanged) -- now isolated-L2 networks INSIDE
# vvr1-dc0. metal-admin is where the rack serves PXE/DHCP to the inner nodes.
module "vr1_dc0_planes" {
  source        = "../modules/dc-planes"
  dc_name       = "vr1-dc0"
  domain_suffix = var.domain_suffix
  mtu           = var.underlay_mtu
  planes        = var.vr1_dc0_planes
}

# D-125 (Model B bridge-in, closes OBS-3): the DC WAN segment is a BRIDGE, not a NAT.
# Under D-123 Model B this network is created INSIDE vvr1-dc0, whose only routed leg is
# the East-West-only transit (SEC-010 FORWARD-dropped), so a NAT here would have no egress
# (that was OBS-3). Instead it bridges onto vvr1-dc0's br-vr1-dc0-wan (an IP-less uplink
# NIC on the vcloud ISP NAT `vr1-dc0-uplink` = 172.30.2.0/24, declared in the outer root's
# vvr1-dc0 netplan). The OPNsense WAN keeps its static .2 on that SAME /24 D-115 already
# assigned vr1-dc0 (bridge-in is single-NAT; the /24 is UNCHANGED from Model A, only the
# libvirt host moved) -- set in the edge config (D-113), no re-address, no new NetBox object.
# There is ONE NAT, at vcloud. host_bridge MUST match the netplan bridge name in opentofu/main.tf.
module "vr1_dc0_wan" {
  source       = "../modules/wan-bridge"
  network_name = "vr1-dc0-wan"
  host_bridge  = "br-vr1-dc0-wan"
  # mtu defaults to 1500 (ISP-uplink domain; NOT the jumbo planes/mesh).
}

# D-122: the DC edge -- 2-NIC (WAN + LAN), Office1 pattern (2048/2/nano). LAN =
# provider-public (external gateway, D-100); WAN = the dedicated uplink above.
module "vr1_dc0_opnsense" {
  source           = "../modules/opnsense-edge"
  vm_name          = "vr1-dc0-opnsense"
  autostart        = true # D-127: the DC edge comes up with its containment VM (routing for the site)
  memory_mib       = 2048
  vcpu             = 2
  pool_name        = module.inner_storage.pool_name
  base_volume_path = var.opnsense_base_path
  lan_network_name = module.vr1_dc0_planes.network_names["provider-public"]
  wan_network_name = module.vr1_dc0_wan.network_name
  # D-129: NEW DC edges boot WITH the qga channel so they never need the retrofit
  # (operator-ruled 2026-07-20). The office1 edge stays untouched at the default OFF.
  expose_qga_channel = true
}

# D-121 Option C layout (R-3 2026-07-16): 3 control + 2 compute + 4 storage = 9 nodes/DC.
# Six NICs each (one per plane); metal-admin FIRST = PXE/boot plane (D-052 default binding).
locals {
  vr1_dc0_node_nics = [
    module.vr1_dc0_planes.network_names["metal-admin"], # PXE / boot fabric first
    module.vr1_dc0_planes.network_names["provider-public"],
    module.vr1_dc0_planes.network_names["metal-internal"],
    module.vr1_dc0_planes.network_names["data-tenant"],
    module.vr1_dc0_planes.network_names["storage"],
    module.vr1_dc0_planes.network_names["replication"],
  ]

  vr1_dc0_nodes = {
    "vr1-dc0-control-01" = { vcpu = 16, mem = 65536, disk_gib = 150 }
    "vr1-dc0-control-02" = { vcpu = 16, mem = 65536, disk_gib = 150 }
    "vr1-dc0-control-03" = { vcpu = 16, mem = 65536, disk_gib = 150 }
    "vr1-dc0-compute-01" = { vcpu = 12, mem = 49152, disk_gib = 100 }
    "vr1-dc0-compute-02" = { vcpu = 12, mem = 49152, disk_gib = 100 }
    "vr1-dc0-storage-01" = { vcpu = 8, mem = 24576, disk_gib = 550 }
    "vr1-dc0-storage-02" = { vcpu = 8, mem = 24576, disk_gib = 550 }
    "vr1-dc0-storage-03" = { vcpu = 8, mem = 24576, disk_gib = 550 }
    "vr1-dc0-storage-04" = { vcpu = 8, mem = 24576, disk_gib = 550 } # R-3: 4th OSD host
  }
}

module "vr1_dc0_node" {
  for_each = local.vr1_dc0_nodes

  source          = "../modules/node-vm"
  vm_name         = each.key
  autostart       = false # D-127: node VMs are MAAS-power-controlled -- MANUAL, never on host boot
  vcpu            = each.value.vcpu
  memory_mib      = each.value.mem
  disk_size_bytes = each.value.disk_gib * 1024 * 1024 * 1024
  pool_name       = module.inner_storage.pool_name
  network_names   = local.vr1_dc0_node_nics
}
