--- /dev/null
+// Write dot file showing the network architecture.
+
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright (C) 2024 Simon Ruderich
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "html"
+ "os"
+ "strings"
+)
+
+func writeDot(cfg *Config, path string) error {
+ f, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ w := bufio.NewWriter(f)
+
+ fmt.Fprintf(w, "graph {\n")
+ fmt.Fprintf(w, "graph [nodesep=3]\n")
+
+ for _, node := range cfg.Nodes {
+ var as []string
+ as = append(as, html.EscapeString(node.Name)+"<br/>")
+ for _, x := range node.Loopbacks {
+ as = append(as, fmt.Sprintf(" <font point-size=\"10\">%s</font> <br/>",
+ html.EscapeString(x.String())))
+ }
+ fmt.Fprintf(w, "%s [label=<%s>]\n",
+ node.Name, strings.Join(as, ""))
+ }
+
+ for _, link := range cfg.Links {
+ var aas []string
+ for _, x := range link.A.Addrs {
+ aas = append(aas, fmt.Sprintf(" %s <br/>",
+ html.EscapeString(x.Addr().String())))
+ }
+ var abs []string
+ for _, x := range link.B.Addrs {
+ abs = append(abs, fmt.Sprintf(" %s <br/>",
+ html.EscapeString(x.Addr().String())))
+ }
+ fmt.Fprintf(w, "%s -- %s [taillabel=<%s>,headlabel=<%s>,labelfontsize=10]\n",
+ link.A.Node.Name, link.B.Node.Name,
+ strings.Join(aas, ""), strings.Join(abs, ""),
+ )
+ }
+
+ fmt.Fprintf(w, "}\n")
+
+ err = w.Flush()
+ if err != nil {
+ return err
+ }
+ err = f.Sync()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// vi: set noet ts=4 sw=4 sts=4:
)
func main() {
- if len(os.Args) != 2 {
+ if len(os.Args) != 2 && len(os.Args) != 3 {
log.SetFlags(0)
- log.Fatalf("usage: %s <config>", os.Args[0])
+ log.Fatalf("usage: %s <config> [<dot-output>]", os.Args[0])
}
cfg, err := LoadConfig(os.Args[1])
log.Fatalf("config %q: %v", os.Args[1], err)
}
+ // Write dot file showing the network architecture
+ if len(os.Args) == 3 {
+ err := writeDot(cfg, os.Args[2])
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+
for _, node := range cfg.Nodes {
log.Printf("Setting up node %q ...", node.Name)