1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-11 07:10:39 +02:00

chore: topology updates and experiments

This commit is contained in:
oddlama 2024-03-02 13:25:03 +01:00
parent d494e19e1c
commit 35275d09d5
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
2 changed files with 67 additions and 129 deletions

View file

@ -52,130 +52,31 @@
nodesById = mapAttrs' (_: node: nameValuePair node.config.topology.id node) nixosConfigurations;
xmlAttrs = attrs: concatStringsSep " " (mapAttrsToList (n: v: "${n}=\"${v}\"") attrs);
font = attrs: text: "<font ${xmlAttrs attrs}>${text}</font>";
fontMono = {face = "JetBrains Mono";};
mono = font fontMono;
monoColor = color: font (fontMono // {inherit color;});
mkCell = cellAttrs: text: "<td ${xmlAttrs cellAttrs}>${text}</td>";
mapToTableRows = xs: {
columnOrder,
columns,
titleRow ? true,
titleRowColor ? colors.base0C,
titleRowAttrs ? {bgcolor = titleRowColor;},
alternateRowAttrs ? {bgcolor = colors.base03b;},
}:
concatLines (
optional titleRow "<tr>${concatStringsSep "" (flip map columnOrder (c: mkCell titleRowAttrs "<b>${mono columns.${c}.title}</b>"))}</tr>"
++ flip imap0 xs (
i: x: "<tr>${concatStringsSep "" (flip map columnOrder (c:
mkCell
(optionalAttrs (pkgs.lib.mod i 2 == 1) alternateRowAttrs // (columns.${c}.cellAttrs or {}))
(columns.${c}.transform x.${c})))}</tr>"
)
);
mkTable = xs: settings: ''
<table border="0" cellborder="0" cellspacing="0" cellpadding="4" bgcolor="${colors.base03}" color="${colors.base04}">
${mapToTableRows xs settings}
</table>
'';
nodeId = str: "\"${escapeXML str}\"";
isGuestOfAny = node: any (x: elem node x.config.topology.guests) (attrValues nodesById);
rootNodes = filterAttrs (n: _: !(isGuestOfAny n)) nodesById;
toDot = node: let
toD2 = node: let
topo = node.config.topology;
in ''
${topo.id}: |md
# ${topo.id}
diskTable = mkTable (attrValues topo.disks) {
titleRowColor = colors.base0F;
columnOrder = ["name"];
columns = {
name = {
title = "Name";
transform = mono;
};
};
};
## Guests:
${concatLines (map (x: "- ${x}") topo.guests)}
interfaceTable = mkTable (attrValues topo.interfaces) {
titleRowColor = colors.base0D;
columnOrder = ["name" "mac" "addresses"];
columns = {
name = {
title = "Name";
transform = x:
if x == null
then ""
else mono x;
};
mac = {
title = "MAC";
transform = x:
if x == null
then ""
else monoColor colors.base09 x;
};
addresses = {
title = "Addr";
transform = xs: mono (concatStringsSep " " xs);
};
};
};
in
''
subgraph ${nodeId "cluster_${topo.id}"} {
color = "${colors.base04}";
## Disks:
${concatLines (mapAttrsToList (_: v: "- ${v.name}") topo.disks)}
${nodeId topo.id} [label=<
<table border="0" cellborder="0" cellspacing="0" cellpadding="4" bgcolor="${colors.base03}" color="${colors.base04}">
<tr><td bgcolor="${colors.base08}"><b>${mono "Attribute"}</b></td><td bgcolor="${colors.base08}"><b>${mono "Value"}</b></td></tr>
<tr><td>${mono "id"}</td><td>${mono topo.id}</td></tr>
<tr><td>${mono "type"}</td><td>${mono topo.type}</td></tr>
</table>
>];
## Interfaces:
${concatLines (mapAttrsToList (_: v: "- ${v.name}, mac ${toString v.mac}, addrs ${toString v.addresses}, network ${toString v.network}") topo.interfaces)}
{
rank = "same";
${nodeId "${topo.id}.disks"} [label=<
${diskTable}
>];
${nodeId "${topo.id}.interfaces"} [label=<
${interfaceTable}
>];
}
## Firewall Zones:
${concatLines (mapAttrsToList (_: v: "- ${v.name}, mac ${toString v.mac}, addrs ${toString v.addresses}, network ${toString v.network}") topo.firewallRules)}
|
'';
${nodeId topo.id} -> ${nodeId "${topo.id}.disks"} [label="disks", color="${colors.base05}", fontcolor="${colors.base06}"];
${nodeId topo.id} -> ${nodeId "${topo.id}.interfaces"} [label="interfaces", color="${colors.base05}", fontcolor="${colors.base06}"];
''
+ optionalString (topo.guests != []) ''
subgraph ${nodeId "cluster_guests_${topo.id}"} {
color = "${colors.base04}";
{
rank = "same";
${concatLines (map (guest: "${nodeId guest};") topo.guests)}
}
${concatLines (map (guest: dotForNodes.${guest}) topo.guests)}
};
${concatLines (map (guest: "${nodeId topo.id} -> ${nodeId guest} [color=\"${colors.base05}\"];") topo.guests)}
}
''
+ optionalString (!isGuestOfAny topo.id) ''
root -> ${nodeId topo.id} [color="${colors.base05}"];
'';
dotForNodes = mapAttrs' (_: node: nameValuePair node.config.topology.id (toDot node)) nodesById;
d2ForNodes = mapAttrs' (_: node: nameValuePair node.config.topology.id (toD2 node)) nodesById;
in
pkgs.writeText "topology.dot" ''
digraph G {
graph [rankdir=TB, splines=spline, bgcolor="${colors.base00}"];
node [shape=plaintext, fontcolor="${colors.base06}", color="${colors.base06}"];
${concatLines (map (x: dotForNodes.${x}) (attrNames rootNodes))}
}
pkgs.writeText "topology.d2" ''
${concatLines (map (x: d2ForNodes.${x}) (attrNames rootNodes))}
''

