Merge pull request 'Renamed GO module name and fixed whitespace issues' (#10) from rename-go-module-name into main

Reviewed-on: https://codeberg.org/lauralani/ipam/pulls/10
This commit is contained in:
Adora Laura Kalb 2023-04-03 08:12:23 +00:00
commit df3f3d6782
19 changed files with 667 additions and 669 deletions

View file

@ -5,32 +5,32 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"errors" "errors"
"net/netip" "net/netip"
"time" "time"
) )
type Subnet struct { type Subnet struct {
Subnet netip.Prefix `json:"subnet"` Subnet netip.Prefix `json:"subnet"`
Name string `json:"name"` Name string `json:"name"`
Vlan string `json:"vlan"` Vlan string `json:"vlan"`
ChangedAt time.Time `json:"changedat,omitempty"` ChangedAt time.Time `json:"changedat,omitempty"`
ChangedBy string `json:"changedby,omitempty"` ChangedBy string `json:"changedby,omitempty"`
Addresses []Address `json:"addresses"` Addresses []Address `json:"addresses"`
} }
// HasIP checks if a Subnet already contains given netip.Addr. // HasIP checks if a Subnet already contains given netip.Addr.
// Returns true if the IP already is present, false otherwise. // Returns true if the IP already is present, false otherwise.
func (s Subnet) HasIP(ip netip.Addr) bool { func (s Subnet) HasIP(ip netip.Addr) bool {
iscontained := false iscontained := false
for _, element := range s.Addresses { for _, element := range s.Addresses {
if element.IP.Compare(ip) == 0 { if element.IP.Compare(ip) == 0 {
iscontained = true iscontained = true
} }
} }
return iscontained return iscontained
} }
// RemoveIP removes the Address object for given ip from // RemoveIP removes the Address object for given ip from
@ -40,20 +40,20 @@ func (s Subnet) HasIP(ip netip.Addr) bool {
// successful, or an empty Subnet and an error if // successful, or an empty Subnet and an error if
// ip could not be deleted. // ip could not be deleted.
func (s Subnet) RemoveIP(ip netip.Addr) (Subnet, error) { func (s Subnet) RemoveIP(ip netip.Addr) (Subnet, error) {
var addrlist []Address var addrlist []Address
if !s.HasIP(ip) { if !s.HasIP(ip) {
return Subnet{}, errors.New("IP " + ip.String() + " wasn't found in subnet " + s.Subnet.String()) return Subnet{}, errors.New("IP " + ip.String() + " wasn't found in subnet " + s.Subnet.String())
} }
for _, item := range s.Addresses { for _, item := range s.Addresses {
if item.IP.Compare(ip) != 0 { if item.IP.Compare(ip) != 0 {
addrlist = append(addrlist, item) addrlist = append(addrlist, item)
} }
} }
s.Addresses = addrlist s.Addresses = addrlist
return s, nil return s, nil
} }
// GetIP returns the Address object for the subnet with // GetIP returns the Address object for the subnet with
@ -62,18 +62,18 @@ func (s Subnet) RemoveIP(ip netip.Addr) (Subnet, error) {
// Returns the Address object and true if a corresponding // Returns the Address object and true if a corresponding
// object was found, an empty Address and false otherwise. // object was found, an empty Address and false otherwise.
func (s Subnet) GetIP(ip netip.Addr) (Address, bool) { func (s Subnet) GetIP(ip netip.Addr) (Address, bool) {
for _, item := range s.Addresses { for _, item := range s.Addresses {
if item.IP.Compare(ip) == 0 { if item.IP.Compare(ip) == 0 {
return item, true return item, true
} }
} }
return Address{}, false return Address{}, false
} }
type Address struct { type Address struct {
IP netip.Addr `json:"ip"` IP netip.Addr `json:"ip"`
FQDN string `json:"fqdn"` FQDN string `json:"fqdn"`
ChangedAt time.Time `json:"changedat,omitempty"` ChangedAt time.Time `json:"changedat,omitempty"`
ChangedBy string `json:"changedby,omitempty"` ChangedBy string `json:"changedby,omitempty"`
} }

View file

@ -5,5 +5,5 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
const ( const (
ipamVersion = "0.2.0" ipamVersion = "0.2.0"
) )

View file

@ -5,92 +5,92 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"path" "path"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// exportCmd represents the export command // exportCmd represents the export command
var exportCmd = &cobra.Command{ var exportCmd = &cobra.Command{
Use: "export", Use: "export",
Short: "Export ipam configuration", Short: "Export ipam configuration",
Long: `Export current ipam contents to importable data format. Long: `Export current ipam contents to importable data format.
You can either export a single subnet or all subnets.`, You can either export a single subnet or all subnets.`,
Example: "ipam export\nipam export 192.168.0.0/24", Example: "ipam export\nipam export 192.168.0.0/24",
Args: cobra.RangeArgs(0, 1), Args: cobra.RangeArgs(0, 1),
Aliases: []string{"ex"}, Aliases: []string{"ex"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var jsonoutput []Subnet var jsonoutput []Subnet
if len(args) == 1 { if len(args) == 1 {
prefix, parseerr := netip.ParsePrefix(args[0]) prefix, parseerr := netip.ParsePrefix(args[0])
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
subnet, geterr := GetSubnet(prefix) subnet, geterr := GetSubnet(prefix)
if geterr != nil { if geterr != nil {
fmt.Println("[ERROR]", geterr) fmt.Println("[ERROR]", geterr)
os.Exit(1) os.Exit(1)
} }
jsonoutput = append(jsonoutput, subnet) jsonoutput = append(jsonoutput, subnet)
} else { } else {
subnetlist := ListSubnets() subnetlist := ListSubnets()
for _, net := range subnetlist { for _, net := range subnetlist {
prefix, parseerr := netip.ParsePrefix(net) prefix, parseerr := netip.ParsePrefix(net)
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
subnet, geterr := GetSubnet(prefix) subnet, geterr := GetSubnet(prefix)
if geterr != nil { if geterr != nil {
fmt.Println("[ERROR]", geterr) fmt.Println("[ERROR]", geterr)
os.Exit(1) os.Exit(1)
} }
jsonoutput = append(jsonoutput, subnet) jsonoutput = append(jsonoutput, subnet)
} }
} }
//workingdir, _ := os.Getwd() //workingdir, _ := os.Getwd()
//timestamp := time.Now().Format("2006-01-02_15-04") //timestamp := time.Now().Format("2006-01-02_15-04")
//exportfilename := workingdir + "/ipam_export_" + timestamp + ".json" //exportfilename := workingdir + "/ipam_export_" + timestamp + ".json"
var exportname string var exportname string
flagpath, _ := cmd.Flags().GetString("file") flagpath, _ := cmd.Flags().GetString("file")
if path.IsAbs(flagpath) { if path.IsAbs(flagpath) {
exportname = flagpath exportname = flagpath
} else { } else {
wd, _ := os.Getwd() wd, _ := os.Getwd()
exportname = path.Join(wd, flagpath) exportname = path.Join(wd, flagpath)
} }
data, _ := json.MarshalIndent(jsonoutput, "", " ") data, _ := json.MarshalIndent(jsonoutput, "", " ")
file, fileerr := os.Create(exportname) file, fileerr := os.Create(exportname)
if fileerr != nil { if fileerr != nil {
fmt.Println("[ERROR]", fileerr) fmt.Println("[ERROR]", fileerr)
os.Exit(1) os.Exit(1)
} }
defer file.Close() defer file.Close()
_, writeerr := file.Write(data) _, writeerr := file.Write(data)
if writeerr != nil { if writeerr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("[INFO] Data was exported to file %v\n", exportname) fmt.Printf("[INFO] Data was exported to file %v\n", exportname)
}, },
} }
func init() { func init() {
rootCmd.AddCommand(exportCmd) rootCmd.AddCommand(exportCmd)
timestamp := time.Now().Format("2006-01-02_15-04") timestamp := time.Now().Format("2006-01-02_15-04")
exportCmd.Flags().StringP("file", "f", "./ipam_export_"+timestamp+".json", "File name for exported data.\nCan be both absolute or relative path.") exportCmd.Flags().StringP("file", "f", "./ipam_export_"+timestamp+".json", "File name for exported data.\nCan be both absolute or relative path.")
} }

View file

@ -1,9 +1,9 @@
package cmd package cmd
func reverse(elements []string) []string { func reverse(elements []string) []string {
for i := 0; i < len(elements)/2; i++ { for i := 0; i < len(elements)/2; i++ {
j := len(elements) - i - 1 j := len(elements) - i - 1
elements[i], elements[j] = elements[j], elements[i] elements[i], elements[j] = elements[j], elements[i]
} }
return elements return elements
} }

View file

@ -4,78 +4,78 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"os/user" "os/user"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// deleteCmd represents the delete command // deleteCmd represents the delete command
var ipdeleteCmd = &cobra.Command{ var ipdeleteCmd = &cobra.Command{
Use: "delete ipaddress", Use: "delete ipaddress",
Short: "Delete an IP address", Short: "Delete an IP address",
Long: `Delete an IP address`, Long: `Delete an IP address`,
Aliases: []string{"d"}, Aliases: []string{"d"},
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Example: "ipam ip delete 192.168.0.1", Example: "ipam ip delete 192.168.0.1",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
ip, parseerr := netip.ParseAddr(args[0]) ip, parseerr := netip.ParseAddr(args[0])
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
subnet, subnetexists := FindBestSubnet(ip) subnet, subnetexists := FindBestSubnet(ip)
if !subnetexists { if !subnetexists {
fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String())
os.Exit(1) os.Exit(1)
} }
address, _ := subnet.GetIP(ip) address, _ := subnet.GetIP(ip)
subnet, removeerr := subnet.RemoveIP(ip) subnet, removeerr := subnet.RemoveIP(ip)
if removeerr != nil { if removeerr != nil {
fmt.Println("[ERROR]", removeerr) fmt.Println("[ERROR]", removeerr)
os.Exit(1) os.Exit(1)
} }
currentuser, _ := user.Current() currentuser, _ := user.Current()
subnet.ChangedAt = time.Now() subnet.ChangedAt = time.Now()
subnet.ChangedBy = currentuser.Username subnet.ChangedBy = currentuser.Username
writeerr := subnet.WriteSubnet() writeerr := subnet.WriteSubnet()
if writeerr != nil { if writeerr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
if address.FQDN == "" { if address.FQDN == "" {
fmt.Printf("deleted ip %v\n", address.IP.String()) fmt.Printf("deleted ip %v\n", address.IP.String())
} else { } else {
fmt.Printf("deleted ip %v (%v)\n", address.IP.String(), address.FQDN) fmt.Printf("deleted ip %v (%v)\n", address.IP.String(), address.FQDN)
dnserr := DeleteDNSFqdn(address.FQDN, address.IP) dnserr := DeleteDNSFqdn(address.FQDN, address.IP)
if dnserr != nil { if dnserr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
} }
}, },
} }
func init() { func init() {
ipCmd.AddCommand(ipdeleteCmd) ipCmd.AddCommand(ipdeleteCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// deleteCmd.PersistentFlags().String("foo", "", "A help for foo") // deleteCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -4,32 +4,32 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var ipeditCmd = &cobra.Command{ var ipeditCmd = &cobra.Command{
Use: "edit", Use: "edit",
Short: "Edit an IP address", Short: "Edit an IP address",
Long: `Edit an IP address`, Long: `Edit an IP address`,
Aliases: []string{"e"}, Aliases: []string{"e"},
//Args: cobra.ExactArgs(1), //Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("not implemented yet; please delete and readd") fmt.Println("not implemented yet; please delete and readd")
}, },
} }
func init() { func init() {
ipCmd.AddCommand(ipeditCmd) ipCmd.AddCommand(ipeditCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// editCmd.PersistentFlags().String("foo", "", "A help for foo") // editCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// editCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // editCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -4,60 +4,60 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// showCmd represents the show command // showCmd represents the show command
var ipshowCmd = &cobra.Command{ var ipshowCmd = &cobra.Command{
Use: "show", Use: "show",
Short: "Show IP and associated name", Short: "Show IP and associated name",
Long: `Show IP and associated name`, Long: `Show IP and associated name`,
Aliases: []string{"s"}, Aliases: []string{"s"},
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Example: "ipam ip show 192.168.0.1", Example: "ipam ip show 192.168.0.1",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
ip, parseerr := netip.ParseAddr(args[0]) ip, parseerr := netip.ParseAddr(args[0])
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
subnet, subnetexists := FindBestSubnet(ip) subnet, subnetexists := FindBestSubnet(ip)
if !subnetexists { if !subnetexists {
fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String())
os.Exit(1) os.Exit(1)
} }
addr, addrexists := subnet.GetIP(ip) addr, addrexists := subnet.GetIP(ip)
if !addrexists { if !addrexists {
fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String())
os.Exit(1) os.Exit(1)
} }
fmt.Printf("IP: %v\n", ip.String()) fmt.Printf("IP: %v\n", ip.String())
fmt.Printf("FQDN: %v\n", addr.FQDN) fmt.Printf("FQDN: %v\n", addr.FQDN)
fmt.Printf("Subnet: %v (%v, vlan %v)\n", subnet.Subnet.String(), subnet.Name, subnet.Vlan) fmt.Printf("Subnet: %v (%v, vlan %v)\n", subnet.Subnet.String(), subnet.Name, subnet.Vlan)
fmt.Printf("Modified at: %v\n", subnet.ChangedAt.Format(time.RFC1123)) fmt.Printf("Modified at: %v\n", subnet.ChangedAt.Format(time.RFC1123))
fmt.Printf("Modified by: %v\n", subnet.ChangedBy) fmt.Printf("Modified by: %v\n", subnet.ChangedBy)
}, },
} }
func init() { func init() {
ipCmd.AddCommand(ipshowCmd) ipCmd.AddCommand(ipshowCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// showCmd.PersistentFlags().String("foo", "", "A help for foo") // showCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// showCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // showCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -5,27 +5,27 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// ipCmd represents the ip command // ipCmd represents the ip command
var ipCmd = &cobra.Command{ var ipCmd = &cobra.Command{
Use: "ip", Use: "ip",
Short: "manage IP addresses", Short: "manage IP addresses",
Long: `Add, delete and show IP addresses`, Long: `Add, delete and show IP addresses`,
Aliases: []string{"i"}, Aliases: []string{"i"},
} }
func init() { func init() {
rootCmd.AddCommand(ipCmd) rootCmd.AddCommand(ipCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// ipCmd.PersistentFlags().String("foo", "", "A help for foo") // ipCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// ipCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // ipCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -229,7 +229,7 @@ func GetBestDNSZone(fqdn string) (DNSZone, error) {
} }
var bestmatch DNSZone var bestmatch DNSZone
var matchfound bool = false var matchfound = false
for _, zone := range zones { for _, zone := range zones {
if strings.HasSuffix(fqdn, "."+zone.Name) { if strings.HasSuffix(fqdn, "."+zone.Name) {
@ -288,7 +288,7 @@ func AddDNSFqdn(fqdn string, addr netip.Addr) error {
if frecordexists { if frecordexists {
fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn) fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn)
} else { } else {
var dotfqdn, dotfzone string = fqdn + ".", "." + fzone.Name var dotfqdn, dotfzone = fqdn + ".", "." + fzone.Name
record := strings.Replace(dotfqdn, dotfzone, "", 1) record := strings.Replace(dotfqdn, dotfzone, "", 1)
fpatcherr := fzone.SendPATCH(record, addr.String(), recordtype, "REPLACE") fpatcherr := fzone.SendPATCH(record, addr.String(), recordtype, "REPLACE")
if fpatcherr != nil { if fpatcherr != nil {

View file

@ -5,73 +5,73 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "ipam", Use: "ipam",
Short: "A cli based ipam", Short: "A cli based ipam",
Long: `A cli based ipam. Long: `A cli based ipam.
You can manage subnets and single IP addresses within those subnets. You can manage subnets and single IP addresses within those subnets.
ipam can also manage the corresponding DNS records in your PowerDNS Instance.`, ipam can also manage the corresponding DNS records in your PowerDNS Instance.`,
Version: ipamVersion, Version: ipamVersion,
} }
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {
err := rootCmd.Execute() err := rootCmd.Execute()
if err != nil { if err != nil {
os.Exit(1) os.Exit(1)
} }
} }
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
rootCmd.Flags().BoolP("debug", "d", false, "Enable debug mode. (may print sensitive Information, so please watch out!)") rootCmd.Flags().BoolP("debug", "d", false, "Enable debug mode. (may print sensitive Information, so please watch out!)")
} }
func initConfig() { func initConfig() {
// Find home directory. // Find home directory.
homedir, err := os.UserHomeDir() homedir, err := os.UserHomeDir()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
var ipamdir string = homedir + "/.ipam/" var ipamdir = homedir + "/.ipam/"
// Search config in home directory with name ".cobra" (without extension). // Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(ipamdir) viper.AddConfigPath(ipamdir)
viper.SetConfigName("ipam") viper.SetConfigName("ipam")
viper.SetConfigType("yaml") viper.SetConfigType("yaml")
viper.SetDefault("DataPath", ipamdir+"data/") viper.SetDefault("DataPath", ipamdir+"data/")
viper.SetDefault("PowerDNSEnabled", false) viper.SetDefault("PowerDNSEnabled", false)
viper.SetDefault("PowerDNSEndpoint", "") viper.SetDefault("PowerDNSEndpoint", "")
viper.SetDefault("PowerDNSApiKey", "") viper.SetDefault("PowerDNSApiKey", "")
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
_, patherr := os.Stat(ipamdir) _, patherr := os.Stat(ipamdir)
if patherr != nil { if patherr != nil {
mkerr := os.MkdirAll(ipamdir, 0755) mkerr := os.MkdirAll(ipamdir, 0755)
if mkerr != nil { if mkerr != nil {
println("[ERROR] Can't create ipam config directory!", mkerr) println("[ERROR] Can't create ipam config directory!", mkerr)
} }
} }
// I have no idea what's happening here... // I have no idea what's happening here...
if _, ok := err.(viper.ConfigFileNotFoundError); ok { if _, ok := err.(viper.ConfigFileNotFoundError); ok {
writeerr := viper.SafeWriteConfig() writeerr := viper.SafeWriteConfig()
if writeerr != nil { if writeerr != nil {
println("[ERROR] Can't write config file!", writeerr) println("[ERROR] Can't write config file!", writeerr)
} }
} else { } else {
println("[ERROR] Can't read config file!", err) println("[ERROR] Can't read config file!", err)
} }
} }
} }

View file

@ -5,14 +5,14 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"sort" "sort"
"strings" "strings"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// FindBestSubnet tries to load the most fitting IP subnet file // FindBestSubnet tries to load the most fitting IP subnet file
@ -22,71 +22,71 @@ import (
// Returns the best subnet as Subnet object and true if a suitable // Returns the best subnet as Subnet object and true if a suitable
// subnet was found, otherwise an empty Subnet object and false. // subnet was found, otherwise an empty Subnet object and false.
func FindBestSubnet(ip netip.Addr) (Subnet, bool) { func FindBestSubnet(ip netip.Addr) (Subnet, bool) {
subnets := ListSubnets() subnets := ListSubnets()
var smallestprefix int = 0 var smallestprefix = 0
bestmatch, _ := netip.ParsePrefix("::/128") bestmatch, _ := netip.ParsePrefix("::/128")
var isipv4 bool = ip.Is4() var isipv4 = ip.Is4()
var subnet Subnet var subnet Subnet
for _, net := range subnets { for _, net := range subnets {
prefix, _ := netip.ParsePrefix(net) prefix, _ := netip.ParsePrefix(net)
if prefix.Addr().Is4() == isipv4 { if prefix.Addr().Is4() == isipv4 {
if prefix.Contains(ip) { if prefix.Contains(ip) {
if prefix.Bits() > smallestprefix { if prefix.Bits() > smallestprefix {
bestmatch = prefix bestmatch = prefix
} }
} }
} }
} }
if !bestmatch.Addr().IsUnspecified() { if !bestmatch.Addr().IsUnspecified() {
var geterr error var geterr error
subnet, geterr = GetSubnet(bestmatch) subnet, geterr = GetSubnet(bestmatch)
if geterr != nil { if geterr != nil {
fmt.Println("[ERROR]", geterr) fmt.Println("[ERROR]", geterr)
os.Exit(1) os.Exit(1)
} }
return subnet, true return subnet, true
} else { } else {
return Subnet{}, false return Subnet{}, false
} }
} }
// SubnetExists will return true if the given subnet already exists // SubnetExists will return true if the given subnet already exists
// on file, false otherwise. // on file, false otherwise.
func SubnetExists(net netip.Prefix) bool { func SubnetExists(net netip.Prefix) bool {
subnets := ListSubnets() subnets := ListSubnets()
for _, b := range subnets { for _, b := range subnets {
if b == net.String() { if b == net.String() {
return true return true
} }
} }
return false return false
} }
// ListSubnets returns a list of strings containing the current // ListSubnets returns a list of strings containing the current
// subnets configured. // subnets configured.
func ListSubnets() []string { func ListSubnets() []string {
subnets := make([]string, 0) subnets := make([]string, 0)
var datadir string = viper.GetString("DataPath") var datadir = viper.GetString("DataPath")
subnetfiles, readerr := os.ReadDir(datadir) subnetfiles, readerr := os.ReadDir(datadir)
if len(subnetfiles) == 0 { if len(subnetfiles) == 0 {
return subnets return subnets
} }
if readerr != nil { if readerr != nil {
fmt.Println("[ERROR]", readerr) fmt.Println("[ERROR]", readerr)
os.Exit(1) os.Exit(1)
} }
for _, element := range subnetfiles { for _, element := range subnetfiles {
a := strings.Replace(element.Name(), "_", "/", 1) a := strings.Replace(element.Name(), "_", "/", 1)
a = strings.Replace(a, ".json", "", 1) a = strings.Replace(a, ".json", "", 1)
subnets = append(subnets, a) subnets = append(subnets, a)
} }
return subnets return subnets
} }
// WriteSubnet takes a given Subnet object and tries to write it to // WriteSubnet takes a given Subnet object and tries to write it to
@ -94,35 +94,35 @@ func ListSubnets() []string {
// //
// Returns nil on success or the error that happened. // Returns nil on success or the error that happened.
func (s Subnet) WriteSubnet() error { func (s Subnet) WriteSubnet() error {
var datadir string = viper.GetString("DataPath") var datadir = viper.GetString("DataPath")
_, direrr := os.Stat(datadir) _, direrr := os.Stat(datadir)
if direrr != nil { if direrr != nil {
mkerr := os.MkdirAll(datadir, 0755) mkerr := os.MkdirAll(datadir, 0755)
if mkerr != nil { if mkerr != nil {
println("[ERROR] Can't create ipam config directory!", mkerr) println("[ERROR] Can't create ipam config directory!", mkerr)
os.Exit(1) os.Exit(1)
} }
} }
filename := datadir + strings.Replace(s.Subnet.String(), "/", "_", 1) + ".json" filename := datadir + strings.Replace(s.Subnet.String(), "/", "_", 1) + ".json"
data, _ := json.Marshal(s) data, _ := json.Marshal(s)
file, fileerr := os.Create(filename) file, fileerr := os.Create(filename)
if fileerr != nil { if fileerr != nil {
fmt.Println("[ERROR]", fileerr) fmt.Println("[ERROR]", fileerr)
os.Exit(1) os.Exit(1)
} }
defer file.Close() defer file.Close()
_, writeerr := file.Write(data) _, writeerr := file.Write(data)
if writeerr != nil { if writeerr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
return nil return nil
} }
// GetSubnet reads the corresponding file for the given // GetSubnet reads the corresponding file for the given
@ -131,35 +131,35 @@ func (s Subnet) WriteSubnet() error {
// Returns the Subnet object and nil if the file read was // Returns the Subnet object and nil if the file read was
// successful, an empty Subnet object and the error otherwise. // successful, an empty Subnet object and the error otherwise.
func GetSubnet(net netip.Prefix) (Subnet, error) { func GetSubnet(net netip.Prefix) (Subnet, error) {
var datadir string = viper.GetString("DataPath") var datadir = viper.GetString("DataPath")
filename := datadir + strings.Replace(net.String(), "/", "_", 1) + ".json" filename := datadir + strings.Replace(net.String(), "/", "_", 1) + ".json"
var subnet Subnet = Subnet{} var subnet = Subnet{}
content, readerr := os.ReadFile(filename) content, readerr := os.ReadFile(filename)
if readerr != nil { if readerr != nil {
return Subnet{}, readerr return Subnet{}, readerr
} }
marsherr := json.Unmarshal(content, &subnet) marsherr := json.Unmarshal(content, &subnet)
if marsherr != nil { if marsherr != nil {
return Subnet{}, marsherr return Subnet{}, marsherr
} }
return subnet, nil return subnet, nil
} }
// SortAddresses sorts the given list of IP addresses // SortAddresses sorts the given list of IP addresses
// using netip.Addr.Less() and returns the sorted slice. // using netip.Addr.Less() and returns the sorted slice.
func SortAddresses(list []Address) []Address { func SortAddresses(list []Address) []Address {
if len(list) <= 1 { if len(list) <= 1 {
return list return list
} }
sort.Slice(list, func(i, j int) bool { sort.Slice(list, func(i, j int) bool {
return list[i].IP.Less(list[j].IP) return list[i].IP.Less(list[j].IP)
}) })
return list return list
} }
// DeleteSubnet deletes the subnet file on disk for netip.Prefix // DeleteSubnet deletes the subnet file on disk for netip.Prefix
@ -167,13 +167,13 @@ func SortAddresses(list []Address) []Address {
// //
// Returns nil on success, or a *PathError on failure // Returns nil on success, or a *PathError on failure
func DeleteSubnet(net netip.Prefix) error { func DeleteSubnet(net netip.Prefix) error {
var datadir string = viper.GetString("DataPath") var datadir = viper.GetString("DataPath")
filename := datadir + strings.Replace(net.String(), "/", "_", 1) + ".json" filename := datadir + strings.Replace(net.String(), "/", "_", 1) + ".json"
removeerr := os.Remove(filename) removeerr := os.Remove(filename)
if removeerr != nil { if removeerr != nil {
return removeerr return removeerr
} else { } else {
return nil return nil
} }
} }

View file

@ -5,83 +5,83 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"os/user" "os/user"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// addCmd represents the add command // addCmd represents the add command
var subnetaddCmd = &cobra.Command{ var subnetaddCmd = &cobra.Command{
Use: "add subnet subnet-name [vlan]", Use: "add subnet subnet-name [vlan]",
Short: "Add a new subnet", Short: "Add a new subnet",
Long: `Add a new subnet`, Long: `Add a new subnet`,
Args: cobra.RangeArgs(2, 3), Args: cobra.RangeArgs(2, 3),
Aliases: []string{"a"}, Aliases: []string{"a"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var subnet string var subnet string
var vlanid string var vlanid string
var netname string var netname string
if len(args) == 2 { if len(args) == 2 {
subnet = args[0] subnet = args[0]
netname = args[1] netname = args[1]
vlanid = "-" vlanid = "-"
} }
if len(args) == 3 { if len(args) == 3 {
subnet = args[0] subnet = args[0]
netname = args[1] netname = args[1]
vlanid = args[2] vlanid = args[2]
} }
// Parse subnet into ParseCIDR to test if it's a valid subnet // Parse subnet into ParseCIDR to test if it's a valid subnet
// _, ipnet, err := net.ParseCIDR(subnet) // _, ipnet, err := net.ParseCIDR(subnet)
ipnet, parseerr := netip.ParsePrefix(subnet) ipnet, parseerr := netip.ParsePrefix(subnet)
// Exit if subnet already exists, no need to add it then // Exit if subnet already exists, no need to add it then
if SubnetExists(ipnet) { if SubnetExists(ipnet) {
fmt.Printf("[ERROR] Subnet already exists: %v\n", subnet) fmt.Printf("[ERROR] Subnet already exists: %v\n", subnet)
os.Exit(1) os.Exit(1)
} }
// Exit if parsed value is no valid IP // Exit if parsed value is no valid IP
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
// Exit if parsed value is an IPv6 Address // Exit if parsed value is an IPv6 Address
// TODO: Implement IPv6 support // TODO: Implement IPv6 support
// if !ipnet.Addr().Is4() { // if !ipnet.Addr().Is4() {
// fmt.Printf("[ERROR] IPv6 is not yet supported!\n") // fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
// os.Exit(1) // os.Exit(1)
// } // }
currentuser, _ := user.Current() currentuser, _ := user.Current()
subnetobject := Subnet{} subnetobject := Subnet{}
subnetobject.Subnet = ipnet subnetobject.Subnet = ipnet
subnetobject.Name = netname subnetobject.Name = netname
subnetobject.Vlan = vlanid subnetobject.Vlan = vlanid
subnetobject.ChangedAt = time.Now() subnetobject.ChangedAt = time.Now()
subnetobject.ChangedBy = currentuser.Username subnetobject.ChangedBy = currentuser.Username
writeerr := subnetobject.WriteSubnet() writeerr := subnetobject.WriteSubnet()
if writeerr != nil { if writeerr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("added subnet:\nnet: %v\nname: %v\nvlan: %v\n", subnet, netname, vlanid) fmt.Printf("added subnet:\nnet: %v\nname: %v\nvlan: %v\n", subnet, netname, vlanid)
}, },
} }
func init() { func init() {
subnetCmd.AddCommand(subnetaddCmd) subnetCmd.AddCommand(subnetaddCmd)
} }

View file

@ -4,82 +4,82 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// deleteCmd represents the delete command // deleteCmd represents the delete command
var subnetdeleteCmd = &cobra.Command{ var subnetdeleteCmd = &cobra.Command{
Use: "delete", Use: "delete",
Short: "delete subnet", Short: "delete subnet",
Long: `Delete a subnet from the ipam.`, Long: `Delete a subnet from the ipam.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Aliases: []string{"d"}, Aliases: []string{"d"},
Example: "ipam subnet delete 192.168.0.0/24", Example: "ipam subnet delete 192.168.0.0/24",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
subnet, parseerr := netip.ParsePrefix(args[0]) subnet, parseerr := netip.ParsePrefix(args[0])
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
if !SubnetExists(subnet) { if !SubnetExists(subnet) {
fmt.Printf("[ERROR] Couldn't find subnet %v\n", subnet.String()) fmt.Printf("[ERROR] Couldn't find subnet %v\n", subnet.String())
os.Exit(1) os.Exit(1)
} }
subnetobj, suberr := GetSubnet(subnet) subnetobj, suberr := GetSubnet(subnet)
if suberr != nil { if suberr != nil {
fmt.Println("[ERROR]", suberr) fmt.Println("[ERROR]", suberr)
os.Exit(1) os.Exit(1)
} }
var confirmation string var confirmation string
skipinteractive, _ := cmd.Flags().GetBool("yes") skipinteractive, _ := cmd.Flags().GetBool("yes")
if skipinteractive { if skipinteractive {
confirmation = "y" confirmation = "y"
} else { } else {
fmt.Printf("[WARNING] Do you really want to delete subnet %v?\n", subnet.String()) 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] This will also delete all DNS records if PowerDNS integration is enabled!\n")
fmt.Printf("[WARNING] Continue? [y/N] ") fmt.Printf("[WARNING] Continue? [y/N] ")
fmt.Scan(&confirmation) fmt.Scan(&confirmation)
} }
if (confirmation == "y") || (confirmation == "Y") { if (confirmation == "y") || (confirmation == "Y") {
for _, address := range subnetobj.Addresses { for _, address := range subnetobj.Addresses {
if address.FQDN != "" { if address.FQDN != "" {
deleteerr := DeleteDNSFqdn(address.FQDN, address.IP) deleteerr := DeleteDNSFqdn(address.FQDN, address.IP)
if deleteerr != nil { if deleteerr != nil {
fmt.Println("[ERROR]", deleteerr) fmt.Println("[ERROR]", deleteerr)
} }
} }
} }
deleteerr := DeleteSubnet(subnet) deleteerr := DeleteSubnet(subnet)
if deleteerr != nil { if deleteerr != nil {
fmt.Println("[ERROR]", deleteerr) fmt.Println("[ERROR]", deleteerr)
os.Exit(1) os.Exit(1)
} else { } else {
fmt.Printf("deleted subnet %v\n", subnet.String()) fmt.Printf("deleted subnet %v\n", subnet.String())
} }
} }
}, },
} }
func init() { func init() {
subnetCmd.AddCommand(subnetdeleteCmd) subnetCmd.AddCommand(subnetdeleteCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// deleteCmd.PersistentFlags().String("foo", "", "A help for foo") // deleteCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
subnetdeleteCmd.Flags().BoolP("yes", "y", false, "suppress interactive prompts and answer yes.") subnetdeleteCmd.Flags().BoolP("yes", "y", false, "suppress interactive prompts and answer yes.")
} }

View file

@ -4,85 +4,85 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"math" "math"
"net/netip" "net/netip"
"os" "os"
"sort" "sort"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// listCmd represents the list command // listCmd represents the list command
var subnetlistCmd = &cobra.Command{ var subnetlistCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List all subnets", Short: "List all subnets",
Long: `List all subnets`, Long: `List all subnets`,
Aliases: []string{"l"}, Aliases: []string{"l"},
Args: cobra.ExactArgs(0), Args: cobra.ExactArgs(0),
Example: "cmdb subnet list", Example: "cmdb subnet list",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
//verbose, _ := cmd.Flags().GetBool("verbose") //verbose, _ := cmd.Flags().GetBool("verbose")
subnetlist := ListSubnets() subnetlist := ListSubnets()
var subnets []Subnet var subnets []Subnet
for _, subnet := range subnetlist { for _, subnet := range subnetlist {
prefix, _ := netip.ParsePrefix(subnet) prefix, _ := netip.ParsePrefix(subnet)
net, err := GetSubnet(prefix) net, err := GetSubnet(prefix)
if err != nil { if err != nil {
fmt.Println("[ERROR]", err) fmt.Println("[ERROR]", err)
os.Exit(1) os.Exit(1)
} }
subnets = append(subnets, net) subnets = append(subnets, net)
} }
sort.Slice(subnets, func(i, j int) bool { sort.Slice(subnets, func(i, j int) bool {
return subnets[i].Subnet.Addr().Less(subnets[j].Subnet.Addr()) return subnets[i].Subnet.Addr().Less(subnets[j].Subnet.Addr())
}) })
fmt.Printf("%-18s VLAN %-25s Free IPs\n", "Prefix", "Name") fmt.Printf("%-18s VLAN %-25s Free IPs\n", "Prefix", "Name")
for _, subnet := range subnets { for _, subnet := range subnets {
var numip, freeip int var numip, freeip int
if subnet.Subnet.Addr().Is4() { if subnet.Subnet.Addr().Is4() {
hostbits := float64(32 - subnet.Subnet.Bits()) hostbits := float64(32 - subnet.Subnet.Bits())
if subnet.Subnet.Bits() == 31 { if subnet.Subnet.Bits() == 31 {
numip = 2 numip = 2
} else { } else {
numip = int(math.Pow(2, hostbits)) - 2 numip = int(math.Pow(2, hostbits)) - 2
} }
freeip = numip - len(subnet.Addresses) freeip = numip - len(subnet.Addresses)
if freeip > 1000 { if freeip > 1000 {
fmt.Printf("%-18s %-4s %-25s >1000\n", subnet.Subnet, subnet.Vlan, subnet.Name) fmt.Printf("%-18s %-4s %-25s >1000\n", subnet.Subnet, subnet.Vlan, subnet.Name)
} else { } else {
//fmt.Printf("| %-20s | %-20s |\n", "vegetables", "fruits") //fmt.Printf("| %-20s | %-20s |\n", "vegetables", "fruits")
fmt.Printf("%-18s %-4s %-25s %v\n", subnet.Subnet, subnet.Vlan, subnet.Name, freeip) fmt.Printf("%-18s %-4s %-25s %v\n", subnet.Subnet, subnet.Vlan, subnet.Name, freeip)
} }
} else { } else {
fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan) fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan)
//todo //todo
} }
//} else { //} else {
// fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan) // fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan)
//} //}
} }
}, },
} }
func init() { func init() {
subnetCmd.AddCommand(subnetlistCmd) subnetCmd.AddCommand(subnetlistCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// listCmd.PersistentFlags().String("foo", "", "A help for foo") // listCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// subnetlistCmd.Flags().BoolP("verbose", "v", false, "Show verbose output like free IPs") // subnetlistCmd.Flags().BoolP("verbose", "v", false, "Show verbose output like free IPs")
} }

View file

@ -4,90 +4,88 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// showCmd represents the show command // showCmd represents the show command
var subnetshowCmd = &cobra.Command{ var subnetshowCmd = &cobra.Command{
Use: "show subnet", Use: "show subnet",
Short: "Displays a subnet.", Short: "Displays a subnet.",
Long: `Displays a subnets details like name and vlan tag, Long: `Displays a subnets details like name and vlan tag,
aswell as a list of containing IP addresses`, aswell as a list of containing IP addresses`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Aliases: []string{"s"}, Aliases: []string{"s"},
Example: "ipam subnet show 192.168.0.0/24\nipam subnet show 2001:db8::/64", Example: "ipam subnet show 192.168.0.0/24\nipam subnet show 2001:db8::/64",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
net, parseerr := netip.ParsePrefix(args[0]) net, parseerr := netip.ParsePrefix(args[0])
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
if !SubnetExists(net) { if !SubnetExists(net) {
fmt.Printf("[ERROR] no subnet found for prefix: %v\n", args[0]) fmt.Printf("[ERROR] no subnet found for prefix: %v\n", args[0])
os.Exit(1) os.Exit(1)
} }
subnet, subneterr := GetSubnet(net) subnet, subneterr := GetSubnet(net)
if subneterr != nil { if subneterr != nil {
fmt.Println("[ERROR]", subneterr) fmt.Println("[ERROR]", subneterr)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("\n") fmt.Printf("\n")
fmt.Printf("Name: %v\n", subnet.Name) fmt.Printf("Name: %v\n", subnet.Name)
fmt.Printf("Vlan: %v\n", subnet.Vlan) fmt.Printf("Vlan: %v\n", subnet.Vlan)
fmt.Printf("Prefix: %v\n", subnet.Subnet) fmt.Printf("Prefix: %v\n", subnet.Subnet)
fmt.Printf("Modified at: %v\n", subnet.ChangedAt.Format(time.RFC1123)) fmt.Printf("Modified at: %v\n", subnet.ChangedAt.Format(time.RFC1123))
fmt.Printf("Modified by: %v\n\n", subnet.ChangedBy) fmt.Printf("Modified by: %v\n\n", subnet.ChangedBy)
//fmt.Printf("%v:\n", subnet.Subnet) //fmt.Printf("%v:\n", subnet.Subnet)
addrlist := SortAddresses(subnet.Addresses) addrlist := SortAddresses(subnet.Addresses)
if subnet.Subnet.Addr().Is4() { if subnet.Subnet.Addr().Is4() {
fmt.Printf("%-15s Hostname\n", "IP Address") fmt.Printf("%-15s Hostname\n", "IP Address")
for _, element := range addrlist { for _, element := range addrlist {
if element.FQDN == "" { if element.FQDN == "" {
fmt.Printf("%v\n", element.IP.String()) fmt.Printf("%v\n", element.IP.String())
} else { } else {
fmt.Printf("%-15s %v\n", element.IP.String(), element.FQDN) fmt.Printf("%-15s %v\n", element.IP.String(), element.FQDN)
} }
} }
} else { } else {
fmt.Printf("%-39s Hostname\n", "IP Address") fmt.Printf("%-39s Hostname\n", "IP Address")
for _, element := range addrlist { for _, element := range addrlist {
if element.FQDN == "" { if element.FQDN == "" {
fmt.Printf("%v\n", element.IP.String()) fmt.Printf("%v\n", element.IP.String())
} else { } else {
fmt.Printf("%-39s %v\n", element.IP.String(), element.FQDN) fmt.Printf("%-39s %v\n", element.IP.String(), element.FQDN)
} }
} }
} }
},
},
} }
func init() { func init() {
subnetCmd.AddCommand(subnetshowCmd) subnetCmd.AddCommand(subnetshowCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// showCmd.PersistentFlags().String("foo", "", "A help for foo") // showCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// showCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // showCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -5,27 +5,27 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// subnetCmd represents the subnet command // subnetCmd represents the subnet command
var subnetCmd = &cobra.Command{ var subnetCmd = &cobra.Command{
Use: "subnet", Use: "subnet",
Short: "Manage IP subnets", Short: "Manage IP subnets",
Long: `Add, delete and show IP subnets`, Long: `Add, delete and show IP subnets`,
Aliases: []string{"s"}, Aliases: []string{"s"},
} }
func init() { func init() {
rootCmd.AddCommand(subnetCmd) rootCmd.AddCommand(subnetCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// subnetCmd.PersistentFlags().String("foo", "", "A help for foo") // subnetCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// subnetCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // subnetCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

2
go.mod
View file

@ -1,4 +1,4 @@
module git.sr.ht/~lauralani/ipam module codeberg.org/lauralani/ipam
go 1.20 go 1.20

18
go.sum
View file

@ -48,6 +48,7 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -55,6 +56,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -96,6 +98,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -128,24 +131,24 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 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 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
@ -167,6 +170,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@ -186,7 +190,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-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/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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -308,8 +311,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-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.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 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -321,8 +322,6 @@ 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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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.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 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -468,6 +467,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=

View file

@ -4,8 +4,8 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package main package main
import "git.sr.ht/~lauralani/ipam/cmd" import "codeberg.org/lauralani/ipam/cmd"
func main() { func main() {
cmd.Execute() cmd.Execute()
} }