network/nix/pkgs/network-cypher-graphs.nix

171 lines
5.2 KiB
Nix

{ config, pkgs, ... }:
let
inherit (pkgs) lib runCommand curl;
netColor = net:
if net == "core"
then "grey"
else if net == "mgmt"
then "brown"
else if builtins.elem net [ "c3d2" "serv" "cluster" ]
then "green"
else if builtins.match "up.+" net != null ||
builtins.match "anon.+" net != null
then "red"
else if builtins.match "priv.+" net != null
then "blue"
else "black";
dedupLinks = links:
builtins.attrValues (
builtins.foldl' (result: { pair, attrs ? {}, startLabel ? "" }:
let
peer1 = builtins.elemAt pair 0;
peer2 = builtins.elemAt pair 1;
sorted =
if peer1 < peer2
then { from = peer1; to = peer2; }
else { from = peer2; to = peer1; };
key = with sorted; "${from} ${to}";
prevAttrs =
if result ? ${key}
then result.${key}.attrs
else {};
newAttrs = attrs // (
if peer1 < peer2
then { taillabel = startLabel; }
else { headlabel = startLabel; }
);
in result // {
"${key}" = {
inherit (sorted) from to;
attrs = prevAttrs // newAttrs;
};
}
) {} links
);
toCypher = { nodes, links, ... }: ''
${lib.concatMapStringsSep " " (name:
let
nodeAttrs = nodes.${name};
id = builtins.replaceStrings ["-"] ["_"] name;
nodeType = nodeAttrs.type;
in ''
CREATE (${id}:`${nodeType}`
{label:"${name}",
${lib.concatMapStringsSep ", " (attr:
let
value = nodeAttrs.${attr};
in
if builtins.isString value
then "${attr}:\"${value}\""
else "${attr}:${toString value}"
) (builtins.attrNames nodeAttrs)}
}
)
'') (builtins.attrNames nodes)}
${lib.concatMapStringsSep " " ({ from, to, attrs }:
let
idFrom = builtins.replaceStrings ["-"] ["_"] from;
idTo = builtins.replaceStrings ["-"] ["_"] to;
in ''
CREATE (${idFrom})-[:CONNECTED_TO
{${lib.concatMapStringsSep ", " (attr:
let
value = attrs.${attr};
in
if builtins.isString value
then "${attr}:\"${value}\""
else "${attr}:${toString value}"
) (builtins.attrNames attrs)}
}]->(${idTo})
'') (dedupLinks links)}
'';
cypherGraph = args@{ name, ... }:
runCommand "${name}.cypher" {
src = builtins.toFile "${name}.cypher" (
toCypher args
);
} ''
sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' $src | tr -d '\n' > $out
'';
in rec {
# Layer 2
physical-cypher-graph = cypherGraph {
name = "physical";
nodes =
builtins.mapAttrs (_: { role, ... }: {
type = {
switch = "Switch";
server = "Server";
container = "Container";
ap = "AccessPoint";
client = "Client";
}.${role};
}) (
lib.filterAttrs (_: { links, ... }:
links != {}
) config.site.hosts
) // builtins.mapAttrs (_: _: {
type = "Other";
}) (
lib.filterAttrs (net: _:
builtins.match "up.*" net != null
) config.site.net
);
links =
builtins.concatMap (hostName:
map (link: {
pair = [ hostName link ];
startLabel = ( #lib.optionalString (config.site.hosts.${hostName}.links ? ${link}) (
lib.concatStringsSep ","
config.site.hosts.${hostName}.links.${link}.ports
);
}) (
builtins.filter (link:
config.site.hosts ? ${link}
||
builtins.match "up.*" link != null
) (builtins.attrNames config.site.hosts.${hostName}.links)
)
) (builtins.attrNames config.site.hosts);
};
# Layer 3
logical-cypher-graph =
let
containers =
lib.filterAttrs (_: { isRouter, role, ... }:
role == "container"
) config.site.hosts;
in
cypherGraph {
name = "logical";
nodes =
builtins.foldl' (result: hostName:
result // {
"${hostName}".type = "Container";
} // builtins.mapAttrs (_: _: {
type = "Container";
}) containers.${hostName}.interfaces
) {} (builtins.attrNames containers);
links =
builtins.concatMap (hostName:
map (net: {
pair = [ hostName net ];
attrs.color = netColor net;
}) (builtins.attrNames containers.${hostName}.interfaces)
) (builtins.attrNames containers);
};
import-network-graphs = runCommand "import-network-graphs" {} ''
${curl}/bin/curl -X POST -H 'Content-type: application/json' http://localhost:7474/db/data/transaction/commit -d "{\"statements\": [{\"statement\": \"$(cat ${physical-cypher-graph})\"}]}"
${curl}/bin/curl -X POST -H 'Content-type: application/json' http://localhost:7474/db/data/transaction/commit -d "{\"statements\": [{\"statement\": \"$(cat ${logical-cypher-graph})\"}]}"
'';
}