View file

@ -19,24 +19,44 @@ in {
options.topology = {
id = mkOption {
description = ''
The attribute name in nixosConfigurations corresponding to this host.
Please overwrite with a unique identifier if your hostnames are not
The attribute name in the given `nodes` which corresponds to this host.
Please overwrite it with a unique identifier if your hostnames are not
unique or don't reflect the name you use to refer to that node.
'';
default = config.networking.hostName;
# TODO ensure unique across the board
type = types.str;
};
guests = mkOption {
description = "TODO guests ids (topology.id)";
type = types.listOf types.str;
default = [];
};
type = mkOption {
description = "TODO";
type = types.enum ["normal" "microvm" "nixos-container"];
default = "normal";
type = types.enum ["normal" "microvm" "nixos-container"];
};
guests = mkOption {
description = "TODO guests ids (topology.node.<name>.id) ensure exists";
default = [];
type = types.listOf types.str;
};
disks = mkOption {
default = {};
type = types.attrsOf (types.submodule (submod: {
options = {
name = mkOption {
description = "The name of this disk";
default = submod.config._module.args.name;
readOnly = true;
type = types.str;
};
};
}));
};
interfaces = mkOption {
description = "TODO";
default = {};
type = types.attrsOf (types.submodule (submod: {
options = {
name = mkOption {
@ -48,30 +68,45 @@ in {
mac = mkOption {
description = "The MAC address of this interface, if known.";
type = types.nullOr types.str;
default = null;
type = types.nullOr types.str;
};
addresses = mkOption {
description = "The configured address(es), or a descriptive string (like DHCP).";
type = types.listOf types.str;
};
network = mkOption {
description = ''
The global name of the attached/spanned network.
If this is given, this interface can be shown in the network graph.
'';
default = null;
type = types.nullOr types.str;
};
};
}));
default = {};
};
disks = mkOption {
firewallRules = mkOption {
description = "TODO";
default = {};
type = types.attrsOf (types.submodule (submod: {
options = {
name = mkOption {
description = "The name of this disk";
description = "The name of this firewall rule";
type = types.str;
readOnly = true;
default = submod.config._module.args.name;
};
contents = mkOption {
description = "A human readable summary of this rule's effects";
type = types.lines;
};
};
}));
default = {};
};
};
@ -89,8 +124,10 @@ in {
disks =
flip mapAttrs (config.disko.devices.disk or {})
(_: _: {});
# TODO: zfs pools from disko / fileSystems
# TODO: microvm shares
# TODO: container shares
# TODO: OCI containers shares
interfaces = let
isNetwork = netDef: (netDef.matchConfig != {}) && (netDef.address != [] || netDef.DHCP != null);