diff --git a/cloud.svg b/cloud.svg new file mode 100644 index 0000000..3de2d1a --- /dev/null +++ b/cloud.svg @@ -0,0 +1 @@ + diff --git a/flake.nix b/flake.nix index 1653482..1a078cb 100644 --- a/flake.nix +++ b/flake.nix @@ -192,47 +192,69 @@ deviceType = "router"; hardware.image = ./fritzbox.png; # interfaces.wan0.network = "internet"; - interfaces.wan0.physicalConnections = [ + interfaces.wan0 = {}; + interfaces.lan0.physicalConnections = [ { node = "ward"; interface = "wan"; } + { + node = "sire"; + interface = "lan"; + } + ]; + }; + + nodes.internet = { + name = "Internet"; + deviceType = "internet"; + hardware.image = ./cloud.svg; + # interfaces.wan0.network = "internet"; + interfaces.wan0.physicalConnections = [ + { + node = "fritzbox"; + interface = "wan0"; + } + { + node = "sentinel"; + interface = "wan"; + } ]; }; - nodes.fritzbox-no-img = { - name = "FritzBox No HImg"; - deviceType = "router"; - interfaces.wan0.physicalConnections = [ - { - node = "ward"; - interface = "wan"; - } - ]; - }; + #nodes.fritzbox-no-img = { + # name = "FritzBox No HImg"; + # deviceType = "router"; + # interfaces.wan0.physicalConnections = [ + # { + # node = "ward"; + # interface = "wan"; + # } + # ]; + #}; - nodes.fritzbox-device-nd = { - name = "FritzBox No DImg"; - deviceType = "device"; - hardware.image = ./fritzbox.png; - interfaces.wan0.physicalConnections = [ - { - node = "ward"; - interface = "wan"; - } - ]; - }; + #nodes.fritzbox-device-nd = { + # name = "FritzBox No DImg"; + # deviceType = "device"; + # hardware.image = ./fritzbox.png; + # interfaces.wan0.physicalConnections = [ + # { + # node = "ward"; + # interface = "wan"; + # } + # ]; + #}; - nodes.fritzbox-device = { - name = "FritzBox No D&HImg"; - deviceType = "device"; - interfaces.wan0.physicalConnections = [ - { - node = "ward"; - interface = "wan"; - } - ]; - }; + #nodes.fritzbox-device = { + # name = "FritzBox No D&HImg"; + # deviceType = "device"; + # interfaces.wan0.physicalConnections = [ + # { + # node = "ward"; + # interface = "wan"; + # } + # ]; + #}; # TODO: #nodes.fritzbox = config.lib.nodes.mkRouter {}; diff --git a/hosts/ward/default.nix b/hosts/ward/default.nix index 31aee08..9537563 100644 --- a/hosts/ward/default.nix +++ b/hosts/ward/default.nix @@ -64,10 +64,6 @@ networking.nftables.firewall = { zones.untrusted.interfaces = [config.guests.${guestName}.networking.mainLinkName]; }; - - # TODO: FIXME: remove!!!! - topology.self.guestType = "microvm"; - topology.self.parent = config.node.name; } ]; }; diff --git a/topology/nixos/extractors/microvm.nix b/topology/nixos/extractors/microvm.nix new file mode 100644 index 0000000..58d9273 --- /dev/null +++ b/topology/nixos/extractors/microvm.nix @@ -0,0 +1,29 @@ +{ + config, + lib, + ... +}: let + inherit + (lib) + attrValues + flip + mkEnableOption + mkIf + mkMerge + optionalAttrs + ; +in { + options.topology.extractors.microvm.enable = mkEnableOption "topology microvm extractor" // {default = true;}; + + config = mkIf (config.topology.extractors.microvm.enable && config ? microvm && config.microvm.host.enable) { + topology.nodes = mkMerge (flip map (attrValues config.microvm.vms) ( + vm: + optionalAttrs (vm.config.config ? topology) { + ${vm.config.config.topology.id} = { + guestType = "microvm"; + parent = config.topology.id; + }; + } + )); + }; +} diff --git a/topology/nixos/extractors/services.nix b/topology/nixos/extractors/services.nix index 756280b..e914623 100644 --- a/topology/nixos/extractors/services.nix +++ b/topology/nixos/extractors/services.nix @@ -60,7 +60,7 @@ in { }; openssh = mkIf config.services.openssh.enable { - hidden = mkDefault true; # Causes a lot of much clutter + hidden = mkDefault true; # Causes a lot of clutter name = "OpenSSH"; icon = "services.openssh"; info = "port: ${concatStringsSep ", " (map toString config.services.openssh.ports)}"; diff --git a/topology/options/nodes.nix b/topology/options/nodes.nix index fae6bf8..53fde7b 100644 --- a/topology/options/nodes.nix +++ b/topology/options/nodes.nix @@ -66,7 +66,7 @@ in values exist that will automatically set some other defaults, most notably the deviceIcon and preferredRenderType. ''; - type = types.either (types.enum ["nixos" "router" "switch" "device"]) types.str; + type = types.either (types.enum ["nixos" "internet" "router" "switch" "device"]) types.str; }; guestType = mkOption { @@ -111,7 +111,7 @@ in } # If the device type is not a full nixos node, try to render it as an image with name. - (mkIf (elem nodeCfg.deviceType ["router" "switch" "device"]) { + (mkIf (elem nodeCfg.deviceType ["internet" "router" "switch" "device"]) { preferredRenderType = mkDefault "image"; }) ]); diff --git a/topology/topology/renderers/elk/default.nix b/topology/topology/renderers/elk/default.nix index b9665b8..83929ac 100644 --- a/topology/topology/renderers/elk/default.nix +++ b/topology/topology/renderers/elk/default.nix @@ -19,6 +19,7 @@ mapAttrsToList mkOption optional + optionals optionalAttrs recursiveUpdate types @@ -60,28 +61,39 @@ }; }; + idForInterface = node: interfaceId: + if (node.preferredRenderType == "card") + then "children.node:${node.id}.ports.interface:${interfaceId}" + else "children.node:${node.id}"; + nodeInterfaceToElk = node: interface: [ - { + (optionalAttrs (node.preferredRenderType == "card") { children."node:${node.id}".ports."interface:${interface.id}" = { - properties = { - "port.side" = "WEST"; - }; + properties."port.side" = "WEST"; + #x = 0; + #y = 82 + 42 * lib.lists.findFirstIndex (x: x == interface.id) 0 (builtins.attrNames node.interfaces); # FIXME: just pass index along in function call width = 8; height = 8; + # TODO: FIXME: not shown currently in svg + # labels.name = { + # text = interface.id; + # width = 33.0; + # height = 15.0; + # }; }; - } + }) ] - ++ flip map interface.physicalConnections (x: + ++ optionals (!interface.virtual) (flip map interface.physicalConnections (x: optionalAttrs ( (!any (y: y.node == node.id && y.interface == interface.id) config.nodes.${x.node}.interfaces.${x.interface}.physicalConnections) || (node.id < x.node) ) { edges."node:${node.id}.ports.interface:${interface.id}-to-node:${x.node}.ports.interface:${x.interface}" = { - sources = ["children.node:${node.id}.ports.interface:${interface.id}"]; - targets = ["children.node:${x.node}.ports.interface:${x.interface}"]; + sources = [(idForInterface node interface.id)]; + targets = [(idForInterface config.nodes.${x.node} x.interface)]; }; - }); + })); nodeToElk = node: [ @@ -91,23 +103,21 @@ file = config.lib.renderers.svg.node.mkPreferredRender node; scale = 0.8; }; - properties = { - "portConstraints" = "FIXED_SIDE"; - }; + properties."portConstraints" = "FIXED_SIDE"; }; } ] ++ optional (node.parent != null) { children."node:${node.parent}".ports.guests = { - properties = { - "port.side" = "EAST"; - }; + properties."port.side" = "EAST"; width = 8; height = 8; }; edges."node:${node.parent}.ports.guests-to-node:${node.id}" = { sources = ["children.node:${node.parent}.ports.guests"]; targets = ["children.node:${node.id}"]; + style.stroke-dasharray = "10,8"; + style.stroke-linecap = "round"; }; } ++ map (nodeInterfaceToElk node) (attrValues node.interfaces); @@ -128,10 +138,23 @@ in { layoutOptions = { "org.eclipse.elk.algorithm" = "layered"; "org.eclipse.elk.edgeRouting" = "ORTHOGONAL"; + "org.eclipse.elk.direction" = "RIGHT"; "org.eclipse.elk.layered.crossingMinimization.strategy" = true; "org.eclipse.elk.layered.nodePlacement.strategy" = "NETWORK_SIMPLEX"; "org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers" = 40; - "org.eclipse.elk.direction" = "RIGHT"; + "org.eclipse.elk.layered.spacing.edgeEdgeBetweenLayers" = 25; + "org.eclipse.elk.spacing.edgeEdge" = 50; + "org.eclipse.elk.spacing.edgeNode" = 50; + }; + } + + # Add service overview + { + children.services-overview = { + svg = { + file = config.lib.renderers.svg.services.mkOverview; + scale = 0.8; + }; }; } ] diff --git a/topology/topology/renderers/svg/default.nix b/topology/topology/renderers/svg/default.nix index 2596763..3b27aba 100644 --- a/topology/topology/renderers/svg/default.nix +++ b/topology/topology/renderers/svg/default.nix @@ -10,6 +10,7 @@ # - search todo and do # - podman / docker harvesting # - adjust device icon based on guest type +# - nixos-container extractor { config, lib, @@ -116,8 +117,7 @@ */ ''
-
-
+
${mkImage "w-6 h-6 mr-2" (config.lib.icons.get interface.icon)} ${interface.id}
@@ -141,7 +141,11 @@ # FIXME: order not respected + concatLines ((map serviceDetail) (attrValues service.details)); - mkService = service: + mkService = { + additionalInfo ? "", + includeDetails ? true, + ... + }: service: /* html */ @@ -152,9 +156,10 @@

