{ config, lib, pkgs, ... }: with lib; let cfg = config.plasmacloud.network; clusterCfg = config.plasmacloud.cluster; # BGP peer type for FiberLB bgpPeerType = types.submodule { options = { address = mkOption { type = types.str; description = "Peer IP address (ToR switch, upstream router)"; example = "192.168.1.1"; }; asn = mkOption { type = types.int; description = "Peer AS number"; example = 65001; }; description = mkOption { type = types.str; default = ""; description = "Human-readable peer description"; example = "ToR switch rack1"; }; }; }; in { options.plasmacloud.network = { fiberlbBgp = { enable = mkEnableOption "FiberLB BGP VIP advertisement"; vips = mkOption { type = types.listOf types.str; default = []; description = "VIPs to advertise via BGP (CIDR notation)"; example = [ "203.0.113.10/32" "203.0.113.11/32" ]; }; peers = mkOption { type = types.listOf bgpPeerType; default = []; description = "BGP peers (ToR switches, upstream routers)"; example = literalExpression '' [ { address = "192.168.1.1"; asn = 65001; description = "ToR switch rack1"; } { address = "192.168.1.2"; asn = 65001; description = "ToR switch rack2"; } ] ''; }; routerId = mkOption { type = types.nullOr types.str; default = null; description = "BGP router ID (auto-detected from primary IP if null)"; }; }; prismnetIntegration = { enable = mkEnableOption "PrismNET OVN integration"; }; }; config = mkMerge [ # FiberLB BGP configuration (mkIf cfg.fiberlbBgp.enable { # Assertions assertions = [ { assertion = clusterCfg.bgp.asn > 0; message = "plasmacloud.cluster.bgp.asn must be configured for FiberLB BGP"; } { assertion = (length cfg.fiberlbBgp.vips) > 0; message = "plasmacloud.network.fiberlbBgp.vips must contain at least one VIP"; } { assertion = (length cfg.fiberlbBgp.peers) > 0; message = "plasmacloud.network.fiberlbBgp.peers must contain at least one BGP peer"; } ]; # Wire to nix-nos.bgp (Layer 1) nix-nos.enable = true; nix-nos.bgp = { enable = true; backend = "gobgp"; # FiberLB uses GoBGP asn = clusterCfg.bgp.asn; # Auto-detect router ID from primary IP or use configured value routerId = if cfg.fiberlbBgp.routerId != null then cfg.fiberlbBgp.routerId else # Fallback to a simple IP extraction from node config let hostname = config.networking.hostName; node = clusterCfg.nodes.${hostname} or null; in if node != null then node.ip else "127.0.0.1"; peers = cfg.fiberlbBgp.peers; # Convert VIPs to BGP announcements announcements = map (vip: { prefix = vip; }) cfg.fiberlbBgp.vips; }; # FiberLB service configuration (if FiberLB is enabled) # Note: This assumes fiberlb service is defined elsewhere # services.fiberlb.bgp.gobgpAddress = mkIf (config.services.fiberlb.enable or false) "127.0.0.1:50051"; }) # PrismNET OVN integration (mkIf cfg.prismnetIntegration.enable { # Enable OVN Controller virtualisation.switch.enable = true; virtualisation.ovn = { enable = true; controller = { enable = true; # Use Geneve encapsulation to avoid VXLAN VNI limitations and allow richer metadata encapType = "geneve"; # Auto-detect IP from cluster config encapIp = let hostname = config.networking.hostName; node = clusterCfg.nodes.${hostname} or null; in if node != null then node.ip else "127.0.0.1"; }; }; }) ]; }