@@ -16,6 +16,7 @@ package v1alpha1
1616import (
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