diff --git a/charts/templates/kube-ovn-crd.yaml b/charts/templates/kube-ovn-crd.yaml index 1e0499707c7..7a899182fc5 100644 --- a/charts/templates/kube-ovn-crd.yaml +++ b/charts/templates/kube-ovn-crd.yaml @@ -1781,9 +1781,6 @@ spec: - name: Subnet type: string jsonPath: .spec.subnet - - name: Protocol - type: string - jsonPath: .spec.protocol - name: IPs type: string jsonPath: .spec.ips @@ -1816,12 +1813,6 @@ spec: x-kubernetes-list-type: set items: type: string - protocol: - type: string - enum: - - IPv4 - - IPv6 - - Dual ips: type: array minItems: 1 diff --git a/dist/images/install.sh b/dist/images/install.sh index 177cfb7fce5..0c4dfd36f2b 100755 --- a/dist/images/install.sh +++ b/dist/images/install.sh @@ -2319,9 +2319,6 @@ spec: - name: Subnet type: string jsonPath: .spec.subnet - - name: Protocol - type: string - jsonPath: .spec.protocol - name: IPs type: string jsonPath: .spec.ips @@ -2354,12 +2351,6 @@ spec: x-kubernetes-list-type: set items: type: string - protocol: - type: string - enum: - - IPv4 - - IPv6 - - Dual ips: type: array minItems: 1 diff --git a/pkg/apis/kubeovn/v1/types.go b/pkg/apis/kubeovn/v1/types.go index 27feb7ba09a..c0d6b69dac8 100644 --- a/pkg/apis/kubeovn/v1/types.go +++ b/pkg/apis/kubeovn/v1/types.go @@ -6,6 +6,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kubeovn/kube-ovn/pkg/internal" "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb" ) @@ -265,7 +266,6 @@ type IPPool struct { type IPPoolSpec struct { Subnet string `json:"subnet,omitempty"` - Protocol string `json:"protocol,omitempty"` Namespaces []string `json:"namespaces,omitempty"` IPs []string `json:"ips,omitempty"` } @@ -275,14 +275,14 @@ type IPPoolSpec struct { type IPPoolCondition Condition type IPPoolStatus struct { - V4AvailableIPs float64 `json:"v4AvailableIPs"` - V4AvailableIPRange string `json:"v4AvailableIPRange"` - V4UsingIPs float64 `json:"v4UsingIPs"` - V4UsingIPRange string `json:"v4UsingIPRange"` - V6AvailableIPs float64 `json:"v6AvailableIPs"` - V6AvailableIPRange string `json:"v6AvailableIPRange"` - V6UsingIPs float64 `json:"v6UsingIPs"` - V6UsingIPRange string `json:"v6UsingIPRange"` + V4AvailableIPs internal.BigInt `json:"v4AvailableIPs"` + V4AvailableIPRange string `json:"v4AvailableIPRange"` + V4UsingIPs internal.BigInt `json:"v4UsingIPs"` + V4UsingIPRange string `json:"v4UsingIPRange"` + V6AvailableIPs internal.BigInt `json:"v6AvailableIPs"` + V6AvailableIPRange string `json:"v6AvailableIPRange"` + V6UsingIPs internal.BigInt `json:"v6UsingIPs"` + V6UsingIPRange string `json:"v6UsingIPRange"` // Conditions represents the latest state of the object // +optional diff --git a/pkg/apis/kubeovn/v1/zz_generated.deepcopy.go b/pkg/apis/kubeovn/v1/zz_generated.deepcopy.go index d00e2c8c08c..eb1ea7ded00 100644 --- a/pkg/apis/kubeovn/v1/zz_generated.deepcopy.go +++ b/pkg/apis/kubeovn/v1/zz_generated.deepcopy.go @@ -249,6 +249,10 @@ func (in *IPPoolSpec) DeepCopy() *IPPoolSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPPoolStatus) DeepCopyInto(out *IPPoolStatus) { *out = *in + in.V4AvailableIPs.DeepCopyInto(&out.V4AvailableIPs) + in.V4UsingIPs.DeepCopyInto(&out.V4UsingIPs) + in.V6AvailableIPs.DeepCopyInto(&out.V6AvailableIPs) + in.V6UsingIPs.DeepCopyInto(&out.V6UsingIPs) if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]IPPoolCondition, len(*in)) diff --git a/pkg/controller/ippool.go b/pkg/controller/ippool.go index 8b8d5cb98e1..32213dfba32 100644 --- a/pkg/controller/ippool.go +++ b/pkg/controller/ippool.go @@ -48,8 +48,7 @@ func (c *Controller) enqueueUpdateIPPool(old, new interface{}) { } if !reflect.DeepEqual(oldIPPool.Spec.Namespaces, newIPPool.Spec.Namespaces) || - !reflect.DeepEqual(oldIPPool.Spec.IPs, newIPPool.Spec.IPs) || - oldIPPool.Spec.Protocol != newIPPool.Spec.Protocol { + !reflect.DeepEqual(oldIPPool.Spec.IPs, newIPPool.Spec.IPs) { klog.V(3).Infof("enqueue update ippool %s", key) c.addOrUpdateIPPoolQueue.Add(key) } diff --git a/pkg/internal/big_int.go b/pkg/internal/big_int.go new file mode 100644 index 00000000000..376f178521e --- /dev/null +++ b/pkg/internal/big_int.go @@ -0,0 +1,47 @@ +package internal + +import ( + "fmt" + "math/big" +) + +type BigInt struct { + big.Int +} + +func (b BigInt) DeepCopyInto(n *BigInt) { + n.Int.FillBytes(b.Int.Bytes()) +} + +func (b BigInt) Equal(n BigInt) bool { + return b.Cmp(n) == 0 +} + +func (b BigInt) Cmp(n BigInt) int { + return b.Int.Cmp(&n.Int) +} + +func (b BigInt) Add(n BigInt) BigInt { + return BigInt{*big.NewInt(0).Add(&b.Int, &n.Int)} +} + +func (b BigInt) Sub(n BigInt) BigInt { + return BigInt{*big.NewInt(0).Sub(&b.Int, &n.Int)} +} + +func (b BigInt) MarshalJSON() ([]byte, error) { + return []byte(b.String()), nil +} + +func (b *BigInt) UnmarshalJSON(p []byte) error { + if string(p) == "null" { + return nil + } + var z big.Int + _, ok := z.SetString(string(p), 10) + if !ok { + return fmt.Errorf("invalid big integer: %q", p) + } + b.Int = z + return nil +} diff --git a/pkg/ipam/ip.go b/pkg/ipam/ip.go index cdb3f334fd6..bef5b48bc67 100644 --- a/pkg/ipam/ip.go +++ b/pkg/ipam/ip.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "net" + "strings" ) type IP net.IP @@ -14,9 +15,10 @@ func NewIP(s string) (IP, error) { return nil, fmt.Errorf("invalid IP address %q", s) } - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 + if !strings.ContainsRune(s, ':') { + ip = ip.To4() } + return IP(ip), nil } @@ -41,11 +43,27 @@ func (a IP) GreaterThan(b IP) bool { } func (a IP) Add(n int64) IP { - return IP(big.NewInt(0).Add(big.NewInt(0).SetBytes([]byte(a)), big.NewInt(n)).Bytes()) + buff := big.NewInt(0).Add(big.NewInt(0).SetBytes([]byte(a)), big.NewInt(n)).Bytes() + if len(buff) < len(a) { + tmp := make([]byte, len(a)) + copy(tmp[len(tmp)-len(buff):], buff) + buff = tmp + } else if len(buff) > len(a) { + buff = buff[len(buff)-len(a):] + } + return IP(buff) } func (a IP) Sub(n int64) IP { - return IP(big.NewInt(0).Sub(big.NewInt(0).SetBytes([]byte(a)), big.NewInt(n)).Bytes()) + buff := big.NewInt(0).Sub(big.NewInt(0).SetBytes([]byte(a)), big.NewInt(n)).Bytes() + if len(buff) < len(a) { + tmp := make([]byte, len(a)) + copy(tmp[len(tmp)-len(buff):], buff) + buff = tmp + } else if len(buff) > len(a) { + buff = buff[len(buff)-len(a):] + } + return IP(buff) } func (a IP) String() string { diff --git a/pkg/ipam/ip_range.go b/pkg/ipam/ip_range.go index 3ab829eb6c1..fea58d9857d 100644 --- a/pkg/ipam/ip_range.go +++ b/pkg/ipam/ip_range.go @@ -3,7 +3,8 @@ package ipam import ( "fmt" "math/big" - "strconv" + + "github.com/kubeovn/kube-ovn/pkg/internal" ) // IPRange represents an IP range of [start, end] @@ -35,10 +36,9 @@ func (r *IPRange) SetEnd(ip IP) { r.end = ip } -func (r *IPRange) Count() float64 { +func (r *IPRange) Count() internal.BigInt { n := big.NewInt(0).Sub(big.NewInt(0).SetBytes([]byte(r.end)), big.NewInt(0).SetBytes([]byte(r.start))) - count, _ := strconv.ParseFloat(n.Add(n, big.NewInt(1)).String(), 64) - return count + return internal.BigInt{Int: *n.Add(n, big.NewInt(1))} } func (r *IPRange) Contains(ip IP) bool { diff --git a/pkg/ipam/ip_range_list.go b/pkg/ipam/ip_range_list.go index a5d2f4d9d42..e4fe2a17151 100644 --- a/pkg/ipam/ip_range_list.go +++ b/pkg/ipam/ip_range_list.go @@ -4,69 +4,49 @@ import ( "fmt" "sort" "strings" + + "github.com/kubeovn/kube-ovn/pkg/internal" ) type IPRangeList struct { ranges []*IPRange } -func NewIPRangeList() *IPRangeList { +func NewEmptyIPRangeList() *IPRangeList { return &IPRangeList{} } +func NewIPRangeList(ips ...IP) (*IPRangeList, error) { + if len(ips)%2 != 0 { + return nil, fmt.Errorf("length of ips must be an even number, but current is %d", len(ips)) + } + + ret := &IPRangeList{make([]*IPRange, len(ips)/2)} + for i := 0; i < len(ips)/2; i++ { + ret.ranges[i] = NewIPRange(ips[i*2], ips[i*2+1]) + } + return ret, nil +} + func NewIPRangeListFrom(x ...string) (*IPRangeList, error) { - ret := &IPRangeList{make([]*IPRange, 0, len(x))} + ret := &IPRangeList{} for _, s := range x { ips := strings.Split(s, "..") + start, err := NewIP(ips[0]) + if err != nil { + return nil, err + } + var r *IPRange if len(ips) == 1 { - ip, err := NewIP(ips[0]) - if err != nil { - return nil, err - } - ret.Add(ip) + r = NewIPRange(start, start) } else { - start, err := NewIP(ips[0]) - if err != nil { - return nil, err - } end, err := NewIP(ips[1]) if err != nil { return nil, err } - if end.LessThan(start) { - return nil, fmt.Errorf("invalid IP range %s: end %s must NOT be less than start %s", s, ips[1], ips[0]) - } - - n1, found1 := ret.Find(start) - n2, found2 := ret.Find(end) - if found1 { - if found2 { - if n1 != n2 { - ret.ranges[n1].SetEnd(ret.ranges[n2].End()) - ret.ranges = append(ret.ranges[:n1+1], ret.ranges[n2+1:]...) - } - } else { - ret.ranges[n1].SetEnd(end) - ret.ranges = append(ret.ranges[:n1+1], ret.ranges[n2:]...) - } - } else { - if found2 { - ret.ranges[n2].SetStart(start) - ret.ranges = append(ret.ranges[:n1], ret.ranges[n2:]...) - } else { - if n1 == n2 { - tmp := make([]*IPRange, ret.Len()+1) - copy(tmp, ret.ranges[:n1]) - tmp[n1] = NewIPRange(start, end) - copy(tmp[n1+1:], ret.ranges[n1:]) - ret.ranges = tmp - } else { - ret.ranges[n1] = NewIPRange(start, end) - ret.ranges = append(ret.ranges[:n1+1], ret.ranges[n2+1:]...) - } - } - } + r = NewIPRange(start, end) } + ret = ret.Merge(&IPRangeList{[]*IPRange{r}}) } return ret, nil } @@ -81,12 +61,12 @@ func (r *IPRangeList) Len() int { return len(r.ranges) } -func (r *IPRangeList) Count() float64 { - var sum float64 +func (r *IPRangeList) Count() internal.BigInt { + var count internal.BigInt for _, v := range r.ranges { - sum += v.Count() + count = count.Add(v.Count()) } - return sum + return count } func (r *IPRangeList) At(i int) *IPRange { @@ -171,7 +151,7 @@ func (r *IPRangeList) Allocate(skipped []IP) IP { return ret } - tmp := NewIPRangeList() + tmp := NewEmptyIPRangeList() for _, ip := range skipped { tmp.Add(ip) } @@ -203,7 +183,7 @@ func (r *IPRangeList) Equal(x *IPRangeList) bool { // Separate returns a new list which contains items which are in `r` but not in `x` func (r *IPRangeList) Separate(x *IPRangeList) *IPRangeList { if r.Len() == 0 { - return NewIPRangeList() + return NewEmptyIPRangeList() } if x.Len() == 0 { return r.Clone() @@ -269,6 +249,7 @@ func (r *IPRangeList) Merge(x *IPRangeList) *IPRangeList { if ret.ranges[i].End().Add(1).Equal(ret.ranges[i+1].Start()) { ret.ranges[i].end = ret.ranges[i+1].end ret.ranges = append(ret.ranges[:i+1], ret.ranges[i+2:]...) + i-- } } diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 28c203bbf1f..d91cf873707 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -11,6 +11,7 @@ import ( "k8s.io/klog/v2" kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "github.com/kubeovn/kube-ovn/pkg/internal" "github.com/kubeovn/kube-ovn/pkg/util" ) @@ -191,7 +192,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr, gw string, excludeIps []strin pool.V4IPs = ips pool.V4Free = subnet.V4Free.Clone() pool.V4Reserved = subnet.V4Reserved.Clone() - pool.V4Released = NewIPRangeList() + pool.V4Released = NewEmptyIPRangeList() pool.V4Using = subnet.V4Using.Clone() for name, p := range subnet.IPPools { @@ -201,7 +202,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr, gw string, excludeIps []strin p.V4Free = ips.Intersect(p.V4IPs) p.V4Reserved = subnet.V4Reserved.Intersect(p.V4IPs) p.V4Available = p.V4Free.Clone() - p.V4Released = NewIPRangeList() + p.V4Released = NewEmptyIPRangeList() pool.V4Free = pool.V4Free.Separate(p.V4IPs) pool.V4Reserved = p.V4Reserved.Separate(p.V4Reserved) } @@ -234,7 +235,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr, gw string, excludeIps []strin pool.V6IPs = ips pool.V6Free = subnet.V6Free.Clone() pool.V6Reserved = subnet.V6Reserved.Clone() - pool.V6Released = NewIPRangeList() + pool.V6Released = NewEmptyIPRangeList() pool.V6Using = subnet.V6Using.Clone() for name, p := range subnet.IPPools { @@ -244,7 +245,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr, gw string, excludeIps []strin p.V6Free = ips.Intersect(p.V6IPs) p.V6Reserved = subnet.V6Reserved.Intersect(p.V6IPs) p.V6Available = p.V6Free.Clone() - p.V6Released = NewIPRangeList() + p.V6Released = NewEmptyIPRangeList() pool.V6Free = pool.V6Free.Separate(p.V6IPs) pool.V6Reserved = p.V6Reserved.Separate(p.V6Reserved) } @@ -384,7 +385,7 @@ func (ipam *IPAM) RemoveIPPool(subnet, ippool string) { } func (ipam *IPAM) IPPoolStatistics(subnet, ippool string) ( - v4Available, v4Using, v6Available, v6Using float64, + v4Available, v4Using, v6Available, v6Using internal.BigInt, v4AvailableRange, v4UsingRange, v6AvailableRange, v6UsingRange string, ) { ipam.mutex.RLock() diff --git a/pkg/ipam/subnet.go b/pkg/ipam/subnet.go index dcc83f976c0..6d379355490 100644 --- a/pkg/ipam/subnet.go +++ b/pkg/ipam/subnet.go @@ -9,6 +9,7 @@ import ( "k8s.io/klog/v2" kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "github.com/kubeovn/kube-ovn/pkg/internal" "github.com/kubeovn/kube-ovn/pkg/util" ) @@ -67,12 +68,12 @@ func NewSubnet(name, cidrStr string, excludeIps []string) (*Subnet, error) { Name: name, CIDR: cidrStr, Protocol: protocol, - V4Free: NewIPRangeList(), - V6Free: NewIPRangeList(), + V4Free: NewEmptyIPRangeList(), + V6Free: NewEmptyIPRangeList(), V4Reserved: v4Reserved, V6Reserved: v6Reserved, - V4Using: NewIPRangeList(), - V6Using: NewIPRangeList(), + V4Using: NewEmptyIPRangeList(), + V6Using: NewEmptyIPRangeList(), V4NicToIP: map[string]IP{}, V6NicToIP: map[string]IP{}, V4IPToPod: map[string]string{}, @@ -107,10 +108,10 @@ func NewSubnet(name, cidrStr string, excludeIps []string) (*Subnet, error) { pool := &IPPool{ V4IPs: subnet.V4Free.Clone(), V6IPs: subnet.V6Free.Clone(), - V4Released: NewIPRangeList(), - V6Released: NewIPRangeList(), - V4Using: NewIPRangeList(), - V6Using: NewIPRangeList(), + V4Released: NewEmptyIPRangeList(), + V6Released: NewEmptyIPRangeList(), + V4Using: NewEmptyIPRangeList(), + V6Using: NewEmptyIPRangeList(), } subnet.V4Free = subnet.V4Free.Separate(subnet.V4Reserved) subnet.V6Free = subnet.V6Free.Separate(subnet.V6Reserved) @@ -223,7 +224,7 @@ func (subnet *Subnet) getV4RandomAddress(ippoolName, podName, nicName string, ma return nil, nil, "", ErrNoAvailable } pool.V4Free = pool.V4Released - pool.V4Released = NewIPRangeList() + pool.V4Released = NewEmptyIPRangeList() } skipped := make([]IP, 0, len(skippedAddrs)) @@ -277,7 +278,7 @@ func (subnet *Subnet) getV6RandomAddress(ippoolName, podName, nicName string, ma return nil, nil, "", ErrNoAvailable } pool.V6Free = pool.V6Released - pool.V6Released = NewIPRangeList() + pool.V6Released = NewEmptyIPRangeList() } skipped := make([]IP, 0, len(skippedAddrs)) @@ -314,6 +315,7 @@ func (subnet *Subnet) GetStaticAddress(podName, nicName string, ip IP, mac *stri var v4, v6 bool isAllocated := false subnet.mutex.Lock() + defer subnet.mutex.Unlock() if ip.To4() != nil { v4 = subnet.V4CIDR != nil @@ -355,7 +357,6 @@ func (subnet *Subnet) GetStaticAddress(podName, nicName string, ip IP, mac *stri pool.V6Using.Add(ip) } } - subnet.mutex.Unlock() }() var macStr string @@ -597,18 +598,18 @@ func (s *Subnet) AddOrUpdateIPPool(name string, ips []string) error { defer s.mutex.Unlock() pool := &IPPool{ - V4IPs: NewIPRangeList(), - V6IPs: NewIPRangeList(), - V4Free: NewIPRangeList(), - V6Free: NewIPRangeList(), - V4Available: NewIPRangeList(), - V6Available: NewIPRangeList(), - V4Reserved: NewIPRangeList(), - V6Reserved: NewIPRangeList(), - V4Released: NewIPRangeList(), - V6Released: NewIPRangeList(), - V4Using: NewIPRangeList(), - V6Using: NewIPRangeList(), + V4IPs: NewEmptyIPRangeList(), + V6IPs: NewEmptyIPRangeList(), + V4Free: NewEmptyIPRangeList(), + V6Free: NewEmptyIPRangeList(), + V4Available: NewEmptyIPRangeList(), + V6Available: NewEmptyIPRangeList(), + V4Reserved: NewEmptyIPRangeList(), + V6Reserved: NewEmptyIPRangeList(), + V4Released: NewEmptyIPRangeList(), + V6Released: NewEmptyIPRangeList(), + V4Using: NewEmptyIPRangeList(), + V6Using: NewEmptyIPRangeList(), } var err error @@ -678,8 +679,8 @@ func (s *Subnet) AddOrUpdateIPPool(name string, ips []string) error { defaultPool.V4Available = defaultPool.V4Free.Clone() defaultPool.V6Available = defaultPool.V6Free.Clone() } - defaultPool.V4Released = NewIPRangeList() - defaultPool.V6Released = NewIPRangeList() + defaultPool.V4Released = NewEmptyIPRangeList() + defaultPool.V6Released = NewEmptyIPRangeList() pool.V4Available = pool.V4Free.Clone() pool.V6Available = pool.V6Free.Clone() s.IPPools[name] = pool @@ -712,7 +713,7 @@ func (s *Subnet) RemoveIPPool(name string) { } func (s *Subnet) IPPoolStatistics(ippool string) ( - v4Available, v4Using, v6Available, v6Using float64, + v4Available, v4Using, v6Available, v6Using internal.BigInt, v4AvailableRange, v4UsingRange, v6AvailableRange, v6UsingRange string, ) { s.mutex.Lock() diff --git a/test/unittest/ipam/ip.go b/test/unittest/ipam/ip.go new file mode 100644 index 00000000000..e78901d0193 --- /dev/null +++ b/test/unittest/ipam/ip.go @@ -0,0 +1,102 @@ +package ipam + +import ( + "fmt" + "math/rand" + "net" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + "github.com/kubeovn/kube-ovn/pkg/ipam" +) + +func uint32ToIPv4(n uint32) string { + return fmt.Sprintf("%d.%d.%d.%d", n>>24, n&0xff0000>>16, n&0xff00>>8, n&0xff) +} + +func uint32ToIPv6(n [4]uint32) string { + return fmt.Sprintf( + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + n[0]>>16, n[0]&0xffff, + n[1]>>16, n[1]&0xffff, + n[2]>>16, n[2]&0xffff, + n[3]>>16, n[3]&0xffff, + ) +} + +var _ = ginkgo.Context("[group:IPAM]", func() { + ginkgo.Context("[IP]", func() { + ginkgo.It("IPv4", func() { + n1 := rand.Uint32() + if n1 == 0xffffffff { + n1 -= 1 + } + n2 := n1 + 1 + + ip1Str := uint32ToIPv4(n1) + ip2Str := uint32ToIPv4(n2) + + ip1, err := ipam.NewIP(ip1Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(ip1.String()).To(gomega.Equal(ip1Str)) + ip2, err := ipam.NewIP(ip2Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(ip2.String()).To(gomega.Equal(ip2Str)) + + gomega.Expect(ip1.Equal(ip1)).To(gomega.BeTrue()) + gomega.Expect(ip1.GreaterThan(ip1)).To(gomega.BeFalse()) + gomega.Expect(ip1.LessThan(ip1)).To(gomega.BeFalse()) + + gomega.Expect(ip1.Equal(ip2)).To(gomega.BeFalse()) + gomega.Expect(ip1.GreaterThan(ip2)).To(gomega.BeFalse()) + gomega.Expect(ip1.LessThan(ip2)).To(gomega.BeTrue()) + + gomega.Expect(ip1.Add(1)).To(gomega.Equal(ip2)) + gomega.Expect(ip2.Add(-1)).To(gomega.Equal(ip1)) + gomega.Expect(ip1.Sub(-1)).To(gomega.Equal(ip2)) + gomega.Expect(ip2.Sub(1)).To(gomega.Equal(ip1)) + }) + + ginkgo.It("IPv6", func() { + n1 := [4]uint32{rand.Uint32(), rand.Uint32(), rand.Uint32(), rand.Uint32()} + if n1[0] == 0xffffffff && n1[1] == 0xffffffff && n1[2] == 0xffffffff && n1[3] == 0xffffffff { + n1[3] -= 1 + } + n2 := [4]uint32{n1[0], n1[1], n1[2], n1[3] + 1} + + ip1Str := uint32ToIPv6(n1) + ip2Str := uint32ToIPv6(n2) + + ip1, err := ipam.NewIP(ip1Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ip2, err := ipam.NewIP(ip2Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + ip1Str = net.ParseIP(ip1Str).String() + ip2Str = net.ParseIP(ip2Str).String() + gomega.Expect(ip1.String()).To(gomega.Equal(ip1Str)) + gomega.Expect(ip2.String()).To(gomega.Equal(ip2Str)) + + ip1, err = ipam.NewIP(ip1Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(ip1.String()).To(gomega.Equal(net.ParseIP(ip1Str).String())) + ip2, err = ipam.NewIP(ip2Str) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(ip2.String()).To(gomega.Equal(net.ParseIP(ip2Str).String())) + + gomega.Expect(ip1.Equal(ip1)).To(gomega.BeTrue()) + gomega.Expect(ip1.GreaterThan(ip1)).To(gomega.BeFalse()) + gomega.Expect(ip1.LessThan(ip1)).To(gomega.BeFalse()) + + gomega.Expect(ip1.Equal(ip2)).To(gomega.BeFalse()) + gomega.Expect(ip1.GreaterThan(ip2)).To(gomega.BeFalse()) + gomega.Expect(ip1.LessThan(ip2)).To(gomega.BeTrue()) + + gomega.Expect(ip1.Add(1)).To(gomega.Equal(ip2)) + gomega.Expect(ip2.Add(-1)).To(gomega.Equal(ip1)) + gomega.Expect(ip1.Sub(-1)).To(gomega.Equal(ip2)) + gomega.Expect(ip2.Sub(1)).To(gomega.Equal(ip1)) + }) + }) +}) diff --git a/test/unittest/ipam/ip_range.go b/test/unittest/ipam/ip_range.go new file mode 100644 index 00000000000..20485f5a5c7 --- /dev/null +++ b/test/unittest/ipam/ip_range.go @@ -0,0 +1,116 @@ +package ipam + +import ( + "fmt" + "math/big" + "math/rand" + "net" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + "github.com/kubeovn/kube-ovn/pkg/ipam" +) + +var _ = ginkgo.Context("[group:IPAM]", func() { + ginkgo.Context("[IPRange]", func() { + ginkgo.It("IPv4", func() { + n1, n2 := rand.Uint32(), rand.Uint32() + if n1 > n2 { + n1, n2 = n2, n1 + } + n := n1 + uint32(rand.Int63n(int64(n2-n1+1))) + startStr := uint32ToIPv4(n1) + endStr := uint32ToIPv4(n2) + ipStr := uint32ToIPv4(n) + + start, err := ipam.NewIP(startStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + end, err := ipam.NewIP(endStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ip, err := ipam.NewIP(ipStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + r := ipam.NewIPRange(start, end) + if n1 == n2 { + gomega.Expect(r.String()).To(gomega.Equal(start.String())) + } else { + gomega.Expect(r.String()).To(gomega.Equal(fmt.Sprintf("%s-%s", start.String(), end.String()))) + } + + c := r.Count() + gomega.Expect(c.Int.Cmp(big.NewInt(int64(n2 - n1 + 1)))).To(gomega.Equal(0)) + gomega.Expect(r.Clone().String()).To(gomega.Equal(r.String())) + gomega.Expect(r.Contains(start)).To(gomega.BeTrue()) + gomega.Expect(r.Contains(end)).To(gomega.BeTrue()) + gomega.Expect(r.Contains(ip)).To(gomega.BeTrue()) + if n1 != 0 { + gomega.Expect(r.Contains(start.Sub(1))).To(gomega.BeFalse()) + } + if n2 != 0xffffffff { + gomega.Expect(r.Contains(end.Add(1))).To(gomega.BeFalse()) + } + if n1 != n2 { + gomega.Expect(r.Contains(start.Add(1))).To(gomega.BeTrue()) + gomega.Expect(r.Contains(end.Sub(1))).To(gomega.BeTrue()) + } + }) + + ginkgo.It("IPv6", func() { + n1 := [4]uint32{rand.Uint32(), rand.Uint32(), rand.Uint32(), rand.Uint32()} + n2 := [4]uint32{rand.Uint32(), rand.Uint32(), rand.Uint32(), rand.Uint32()} + for i := 0; i < 4; i++ { + if n1[i] < n2[i] { + break + } + if n1[i] > n2[i] { + n1, n2 = n2, n1 + break + } + } + + var n [4]uint32 + for i := 0; i < 4; i++ { + n[i] = n1[i] + uint32(rand.Int63n(int64(n2[i]-n1[i]+1))) + } + + startStr := uint32ToIPv6(n1) + endStr := uint32ToIPv6(n2) + ipStr := uint32ToIPv6(n) + + start, err := ipam.NewIP(startStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + end, err := ipam.NewIP(endStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ip, err := ipam.NewIP(ipStr) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + r := ipam.NewIPRange(start, end) + if n1 == n2 { + gomega.Expect(r.String()).To(gomega.Equal(start.String())) + } else { + gomega.Expect(r.String()).To(gomega.Equal(fmt.Sprintf("%s-%s", start.String(), end.String()))) + } + + count := r.Count() + expectedCount := big.NewInt(0).Sub(big.NewInt(0).SetBytes(net.ParseIP(endStr).To16()), big.NewInt(0).SetBytes(net.ParseIP(startStr).To16())) + expectedCount.Add(expectedCount, big.NewInt(1)) + + gomega.Expect(count.Int.Cmp(expectedCount)).To(gomega.Equal(0)) + gomega.Expect(r.Clone().String()).To(gomega.Equal(r.String())) + gomega.Expect(r.Contains(start)).To(gomega.BeTrue()) + gomega.Expect(r.Contains(end)).To(gomega.BeTrue()) + gomega.Expect(r.Contains(ip)).To(gomega.BeTrue()) + if n1 != [4]uint32{0, 0, 0, 0} { + gomega.Expect(r.Contains(start.Sub(1))).To(gomega.BeFalse()) + } + if n2 != [4]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff} { + gomega.Expect(r.Contains(end.Add(1))).To(gomega.BeFalse()) + } + if n1 != n2 { + gomega.Expect(r.Contains(start.Add(1))).To(gomega.BeTrue()) + gomega.Expect(r.Contains(end.Sub(1))).To(gomega.BeTrue()) + } + }) + }) +}) diff --git a/test/unittest/ipam/ip_range_list.go b/test/unittest/ipam/ip_range_list.go new file mode 100644 index 00000000000..7732fcb18c8 --- /dev/null +++ b/test/unittest/ipam/ip_range_list.go @@ -0,0 +1,275 @@ +package ipam + +import ( + "fmt" + "math/rand" + "sort" + "strings" + + "github.com/scylladb/go-set/strset" + "github.com/scylladb/go-set/u32set" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + "github.com/kubeovn/kube-ovn/pkg/ipam" +) + +var _ = ginkgo.Context("[group:IPAM]", func() { + ginkgo.Context("[IPRangeList]", func() { + newIP := func(s string) ipam.IP { + ip, err := ipam.NewIP(s) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + return ip + } + + ginkgo.It("Contains", func() { + v, err := ipam.NewIPRangeList( + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.13"), newIP("10.0.0.18"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + gomega.Expect(v.Contains(newIP("10.0.0.4"))).To(gomega.BeFalse()) + gomega.Expect(v.Contains(newIP("10.0.0.5"))).To(gomega.BeTrue()) + gomega.Expect(v.Contains(newIP("10.0.0.6"))).To(gomega.BeFalse()) + + gomega.Expect(v.Contains(newIP("10.0.0.12"))).To(gomega.BeFalse()) + gomega.Expect(v.Contains(newIP("10.0.0.13"))).To(gomega.BeTrue()) + gomega.Expect(v.Contains(newIP("10.0.0.14"))).To(gomega.BeTrue()) + gomega.Expect(v.Contains(newIP("10.0.0.17"))).To(gomega.BeTrue()) + gomega.Expect(v.Contains(newIP("10.0.0.18"))).To(gomega.BeTrue()) + gomega.Expect(v.Contains(newIP("10.0.0.19"))).To(gomega.BeFalse()) + }) + + ginkgo.It("Add", func() { + v, err := ipam.NewIPRangeList( + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.13"), newIP("10.0.0.18"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + gomega.Expect(v.Add(newIP("10.0.0.4"))).To(gomega.BeTrue()) + gomega.Expect(v.Add(newIP("10.0.0.5"))).To(gomega.BeFalse()) + gomega.Expect(v.Add(newIP("10.0.0.6"))).To(gomega.BeTrue()) + + gomega.Expect(v.Add(newIP("10.0.0.12"))).To(gomega.BeTrue()) + gomega.Expect(v.Add(newIP("10.0.0.13"))).To(gomega.BeFalse()) + gomega.Expect(v.Add(newIP("10.0.0.14"))).To(gomega.BeFalse()) + gomega.Expect(v.Add(newIP("10.0.0.17"))).To(gomega.BeFalse()) + gomega.Expect(v.Add(newIP("10.0.0.18"))).To(gomega.BeFalse()) + gomega.Expect(v.Add(newIP("10.0.0.19"))).To(gomega.BeTrue()) + + expected, err := ipam.NewIPRangeList( + newIP("10.0.0.4"), newIP("10.0.0.6"), + newIP("10.0.0.12"), newIP("10.0.0.19"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(v.Equal(expected)).To(gomega.BeTrue()) + }) + + ginkgo.It("Remove", func() { + v, err := ipam.NewIPRangeList( + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.13"), newIP("10.0.0.18"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + gomega.Expect(v.Remove(newIP("10.0.0.4"))).To(gomega.BeFalse()) + gomega.Expect(v.Remove(newIP("10.0.0.5"))).To(gomega.BeTrue()) + gomega.Expect(v.Remove(newIP("10.0.0.6"))).To(gomega.BeFalse()) + + gomega.Expect(v.Remove(newIP("10.0.0.12"))).To(gomega.BeFalse()) + gomega.Expect(v.Remove(newIP("10.0.0.13"))).To(gomega.BeTrue()) + gomega.Expect(v.Remove(newIP("10.0.0.14"))).To(gomega.BeTrue()) + gomega.Expect(v.Remove(newIP("10.0.0.17"))).To(gomega.BeTrue()) + gomega.Expect(v.Remove(newIP("10.0.0.18"))).To(gomega.BeTrue()) + gomega.Expect(v.Remove(newIP("10.0.0.19"))).To(gomega.BeFalse()) + + expected, err := ipam.NewIPRangeList( + newIP("10.0.0.15"), newIP("10.0.0.16"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(v.Equal(expected)).To(gomega.BeTrue()) + }) + + ginkgo.It("Allocate", func() { + v, err := ipam.NewIPRangeList( + newIP("10.0.0.13"), newIP("10.0.0.16"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + ip := v.Allocate(nil) + gomega.Expect(ip).NotTo(gomega.BeNil()) + gomega.Expect(ip.String()).To(gomega.Equal("10.0.0.13")) + + ip = v.Allocate(nil) + gomega.Expect(ip).NotTo(gomega.BeNil()) + gomega.Expect(ip.String()).To(gomega.Equal("10.0.0.14")) + + ip = v.Allocate([]ipam.IP{newIP("10.0.0.15"), newIP("10.0.0.16")}) + gomega.Expect(ip).To(gomega.BeNil()) + }) + + ginkgo.It("Separate", func() { + v1, err := ipam.NewIPRangeList( + newIP("10.0.0.1"), newIP("10.0.0.1"), + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.13"), newIP("10.0.0.18"), + newIP("10.0.0.23"), newIP("10.0.0.28"), + newIP("10.0.0.33"), newIP("10.0.0.38"), + newIP("10.0.0.43"), newIP("10.0.0.48"), + newIP("10.0.0.53"), newIP("10.0.0.58"), + newIP("10.0.0.63"), newIP("10.0.0.68"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + v2, err := ipam.NewIPRangeList( + newIP("10.0.0.1"), newIP("10.0.0.1"), + newIP("10.0.0.11"), newIP("10.0.0.15"), + newIP("10.0.0.17"), newIP("10.0.0.19"), + newIP("10.0.0.23"), newIP("10.0.0.25"), + newIP("10.0.0.27"), newIP("10.0.0.28"), + newIP("10.0.0.33"), newIP("10.0.0.38"), + newIP("10.0.0.42"), newIP("10.0.0.49"), + newIP("10.0.0.53"), newIP("10.0.0.58"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + expected, err := ipam.NewIPRangeList( + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.16"), newIP("10.0.0.16"), + newIP("10.0.0.26"), newIP("10.0.0.26"), + newIP("10.0.0.63"), newIP("10.0.0.68"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + separated := v1.Separate(v2) + gomega.Expect(separated.Equal(expected)).To(gomega.BeTrue()) + }) + + ginkgo.It("Merge", func() { + v1, err := ipam.NewIPRangeList( + newIP("10.0.0.1"), newIP("10.0.0.1"), + newIP("10.0.0.3"), newIP("10.0.0.3"), + newIP("10.0.0.5"), newIP("10.0.0.5"), + newIP("10.0.0.13"), newIP("10.0.0.18"), + newIP("10.0.0.23"), newIP("10.0.0.28"), + newIP("10.0.0.33"), newIP("10.0.0.38"), + newIP("10.0.0.43"), newIP("10.0.0.48"), + newIP("10.0.0.53"), newIP("10.0.0.58"), + newIP("10.0.0.63"), newIP("10.0.0.68"), + newIP("10.0.0.73"), newIP("10.0.0.78"), + newIP("10.0.0.83"), newIP("10.0.0.88"), + newIP("10.0.0.93"), newIP("10.0.0.95"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + v2, err := ipam.NewIPRangeList( + newIP("10.0.0.1"), newIP("10.0.0.1"), + newIP("10.0.0.4"), newIP("10.0.0.4"), + newIP("10.0.0.11"), newIP("10.0.0.15"), + newIP("10.0.0.17"), newIP("10.0.0.19"), + newIP("10.0.0.23"), newIP("10.0.0.25"), + newIP("10.0.0.27"), newIP("10.0.0.28"), + newIP("10.0.0.33"), newIP("10.0.0.38"), + newIP("10.0.0.42"), newIP("10.0.0.49"), + newIP("10.0.0.53"), newIP("10.0.0.58"), + newIP("10.0.0.75"), newIP("10.0.0.85"), + newIP("10.0.0.96"), newIP("10.0.0.98"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + expected, err := ipam.NewIPRangeList( + newIP("10.0.0.1"), newIP("10.0.0.1"), + newIP("10.0.0.3"), newIP("10.0.0.5"), + newIP("10.0.0.11"), newIP("10.0.0.19"), + newIP("10.0.0.23"), newIP("10.0.0.28"), + newIP("10.0.0.33"), newIP("10.0.0.38"), + newIP("10.0.0.42"), newIP("10.0.0.49"), + newIP("10.0.0.53"), newIP("10.0.0.58"), + newIP("10.0.0.63"), newIP("10.0.0.68"), + newIP("10.0.0.73"), newIP("10.0.0.88"), + newIP("10.0.0.93"), newIP("10.0.0.98"), + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + merged := v1.Merge(v2) + gomega.Expect(merged.Equal(expected)).To(gomega.BeTrue()) + }) + + }) + + ginkgo.It("NewIPRangeListFrom", func() { + n := 100 + rand.Intn(50) + set := u32set.NewWithSize(n) + for set.Size() != n { + set.Add(rand.Uint32()) + } + + ints := set.List() + sort.Slice(ints, func(i, j int) bool { return ints[i] < ints[j] }) + + var ips, mergedIPs []string + var expectedCount uint32 + for i := 0; i < len(ints); i++ { + start := uint32ToIPv4(ints[i]) + var merged string + if rand.Int()%2 == 0 && i+1 != len(ints) { + end := uint32ToIPv4(ints[i+1]) + ips = append(ips, fmt.Sprintf("%s..%s", start, end)) + if i != 0 && ints[i] == ints[i-1]+1 { + merged = fmt.Sprintf("%s..%s", strings.Split(mergedIPs[len(mergedIPs)-1], "..")[0], end) + } + expectedCount += ints[i+1] - ints[i] + 1 + i++ + } else { + ips = append(ips, start) + if i != 0 && ints[i] == ints[i-1]+1 { + merged = fmt.Sprintf("%s..%s", strings.Split(mergedIPs[len(mergedIPs)-1], "..")[0], start) + } + expectedCount++ + } + + if merged != "" { + mergedIPs[len(mergedIPs)-1] = merged + } else { + mergedIPs = append(mergedIPs, ips[len(ips)-1]) + } + } + + list, err := ipam.NewIPRangeListFrom(strset.New(ips...).List()...) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(list.Len()).To(gomega.Equal(len(mergedIPs))) + gomega.Expect(list.String()).To(gomega.Equal(strings.ReplaceAll(strings.Join(mergedIPs, ","), "..", "-"))) + + count := list.Count() + gomega.Expect(count.Int64()).To(gomega.Equal(int64(expectedCount))) + + for _, s := range mergedIPs { + fields := strings.Split(s, "..") + start, err := ipam.NewIP(fields[0]) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(list.Contains(start)).To(gomega.BeTrue()) + + end := start + if len(fields) != 1 { + end, err = ipam.NewIP(fields[1]) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(list.Contains(end)).To(gomega.BeTrue()) + } + + if start.String() != "0.0.0.0" { + gomega.Expect(list.Contains(start.Sub(1))).To(gomega.BeFalse()) + } + if end.String() != "255.255.255.255" { + gomega.Expect(list.Contains(end.Add(1))).To(gomega.BeFalse()) + } + + if !start.Equal(end) { + gomega.Expect(list.Contains(start.Add(1))).To(gomega.BeTrue()) + gomega.Expect(list.Contains(end.Sub(1))).To(gomega.BeTrue()) + } + } + }) +}) diff --git a/test/unittest/ipam/ipam.go b/test/unittest/ipam/ipam.go index 23ce7d14e62..7a75b5335c2 100644 --- a/test/unittest/ipam/ipam.go +++ b/test/unittest/ipam/ipam.go @@ -9,7 +9,6 @@ import ( . "github.com/onsi/gomega" "github.com/kubeovn/kube-ovn/pkg/ipam" - "github.com/kubeovn/kube-ovn/pkg/util" ) var _ = Describe("[IPAM]", func() { @@ -38,7 +37,7 @@ var _ = Describe("[IPAM]", func() { // TODO test case use random ip and ipcidr, and input test data should separate from test case - Describe("[IPAM]", func() { + Context("[Subnet]", func() { Context("[IPv4]", func() { It("invalid subnet", func() { im := ipam.NewIPAM() @@ -538,417 +537,4 @@ var _ = Describe("[IPAM]", func() { }) }) }) - - Describe("[IP]", func() { - It("IPv4 operation", func() { - ip1, _ := ipam.NewIP("10.0.0.16") - ip2, _ := ipam.NewIP("10.0.0.17") - - Expect(ip1.Equal(ip1)).To(BeTrue()) - Expect(ip1.GreaterThan(ip1)).To(BeFalse()) - Expect(ip1.LessThan(ip1)).To(BeFalse()) - - Expect(ip1.Equal(ip2)).To(BeFalse()) - Expect(ip1.GreaterThan(ip1)).To(BeFalse()) - Expect(ip1.LessThan(ip2)).To(BeTrue()) - - Expect(ip1.Add(1)).To(Equal(ip2)) - Expect(ip2.Add(-1)).To(Equal(ip1)) - Expect(ip1.Sub(-1)).To(Equal(ip2)) - Expect(ip2.Sub(1)).To(Equal(ip1)) - - first, _ := ipam.NewIP("10.0.0.1") - last, _ := ipam.NewIP("10.0.0.254") - ipr := ipam.NewIPRange(first, last) - Expect(ipr.Contains(ip1)).To(BeTrue()) - Expect(ipr.Contains(ip2)).To(BeTrue()) - - iprList, _ := ipam.NewIPRangeListFrom(fmt.Sprintf("%s..%s", ipr.Start(), ipr.End())) - Expect(iprList.Contains(ip1)).To(BeTrue()) - }) - - It("IPv6 operation", func() { - ip1, _ := ipam.NewIP("fd00::16") - ip2, _ := ipam.NewIP("fd00::17") - - Expect(ip1.Equal(ip1)).To(BeTrue()) - Expect(ip1.GreaterThan(ip1)).To(BeFalse()) - Expect(ip1.LessThan(ip1)).To(BeFalse()) - - Expect(ip1.Equal(ip2)).To(BeFalse()) - Expect(ip1.GreaterThan(ip1)).To(BeFalse()) - Expect(ip1.LessThan(ip2)).To(BeTrue()) - - Expect(ip1.Add(1)).To(Equal(ip2)) - Expect(ip2.Add(-1)).To(Equal(ip1)) - Expect(ip1.Sub(-1)).To(Equal(ip2)) - Expect(ip2.Sub(1)).To(Equal(ip1)) - - first, _ := ipam.NewIP("fd00::01") - last, _ := ipam.NewIP("fd00::ff") - ipr := ipam.NewIPRange(first, last) - Expect(ipr.Contains(ip1)).To(BeTrue()) - Expect(ipr.Contains(ip2)).To(BeTrue()) - - iprList, _ := ipam.NewIPRangeListFrom(fmt.Sprintf("%s..%s", ipr.Start(), ipr.End())) - Expect(iprList.Contains(ip1)).To(BeTrue()) - }) - }) - - Describe("[Subnet]", func() { - Context("[IPv4]", func() { - It("init subnet", func() { - subnet, err := ipam.NewSubnet(subnetName, ipv4CIDR, ipv4ExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - Expect(subnet.Name).To(Equal(subnetName)) - Expect(subnet.IPPools[""].V4Reserved.Len()).To(Equal(len(ipv4ExcludeIPs) - 2)) - Expect(subnet.V4Free.Len()).To(Equal(3)) - expected, _ := ipam.NewIPRangeListFrom( - "10.16.0.2..10.16.0.3", - "10.16.0.5..10.16.0.9", - "10.16.0.24..10.16.255.254", - ) - Expect(subnet.V4Free).To(Equal(expected)) - }) - - It("static allocation", func() { - ip2, _ := ipam.NewIP("10.16.0.2") - ip3, _ := ipam.NewIP("10.16.0.3") - ip20, _ := ipam.NewIP("10.16.0.20") - - subnet, err := ipam.NewSubnet(subnetName, ipv4CIDR, ipv4ExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - - pod1 := "pod1.ns" - pod1Nic1 := "pod1Nic1.ns" - pod1Nic1mac := util.GenerateMac() - - _, _, err = subnet.GetStaticAddress(pod1, pod1Nic1, ip2, &pod1Nic1mac, false, true) - Expect(err).ShouldNot(HaveOccurred()) - - pod2 := "pod2.ns" - pod2Nic1 := "pod2Nic1" - _, _, err = subnet.GetStaticAddress(pod2, pod2Nic1, ip3, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - - pod2Nic2 := "pod2Nic2" - _, _, err = subnet.GetStaticAddress(pod2, pod2Nic2, ip20, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - expected, _ := ipam.NewIPRangeListFrom( - "10.16.0.5..10.16.0.9", - "10.16.0.24..10.16.255.254", - ) - Expect(subnet.V4Free).To(Equal(expected)) - - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.2", pod1)) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.3", pod2)) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.20", pod2)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue(pod1Nic1, ip2)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue(pod2Nic1, ip3)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue(pod2Nic2, ip20)) - Expect(subnet.NicToMac).To(HaveKeyWithValue(pod1Nic1, pod1Nic1mac)) - Expect(subnet.MacToPod).To(HaveKeyWithValue(pod1Nic1mac, pod1)) - - _, _, err = subnet.GetStaticAddress("pod4.ns", "pod4.ns", ip3, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod5.ns", "pod5.ns", ip3, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - ip, _ := ipam.NewIP("10.16.0.121") - _, _, err = subnet.GetStaticAddress("pod6.ns", "pod5.ns", ip, &pod1Nic1mac, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - - subnet.ReleaseAddress(pod1) - subnet.ReleaseAddress(pod2) - Expect(subnet.V4Free).To(Equal(expected)) - - Expect(subnet.V4NicToIP).To(BeEmpty()) - Expect(subnet.V4IPToPod).To(BeEmpty()) - }) - - It("random allocation", func() { - subnet, err := ipam.NewSubnet(subnetName, "10.16.0.0/30", nil) - Expect(err).ShouldNot(HaveOccurred()) - - ip1, _, _, err := subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip1.String()).To(Equal("10.16.0.1")) - ip1, _, _, err = subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip1.String()).To(Equal("10.16.0.1")) - - ip2, _, _, err := subnet.GetRandomAddress("", "pod2.ns", "pod2.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip2.String()).To(Equal("10.16.0.2")) - - _, _, _, err = subnet.GetRandomAddress("", "pod3.ns", "pod3.ns", nil, nil, true) - Expect(err).Should(MatchError(ipam.ErrNoAvailable)) - Expect(subnet.V4Free.Len()).To(Equal(0)) - - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.1", "pod1.ns")) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.2", "pod2.ns")) - ip01, _ := ipam.NewIP("10.16.0.1") - ip02, _ := ipam.NewIP("10.16.0.2") - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod1.ns", ip01)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod2.ns", ip02)) - - subnet.ReleaseAddress("pod1.ns") - subnet.ReleaseAddress("pod2.ns") - Expect(subnet.V4Free.Len()).To(Equal(0)) - expected, _ := ipam.NewIPRangeListFrom("10.16.0.1..10.16.0.2") - Expect(subnet.IPPools[""].V4Released).To(Equal(expected)) - Expect(subnet.V4IPToPod).To(BeEmpty()) - Expect(subnet.V4NicToIP).To(BeEmpty()) - }) - }) - - Context("[IPv6]", func() { - It("init subnet", func() { - subnet, err := ipam.NewSubnet(subnetName, ipv6CIDR, ipv6ExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - Expect(subnet.Name).To(Equal(subnetName)) - Expect(subnet.IPPools[""].V6Reserved.Len()).To(Equal(len(ipv6ExcludeIPs) - 2)) - Expect(subnet.V6Free.Len()).To(Equal(3)) - expected, _ := ipam.NewIPRangeListFrom( - "fd00::2..fd00::3", - "fd00::5..fd00::9", - "fd00::18..fd00::fffe", - ) - Expect(subnet.V6Free).To(Equal(expected)) - }) - - It("static allocation", func() { - ip2, _ := ipam.NewIP("fd00::2") - ip3, _ := ipam.NewIP("fd00::3") - ip14, _ := ipam.NewIP("fd00::14") - ipf9, _ := ipam.NewIP("fd00::f9") - - subnet, err := ipam.NewSubnet(subnetName, ipv6CIDR, ipv6ExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - - pod1 := "pod1.ns" - pod1Nic1 := "pod1Nic1.ns" - pod1Nic1mac := util.GenerateMac() - - _, _, err = subnet.GetStaticAddress(pod1, pod1Nic1, ip2, &pod1Nic1mac, false, true) - Expect(err).ShouldNot(HaveOccurred()) - - pod2 := "pod2.ns" - pod2Nic1 := "pod2Nic1.ns" - - _, _, err = subnet.GetStaticAddress(pod2, pod2Nic1, ip3, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - - pod2Nic2 := "pod2Nic2.ns" - _, _, err = subnet.GetStaticAddress(pod2, pod2Nic2, ip14, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - expected, _ := ipam.NewIPRangeListFrom( - "fd00::5..fd00::9", - "fd00::18..fd00::fffe", - ) - Expect(subnet.V6Free).To(Equal(expected)) - - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::2", pod1)) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::3", pod2)) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::14", pod2)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue(pod1Nic1, ip2)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue(pod2Nic1, ip3)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue(pod2Nic2, ip14)) - Expect(subnet.NicToMac).To(HaveKeyWithValue(pod1Nic1, pod1Nic1mac)) - Expect(subnet.MacToPod).To(HaveKeyWithValue(pod1Nic1mac, pod1)) - - _, _, err = subnet.GetStaticAddress("pod4.ns", "pod4.ns", ip3, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod5.ns", "pod5.ns", ip3, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod6.ns", "pod5.ns", ipf9, &pod1Nic1mac, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - - subnet.ReleaseAddress(pod1) - subnet.ReleaseAddress(pod2) - Expect(subnet.V6Free).To(Equal(expected)) - - Expect(subnet.V6NicToIP).To(BeEmpty()) - Expect(subnet.V6IPToPod).To(BeEmpty()) - }) - - It("random allocation", func() { - ip01, _ := ipam.NewIP("fd00::1") - ip02, _ := ipam.NewIP("fd00::2") - - subnet, err := ipam.NewSubnet(subnetName, "fd00::/126", nil) - Expect(err).ShouldNot(HaveOccurred()) - - _, ip1, _, err := subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip1.String()).To(Equal("fd00::1")) - _, ip1, _, err = subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip1.String()).To(Equal("fd00::1")) - - _, ip2, _, err := subnet.GetRandomAddress("", "pod2.ns", "pod2.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ip2.String()).To(Equal("fd00::2")) - - _, _, _, err = subnet.GetRandomAddress("", "pod3.ns", "pod3.ns", nil, nil, true) - Expect(err).Should(MatchError(ipam.ErrNoAvailable)) - Expect(subnet.V6Free.Len()).To(Equal(0)) - - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::1", "pod1.ns")) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::2", "pod2.ns")) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod1.ns", ip01)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod2.ns", ip02)) - - subnet.ReleaseAddress("pod1.ns") - subnet.ReleaseAddress("pod2.ns") - Expect(subnet.V6Free.Len()).To(Equal(0)) - expected, _ := ipam.NewIPRangeListFrom("fd00::1..fd00::2") - Expect(subnet.IPPools[""].V6Released).To(Equal(expected)) - Expect(subnet.V6IPToPod).To(BeEmpty()) - Expect(subnet.V6NicToIP).To(BeEmpty()) - }) - }) - - Context("[DualStack]", func() { - It("init subnet", func() { - subnet, err := ipam.NewSubnet(subnetName, dualCIDR, dualExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - Expect(subnet.Name).To(Equal(subnetName)) - Expect(subnet.V4Reserved.Len()).To(Equal(len(ipv4ExcludeIPs) - 2)) - Expect(subnet.V4Free.Len()).To(Equal(3)) - expectedV4, _ := ipam.NewIPRangeListFrom( - "10.16.0.2..10.16.0.3", - "10.16.0.5..10.16.0.9", - "10.16.0.24..10.16.255.254", - ) - Expect(subnet.V4Free).To(Equal(expectedV4)) - Expect(subnet.IPPools[""].V6Reserved.Len()).To(Equal(len(ipv6ExcludeIPs) - 2)) - Expect(subnet.V6Free.Len()).To(Equal(3)) - expectedV6, _ := ipam.NewIPRangeListFrom( - "fd00::2..fd00::3", - "fd00::5..fd00::9", - "fd00::18..fd00::fffe", - ) - Expect(subnet.V6Free).To(Equal(expectedV6)) - }) - - It("static allocation", func() { - ip2V4, _ := ipam.NewIP("10.16.0.2") - ip3V4, _ := ipam.NewIP("10.16.0.3") - ip20V4, _ := ipam.NewIP("10.16.0.20") - ip2V6, _ := ipam.NewIP("fd00::2") - ip3V6, _ := ipam.NewIP("fd00::3") - ip14V6, _ := ipam.NewIP("fd00::14") - - subnet, err := ipam.NewSubnet(subnetName, dualCIDR, dualExcludeIPs) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod1.ns", "pod1.ns", ip2V4, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod1.ns", "pod1.ns", ip2V6, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod2.ns", "pod2.ns", ip3V4, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod2.ns", "pod2.ns", ip3V6, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod3.ns", "pod3.ns", ip20V4, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - _, _, err = subnet.GetStaticAddress("pod3.ns", "pod3.ns", ip14V6, nil, false, true) - Expect(err).ShouldNot(HaveOccurred()) - - expectedV4, _ := ipam.NewIPRangeListFrom( - "10.16.0.5..10.16.0.9", - "10.16.0.24..10.16.255.254", - ) - Expect(subnet.V4Free).To(Equal(expectedV4)) - expectedV6, _ := ipam.NewIPRangeListFrom( - "fd00::5..fd00::9", - "fd00::18..fd00::fffe", - ) - Expect(subnet.V6Free).To(Equal(expectedV6)) - - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.2", "pod1.ns")) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.3", "pod2.ns")) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.20", "pod3.ns")) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod1.ns", ip2V4)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod2.ns", ip3V4)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod3.ns", ip20V4)) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::2", "pod1.ns")) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::3", "pod2.ns")) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::14", "pod3.ns")) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod1.ns", ip2V6)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod2.ns", ip3V6)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod3.ns", ip14V6)) - - _, _, err = subnet.GetStaticAddress("pod4.ns", "pod4.ns", ip3V4, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod4.ns", "pod4.ns", ip3V6, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod5.ns", "pod5.ns", ip3V4, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - _, _, err = subnet.GetStaticAddress("pod1.ns", "pod5.ns", ip3V6, nil, false, true) - Expect(err).Should(MatchError(ipam.ErrConflict)) - - subnet.ReleaseAddress("pod1.ns") - subnet.ReleaseAddress("pod2.ns") - subnet.ReleaseAddress("pod3.ns") - Expect(subnet.V4Free).To(Equal(expectedV4)) - Expect(subnet.V6Free).To(Equal(expectedV6)) - - Expect(subnet.V4NicToIP).To(BeEmpty()) - Expect(subnet.V4IPToPod).To(BeEmpty()) - Expect(subnet.V6NicToIP).To(BeEmpty()) - Expect(subnet.V6IPToPod).To(BeEmpty()) - }) - - It("random allocation", func() { - ip1V4, _ := ipam.NewIP("10.16.0.1") - ip2V4, _ := ipam.NewIP("10.16.0.2") - ip1V6, _ := ipam.NewIP("fd00::1") - ip2V6, _ := ipam.NewIP("fd00::2") - - subnet, err := ipam.NewSubnet(subnetName, "10.16.0.0/30,fd00::/126", nil) - Expect(err).ShouldNot(HaveOccurred()) - - ipv4, ipv6, _, err := subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ipv4.String()).To(Equal("10.16.0.1")) - Expect(ipv6.String()).To(Equal("fd00::1")) - ipv4, ipv6, _, err = subnet.GetRandomAddress("", "pod1.ns", "pod1.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ipv4.String()).To(Equal("10.16.0.1")) - Expect(ipv6.String()).To(Equal("fd00::1")) - - ipv4, ipv6, _, err = subnet.GetRandomAddress("", "pod2.ns", "pod2.ns", nil, nil, true) - Expect(err).ShouldNot(HaveOccurred()) - Expect(ipv4.String()).To(Equal("10.16.0.2")) - Expect(ipv6.String()).To(Equal("fd00::2")) - - _, _, _, err = subnet.GetRandomAddress("", "pod3.ns", "pod3.ns", nil, nil, true) - Expect(err).Should(MatchError(ipam.ErrNoAvailable)) - Expect(subnet.V4Free.Len()).To(Equal(0)) - Expect(subnet.V6Free.Len()).To(Equal(0)) - - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.1", "pod1.ns")) - Expect(subnet.V4IPToPod).To(HaveKeyWithValue("10.16.0.2", "pod2.ns")) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod1.ns", ip1V4)) - Expect(subnet.V4NicToIP).To(HaveKeyWithValue("pod2.ns", ip2V4)) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::1", "pod1.ns")) - Expect(subnet.V6IPToPod).To(HaveKeyWithValue("fd00::2", "pod2.ns")) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod1.ns", ip1V6)) - Expect(subnet.V6NicToIP).To(HaveKeyWithValue("pod2.ns", ip2V6)) - - subnet.ReleaseAddress("pod1.ns") - subnet.ReleaseAddress("pod2.ns") - Expect(subnet.V4Free.Len()).To(Equal(0)) - expected, _ := ipam.NewIPRangeListFrom("10.16.0.1..10.16.0.2") - Expect(subnet.IPPools[""].V4Released).To(Equal(expected)) - Expect(subnet.V6Free.Len()).To(Equal(0)) - expected, _ = ipam.NewIPRangeListFrom("fd00::1..fd00::2") - Expect(subnet.IPPools[""].V6Released).To(Equal(expected)) - Expect(subnet.V4IPToPod).To(BeEmpty()) - Expect(subnet.V4NicToIP).To(BeEmpty()) - Expect(subnet.V6IPToPod).To(BeEmpty()) - Expect(subnet.V6NicToIP).To(BeEmpty()) - }) - }) - }) }) diff --git a/yamls/crd.yaml b/yamls/crd.yaml index ab70b95ec32..baba1253a6b 100644 --- a/yamls/crd.yaml +++ b/yamls/crd.yaml @@ -2090,9 +2090,6 @@ spec: - name: Subnet type: string jsonPath: .spec.subnet - - name: Protocol - type: string - jsonPath: .spec.protocol - name: IPs type: string jsonPath: .spec.ips @@ -2125,12 +2122,6 @@ spec: x-kubernetes-list-type: set items: type: string - protocol: - type: string - enum: - - IPv4 - - IPv6 - - Dual ips: type: array minItems: 1