From 5dbb35f0a5aa757a1d94b1459295f497cf8473c3 Mon Sep 17 00:00:00 2001 From: lauralani Date: Thu, 23 Mar 2023 13:53:46 +0100 Subject: [PATCH] add PowerDNS integration --- cmd/generic.go | 9 + cmd/ip-add.go | 5 + cmd/ip-delete.go | 5 + cmd/powerdns.go | 403 +++++++++++++++++++++++++++++++++++++++++++ cmd/subnet-delete.go | 23 ++- cmd/subnet-list.go | 7 +- go.mod | 36 ++-- go.sum | 13 ++ test/192.168.35.0_24 | 5 - test/subnet.yaml | 7 - test/test_yaml.go | 40 ----- 11 files changed, 481 insertions(+), 72 deletions(-) create mode 100644 cmd/generic.go delete mode 100644 test/192.168.35.0_24 delete mode 100644 test/subnet.yaml delete mode 100644 test/test_yaml.go diff --git a/cmd/generic.go b/cmd/generic.go new file mode 100644 index 0000000..a07f7e2 --- /dev/null +++ b/cmd/generic.go @@ -0,0 +1,9 @@ +package cmd + +func reverse(elements []string) []string { + for i := 0; i < len(elements)/2; i++ { + j := len(elements) - i - 1 + elements[i], elements[j] = elements[j], elements[i] + } + return elements +} diff --git a/cmd/ip-add.go b/cmd/ip-add.go index 5b93300..4fed034 100644 --- a/cmd/ip-add.go +++ b/cmd/ip-add.go @@ -69,6 +69,11 @@ var ipaddCmd = &cobra.Command{ fmt.Printf("added ip:\nip: %v\n", ipaddress) } else { fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname) + dnserr := AddDNSFqdn(hostname, ip) + if dnserr != nil { + fmt.Println("[ERROR]", writeerr) + os.Exit(1) + } } }, } diff --git a/cmd/ip-delete.go b/cmd/ip-delete.go index 01ed097..e9cf5b8 100644 --- a/cmd/ip-delete.go +++ b/cmd/ip-delete.go @@ -51,6 +51,11 @@ var ipdeleteCmd = &cobra.Command{ fmt.Printf("deleted ip %v\n", address.IP.String()) } else { fmt.Printf("deleted ip %v (%v)\n", address.IP.String(), address.FQDN) + dnserr := DeleteDNSFqdn(address.FQDN, address.IP) + if dnserr != nil { + fmt.Println("[ERROR]", writeerr) + os.Exit(1) + } } }, } diff --git a/cmd/powerdns.go b/cmd/powerdns.go index 2e837ad..661cc93 100644 --- a/cmd/powerdns.go +++ b/cmd/powerdns.go @@ -3,3 +3,406 @@ Copyright © 2023 Laura Kalb */ package cmd + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/netip" + "strings" + + "github.com/spf13/viper" +) + +type DNSZone struct { + ID string `json:"id"` + Name string `json:"name"` + Kind string `json:"kind"` + RRsets []DNSRecordSet `json:"rrsets"` + Metadata map[string]string `json:"metadata"` + DNSSEC bool `json:"dnssec"` + NSEC3Param string `json:"nsec3param,omitempty"` + Account string `json:"account,omitempty"` + Serial int `json:"serial"` +} + +type Patch struct { + Rrsets []DNSRecordSet `json:"rrsets"` +} + +// Checks if a given Record already exists in the DNSRecordSet list. +// +// Returns the DNSRecordSet and true if record exists, empty +// DNSRecordSet and false if not. +func (z DNSZone) GetRecord(fqdn string, rtype string, rcontent string) (DNSRecordSet, bool) { + if !strings.HasSuffix(fqdn, ".") { + fqdn = fqdn + "." + } + if (rtype == "PTR") && !strings.HasSuffix(rcontent, ".") { + rcontent = rcontent + "." + } + for _, recordset := range z.RRsets { + if recordset.Name == fqdn && recordset.Type == rtype { + for _, record := range recordset.Records { + if record.Content == rcontent { + return recordset, true + } + } + } + } + return DNSRecordSet{}, false +} + +// Sends a PATCH API request for DNSZone z. Returns error or nil +// +// Example args for "test.example.com IN A 127.0.0.1" +// +// z.Name = "example.com." +// record = "test" +// value = "127.0.0.1" +// recordtype = "A" +// changetype = "REPLACE" +func (z DNSZone) SendPATCH(record string, value string, recordtype string, changetype string) error { + pdnsendpoint := viper.GetString("powerdnsendpoint") + pdnsapikey := viper.GetString("powerdnsapikey") + debug, _ := rootCmd.Flags().GetBool("debug") + + if !viper.GetBool("powerdnsenabled") { + return nil + } + + url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + z.Name + if debug { + fmt.Println("[DEBUG] PowerDNS URL: " + url) + } + + rset := DNSRecordSet{} + rset.Changetype = changetype + rset.Name = strings.Join([]string{record, z.Name}, ".") + rset.TTL = 3600 + rset.Type = recordtype + rec := DNSRecord{} + if recordtype == "PTR" { + rec.Content = value + "." + } else { + rec.Content = value + } + rset.Records = append(rset.Records, rec) + + patch := Patch{} + patch.Rrsets = append(patch.Rrsets, rset) + + payload, marsherr := json.Marshal(patch) + if marsherr != nil { + return marsherr + } + + req, reqerr := http.NewRequest("PATCH", url, bytes.NewBuffer(payload)) + if reqerr != nil { + return reqerr + } + + req.Header.Add("X-API-Key", pdnsapikey) + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + resp, resperr := client.Do(req) + if resperr != nil { + return resperr + } + + if resp.StatusCode != 204 { + defer resp.Body.Close() + + body, readerr := io.ReadAll(resp.Body) + if readerr != nil { + fmt.Println(readerr) + } + return errors.New("HTTP Error: " + resp.Status + "\n" + string(body)) + } + return nil +} + +type DNSRecordSet struct { + Name string `json:"name"` + Type string `json:"type"` + TTL int `json:"ttl"` + Records []DNSRecord `json:"records"` + Changetype string `json:"changetype,omitempty"` +} + +type DNSRecord struct { + Content string `json:"content"` + Disabled bool `json:"disabled,omitempty"` + SetPTR bool `json:"set-ptr,omitempty"` +} + +// GetDNSZone retrieves the corresponding DNSZone for string zone +// from the DNS server. +// +// Returns the DNSZone and nil if successful, empty DNSZone and an +// error otherwise +func GetDNSZone(zone string) (DNSZone, error) { + if !strings.HasSuffix(zone, ".") { + zone = zone + "." + } + pdnsendpoint := viper.GetString("powerdnsendpoint") + pdnsapikey := viper.GetString("powerdnsapikey") + + url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + zone + + req, reqerr := http.NewRequest("GET", url, nil) + if reqerr != nil { + fmt.Println(reqerr) + return DNSZone{}, reqerr + } + + req.Header.Add("X-API-Key", pdnsapikey) + req.Header.Add("Accept", "application/json") + + client := &http.Client{} + resp, resperr := client.Do(req) + if resperr != nil { + fmt.Println(resperr) + return DNSZone{}, resperr + } + defer resp.Body.Close() + + body, readerr := io.ReadAll(resp.Body) + if readerr != nil { + fmt.Println(readerr) + return DNSZone{}, readerr + } + + var zoneobj DNSZone + marsherr := json.Unmarshal(body, &zoneobj) + if marsherr != nil { + fmt.Println(marsherr) + return DNSZone{}, marsherr + } + + return zoneobj, nil +} + +// GetBestDNSZone requests a list of all zones from PowerDNS +// and determines the best zone, if possible. +// +// Returns the found DNSZone and nil if a suitable zone was +// found, an empty DNSZone object and an error if not. +func GetBestDNSZone(fqdn string) (DNSZone, error) { + pdnsendpoint := viper.GetString("powerdnsendpoint") + pdnsapikey := viper.GetString("powerdnsapikey") + fqdn = fqdn + "." + + if !viper.GetBool("powerdnsenabled") { + return DNSZone{}, errors.New("PowerDNS integration not enabled") + } + + url := pdnsendpoint + "/api/v1/servers/localhost/zones" + + req, reqerr := http.NewRequest("GET", url, nil) + if reqerr != nil { + fmt.Println(reqerr) + return DNSZone{}, reqerr + } + + req.Header.Add("X-API-Key", pdnsapikey) + + client := &http.Client{} + resp, resperr := client.Do(req) + if resperr != nil { + fmt.Println(resperr) + return DNSZone{}, resperr + } + defer resp.Body.Close() + + body, readerr := io.ReadAll(resp.Body) + if readerr != nil { + fmt.Println(readerr) + return DNSZone{}, readerr + } + + var zones []DNSZone + marsherr := json.Unmarshal(body, &zones) + if marsherr != nil { + fmt.Println(marsherr) + return DNSZone{}, marsherr + } + + var bestmatch DNSZone + var matchfound bool = false + + for _, zone := range zones { + if strings.HasSuffix(fqdn, zone.Name) { + if !matchfound { + matchfound = true + bestmatch = zone + } + if matchfound && len(zone.Name) > len(bestmatch.Name) { + bestmatch = zone + } + } + } + + if !matchfound { + return DNSZone{}, errors.New("No suitable zone found for " + fqdn) + } + + zone, geterr := GetDNSZone(bestmatch.ID) + if geterr != nil { + return DNSZone{}, geterr + } + + return zone, nil +} + +// AddDNSfqdn tries to create forward and reverse lookup records +// for given fqdn with netip.Addr addr. +// +// Returns nil on success, error otherwise +func AddDNSFqdn(fqdn string, addr netip.Addr) error { + debug, _ := rootCmd.Flags().GetBool("debug") + if !viper.GetBool("powerdnsenabled") { + if debug { + fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.") + } + return nil + } + + var recordtype string + if addr.Is4() { + recordtype = "A" + } else if addr.Is6() { + recordtype = "AAAA" + } else { + return errors.New(addr.String() + " is not a valid IP address") + } + + fzone, fzoneerr := GetBestDNSZone(fqdn) + if fzoneerr != nil { + fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", fqdn) + } else { + + _, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String()) + + if frecordexists { + fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn) + } else { + fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "REPLACE") + if fpatcherr != nil { + return fpatcherr + } + fmt.Printf("[DNS] + %v IN %v %v\n", fqdn, recordtype, addr.String()) + } + } + + baseip := addr.StringExpanded() + var rfqdn string + if addr.Is4() { + a := strings.Split(baseip, ".") + b := reverse(a) + rfqdn = strings.Join(b, ".") + ".in-addr.arpa" + } else if addr.Is6() { + a := strings.Replace(baseip, ":", "", -1) + b := strings.Split(a, "") + c := reverse(b) + rfqdn = strings.Join(c, ".") + ".ip6.arpa" + } + + rzone, rzoneerr := GetBestDNSZone(rfqdn) + if rzoneerr != nil { + fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", rfqdn) + } else { + + _, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn) + rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1) + + if rrecordexists { + fmt.Printf("[DNS] Reverse DNS Record for %v already exists, no need to change DNS.\n", addr.String()) + } else { + rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "REPLACE") + if rpatcherr != nil { + return rpatcherr + } + fmt.Printf("[DNS] + %v IN %v %v\n", rfqdn, "PTR", fqdn) + } + } + + return nil +} + +// DeleteDNSFqdn tries to delete the corresponding record for +// given fqdn with netip.Addr addr from the DNS server. +// +// Returns nil on success, error otherwise +func DeleteDNSFqdn(fqdn string, addr netip.Addr) error { + debug, _ := rootCmd.Flags().GetBool("debug") + if !viper.GetBool("powerdnsenabled") { + if debug { + fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.") + } + return nil + } + + var recordtype string + if addr.Is4() { + recordtype = "A" + } else if addr.Is6() { + recordtype = "AAAA" + } else { + return errors.New(addr.String() + " is not a valid IP address") + } + + fzone, fzoneerr := GetBestDNSZone(fqdn) + if fzoneerr != nil { + fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS deletion op\n", fqdn) + } else { + _, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String()) + + if !frecordexists { + fmt.Printf("[DNS] DNS Record for %v doesn't exists, no need to change DNS.\n", fqdn) + } else { + fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "DELETE") + if fpatcherr != nil { + return fpatcherr + } + fmt.Printf("[DNS] - %v IN %v %v\n", fqdn, recordtype, addr.String()) + } + } + + baseip := addr.StringExpanded() + var rfqdn string + if addr.Is4() { + a := strings.Split(baseip, ".") + b := reverse(a) + rfqdn = strings.Join(b, ".") + ".in-addr.arpa" + } else if addr.Is6() { + a := strings.Replace(baseip, ":", "", -1) + b := strings.Split(a, "") + c := reverse(b) + rfqdn = strings.Join(c, ".") + ".ip6.arpa" + } + + rzone, rzoneerr := GetBestDNSZone(rfqdn) + if rzoneerr != nil { + fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", rfqdn) + } else { + _, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn) + rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1) + + if !rrecordexists { + fmt.Printf("[DNS] Reverse DNS Record for %v doesn't exists, no need to change DNS.\n", addr.String()) + } else { + rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "DELETE") + if rpatcherr != nil { + return rpatcherr + } + fmt.Printf("[DNS] - %v IN %v %v\n", rfqdn, "PTR", fqdn) + } + } + + return nil +} diff --git a/cmd/subnet-delete.go b/cmd/subnet-delete.go index 829e92d..20d397f 100644 --- a/cmd/subnet-delete.go +++ b/cmd/subnet-delete.go @@ -31,11 +31,28 @@ var subnetdeleteCmd = &cobra.Command{ os.Exit(1) } + subnetobj, suberr := GetSubnet(subnet) + if suberr != nil { + fmt.Println("[ERROR]", suberr) + os.Exit(1) + } + var confirmation string - fmt.Printf("[WARNING] Do you really want to delete subnet %v? [y/N] ", subnet.String()) - fmt.Scan(&confirmation) + fmt.Printf("[WARNING] Do you really want to delete subnet %v?\n", subnet.String()) + fmt.Printf("[WARNING] This will also delete all DNS records if PowerDNS integration is enabled!\n") + fmt.Printf("[WARNING] Continue? [y/N] ") + //fmt.Scan(&confirmation) + confirmation = "y" if (confirmation == "y") || (confirmation == "Y") { + for _, address := range subnetobj.Addresses { + if address.FQDN != "" { + deleteerr := DeleteDNSFqdn(address.FQDN, address.IP) + if deleteerr != nil { + fmt.Println("[ERROR]", deleteerr) + } + } + } deleteerr := DeleteSubnet(subnet) if deleteerr != nil { fmt.Println("[ERROR]", deleteerr) @@ -43,6 +60,7 @@ var subnetdeleteCmd = &cobra.Command{ } else { fmt.Printf("deleted subnet %v\n", subnet.String()) } + } }, } @@ -59,4 +77,5 @@ func init() { // Cobra supports local flags which will only run when this command // is called directly, e.g.: // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + subnetdeleteCmd.Flags().BoolP("yes", "y", false, "suppress interactive prompts and answer yes.") } diff --git a/cmd/subnet-list.go b/cmd/subnet-list.go index 333c888..46999f2 100644 --- a/cmd/subnet-list.go +++ b/cmd/subnet-list.go @@ -45,7 +45,12 @@ var subnetlistCmd = &cobra.Command{ if subnet.Subnet.Addr().Is4() { hostbits := float64(32 - subnet.Subnet.Bits()) - numip = int(math.Pow(2, hostbits)) - 2 + if subnet.Subnet.Bits() == 31 { + numip = 2 + } else { + numip = int(math.Pow(2, hostbits)) - 2 + } + freeip = numip - len(subnet.Addresses) fmt.Printf("%v:\t%v\t(vl: %v)\tfree: %v\n", subnet.Subnet, subnet.Name, subnet.Vlan, freeip) diff --git a/go.mod b/go.mod index d129b9f..9691def 100644 --- a/go.mod +++ b/go.mod @@ -2,23 +2,25 @@ module git.sr.ht/~lauralani/ipam go 1.20 -require github.com/spf13/cobra v1.6.1 +require ( + github.com/spf13/cobra v1.6.1 + github.com/spf13/viper v1.15.0 +) require ( - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.15.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6648110..af87baf 100644 --- a/go.sum +++ b/go.sum @@ -136,6 +136,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= +github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -144,6 +146,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= @@ -183,6 +187,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -247,6 +252,7 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -298,11 +304,14 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -310,8 +319,12 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/test/192.168.35.0_24 b/test/192.168.35.0_24 deleted file mode 100644 index 85a8a04..0000000 --- a/test/192.168.35.0_24 +++ /dev/null @@ -1,5 +0,0 @@ -name: test-net -vlan-id: 23 -192.168.35.2: test1.example.com -192.168.35.3: test2.example.com -192.168.35.4: diff --git a/test/subnet.yaml b/test/subnet.yaml deleted file mode 100644 index 601377b..0000000 --- a/test/subnet.yaml +++ /dev/null @@ -1,7 +0,0 @@ -name: "test-net" -vlan: "-" -addresses: - - 192.168.35.2: - fqdn: "dns-01.lauka-home.net" - - 192.168.35.3: - fqdn: "" diff --git a/test/test_yaml.go b/test/test_yaml.go deleted file mode 100644 index ac6a789..0000000 --- a/test/test_yaml.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - - "gopkg.in/yaml.v3" -) - -type Address struct { - Fqdn string `yaml:"fqdn"` -} - -type SubnetFile struct { - Name string `yaml:"name"` - Vlan string `yaml:"vlan"` - Addresses []Address `yaml:"addresses"` -} - -func main() { - // Read the file - data, err := ioutil.ReadFile("subnet.yaml") - if err != nil { - fmt.Println(err) - return - } - - // Create a struct to hold the YAML data - var asd SubnetFile - - // Unmarshal the YAML data into the struct - err = yaml.Unmarshal(data, &asd) - if err != nil { - fmt.Println(err) - return - } - - // Print the data - fmt.Println(asd) -}