mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
81 lines
2.1 KiB
Go
81 lines
2.1 KiB
Go
package cluster
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"go4.org/netipx"
|
|
"net/netip"
|
|
)
|
|
|
|
const DefaultSubnetBits = 24
|
|
|
|
var DefaultNetwork = netip.MustParsePrefix("10.210.0.0/16")
|
|
|
|
// IPAM is an in-memory IP address manager for allocating and releasing subnets for machines from a cluster network.
|
|
type IPAM struct {
|
|
network netip.Prefix
|
|
allocated netipx.IPSetBuilder
|
|
}
|
|
|
|
func NewIPAM(network netip.Prefix) (*IPAM, error) {
|
|
if !network.IsValid() || network.Bits() == 0 {
|
|
return nil, errors.New("invalid network")
|
|
}
|
|
return &IPAM{
|
|
network: network.Masked(),
|
|
}, nil
|
|
}
|
|
|
|
// NewIPAMWithAllocated creates a new IPAM with the given network and already allocated subnets.
|
|
func NewIPAMWithAllocated(network netip.Prefix, subnets []netip.Prefix) (*IPAM, error) {
|
|
ipam, err := NewIPAM(network)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, sn := range subnets {
|
|
if err = ipam.AllocateSubnet(sn); err != nil {
|
|
return nil, fmt.Errorf("allocate subnet %s: %w", sn, err)
|
|
}
|
|
}
|
|
return ipam, nil
|
|
}
|
|
|
|
func (ipam *IPAM) AllocateSubnetLen(bits int) (netip.Prefix, error) {
|
|
if bits < ipam.network.Bits() || bits > ipam.network.Addr().BitLen() {
|
|
return netip.Prefix{}, errors.New("invalid subnet size")
|
|
}
|
|
|
|
subnet := netip.PrefixFrom(ipam.network.Addr(), bits)
|
|
for ipam.network.Contains(subnet.Addr()) {
|
|
ipset, err := ipam.allocated.IPSet()
|
|
if err != nil {
|
|
return netip.Prefix{}, fmt.Errorf("get allocated IP set: %w", err)
|
|
}
|
|
if !ipset.OverlapsPrefix(subnet) {
|
|
ipam.allocated.AddPrefix(subnet)
|
|
return subnet, nil
|
|
}
|
|
|
|
nextSubnetAddr := netipx.PrefixLastIP(subnet).Next()
|
|
subnet = netip.PrefixFrom(nextSubnetAddr, bits)
|
|
}
|
|
|
|
return netip.Prefix{}, errors.New("no available subnet")
|
|
}
|
|
|
|
func (ipam *IPAM) AllocateSubnet(subnet netip.Prefix) error {
|
|
subnetLastIP := netipx.PrefixLastIP(subnet)
|
|
if !ipam.network.Contains(subnet.Addr()) || !ipam.network.Contains(subnetLastIP) {
|
|
return errors.New("subnet not in network")
|
|
}
|
|
ipset, err := ipam.allocated.IPSet()
|
|
if err != nil {
|
|
return fmt.Errorf("get allocated IP set: %w", err)
|
|
}
|
|
if ipset.OverlapsPrefix(subnet) {
|
|
return errors.New("subnet overlaps with allocated subnets")
|
|
}
|
|
ipam.allocated.AddPrefix(subnet)
|
|
return nil
|
|
}
|