${service.name}

${optionalString (service.info != "") ''

${service.info}

''} + ${additionalInfo}
- ${serviceDetails service} + ${optionalString includeDetails (serviceDetails service)}
''; @@ -223,7 +228,7 @@ ${concatLines (map mkGuest guests)} ${optionalString (guests != []) spacingMt2} - ${concatLines (map mkService services)} + ${concatLines (map (mkService {}) services)} ${optionalString (services != []) spacingMt2} ${mkImageMaybe "w-full h-24" node.hardware.image} @@ -240,12 +245,12 @@ */ ''
- ${mkImageMaybe "w-12 h-12" (config.lib.icons.get node.icon)} + ${mkImageMaybe "w-8 h-8" (config.lib.icons.get node.icon)}

${node.name}

${optionalString (node.hardware.image != null -> deviceIconImage != node.hardware.image) '' -
- ${mkImageMaybe "w-16 h-16" deviceIconImage} +
+ ${mkImageMaybe "w-12 h-12" deviceIconImage} ''}
@@ -261,6 +266,36 @@ ) node; }; + + services.mkOverview = { + width = 480; + html = + mkCardContainer + /* + html + */ + '' +
+

Services Overview

+
+ + ${concatLines (flip map (attrValues config.nodes) ( + node: let + services = filter (x: !x.hidden) (attrValues node.services); + in + concatLines ( + flip map services ( + html.node.mkService { + additionalInfo = ''

${node.name}

''; + includeDetails = false; + } + ) + ) + ))} + + ${spacingMt2} + ''; + }; }; in { options.renderers.svg = { @@ -274,10 +309,14 @@ in { }; config = { - lib.renderers.svg.node = { - mkNetCard = node: renderHtmlToSvg (html.node.mkNetCard node) "card-network-${node.id}"; - mkCard = node: renderHtmlToSvg (html.node.mkCard node) "card-node-${node.id}"; - mkPreferredRender = node: renderHtmlToSvg (html.node.mkPreferredRender node) "preferred-render-node-${node.id}"; + lib.renderers.svg = { + services.mkOverview = renderHtmlToSvg html.services.mkOverview "services-overview"; + + node = { + mkNetCard = node: renderHtmlToSvg (html.node.mkNetCard node) "card-network-${node.id}"; + mkCard = node: renderHtmlToSvg (html.node.mkCard node) "card-node-${node.id}"; + mkPreferredRender = node: renderHtmlToSvg (html.node.mkPreferredRender node) "preferred-render-node-${node.id}"; + }; }; renderers.svg.output = pkgs.runCommand "topology-svgs" {} ''