package main import ( "fmt" "log" "net/netip" "os" "utils/yaml_ipam" ) func main() { err := work() if err != nil { _, _ = fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err) os.Exit(1) } } func work() error { ipamYaml := "../ansible/group_vars/all/ipam.yml" bs, err := os.ReadFile(ipamYaml) if err != nil { return err } ipam, err := loadIpam(bs) if err != nil { return err } fmt.Printf("digraph {\n") //fmt.Printf(" layout=dot\n") //fmt.Printf(" layout=twopi\n") fmt.Printf(" layout=circo\n") //fmt.Printf(" ranksep=3\n") //fmt.Printf(" ratio=auto\n") //fmt.Printf(" rankdir=LR\n") //fmt.Printf(" rankdir=RL\n") //fmt.Printf(" rankdir=TB\n") fmt.Printf("\n") fmt.Printf(" # Nodes\n") for _, n := range ipam.networks { fmt.Printf(" %s [ label = \"%s\\n%s\"];\n", n.name, n.name, n.prefix) } for _, n := range ipam.FindRoots() { fmt.Printf(" %s [ root = true ];\n", n.name) } fmt.Printf("\n") fmt.Printf(" # Relationships\n") for _, n := range ipam.networks { for _, c := range ipam.networks { if c.parent == n { fmt.Printf(" %s -> %s;\n", n.name, c.name) } } } fmt.Printf("}\n") return nil } func loadIpam(bs []byte) (*Ipam, error) { parsed, err := yaml_ipam.Parse(bs) if err != nil { return nil, err } ipam_, err := processNetworks(parsed.Ipam6.Networks) if err != nil { return nil, err } ipam_.ResolveParents() return ipam_, nil } type Ipam struct { networks []*Network } func (ipam *Ipam) ResolveParents() { for _, n := range ipam.networks { log.Printf("network %s/%s", n.name, n.prefix) for _, p := range ipam.networks { if n == p { continue } //log.Printf("candidate %s/%s", p.name, p.prefix) if p.prefix.Contains(n.prefix.Addr()) { if n.parent == nil { log.Printf("found parent %s/%s", p.name, p.prefix) n.parent = p } else { if n.parent.prefix.Contains(p.prefix.Addr()) { log.Printf("found better parent %s/%s", p.name, p.prefix) n.parent = p } } } } } return } func (ipam *Ipam) FindRoots() []*Network { var ns []*Network for _, n := range ipam.networks { if n.parent == nil { ns = append(ns, n) } } return ns } type Network struct { name string parent *Network prefix netip.Prefix hosts []networkHost } type networkHost struct { name string address netip.Addr } func processNetworks(networks map[string]yaml_ipam.Network6Yaml) (*Ipam, error) { var ns []*Network for name, net := range networks { log.Printf("Processing net %v\net", name) prefix, err := netip.ParsePrefix(net.Range) if err != nil { return nil, fmt.Errorf("error parsing net range: %v\net", err) } log.Printf("prefix: %s", prefix.String()) n := Network{name: name, prefix: prefix} for hostname, a := range net.Hosts { addr, err := netip.ParseAddr(a) if err != nil { return nil, fmt.Errorf("network: %s, unable to parse host address %s\net", n.name, a) } n.hosts = append(n.hosts, networkHost{ name: hostname, address: addr, }) } ns = append(ns, &n) } return &Ipam{networks: ns}, nil }