diff --git a/.gitignore b/.gitignore index e88debd..4930d72 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ go.work # build output -output/ +bin/ # vs code stuff .vscode/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2fcdbcf --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OUTPUT_FOLDER=bin +FILENAME=ipam + +all: build + +build: + go build -o ${OUTPUT_FOLDER}/${FILENAME} main.go + +install: + cp ${OUTPUT_FOLDER}/${FILENAME} /usr/local/bin/${FILENAME} + +clean: + go clean + rm -r ${OUTPUT_FOLDER}/ diff --git a/README.md b/README.md index fdb5ed9..d1f85ad 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,28 @@ # ipam This project is in development. There is no working version yet! + +## CLI +``` +❯ ipam +A cli based ipam. +You can manage subnets, single ip addresses within those, and the corresponding A records. +PowerDNS and IPV6-Support will follow + +Usage: + ipam [command] + +Available Commands: + completion Generate the autocompletion script for the specified shell + export Export current ipam configuration (not implemented) + help Help about any command + import Import ipam configuration (not implemented) + ip manage ip addresses + subnet Manage ip subnets + +Flags: + -d, --debug Enable debug mode. (may print sensitive Information, so please watch out!) + -h, --help help for ipam + +Use "ipam [command] --help" for more information about a command. +``` diff --git a/cmd/classes.go b/cmd/classes.go index de0f185..0082355 100644 --- a/cmd/classes.go +++ b/cmd/classes.go @@ -5,6 +5,7 @@ Copyright © 2023 Laura Kalb package cmd import ( + "errors" "net/netip" ) @@ -29,12 +30,42 @@ func (s Subnet) HasIP(ip netip.Addr) bool { return iscontained } -func (s Subnet) RemoveIP(ip netip.Addr) bool { - return true +// RemoveIP removes the Address object for given ip from +// the Address list of the subnet. +// +// Returns the changed Subnet and nil if delete was +// successful, or an empty Subnet and an error if +// ip could not be deleted. +func (s Subnet) RemoveIP(ip netip.Addr) (Subnet, error) { + var addrlist []Address + + if !s.HasIP(ip) { + + return Subnet{}, errors.New("IP " + ip.String() + " wasn't found in subnet " + s.Subnet.String()) + } + + for _, item := range s.Addresses { + if item.IP.Compare(ip) != 0 { + addrlist = append(addrlist, item) + } + } + s.Addresses = addrlist + return s, nil } -func (s Subnet) GetIP(ip netip.Addr) bool { - return true +// GetIP returns the Address object for the subnet with +// netip.Addr ip. +// +// Returns the Address object and true if a corresponding +// object was found, an empty Address and false otherwise. +func (s Subnet) GetIP(ip netip.Addr) (Address, bool) { + for _, item := range s.Addresses { + if item.IP.Compare(ip) == 0 { + return item, true + } + } + + return Address{}, false } type Address struct { diff --git a/cmd/ip-add.go b/cmd/ip-add.go index 943bcb6..5b93300 100644 --- a/cmd/ip-add.go +++ b/cmd/ip-add.go @@ -44,7 +44,7 @@ var ipaddCmd = &cobra.Command{ os.Exit(1) } - subnet, subnetexists := SearchBestSubnet(ip) + subnet, subnetexists := FindBestSubnet(ip) if !subnetexists { fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress) diff --git a/cmd/ip-delete.go b/cmd/ip-delete.go index 4eee4aa..01ed097 100644 --- a/cmd/ip-delete.go +++ b/cmd/ip-delete.go @@ -4,6 +4,10 @@ Copyright © 2023 Laura Kalb package cmd import ( + "fmt" + "net/netip" + "os" + "github.com/spf13/cobra" ) @@ -16,6 +20,38 @@ var ipdeleteCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Example: "ipam ip delete 192.168.0.1", Run: func(cmd *cobra.Command, args []string) { + ip, parseerr := netip.ParseAddr(args[0]) + + if parseerr != nil { + fmt.Println("[ERROR]", parseerr) + os.Exit(1) + } + + subnet, subnetexists := FindBestSubnet(ip) + if !subnetexists { + fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) + os.Exit(1) + } + + address, _ := subnet.GetIP(ip) + + subnet, removeerr := subnet.RemoveIP(ip) + if removeerr != nil { + fmt.Println("[ERROR]", removeerr) + os.Exit(1) + } + + writeerr := WriteSubnet(subnet) + if writeerr != nil { + fmt.Println("[ERROR]", writeerr) + os.Exit(1) + } + + if address.FQDN == "" { + fmt.Printf("deleted ip %v\n", address.IP.String()) + } else { + fmt.Printf("deleted ip %v (%v)\n", address.IP.String(), address.FQDN) + } }, } diff --git a/cmd/ip-edit.go b/cmd/ip-edit.go index 2c56b03..8aa07a0 100644 --- a/cmd/ip-edit.go +++ b/cmd/ip-edit.go @@ -16,7 +16,7 @@ var ipeditCmd = &cobra.Command{ Aliases: []string{"e"}, Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - fmt.Println("edit called") + fmt.Println("not implemented yet; please delete and readd") }, } diff --git a/cmd/ip-show.go b/cmd/ip-show.go index c27c2d1..026c6eb 100644 --- a/cmd/ip-show.go +++ b/cmd/ip-show.go @@ -5,6 +5,8 @@ package cmd import ( "fmt" + "net/netip" + "os" "github.com/spf13/cobra" ) @@ -16,8 +18,30 @@ var ipshowCmd = &cobra.Command{ Long: `Show IP and associated name`, Aliases: []string{"s"}, Args: cobra.ExactArgs(1), + Example: "ipam ip show 192.168.0.1", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("ip show called") + ip, parseerr := netip.ParseAddr(args[0]) + + if parseerr != nil { + fmt.Println("[ERROR]", parseerr) + os.Exit(1) + } + + subnet, subnetexists := FindBestSubnet(ip) + if !subnetexists { + fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) + os.Exit(1) + } + + addr, addrexists := subnet.GetIP(ip) + if !addrexists { + fmt.Printf("[ERROR] Couldn't find IP %v\n", ip.String()) + os.Exit(1) + } + + fmt.Printf("IP: %v\n", ip.String()) + fmt.Printf("FQDN: %v\n", addr.FQDN) + fmt.Printf("Subnet: %v (%v)\n", subnet.Subnet.String(), subnet.Name) }, } diff --git a/cmd/powerdns.go b/cmd/powerdns.go new file mode 100644 index 0000000..2e837ad --- /dev/null +++ b/cmd/powerdns.go @@ -0,0 +1,5 @@ +/* +Copyright © 2023 Laura Kalb +*/ + +package cmd