mirror of
https://codeberg.org/lauralani/ipam.git
synced 2024-11-23 20:20:39 +01:00
add PowerDNS integration
This commit is contained in:
parent
2db0e27223
commit
5dbb35f0a5
11 changed files with 481 additions and 72 deletions
9
cmd/generic.go
Normal file
9
cmd/generic.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
403
cmd/powerdns.go
403
cmd/powerdns.go
|
@ -3,3 +3,406 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
|
|||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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.")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
36
go.mod
36
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
|
||||
)
|
||||
|
|
13
go.sum
13
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=
|
||||
|
|
|
@ -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:
|
|
@ -1,7 +0,0 @@
|
|||
name: "test-net"
|
||||
vlan: "-"
|
||||
addresses:
|
||||
- 192.168.35.2:
|
||||
fqdn: "dns-01.lauka-home.net"
|
||||
- 192.168.35.3:
|
||||
fqdn: ""
|
|
@ -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)
|
||||
}
|
Loading…
Reference in a new issue