{ config, lib, pkgs, ... }: with lib; let cfg = config.plasmacloud.tenantNetworking; jsonFormat = pkgs.formats.json {}; serviceIpPoolType = types.submodule { options = { name = mkOption { type = types.str; description = "Service IP pool name"; }; cidr_block = mkOption { type = types.str; description = "Service IP pool CIDR"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Service IP pool description"; }; pool_type = mkOption { type = types.nullOr (types.enum [ "cluster_ip" "load_balancer" "node_port" ]); default = null; description = "Service IP pool type"; }; }; }; portType = types.submodule { options = { name = mkOption { type = types.str; description = "Port name"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Port description"; }; ip_address = mkOption { type = types.nullOr types.str; default = null; description = "Requested fixed IP address"; }; security_groups = mkOption { type = types.listOf types.str; default = []; description = "Security group names attached to the port"; }; admin_state_up = mkOption { type = types.nullOr types.bool; default = null; description = "Administrative state for the port"; }; }; }; subnetType = types.submodule { options = { name = mkOption { type = types.str; description = "Subnet name"; }; cidr_block = mkOption { type = types.str; description = "Subnet CIDR"; }; gateway_ip = mkOption { type = types.nullOr types.str; default = null; description = "Gateway IP"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Subnet description"; }; dhcp_enabled = mkOption { type = types.nullOr types.bool; default = null; description = "Enable DHCP for the subnet"; }; ports = mkOption { type = types.listOf portType; default = []; description = "Ports within the subnet"; }; }; }; routerType = types.submodule { options = { name = mkOption { type = types.str; description = "Router name"; }; gateway_cidr = mkOption { type = types.str; description = "Gateway interface CIDR attached to the VPC"; }; mac_address = mkOption { type = types.str; description = "Router interface MAC address"; }; external_ip = mkOption { type = types.str; description = "SNAT external IPv4 address"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Router description"; }; }; }; vpcType = types.submodule { options = { name = mkOption { type = types.str; description = "VPC name"; }; cidr_block = mkOption { type = types.str; description = "VPC CIDR"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "VPC description"; }; router = mkOption { type = types.nullOr routerType; default = null; description = "Optional tenant edge router for the VPC"; }; subnets = mkOption { type = types.listOf subnetType; default = []; description = "Subnets within the VPC"; }; }; }; securityGroupRuleType = types.submodule { options = { direction = mkOption { type = types.enum [ "ingress" "egress" ]; description = "Rule direction"; }; protocol = mkOption { type = types.nullOr (types.enum [ "any" "tcp" "udp" "icmp" "icmpv6" ]); default = null; description = "IP protocol"; }; port_range_min = mkOption { type = types.nullOr types.int; default = null; description = "Minimum port in range"; }; port_range_max = mkOption { type = types.nullOr types.int; default = null; description = "Maximum port in range"; }; remote_cidr = mkOption { type = types.nullOr types.str; default = null; description = "Remote CIDR"; }; remote_group = mkOption { type = types.nullOr types.str; default = null; description = "Remote security group name"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Rule description"; }; }; }; securityGroupType = types.submodule { options = { name = mkOption { type = types.str; description = "Security group name"; }; description = mkOption { type = types.nullOr types.str; default = null; description = "Security group description"; }; rules = mkOption { type = types.listOf securityGroupRuleType; default = []; description = "Security group rules"; }; }; }; tenantType = types.submodule { options = { org_id = mkOption { type = types.str; description = "Tenant organization ID"; }; project_id = mkOption { type = types.str; description = "Tenant project ID"; }; security_groups = mkOption { type = types.listOf securityGroupType; default = []; description = "Tenant-scoped security groups"; }; service_ip_pools = mkOption { type = types.listOf serviceIpPoolType; default = []; description = "Tenant-scoped Service IP pools"; }; vpcs = mkOption { type = types.listOf vpcType; default = []; description = "Tenant-scoped VPCs and their nested resources"; }; }; }; configFile = jsonFormat.generate "plasmacloud-tenant-networking.json" { inherit (cfg) tenants; }; configPath = cfg.configPath; configRelative = removePrefix "/etc/" configPath; in { options.plasmacloud.tenantNetworking = { enable = mkEnableOption "tenant-scoped PrismNET declarations"; endpoint = mkOption { type = types.str; default = "http://127.0.0.1:50081"; description = "PrismNET gRPC endpoint"; }; iamEndpoint = mkOption { type = types.str; default = "http://127.0.0.1:50080"; description = "IAM gRPC endpoint used to mint tenant-scoped controller tokens"; }; controllerPrincipalId = mkOption { type = types.str; default = "plasmacloud-reconciler"; description = "Service account used by the reconciler when applying tenant declarations"; }; tenants = mkOption { type = types.listOf tenantType; default = []; description = "Tenant-scoped network declarations. This is separate from platform networking under plasmacloud.network."; }; configPath = mkOption { type = types.str; default = "/etc/plasmacloud/tenant-networking.json"; description = "Path for rendered tenant networking config"; }; applyOnBoot = mkOption { type = types.bool; default = true; description = "Apply declarations at boot"; }; applyOnChange = mkOption { type = types.bool; default = true; description = "Apply declarations when the config file changes"; }; prune = mkOption { type = types.bool; default = false; description = "Delete tenant network resources not declared for managed tenants"; }; package = mkOption { type = types.package; default = pkgs.plasmacloud-reconciler or (throw "plasmacloud-reconciler package not found"); description = "Reconciler package for tenant networking declarations"; }; }; config = mkIf cfg.enable { assertions = [ { assertion = hasPrefix "/etc/" configPath; message = "plasmacloud.tenantNetworking.configPath must be under /etc"; } ]; environment.etc."${configRelative}".source = configFile; systemd.services.plasmacloud-tenant-networking-apply = { description = "Apply PlasmaCloud tenant networking declarations"; after = [ "network-online.target" ] ++ optional config.services.prismnet.enable "prismnet.service" ++ optional config.services.iam.enable "iam.service"; wants = [ "network-online.target" ] ++ optional config.services.prismnet.enable "prismnet.service" ++ optional config.services.iam.enable "iam.service"; wantedBy = optional cfg.applyOnBoot "multi-user.target"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "${cfg.package}/bin/plasmacloud-reconciler tenant-network" + " --config ${configPath}" + " --endpoint ${cfg.endpoint}" + " --iam-endpoint ${cfg.iamEndpoint}" + " --controller-principal-id ${cfg.controllerPrincipalId}" + optionalString cfg.prune " --prune"; }; }; systemd.paths.plasmacloud-tenant-networking-apply = mkIf cfg.applyOnChange { wantedBy = [ "multi-user.target" ]; pathConfig = { PathChanged = configPath; }; }; }; }