mirror of
https://codeberg.org/lauralani/ipam.git
synced 2024-11-23 20:20:39 +01:00
changed net module to net/netip and added file ops
This commit is contained in:
parent
8475bad2d7
commit
928b08d0ce
4 changed files with 223 additions and 40 deletions
|
@ -6,7 +6,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -29,30 +29,42 @@ var ipaddCmd = &cobra.Command{
|
|||
hostname = args[1]
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ipaddress)
|
||||
ip, parseerr := netip.ParseAddr(ipaddress)
|
||||
|
||||
// Exit if parsed value is no valid IP
|
||||
if ip == nil {
|
||||
fmt.Printf("[ERROR] not a valid IP: %v\n", ipaddress)
|
||||
if parseerr != nil {
|
||||
fmt.Println("[ERROR]", parseerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Exit if parsed value is an IPv6 Address
|
||||
// TODO: Implement IPv6 support
|
||||
if ip.To4() == nil {
|
||||
if !ip.Is4() {
|
||||
fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// TODO: Check if there is already a subnet that can contain this IP, err if not
|
||||
subnet, subnetexists := SearchBestSubnet(ip)
|
||||
|
||||
if hostname == "" {
|
||||
fmt.Printf("Adding IP %v\n", ipaddress)
|
||||
} else {
|
||||
fmt.Printf("Adding IP %v with hostname %v\n", ipaddress, hostname)
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO: Save to file
|
||||
subnet.Addresses = append(subnet.Addresses, Address{ip.String(), hostname})
|
||||
|
||||
writeerr := WriteSubnet(subnet)
|
||||
if writeerr != nil {
|
||||
fmt.Println("[ERROR]", writeerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if hostname == "" {
|
||||
fmt.Printf("added ip:\nip: %v\n", ipaddress)
|
||||
} else {
|
||||
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,4 @@ func initConfig() {
|
|||
println("[ERROR] Can't read config file!", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(viper.AllKeys())
|
||||
|
||||
}
|
||||
|
|
177
cmd/storage.go
177
cmd/storage.go
|
@ -5,11 +5,17 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Subnet struct {
|
||||
Subnet net.IPNet
|
||||
Subnet netip.Prefix
|
||||
Name string
|
||||
Vlan string
|
||||
Addresses []Address
|
||||
|
@ -20,15 +26,76 @@ type Address struct {
|
|||
FQDN string
|
||||
}
|
||||
|
||||
// GetSubnet 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).
|
||||
// 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).
|
||||
//
|
||||
// Returns the best subnet as Subnet object and true if a suitable
|
||||
// subnet was found, otherwise an empty Subnet object and false.
|
||||
func GetSubnet(ip net.IP) (Subnet, bool) {
|
||||
func SearchBestSubnet(ip netip.Addr) (Subnet, bool) {
|
||||
subnets := ListSubnets()
|
||||
var smallestprefix int = 0
|
||||
bestmatch, _ := netip.ParsePrefix("0.0.0.0/32")
|
||||
var isipv4 bool = ip.Is4()
|
||||
var subnet Subnet
|
||||
|
||||
return Subnet{}, false
|
||||
for _, net := range subnets {
|
||||
prefix, _ := netip.ParsePrefix(net)
|
||||
if prefix.Addr().Is4() == isipv4 {
|
||||
if prefix.Contains(ip) {
|
||||
if prefix.Bits() > smallestprefix {
|
||||
bestmatch = prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !bestmatch.Addr().IsUnspecified() {
|
||||
var geterr error
|
||||
subnet, geterr = GetSubnet(bestmatch)
|
||||
if geterr != nil {
|
||||
fmt.Println("[ERROR]", geterr)
|
||||
os.Exit(1)
|
||||
}
|
||||
return subnet, true
|
||||
} else {
|
||||
return Subnet{}, false
|
||||
}
|
||||
}
|
||||
|
||||
// SubnetExists will return true if the given subnet already exists
|
||||
// on file, false otherwise.
|
||||
func SubnetExists(net netip.Prefix) bool {
|
||||
subnets := ListSubnets()
|
||||
|
||||
for _, b := range subnets {
|
||||
if b == net.String() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListSubnets returns a list of strings containing the current
|
||||
// subnets configured.
|
||||
func ListSubnets() []string {
|
||||
subnets := make([]string, 0)
|
||||
var datadir string = viper.GetString("DataPath")
|
||||
|
||||
subnetfiles, readerr := os.ReadDir(datadir)
|
||||
|
||||
if len(subnetfiles) == 0 {
|
||||
return subnets
|
||||
}
|
||||
|
||||
if readerr != nil {
|
||||
fmt.Println("[ERROR]", readerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, element := range subnetfiles {
|
||||
subnets = append(subnets, strings.Replace(element.Name(), "_", "/", 1))
|
||||
}
|
||||
|
||||
return subnets
|
||||
}
|
||||
|
||||
// WriteSubnet takes a given Subnet object and tries to write it to
|
||||
|
@ -36,7 +103,101 @@ func GetSubnet(ip net.IP) (Subnet, bool) {
|
|||
//
|
||||
// Returns nil on success or the error that happened.
|
||||
func WriteSubnet(subnet Subnet) error {
|
||||
//if subnet.Subnet
|
||||
var datadir string = viper.GetString("DataPath")
|
||||
|
||||
_, direrr := os.Stat(datadir)
|
||||
if direrr != nil {
|
||||
mkerr := os.MkdirAll(datadir, 0755)
|
||||
if mkerr != nil {
|
||||
println("[ERROR] Can't create ipam config directory!", mkerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
filename := datadir + strings.Replace(subnet.Subnet.String(), "/", "_", 1)
|
||||
|
||||
file, fileerr := os.Create(filename)
|
||||
if fileerr != nil {
|
||||
fmt.Println("[ERROR]", fileerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, suberr := file.WriteString(subnet.Subnet.String() + "\n")
|
||||
if suberr != nil {
|
||||
fmt.Println("[ERROR]", suberr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, nameerr := file.WriteString(subnet.Name + "\n")
|
||||
if nameerr != nil {
|
||||
fmt.Println("[ERROR]", nameerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, vlanerr := file.WriteString(subnet.Vlan + "\n")
|
||||
if vlanerr != nil {
|
||||
fmt.Println("[ERROR]", vlanerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(subnet.Addresses) != 0 {
|
||||
for _, element := range subnet.Addresses {
|
||||
_, err := file.WriteString(element.IP + ":" + element.FQDN + "\n")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubnet reads the corresponding file for the given
|
||||
// netip.Prefix net and constructs a Subnet object.
|
||||
//
|
||||
// Returns the Subnet object and nil if the file read was
|
||||
// successful, an empty Subnet object and the error otherwise.
|
||||
func GetSubnet(net netip.Prefix) (Subnet, error) {
|
||||
var datadir string = viper.GetString("DataPath")
|
||||
filename := datadir + strings.Replace(net.String(), "/", "_", 1)
|
||||
var subnet Subnet = Subnet{}
|
||||
|
||||
// open file
|
||||
file, openerr := os.Open(filename)
|
||||
if openerr != nil {
|
||||
return Subnet{}, openerr
|
||||
}
|
||||
// remember to close the file at the end of the program
|
||||
defer file.Close()
|
||||
|
||||
// read the file line by line using scanner
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
var counter int = 0
|
||||
for scanner.Scan() {
|
||||
switch counter {
|
||||
case 0:
|
||||
subnet.Subnet, _ = netip.ParsePrefix(scanner.Text())
|
||||
|
||||
case 1:
|
||||
subnet.Name = scanner.Text()
|
||||
|
||||
case 2:
|
||||
subnet.Vlan = scanner.Text()
|
||||
|
||||
default:
|
||||
s := strings.Split(scanner.Text(), ":")
|
||||
a := Address{s[0], s[1]}
|
||||
subnet.Addresses = append(subnet.Addresses, a)
|
||||
}
|
||||
counter = counter + 1
|
||||
}
|
||||
|
||||
if scanerr := scanner.Err(); scanerr != nil {
|
||||
return Subnet{}, openerr
|
||||
}
|
||||
|
||||
return subnet, nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -14,29 +14,37 @@ import (
|
|||
|
||||
// addCmd represents the add command
|
||||
var subnetaddCmd = &cobra.Command{
|
||||
Use: "add subnet [vlan]",
|
||||
Use: "add subnet subnet-name [vlan]",
|
||||
Short: "Add a new subnet",
|
||||
Long: `Add a new subnet`,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Args: cobra.RangeArgs(2, 3),
|
||||
Aliases: []string{"a"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var subnet string
|
||||
var vlanid string
|
||||
var netname string
|
||||
|
||||
if len(args) == 1 {
|
||||
if len(args) == 2 {
|
||||
subnet = args[0]
|
||||
|
||||
netname = args[1]
|
||||
vlanid = "-"
|
||||
}
|
||||
|
||||
if len(args) == 2 {
|
||||
if len(args) == 3 {
|
||||
subnet = args[0]
|
||||
vlanid = args[1]
|
||||
netname = args[1]
|
||||
vlanid = args[2]
|
||||
}
|
||||
|
||||
// Parse subnet into ParseCIDR to test if it's a valid subnet
|
||||
_, ipnet, err := net.ParseCIDR(subnet)
|
||||
// _, ipnet, err := net.ParseCIDR(subnet)
|
||||
ipnet, err := netip.ParsePrefix(subnet)
|
||||
|
||||
// Exit if subnet already exists, no need to add it then
|
||||
if SubnetExists(ipnet) {
|
||||
fmt.Printf("[ERROR] Subnet already exists: %v\n", subnet)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Exit if parsed value is no valid IP
|
||||
if err != nil {
|
||||
|
@ -46,23 +54,28 @@ var subnetaddCmd = &cobra.Command{
|
|||
|
||||
// Exit if parsed value is an IPv6 Address
|
||||
// TODO: Implement IPv6 support
|
||||
if ipnet.IP.To4() == nil {
|
||||
if !ipnet.Addr().Is4() {
|
||||
fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ask for Subnet Name
|
||||
// TODO: Check if net name only contains letters, numbers and hyphens
|
||||
fmt.Printf("Subnet name: ")
|
||||
fmt.Scan(&netname)
|
||||
subnetobject := Subnet{}
|
||||
subnetobject.Subnet = ipnet
|
||||
subnetobject.Name = netname
|
||||
subnetobject.Vlan = vlanid
|
||||
|
||||
if vlanid == "-" {
|
||||
fmt.Printf("Adding Subnet %v.\n", subnet)
|
||||
} else {
|
||||
fmt.Printf("Adding Subnet %v with VLAN Tag %v.\n", subnet, vlanid)
|
||||
writeerr := WriteSubnet(subnetobject)
|
||||
|
||||
if writeerr != nil {
|
||||
fmt.Println("[ERROR]", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// TODO: Save subnet to file
|
||||
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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue