Better addressing?

This commit is contained in:
Yaro Kasear 2026-05-01 13:07:30 -05:00
parent b5c1a43863
commit f35e402b4d
3 changed files with 394 additions and 261 deletions

View file

@ -1,4 +1,4 @@
{ lib ? import <nixpkgs/lib> { } }:
{ lib, ... }:
let
inherit (lib)
@ -6,27 +6,26 @@ let
attrNames
concatMapAttrs
concatStringsSep
filterAttrs
listToAttrs
mapAttrs'
nameValuePair
optionalAttrs
imap1
mapAttrs'
imap0
length
filter
findFirst
toLower;#
toLower
unique;
mkInitials =
name:
let
lower = toLower name; # was builtins.toLower
lower = toLower name;
noApos = builtins.replaceStrings [ "'" ] [ " " ] lower;
words = filter (w: w != "") (builtins.split " +" noApos);
letters =
concatStringsSep ""
(builtins.map (w: builtins.substring 0 1 w) words);
letters = concatStringsSep "" (builtins.map (w: builtins.substring 0 1 w) words);
in
if letters == "" then "loc" else builtins.substring 0 3 letters;
@ -38,7 +37,7 @@ let
in
"${base}-${hash}";
# Manual elemIndex so we don't care what nixpkgs version you're on
# Manual elemIndex so we do not care what nixpkgs version you are on.
elemIndex =
name: list:
let
@ -180,9 +179,9 @@ let
mkRoleRangeNamed =
{ location, typeName, roleName }:
let
_ = assertMsg (subnetTypes ? typeName)
_ = assertMsg (builtins.hasAttr typeName subnetTypes)
"metatron-addressing: unknown subnet type '${typeName}'";
__ = assertMsg (roles ? roleName)
__ = assertMsg (builtins.hasAttr roleName roles)
"metatron-addressing: unknown role '${roleName}'";
type = subnetTypes.${typeName};
@ -193,9 +192,9 @@ let
mkIpNamed =
{ location, typeName, roleName, host }:
let
_ = assertMsg (subnetTypes ? typeName)
_ = assertMsg (builtins.hasAttr typeName subnetTypes)
"metatron-addressing: unknown subnet type '${typeName}'";
__ = assertMsg (roles ? roleName)
__ = assertMsg (builtins.hasAttr roleName roles)
"metatron-addressing: unknown role '${roleName}'";
type = subnetTypes.${typeName};
@ -217,7 +216,7 @@ let
mkSubnetNamed =
{ location, typeName }:
let
_ = assertMsg (subnetTypes ? typeName)
_ = assertMsg (builtins.hasAttr typeName subnetTypes)
"metatron-addressing: unknown subnet type '${typeName}'";
type = subnetTypes.${typeName};
in
@ -236,18 +235,59 @@ let
mkVlanNamed =
{ location, typeName }:
let
_ = assertMsg (subnetTypes ? typeName)
_ = assertMsg (builtins.hasAttr typeName subnetTypes)
"metatron-addressing: unknown subnet type '${typeName}'";
type = subnetTypes.${typeName};
in
mkVlan { inherit location type; };
# locationName → numeric id (0, 1, 2, …)
asList = value:
if value == null then [ ]
else if builtins.isList value then value
else [ value ];
scopeFromCfg = cfg: {
owners =
asList (cfg.owners or null)
++ asList (cfg.owner or null);
admins =
asList (cfg.admins or null)
++ asList (cfg.admin or null);
users = asList (cfg.users or null);
domain = cfg.domain or null;
};
mergeScopes = parent: child:
let
c = scopeFromCfg child;
in
{
owners = unique (parent.owners ++ c.owners);
admins = unique (parent.admins ++ c.admins);
users = unique (parent.users ++ c.users);
domain = if c.domain != null then c.domain else parent.domain;
};
emptyScope = {
owners = [ ];
admins = [ ];
users = [ ];
domain = null;
};
materializeScope = scope:
{
inherit (scope) owners admins users;
}
// optionalAttrs (scope.domain != null) {
inherit (scope) domain;
};
# locationName -> numeric id (0, 1, 2, ...)
locationIdForSpec =
spec:
let
locations =
spec.locations or(throw "metatron-addressing: spec.locations is required");
locations = spec.locations or (throw "metatron-addressing: spec.locations is required");
locationNames = attrNames locations;
in
name:
@ -259,183 +299,197 @@ let
else
idx;
# Just the host map: { deimos = { ip-address = "..."; fqdn = "..."; ... }; ... }
hostRole = path: hostCfg:
hostCfg.role or (throw "metatron-addressing: host '${path}' is missing required field 'role' for address generation");
hostMapForSubnet =
{ loc, locCode, locationName, networkName, typeName, subnetCfg, scope }:
let
hostsForSubnet = subnetCfg.hosts or { };
hostNames = filter (hn: builtins.substring 0 1 hn != "_") (attrNames hostsForSubnet);
subnetSlug =
if builtins.hasAttr typeName subnetSlugs
then subnetSlugs.${typeName}
else typeName;
in
listToAttrs (imap1
(idx: hostName:
let
path = "${locationName}.${networkName}.${typeName}.${hostName}";
hostCfg = hostsForSubnet.${hostName};
hostScope = mergeScopes scope hostCfg;
roleName = hostRole path hostCfg;
# Count how many hosts of this role exist up to and including this one.
roleIndex =
let
prefix = lib.lists.sublist 0 idx hostNames;
in
length (filter
(hn: (hostsForSubnet.${hn}.role or null) == roleName)
prefix);
hostId = hostCfg.hostId or roleIndex;
ip = mkIpNamed {
location = loc;
inherit typeName;
roleName = roleName;
host = hostId;
};
roleSlug =
if builtins.hasAttr roleName roleSlugs
then roleSlugs.${roleName}
else roleName;
hostnameBase = "${roleSlug}-${builtins.toString hostId}";
hostname = hostCfg.hostname or hostnameBase;
in
nameValuePair hostName (
{
ip-address = ip;
inherit hostname;
location = locationName;
network = networkName;
subnetType = typeName;
subnetKey = "${locationName}-${networkName}-${typeName}";
}
// optionalAttrs (hostScope.domain != null) {
fqdn = "${hostname}.${subnetSlug}.${locCode}.${hostScope.domain}";
}
// materializeScope hostScope
// optionalAttrs (hostCfg ? hw-address) {
inherit (hostCfg) hw-address;
}
// optionalAttrs (hostCfg ? dns) {
inherit (hostCfg) dns;
}
// optionalAttrs (hostCfg ? aliases) {
inherit (hostCfg) aliases;
}
// optionalAttrs (hostCfg ? interface) {
inherit (hostCfg) interface;
}
// optionalAttrs (hostCfg ? tags) {
inherit (hostCfg) tags;
}
))
hostNames);
mkHostsFromSpec =
spec:
let
locations =
spec.locations or (throw "metatron-addressing: spec.locations is required");
locations = spec.locations or (throw "metatron-addressing: spec.locations is required");
locationId = locationIdForSpec spec;
domain =
spec.domain or (throw "metatron-addressing: spec.domain is required for FQDN generation");
in
concatMapAttrs
(locationName: locationCfg:
let
loc = locationId locationName;
locCode = mkLocationCode locationName;
# Only treat attributes that are attrsets with a `hosts` attr as subnets
subnets =
lib.filterAttrs (_: v: builtins.isAttrs v && v ? hosts) locationCfg;
locationScope = mergeScopes emptyScope locationCfg;
networks = locationCfg.networks or { };
in
concatMapAttrs
(typeName: subnetCfg:
(networkName: networkCfg:
let
hostsForSubnet = subnetCfg.hosts or { };
# Only treat non-underscore-prefixed keys as hosts
hostNames =
filter (hn: builtins.substring 0 1 hn != "_") (attrNames hostsForSubnet);
networkScope = mergeScopes locationScope networkCfg;
subnets = networkCfg.subnets or { };
in
listToAttrs (imap1
(idx: hostName:
concatMapAttrs
(typeName: subnetCfg:
let
hostCfg = hostsForSubnet.${hostName};
roleName = hostCfg.role;
subnetScope = mergeScopes networkScope subnetCfg;
in
hostMapForSubnet {
inherit loc locCode locationName networkName typeName subnetCfg;
scope = subnetScope;
})
subnets)
networks)
locations;
# Count how many hosts of this role exist up to *and including* this one
roleIndex =
let
prefix = lib.lists.sublist 0 idx hostNames;
in
length (filter
(hn: (hostsForSubnet.${hn}.role or null) == roleName)
prefix);
mkSubnetsFromSpec =
spec:
let
locations = spec.locations or (throw "metatron-addressing: spec.locations is required");
locationId = locationIdForSpec spec;
in
concatMapAttrs
(locationName: locationCfg:
let
loc = locationId locationName;
locCode = mkLocationCode locationName;
locationScope = mergeScopes emptyScope locationCfg;
networks = locationCfg.networks or { };
in
concatMapAttrs
(networkName: networkCfg:
let
networkScope = mergeScopes locationScope networkCfg;
subnets = networkCfg.subnets or { };
in
mapAttrs'
(typeName: subnetCfg:
let
subnetScope = mergeScopes networkScope subnetCfg;
hostId = hostCfg.hostId or roleIndex;
ip = mkIpNamed {
location = loc;
inherit typeName;
roleName = hostCfg.role;
host = hostId;
};
cidr = mkSubnetNamed { location = loc; inherit typeName; };
subnetSlug =
if builtins.hasAttr typeName subnetSlugs
then subnetSlugs.${typeName}
else typeName;
roleSlug =
if builtins.hasAttr roleName roleSlugs
then roleSlugs.${roleName}
else roleName;
vlan =
subnetCfg.vlan or
subnetCfg._vlan or
(mkVlanNamed { location = loc; inherit typeName; });
hostnameBase = "${roleSlug}-${builtins.toString hostId}";
hostname = hostCfg.hostname or hostnameBase;
dhcpCfg = subnetCfg.dhcp or null;
fqdn = "${hostname}.${subnetSlug}.${locCode}.${domain}";
dhcpRange =
if dhcpCfg == null then null else {
startIp = mkIpNamed {
location = loc;
inherit typeName;
roleName = "pool";
host = dhcpCfg.start;
};
endIp = mkIpNamed {
location = loc;
inherit typeName;
roleName = "pool";
host = dhcpCfg.end;
};
};
in
nameValuePair hostName (
{
ip-address = ip;
inherit hostname fqdn;
# New context fields:
location = locationName;
subnetType = typeName;
subnetKey = "${locationName}-${typeName}";
}
// optionalAttrs (hostCfg ? hw-address) {
inherit (hostCfg) hw-address;
}
// optionalAttrs (hostCfg ? dns) {
inherit (hostCfg) dns;
}
// optionalAttrs (hostCfg ? aliases) {
inherit (hostCfg) aliases;
}
// optionalAttrs (hostCfg ? interface) {
inherit (hostCfg) interface;
}
)
)
hostNames))
subnets)
nameValuePair
"${locationName}-${networkName}-${typeName}"
(
{
inherit cidr locationName networkName typeName vlan;
}
// optionalAttrs (subnetScope.domain != null) {
zone = "${subnetSlug}.${locCode}.${subnetScope.domain}";
}
// materializeScope subnetScope
// optionalAttrs (dhcpCfg != null) {
dhcp = dhcpCfg;
dhcpRange = dhcpRange;
}
))
subnets)
networks)
locations;
# Subnet map: { "home-dmz" = "10.1.0.0/19"; "home-main" = "..."; ... }
mkSubnetsFromSpec =
spec:
let
locations =
spec.locations or (throw "metatron-addressing: spec.locations is required");
locationId = locationIdForSpec spec;
domain =
spec.domain or (throw "metatron-addressing: spec.domain is required");
in
concatMapAttrs
(locationName: locationCfg:
let
loc = locationId locationName;
locCode = mkLocationCode locationName;
# Only attributes with `hosts` are subnets
subnets =
lib.filterAttrs (_: v: builtins.isAttrs v && v ? hosts) locationCfg;
in
mapAttrs'
(typeName: subnetCfg:
let
cidr = mkSubnetNamed { location = loc; inherit typeName; };
subnetSlug =
if builtins.hasAttr typeName subnetSlugs
then subnetSlugs.${typeName}
else typeName;
zone = "${subnetSlug}.${locCode}.${domain}";
vlan =
subnetCfg.vlan or
subnetCfg._vlan or
(mkVlanNamed { location = loc; inherit typeName; });
dhcpCfg = subnetCfg.dhcp or null;
dhcpRange =
if dhcpCfg == null then null else {
startIp = mkIpNamed {
location = loc;
inherit typeName;
roleName = "pool";
host = dhcpCfg.start;
};
endIp = mkIpNamed {
location = loc;
inherit typeName;
roleName = "pool";
host = dhcpCfg.end;
};
};
in
nameValuePair
"${locationName}-${typeName}"
(
{
inherit cidr locationName typeName zone vlan;
}
// optionalAttrs (dhcpCfg != null) {
dhcp = dhcpCfg;
dhcpRange = dhcpRange;
}
))
subnets)
locations;
# Combined view, if you want both
mkNetworkFromSpec =
spec:
let
domain =
spec.domain or (throw "metatron-addressing: spec.domain is required");
in
{
inherit domain;
hosts = mkHostsFromSpec spec;
subnets = mkSubnetsFromSpec spec;
};
mkNetworkFromSpec = spec: {
hosts = mkHostsFromSpec spec;
subnets = mkSubnetsFromSpec spec;
};
in
{
@ -448,12 +502,15 @@ in
mkIpNamed
mkSubnet
mkSubnetNamed
mkVlan
mkVlanNamed
mkHostsFromSpec
mkSubnetsFromSpec
mkNetworkFromSpec
mkRoleRange
mkRoleRangeNamed;
mkRoleRangeNamed
mergeScopes;
# Shorthand; you were calling mkHosts before
# Shorthand; you were calling mkHosts before.
mkHosts = mkNetworkFromSpec;
}