# Real-router extension: push `bmp server 2` to every lab router pointing # at the test collector, so real-router BMP output flows into the test # stack alongside the internal gobgp synthetic feed. Nothing touches # `bmp server 1` (production). # # Two-step safety: first apply is a DRY RUN (prints per-router pending # diffs, does not commit). Set `confirm_router_push=true` and re-apply # to actually commit the config on all routers. # # Lifecycle: # enable_router_bmp=false -> resource absent, no router-side changes # enable=true, confirm=false -> dry-run provisioner (safe; abort at end) # enable=true, confirm=true -> apply provisioner (commits + verifies) # flip either back -> destroy provisioner runs `remove` (idempotent) # # The destroy provisioner uses `on_failure = continue` so a partial router # outage never blocks the VM teardown in scripts/teardown.sh. variable "enable_router_bmp" { description = "Enable the real-router BMP extension (push bmp server 2 to lab routers)." type = bool default = false } variable "confirm_router_push" { description = "Set to true on second apply to actually commit `bmp server 2` on routers. First apply (false) is a dry-run only." type = bool default = false } locals { router_bmp_action = var.confirm_router_push ? "apply" : "dry-run" router_bmp_script = abspath("${path.module}/../scripts/router_bmp.py") # The collector's address on the routers' management path. With DHCP, # this only becomes known after the store VM's guest-agent reports. router_bmp_target = try( proxmox_virtual_environment_vm.store.ipv4_addresses[1][0], "" ) } resource "terraform_data" "router_bmp" { count = var.enable_router_bmp ? 1 : 0 # Any change to these values REPLACES the resource -- destroy provisioner # runs first (removes any prior state on routers), then create provisioner # runs (applies the new state). That gives us clean transitions: # dry-run -> apply, or apply -> dry-run. triggers_replace = [ local.router_bmp_action, local.router_bmp_target, var.router_facing_port, ] # `input` is available to provisioners as `self.input`. input = { action = local.router_bmp_action script = local.router_bmp_script target_ip = local.router_bmp_target target_port = tostring(var.router_facing_port) } # CREATE (or replace): push (or dry-run) `bmp server 2` to all routers. provisioner "local-exec" { command = "python3 ${self.input.script} ${self.input.action} --target-ip ${self.input.target_ip} --target-port ${self.input.target_port}" } # DESTROY: remove `bmp server 2` from all routers. Idempotent: routers # without the config are no-ops. `on_failure = continue` so a single # unreachable router does not block the VM teardown. provisioner "local-exec" { when = destroy on_failure = continue command = "python3 ${self.input.script} remove --target-ip ${self.input.target_ip} --target-port ${self.input.target_port}" } } output "router_bmp_status" { description = "Real-router BMP extension state." value = var.enable_router_bmp ? { enabled = true action = local.router_bmp_action confirm_needed = !var.confirm_router_push target = "${local.router_bmp_target}:${var.router_facing_port}" hint = var.confirm_router_push ? "committed on last apply; destroy will remove" : "set confirm_router_push=true and re-apply to commit" } : { enabled = false hint = "set enable_router_bmp=true (and later confirm_router_push=true) to add bmp server 2 to lab routers" } }