mirror of
https://codeberg.org/lauralani/ipam.git
synced 2024-11-23 20:20:39 +01:00
Add feature to add next free IP
This commit is contained in:
parent
df3f3d6782
commit
78887fb1ed
3 changed files with 106 additions and 35 deletions
|
@ -6,6 +6,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"net/netip"
|
||||
"time"
|
||||
)
|
||||
|
@ -19,6 +20,21 @@ type Subnet struct {
|
|||
Addresses []Address `json:"addresses"`
|
||||
}
|
||||
|
||||
// GetIPCount gets the IP count for the Subnet
|
||||
//
|
||||
// Returns the IP count or -1 if it's a IPv6 prefix
|
||||
func (s Subnet) GetIPCount() int {
|
||||
if !s.Subnet.Addr().Is4() {
|
||||
return -1
|
||||
}
|
||||
hostbits := float64(32 - s.Subnet.Bits())
|
||||
if s.Subnet.Bits() == 31 {
|
||||
return 2
|
||||
} else {
|
||||
return int(math.Pow(2, hostbits)) - 2
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -33,6 +49,42 @@ func (s Subnet) HasIP(ip netip.Addr) bool {
|
|||
return iscontained
|
||||
}
|
||||
|
||||
// FindFirstFreeIP finds and returns the next free netip.Addr
|
||||
// or an invalid netip.Addr if no free IP was found
|
||||
func (s Subnet) FindFirstFreeIP() netip.Addr {
|
||||
var ip netip.Addr
|
||||
|
||||
if s.Subnet.Addr().Is4() {
|
||||
subnetips := s.GetIPCount()
|
||||
|
||||
// handling /31 prefixes
|
||||
if subnetips == 2 {
|
||||
ip = s.Subnet.Addr()
|
||||
} else {
|
||||
ip = s.Subnet.Addr().Next()
|
||||
}
|
||||
|
||||
// start at first free IP
|
||||
for count := 0; count < subnetips; count++ {
|
||||
|
||||
if s.HasIP(ip) {
|
||||
ip = ip.Next()
|
||||
} else {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ip = s.Subnet.Addr().Next()
|
||||
for ; s.Subnet.Contains(ip); ip = ip.Next() {
|
||||
if !s.HasIP(ip) {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// RemoveIP removes the Address object for given ip from
|
||||
// the Address list of the subnet.
|
||||
//
|
||||
|
|
|
@ -15,67 +15,80 @@ import (
|
|||
)
|
||||
|
||||
var ipaddCmd = &cobra.Command{
|
||||
Use: "add ipaddress [hostname]",
|
||||
Use: "add ipaddress|subnet [hostname]",
|
||||
Short: "Add new IP address",
|
||||
Long: `Add new IP address`,
|
||||
Long: `Adds a new IP address OR the next free IP address from a subnet`,
|
||||
Aliases: []string{"a"},
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var ipaddress, hostname string
|
||||
var iparg, hostname string
|
||||
|
||||
if len(args) == 1 {
|
||||
ipaddress = args[0]
|
||||
iparg = args[0]
|
||||
hostname = ""
|
||||
} else {
|
||||
ipaddress = args[0]
|
||||
iparg = args[0]
|
||||
hostname = args[1]
|
||||
}
|
||||
|
||||
ip, parseerr := netip.ParseAddr(ipaddress)
|
||||
var bestsubnet Subnet
|
||||
var ip netip.Addr
|
||||
|
||||
// Exit if parsed value is no valid IP
|
||||
if parseerr != nil {
|
||||
fmt.Println("[ERROR]", parseerr)
|
||||
argip, ipparseerr := netip.ParseAddr(iparg)
|
||||
argsubnet, subparseerr := netip.ParsePrefix(iparg)
|
||||
|
||||
if ipparseerr != nil && subparseerr != nil {
|
||||
fmt.Printf("[ERROR] Argument is neither a valid IP address nor a valid Subnet: %v", iparg)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if ipparseerr == nil && subparseerr != nil {
|
||||
// argument was a single IP
|
||||
var subnetexists bool
|
||||
bestsubnet, subnetexists = FindBestSubnet(argip)
|
||||
if !subnetexists {
|
||||
fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", iparg)
|
||||
fmt.Printf("[ERROR] Maybe you need to add it first?\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
if bestsubnet.HasIP(argip) {
|
||||
fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", argip.String(), bestsubnet.Subnet.String())
|
||||
os.Exit(1)
|
||||
}
|
||||
ip = argip
|
||||
|
||||
// Exit if parsed value is an IPv6 Address
|
||||
// TODO: Implement IPv6 support
|
||||
//if !ip.Is4() {
|
||||
// fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
|
||||
// os.Exit(1)
|
||||
//}
|
||||
} else if subparseerr == nil && ipparseerr != nil {
|
||||
// argument was a subnet
|
||||
var subneterr error
|
||||
bestsubnet, subneterr = GetSubnet(argsubnet)
|
||||
if subneterr != nil {
|
||||
fmt.Println("[ERROR]", subneterr)
|
||||
os.Exit(1)
|
||||
}
|
||||
ip = bestsubnet.FindFirstFreeIP()
|
||||
|
||||
subnet, subnetexists := FindBestSubnet(ip)
|
||||
if !ip.IsValid() {
|
||||
fmt.Printf("[ERROR] Found no free IP in Subnet %v\n", argsubnet.String())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !subnetexists {
|
||||
fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress)
|
||||
fmt.Printf("[ERROR] Maybe you need to add it first?\n")
|
||||
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)
|
||||
}
|
||||
|
||||
currentuser, _ := user.Current()
|
||||
timestamp := time.Now()
|
||||
|
||||
subnet.Addresses = append(subnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username})
|
||||
subnet.ChangedBy = currentuser.Username
|
||||
subnet.ChangedAt = timestamp
|
||||
bestsubnet.Addresses = append(bestsubnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username})
|
||||
bestsubnet.ChangedBy = currentuser.Username
|
||||
bestsubnet.ChangedAt = timestamp
|
||||
|
||||
writeerr := subnet.WriteSubnet()
|
||||
writeerr := bestsubnet.WriteSubnet()
|
||||
if writeerr != nil {
|
||||
fmt.Println("[ERROR]", writeerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if hostname == "" {
|
||||
fmt.Printf("added ip:\nip: %v\n", ipaddress)
|
||||
fmt.Printf("added ip:\nip: %v\n", ip.String())
|
||||
} else {
|
||||
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname)
|
||||
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ip.String(), hostname)
|
||||
dnserr := AddDNSFqdn(hostname, ip)
|
||||
if dnserr != nil {
|
||||
fmt.Println("[ERROR]", dnserr)
|
||||
|
|
|
@ -114,7 +114,13 @@ func (s Subnet) WriteSubnet() error {
|
|||
fmt.Println("[ERROR]", fileerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}(file)
|
||||
|
||||
_, writeerr := file.Write(data)
|
||||
if writeerr != nil {
|
||||
|
@ -137,12 +143,12 @@ func GetSubnet(net netip.Prefix) (Subnet, error) {
|
|||
|
||||
content, readerr := os.ReadFile(filename)
|
||||
if readerr != nil {
|
||||
return Subnet{}, readerr
|
||||
return Subnet{}, fmt.Errorf("can't open file for subnet %v for reading", net.String())
|
||||
}
|
||||
|
||||
marsherr := json.Unmarshal(content, &subnet)
|
||||
if marsherr != nil {
|
||||
return Subnet{}, marsherr
|
||||
return Subnet{}, fmt.Errorf("can't unmarshal file contents of file %v\n%v", filename, marsherr)
|
||||
}
|
||||
|
||||
return subnet, nil
|
||||
|
|
Loading…
Reference in a new issue