{ lib, dsl }: let plain = v: if lib.isFunction v then null else if lib.isDerivation v then "${v}" else if lib.isList v then map plain v else if lib.isAttrs v then lib.mapAttrs (_: plain) v else if builtins.isPath v then toString v else v; relations = [ { field = "after"; kind = "after"; reverse = false; } { field = "before"; kind = "after"; reverse = true; } { field = "wants"; kind = "wants"; reverse = false; } { field = "wantedBy"; kind = "wants"; reverse = true; } { field = "requires"; kind = "requires"; reverse = false; } { field = "requiredBy"; kind = "requires"; reverse = true; } { field = "upholds"; kind = "upholds"; reverse = false; } { field = "upheldBy"; kind = "upholds"; reverse = true; } { field = "bindsTo"; kind = "bindsTo"; reverse = false; } { field = "partOf"; kind = "partOf"; reverse = false; } { field = "requisite"; kind = "requisite"; reverse = false; } { field = "conflicts"; kind = "conflicts"; reverse = false; } { field = "onFailure"; kind = "onFailure"; reverse = false; } { field = "onSuccess"; kind = "onSuccess"; reverse = false; } ]; relationFields = map (r: r.field) relations; # raw NixOS command options, mirrors every generated Exec* wrapper commandFields = [ "script" "scriptArgs" "preStart" "postStart" "reload" "preStop" "postStop" ]; allUnits = c: lib.concatMap lib.attrValues [ c.systemd.paths c.systemd.services c.systemd.slices c.systemd.sockets c.systemd.targets c.systemd.timers ] ++ c.systemd.mounts ++ c.systemd.automounts; unitSummary = u: { inherit (u) name; description = u.description or ""; enable = u.enable or true; unitConfig = u.unitConfig or { }; } // lib.filterAttrs (_: v: v != [ ]) (lib.genAttrs relationFields (f: u.${f} or [ ])) // lib.optionalAttrs (u ? serviceConfig) ( { inherit (u) serviceConfig; environment = u.environment or { }; path = u.path or [ ]; restartIfChanged = u.restartIfChanged or null; stopIfChanged = u.stopIfChanged or null; } # raw command text: the real logic behind the store-path Exec* wrappers // lib.filterAttrs (_: v: v != "") (lib.genAttrs commandFields (f: u.${f} or "")) ) // lib.optionalAttrs (u ? timerConfig) { inherit (u) timerConfig; } // lib.optionalAttrs (u ? socketConfig) { inherit (u) socketConfig; listenStreams = u.listenStreams or [ ]; listenDatagrams = u.listenDatagrams or [ ]; } // lib.optionalAttrs (u ? pathConfig) { inherit (u) pathConfig; } // lib.optionalAttrs (u ? what) { inherit (u) what; where = u.where or ""; type = u.type or ""; options = u.options or ""; }; unitEdges = node: u: let ref = n: "${node}:${n}"; in lib.concatMap ( r: map ( peer: { inherit (r) kind; } // ( if r.reverse then { from = ref peer; to = ref u.name; } else { from = ref u.name; to = ref peer; } ) ) (u.${r.field} or [ ]) ) relations; fileSystemsOf = c: lib.mapAttrsToList (mountPoint: fs: { inherit mountPoint; inherit (fs) device fsType options neededForBoot depends ; }) c.fileSystems; firewallOf = c: let fw = c.networking.firewall; portsOf = f: { inherit (f) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges ; }; in { inherit (fw) enable trustedInterfaces; interfaces = lib.mapAttrs (_: portsOf) fw.interfaces; } // portsOf fw; isBuildUser = n: builtins.match "nixbld[0-9]+" n != null; usersOf = c: { inherit (c.users) mutableUsers; groups = lib.mapAttrs (_: g: { inherit (g) gid; members = lib.filter (m: !isBuildUser m) g.members; }) c.users.groups; users = lib.mapAttrs (_: u: { inherit (u) uid group home shell description createHome isNormalUser isSystemUser extraGroups ; authorizedKeys = u.openssh.authorizedKeys.keys; passwordSet = lib.any (v: v != null) [ u.hashedPassword u.hashedPasswordFile u.password u.initialPassword u.initialHashedPassword ]; }) (lib.filterAttrs (n: _: !isBuildUser n) c.users.users); }; in { hostReport = { host }: let cfg = host.hostCfg.config; deployments = host.containers; depList = lib.attrValues deployments; configOf = c: if c.kind == "nspawn" then cfg.containers.${c.name}.config else cfg.microvm.vms.${c.name}.config.config; unitOf = c: "${dsl.serviceUnitName c}.service"; nodes = { "@host" = cfg; } // lib.mapAttrs (_: configOf) deployments; forward = proto: c: p: let q = dsl.normalizePort p; in { inherit proto; hostPort = q.host; target = c.name; targetPort = q.container; targetAddr = c.addr; }; mountsOf = c: if c.kind == "nspawn" then lib.mapAttrsToList (mountPoint: bm: { inherit mountPoint; inherit (bm) hostPath isReadOnly; }) c.bindMounts else map (s: { mountPoint = s.mountPoint or null; hostPath = s.source; isReadOnly = s.readOnly or false; inherit (s) proto tag; }) c.shares; in plain { meta = { host = host.hostName; system = cfg.nixpkgs.hostPlatform.system; inherit (cfg.system) stateVersion; inherit (host.specialArgs.hostInfo) paths mainGateway; toplevel = cfg.system.build.toplevel; }; deployments = lib.mapAttrs (_: c: { inherit (c) name kind autoStart access allowInternet needsGateway includedInTests ; # names only; c.depends holds whole deployment objects depends = map (d: d.name) c.depends; unit = unitOf c; limits = if c.kind == "nspawn" then { inherit (c) memoryMax cpuQuota privateUsers; } else { inherit (c) vcpu mem hypervisor; }; inherit (c) miscProperties; }) deployments; access = { specials = [ "@host" "@internet" ]; edges = lib.concatMap ( c: map (a: { from = c.name; inherit (a) to; tcp = a.tcp or [ ]; udp = a.udp or [ ]; }) c.access ) depList; }; kernel = { sysctl = cfg.boot.kernel.sysctl; params = cfg.boot.kernelParams; modules = cfg.boot.kernelModules; }; network = { inherit (cfg.networking) nameservers; links = lib.mapAttrs (_: c: { inherit (c) kind iface mac addr hostAddr allowInternet needsGateway ; }) deployments; hostInterfaces = lib.mapAttrs (_: i: { inherit (i) ipv4; }) cfg.networking.interfaces; dnsmasq = cfg.services.dnsmasq.settings; }; ports = { forwards = lib.concatMap ( c: map (forward "tcp" c) c.hostPorts ++ map (forward "udp" c) c.hostUdpPorts ) depList; declared = lib.mapAttrs (_: c: c.ports or [ ]) deployments; firewall = lib.mapAttrs (_: firewallOf) nodes; }; mounts = { fileSystems = lib.mapAttrs (_: fileSystemsOf) nodes; declared = lib.mapAttrs (_: mountsOf) deployments; }; users = lib.mapAttrs (_: usersOf) nodes; units = lib.mapAttrs ( _: c: lib.listToAttrs (map (u: lib.nameValuePair u.name (unitSummary u)) (allUnits c)) ) nodes; dependencies = { edges = lib.concatLists (lib.mapAttrsToList (node: c: lib.concatMap (unitEdges node) (allUnits c)) nodes) ++ map (c: { from = "@host:${unitOf c}"; to = "${c.name}:multi-user.target"; kind = "spawns"; }) depList; }; nftables = { inherit (cfg.networking.nftables) enable ruleset; tables = lib.mapAttrs (n: t: { name = t.name or n; inherit (t) family enable content; }) cfg.networking.nftables.tables; rendered = builtins.readFile ( builtins.elemAt cfg.systemd.services.nftables.serviceConfig.ExecStart 1 ); }; }; }