diff --git a/cmd/classes.go b/cmd/classes.go new file mode 100644 index 0000000..de0f185 --- /dev/null +++ b/cmd/classes.go @@ -0,0 +1,43 @@ +/* +Copyright © 2023 Laura Kalb +*/ + +package cmd + +import ( + "net/netip" +) + +type Subnet struct { + Subnet netip.Prefix + Name string + Vlan string + Addresses []Address +} + +// HasIP checks if a Subnet already contains given netip.Addr. +// Returns true if the IP already is present, false otherwise. +func (s Subnet) HasIP(ip netip.Addr) bool { + iscontained := false + + for _, element := range s.Addresses { + if element.IP.Compare(ip) == 0 { + iscontained = true + } + } + + return iscontained +} + +func (s Subnet) RemoveIP(ip netip.Addr) bool { + return true +} + +func (s Subnet) GetIP(ip netip.Addr) bool { + return true +} + +type Address struct { + IP netip.Addr + FQDN string +} diff --git a/cmd/ip-add.go b/cmd/ip-add.go index 007ab4f..943bcb6 100644 --- a/cmd/ip-add.go +++ b/cmd/ip-add.go @@ -52,6 +52,11 @@ var ipaddCmd = &cobra.Command{ os.Exit(1) } + if subnet.HasIP(ip) { + fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", ip.String(), subnet.Subnet.String()) + os.Exit(1) + } + subnet.Addresses = append(subnet.Addresses, Address{ip, hostname}) writeerr := WriteSubnet(subnet) diff --git a/cmd/ip-delete.go b/cmd/ip-delete.go index 632057a..4eee4aa 100644 --- a/cmd/ip-delete.go +++ b/cmd/ip-delete.go @@ -4,8 +4,6 @@ Copyright © 2023 Laura Kalb package cmd import ( - "fmt" - "github.com/spf13/cobra" ) @@ -16,8 +14,8 @@ var ipdeleteCmd = &cobra.Command{ Long: `Delete an IP address`, Aliases: []string{"d"}, Args: cobra.ExactArgs(1), + Example: "ipam ip delete 192.168.0.1", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("ip delete called") }, } diff --git a/cmd/storage.go b/cmd/storage.go index fe0a3ea..aa256b8 100644 --- a/cmd/storage.go +++ b/cmd/storage.go @@ -15,18 +15,6 @@ import ( "github.com/spf13/viper" ) -type Subnet struct { - Subnet netip.Prefix - Name string - Vlan string - Addresses []Address -} - -type Address struct { - IP netip.Addr - FQDN string -} - // SearchBestSubnet tries to load the most fitting IP subnet file // on disk. It takes an IP object and tries to get the best subnet // (meaning the subnet with the smallest subnet size). @@ -218,3 +206,19 @@ func SortAddresses(list []Address) []Address { }) return list } + +// DeleteSubnet deletes the subnet file on disk for netip.Prefix +// net. +// +// Returns nil on success, or a *PathError on failure +func DeleteSubnet(net netip.Prefix) error { + var datadir string = viper.GetString("DataPath") + filename := datadir + strings.Replace(net.String(), "/", "_", 1) + + removeerr := os.Remove(filename) + if removeerr != nil { + return removeerr + } else { + return nil + } +} diff --git a/cmd/subnet-add.go b/cmd/subnet-add.go index 5c1f0f2..7a30dd7 100644 --- a/cmd/subnet-add.go +++ b/cmd/subnet-add.go @@ -38,7 +38,7 @@ var subnetaddCmd = &cobra.Command{ // Parse subnet into ParseCIDR to test if it's a valid subnet // _, ipnet, err := net.ParseCIDR(subnet) - ipnet, err := netip.ParsePrefix(subnet) + ipnet, parseerr := netip.ParsePrefix(subnet) // Exit if subnet already exists, no need to add it then if SubnetExists(ipnet) { @@ -47,8 +47,8 @@ var subnetaddCmd = &cobra.Command{ } // Exit if parsed value is no valid IP - if err != nil { - fmt.Println("[ERROR]", err) + if parseerr != nil { + fmt.Println("[ERROR]", parseerr) os.Exit(1) } @@ -67,15 +67,11 @@ var subnetaddCmd = &cobra.Command{ writeerr := WriteSubnet(subnetobject) if writeerr != nil { - fmt.Println("[ERROR]", err) + fmt.Println("[ERROR]", writeerr) os.Exit(1) } - if vlanid == "-" { - fmt.Printf("added subnet:\nnet: %v\nname: %v\n", subnet, netname) - } else { - 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) }, } diff --git a/cmd/subnet-delete.go b/cmd/subnet-delete.go index 97015c7..ca34787 100644 --- a/cmd/subnet-delete.go +++ b/cmd/subnet-delete.go @@ -5,6 +5,7 @@ package cmd import ( "fmt" + "net/netip" "os" "github.com/spf13/cobra" @@ -17,20 +18,27 @@ var subnetdeleteCmd = &cobra.Command{ Long: `Delete a subnet from the ipam.`, Args: cobra.ExactArgs(1), Aliases: []string{"d"}, + Example: "ipam subnet delete 192.168.0.0/24", Run: func(cmd *cobra.Command, args []string) { - if len(args) < 1 { - fmt.Println("Error: Too few arguments!") - fmt.Print("Usage:\n ipam subnet delete [subnet]") + subnet, parseerr := netip.ParsePrefix(args[0]) + if parseerr != nil { + fmt.Println("[ERROR]", parseerr) os.Exit(1) } - if len(args) > 1 { - fmt.Println("Error: Too many arguments!") - fmt.Print("Usage:\n ipam subnet delete [subnet]") - os.Exit(1) - } - subnet := args[0] - fmt.Printf("Deleting %v\n", subnet) + var confirmation string + fmt.Printf("[WARNING] Do you really want to delete subnet %v? [y/N] ", subnet.String()) + fmt.Scan(&confirmation) + + if (confirmation == "y") || (confirmation == "Y") { + deleteerr := DeleteSubnet(subnet) + if deleteerr != nil { + fmt.Println("[ERROR]", deleteerr) + os.Exit(1) + } else { + fmt.Printf("deleted subnet %v\n", subnet.String()) + } + } }, } diff --git a/cmd/subnet-list.go b/cmd/subnet-list.go index bcb0889..1efd4af 100644 --- a/cmd/subnet-list.go +++ b/cmd/subnet-list.go @@ -5,6 +5,10 @@ package cmd import ( "fmt" + "math" + "net/netip" + "os" + "sort" "github.com/spf13/cobra" ) @@ -16,8 +20,43 @@ var subnetlistCmd = &cobra.Command{ Long: `List all subnets`, Aliases: []string{"l"}, Args: cobra.ExactArgs(0), + Example: "cmdb subnet list", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("subnet list called") + verbose, _ := cmd.Flags().GetBool("verbose") + subnetlist := ListSubnets() + var subnets []Subnet + + for _, subnet := range subnetlist { + prefix, _ := netip.ParsePrefix(subnet) + net, err := GetSubnet(prefix) + if err != nil { + fmt.Println("[ERROR]", err) + os.Exit(1) + } + subnets = append(subnets, net) + } + sort.Slice(subnets, func(i, j int) bool { + return subnets[i].Subnet.Addr().Less(subnets[j].Subnet.Addr()) + }) + + for _, subnet := range subnets { + if verbose { + var numip, freeip int + + if subnet.Subnet.Addr().Is4() { + hostbits := float64(32 - subnet.Subnet.Bits()) + 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) + } else { + fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan) + } + } else { + fmt.Printf("%v:\t%v\t(vl: %v)\n", subnet.Subnet, subnet.Name, subnet.Vlan) + } + + } }, } @@ -32,5 +71,5 @@ func init() { // Cobra supports local flags which will only run when this command // is called directly, e.g.: - // listCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + subnetlistCmd.Flags().BoolP("verbose", "v", false, "Show verbose output like free IPs") }