Skip to content

Commit e459679

Browse files
authored
Merge pull request #184 from adrianchiris/skip-fully-excluded-ranges
feat: skip fully excluded ranges
2 parents 14eaf64 + 735d3d6 commit e459679

File tree

13 files changed

+1832
-175
lines changed

13 files changed

+1832
-175
lines changed

api/v1alpha1/helpers.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package v1alpha1
1616
import (
1717
"math/big"
1818
"net"
19+
"sort"
1920

2021
"github.com/Mellanox/nvidia-k8s-ipam/pkg/ip"
2122
)
@@ -70,3 +71,156 @@ func GetPossibleIPCount(subnet *net.IPNet) *big.Int {
7071
}
7172
return ipCount
7273
}
74+
75+
// ExcludeIndexRangeToExcludeRange converts index-based exclusions to IP-based exclusions.
76+
// IPs are calculated as offset from the provided startIP.
77+
func ExcludeIndexRangeToExcludeRange(excludeIndexRanges []ExcludeIndexRange, startIP string) []ExcludeRange {
78+
// startIP, endIP and ranges are assumed to be valid and are checked with the pool validate function.
79+
if len(excludeIndexRanges) == 0 {
80+
return nil
81+
}
82+
83+
rangeStart := net.ParseIP(startIP)
84+
if rangeStart == nil {
85+
return nil
86+
}
87+
88+
excludeRanges := make([]ExcludeRange, 0, len(excludeIndexRanges))
89+
for _, excludeIndexRange := range excludeIndexRanges {
90+
excludeRangeStart := ip.NextIPWithOffset(rangeStart, int64(excludeIndexRange.StartIndex))
91+
if excludeRangeStart == nil {
92+
continue
93+
}
94+
excludeRangeEnd := ip.NextIPWithOffset(rangeStart, int64(excludeIndexRange.EndIndex))
95+
if excludeRangeEnd == nil {
96+
continue
97+
}
98+
99+
excludeRanges = append(excludeRanges,
100+
ExcludeRange{StartIP: excludeRangeStart.String(), EndIP: excludeRangeEnd.String()})
101+
}
102+
103+
return excludeRanges
104+
}
105+
106+
// ClampExcludeRange clamps the exclusion range to the provided startIP and endIP range.
107+
// Entries outside the range between startIP and endIP (included) are omitted from the result.
108+
// Entries within the range are clamped to the start and end ip if they end up partly outside the range.
109+
func ClampExcludeRanges(excludeRanges []ExcludeRange, startIP string, endIP string) []ExcludeRange {
110+
// startIP, endIP and ranges are assumed to be valid and are checked with the pool validate function.
111+
if len(excludeRanges) == 0 {
112+
return nil
113+
}
114+
115+
rangeStart := net.ParseIP(startIP)
116+
rangeEnd := net.ParseIP(endIP)
117+
if rangeStart == nil || rangeEnd == nil {
118+
return nil
119+
}
120+
121+
clampedRanges := make([]ExcludeRange, 0)
122+
for _, excludeRange := range excludeRanges {
123+
excludeRangeStart := net.ParseIP(excludeRange.StartIP)
124+
excludeRangeEnd := net.ParseIP(excludeRange.EndIP)
125+
126+
if excludeRangeStart == nil || excludeRangeEnd == nil {
127+
continue
128+
}
129+
130+
// if exclude range start ip is after range end ip or exclude range end ip is before range start ip, skip
131+
if ip.Cmp(excludeRangeStart, rangeEnd) > 0 || ip.Cmp(excludeRangeEnd, rangeStart) < 0 {
132+
continue
133+
}
134+
135+
// clamp to the start and end ip
136+
if ip.Cmp(excludeRangeStart, rangeStart) < 0 {
137+
excludeRangeStart = rangeStart
138+
}
139+
if ip.Cmp(excludeRangeEnd, rangeEnd) > 0 {
140+
excludeRangeEnd = rangeEnd
141+
}
142+
143+
clampedRanges = append(clampedRanges,
144+
ExcludeRange{StartIP: excludeRangeStart.String(), EndIP: excludeRangeEnd.String()})
145+
}
146+
147+
return clampedRanges
148+
}
149+
150+
// MergeExcludeRanges merges overlapping exclude ranges into a single range.
151+
// it returns a slice of non-overlapping exclude ranges.
152+
func MergeExcludeRanges(excludeRanges []ExcludeRange) []ExcludeRange {
153+
// excludeRanges are assumed to be valid and are checked with the pool validate function.
154+
if len(excludeRanges) == 0 {
155+
return nil
156+
}
157+
158+
// sort by start IP
159+
type excludeRangeIP struct {
160+
startIP net.IP
161+
endIP net.IP
162+
}
163+
164+
excludeRangeIPs := make([]excludeRangeIP, 0, len(excludeRanges))
165+
for _, excludeRange := range excludeRanges {
166+
start := net.ParseIP(excludeRange.StartIP)
167+
end := net.ParseIP(excludeRange.EndIP)
168+
if start == nil || end == nil {
169+
continue
170+
}
171+
172+
excludeRangeIPs = append(excludeRangeIPs, excludeRangeIP{startIP: start, endIP: end})
173+
}
174+
175+
if len(excludeRangeIPs) == 0 {
176+
return nil
177+
}
178+
179+
sort.Slice(excludeRangeIPs, func(i, j int) bool {
180+
return ip.Cmp(excludeRangeIPs[i].startIP, excludeRangeIPs[j].startIP) < 0
181+
})
182+
183+
// merge ranges
184+
current := excludeRangeIPs[0]
185+
mergedRangesIPs := make([]excludeRangeIP, 0)
186+
for i := 1; i < len(excludeRangeIPs); i++ {
187+
next := excludeRangeIPs[i]
188+
189+
// case1: current range overlaps with next range, merge them
190+
// case2: next range starts immediately after current range ends, merge them
191+
if ip.Cmp(current.endIP, next.startIP) >= 0 ||
192+
ip.Cmp(ip.NextIP(current.endIP), next.startIP) == 0 {
193+
if ip.Cmp(current.endIP, next.endIP) < 0 {
194+
current.endIP = next.endIP
195+
}
196+
} else {
197+
mergedRangesIPs = append(mergedRangesIPs, excludeRangeIP{startIP: current.startIP, endIP: current.endIP})
198+
current = next
199+
}
200+
}
201+
// add the last merged element
202+
mergedRangesIPs = append(mergedRangesIPs, excludeRangeIP{startIP: current.startIP, endIP: current.endIP})
203+
204+
// convert back to ExcludeRange
205+
mergedRanges := make([]ExcludeRange, 0, len(mergedRangesIPs))
206+
for _, mergedRangeIP := range mergedRangesIPs {
207+
mergedRanges = append(mergedRanges,
208+
ExcludeRange{StartIP: mergedRangeIP.startIP.String(), EndIP: mergedRangeIP.endIP.String()})
209+
}
210+
return mergedRanges
211+
}
212+
213+
// Contains checks if the given IP is contained in exclude range.
214+
func (er *ExcludeRange) Contains(ipp net.IP) bool {
215+
if ipp == nil {
216+
return false
217+
}
218+
219+
start := net.ParseIP(er.StartIP)
220+
end := net.ParseIP(er.EndIP)
221+
if start == nil || end == nil {
222+
return false
223+
}
224+
225+
return ip.Cmp(start, ipp) <= 0 && ip.Cmp(end, ipp) >= 0
226+
}

0 commit comments

Comments
 (0)