kubesphere.io/api@v0.0.0-20231107125330-c9a03957060c/network/v1alpha1/ipamblock_types.go (about) 1 /* 2 Copyright 2020 The KubeSphere authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package v1alpha1 18 19 import ( 20 "fmt" 21 "math/big" 22 "reflect" 23 "strings" 24 25 "github.com/projectcalico/calico/libcalico-go/lib/names" 26 cnet "github.com/projectcalico/calico/libcalico-go/lib/net" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 ) 29 30 const ( 31 ResourceKindIPAMBlock = "IPAMBlock" 32 ResourceSingularIPAMBlock = "ipamblock" 33 ResourcePluralIPAMBlock = "ipamblocks" 34 35 IPAMBlockAttributePod = "pod" 36 IPAMBlockAttributeVm = "vm" 37 IPAMBlockAttributeWorkloadType = "workload-type" 38 IPAMBlockAttributeNamespace = "namespace" 39 IPAMBlockAttributeWorkspace = "workspace" 40 IPAMBlockAttributeNode = "node" 41 IPAMBlockAttributePool = "pool-name" 42 IPAMBlockAttributeType = "pool-type" 43 44 ReservedHandle = "kubesphere-reserved-handle" 45 ReservedNote = "kubesphere reserved" 46 ) 47 48 // +genclient 49 // +genclient:nonNamespaced 50 // +kubebuilder:object:root=true 51 // +k8s:openapi-gen=true 52 // +kubebuilder:resource:scope=Cluster 53 type IPAMBlock struct { 54 metav1.TypeMeta `json:",inline"` 55 metav1.ObjectMeta `json:"metadata,omitempty"` 56 // Specification of the IPAMBlock. 57 Spec IPAMBlockSpec `json:"spec,omitempty"` 58 } 59 60 // IPAMBlockSpec contains the specification for an IPAMBlock resource. 61 type IPAMBlockSpec struct { 62 ID uint32 `json:"id"` 63 CIDR string `json:"cidr"` 64 Allocations []*int `json:"allocations"` 65 Unallocated []int `json:"unallocated"` 66 Attributes []AllocationAttribute `json:"attributes"` 67 Deleted bool `json:"deleted"` 68 } 69 70 type AllocationAttribute struct { 71 AttrPrimary string `json:"handle_id,omitempty"` 72 AttrSecondary map[string]string `json:"secondary,omitempty"` 73 } 74 75 // +kubebuilder:object:root=true 76 // +genclient:nonNamespaced 77 type IPAMBlockList struct { 78 metav1.TypeMeta `json:",inline"` 79 metav1.ListMeta `json:"metadata"` 80 Items []IPAMBlock `json:"items"` 81 } 82 83 // The caller needs to check that the returned slice length is correct. 84 func (b *IPAMBlock) AutoAssign( 85 num int, handleID string, attrs map[string]string) []cnet.IPNet { 86 87 // Walk the allocations until we find enough addresses. 88 ordinals := []int{} 89 for len(b.Spec.Unallocated) > 0 && len(ordinals) < num { 90 ordinals = append(ordinals, b.Spec.Unallocated[0]) 91 b.Spec.Unallocated = b.Spec.Unallocated[1:] 92 } 93 94 // Create slice of IPs and perform the allocations. 95 ips := []cnet.IPNet{} 96 ip, mask, _ := cnet.ParseCIDR(b.Spec.CIDR) 97 for _, o := range ordinals { 98 attrIndex := b.findOrAddAttribute(handleID, attrs) 99 b.Spec.Allocations[o] = &attrIndex 100 ipNets := cnet.IPNet(*mask) 101 ipNets.IP = cnet.IncrementIP(*ip, big.NewInt(int64(o))).IP 102 ips = append(ips, ipNets) 103 } 104 105 return ips 106 } 107 108 func (b *IPAMBlock) String() string { 109 return fmt.Sprintf("%d-%s", b.Spec.ID, b.Spec.CIDR) 110 } 111 112 func (b *IPAMBlock) ID() uint32 { 113 return b.Spec.ID 114 } 115 116 func (b *IPAMBlock) BlockName() string { 117 _, cidr, _ := cnet.ParseCIDR(b.Spec.CIDR) 118 return fmt.Sprintf("%d-%s", b.ID(), names.CIDRToName(*cidr)) 119 } 120 121 // Get number of addresses covered by the block 122 func (b *IPAMBlock) NumAddresses() int { 123 _, cidr, _ := cnet.ParseCIDR(b.Spec.CIDR) 124 ones, size := cidr.Mask.Size() 125 numAddresses := 1 << uint(size-ones) 126 return numAddresses 127 } 128 129 // Find the ordinal (i.e. how far into the block) a given IP lies. Returns an error if the IP is outside the block. 130 func (b *IPAMBlock) IPToOrdinal(ip cnet.IP) (int, error) { 131 netIP, _, _ := cnet.ParseCIDR(b.Spec.CIDR) 132 ipAsInt := cnet.IPToBigInt(ip) 133 baseInt := cnet.IPToBigInt(*netIP) 134 ord := big.NewInt(0).Sub(ipAsInt, baseInt).Int64() 135 if ord < 0 || ord >= int64(b.NumAddresses()) { 136 return 0, fmt.Errorf("IP %s not in block %d-%s", ip, b.Spec.ID, b.Spec.CIDR) 137 } 138 return int(ord), nil 139 } 140 141 func (b *IPAMBlock) NumFreeAddresses() int { 142 return len(b.Spec.Unallocated) 143 } 144 145 // empty returns true if the block has released all of its assignable addresses, 146 // and returns false if any assignable addresses are in use. 147 func (b *IPAMBlock) Empty() bool { 148 return b.containsOnlyReservedIPs() 149 } 150 151 func (b *IPAMBlock) MarkDeleted() { 152 b.Spec.Deleted = true 153 } 154 155 func (b *IPAMBlock) IsDeleted() bool { 156 return b.Spec.Deleted 157 } 158 159 // containsOnlyReservedIPs returns true if the block is empty excepted for 160 // expected "reserved" IP addresses. 161 func (b *IPAMBlock) containsOnlyReservedIPs() bool { 162 for _, attrIdx := range b.Spec.Allocations { 163 if attrIdx == nil { 164 continue 165 } 166 attrs := b.Spec.Attributes[*attrIdx] 167 if strings.ToLower(attrs.AttrPrimary) != ReservedHandle { 168 return false 169 } 170 } 171 return true 172 } 173 174 func (b *IPAMBlock) NumReservedAddresses() int { 175 sum := 0 176 for _, attrIdx := range b.Spec.Allocations { 177 if attrIdx == nil { 178 continue 179 } 180 attrs := b.Spec.Attributes[*attrIdx] 181 if strings.ToLower(attrs.AttrPrimary) == ReservedHandle { 182 sum += 1 183 } 184 } 185 return sum 186 } 187 188 func (b IPAMBlock) attributeIndexesByHandle(handleID string) []int { 189 indexes := []int{} 190 for i, attr := range b.Spec.Attributes { 191 if attr.AttrPrimary == handleID { 192 indexes = append(indexes, i) 193 } 194 } 195 return indexes 196 } 197 198 func (b *IPAMBlock) deleteAttributes(delIndexes, ordinals []int) { 199 newIndexes := make([]*int, len(b.Spec.Attributes)) 200 newAttrs := []AllocationAttribute{} 201 y := 0 // Next free slot in the new attributes list. 202 for x := range b.Spec.Attributes { 203 if !intInSlice(x, delIndexes) { 204 // Attribute at x is not being deleted. Build a mapping 205 // of old attribute index (x) to new attribute index (y). 206 newIndex := y 207 newIndexes[x] = &newIndex 208 y += 1 209 newAttrs = append(newAttrs, b.Spec.Attributes[x]) 210 } 211 } 212 b.Spec.Attributes = newAttrs 213 214 // Update attribute indexes for all allocations in this block. 215 for i := 0; i < b.NumAddresses(); i++ { 216 if b.Spec.Allocations[i] != nil { 217 // Get the new index that corresponds to the old index 218 // and update the allocation. 219 newIndex := newIndexes[*b.Spec.Allocations[i]] 220 b.Spec.Allocations[i] = newIndex 221 } 222 } 223 } 224 225 func (b *IPAMBlock) ReleaseByHandle(handleID string) int { 226 attrIndexes := b.attributeIndexesByHandle(handleID) 227 if len(attrIndexes) == 0 { 228 // Nothing to release. 229 return 0 230 } 231 232 // There are addresses to release. 233 ordinals := []int{} 234 var o int 235 for o = 0; o < b.NumAddresses(); o++ { 236 // Only check allocated ordinals. 237 if b.Spec.Allocations[o] != nil && intInSlice(*b.Spec.Allocations[o], attrIndexes) { 238 // Release this ordinal. 239 ordinals = append(ordinals, o) 240 } 241 } 242 243 // Clean and reorder attributes. 244 b.deleteAttributes(attrIndexes, ordinals) 245 246 // Release the addresses. 247 for _, o := range ordinals { 248 b.Spec.Allocations[o] = nil 249 b.Spec.Unallocated = append(b.Spec.Unallocated, o) 250 } 251 return len(ordinals) 252 } 253 254 func (b *IPAMBlock) findOrAddAttribute(handleID string, attrs map[string]string) int { 255 attr := AllocationAttribute{handleID, attrs} 256 for idx, existing := range b.Spec.Attributes { 257 if reflect.DeepEqual(attr, existing) { 258 return idx 259 } 260 } 261 262 // Does not exist - add it. 263 attrIndex := len(b.Spec.Attributes) 264 b.Spec.Attributes = append(b.Spec.Attributes, attr) 265 return attrIndex 266 } 267 268 func intInSlice(searchInt int, slice []int) bool { 269 for _, v := range slice { 270 if v == searchInt { 271 return true 272 } 273 } 274 return false 275 } 276 277 // This just initializes the data structure and does not call the api to create 278 func NewBlock(pool *IPPool, cidr cnet.IPNet, rsvdAttr *ReservedAttr) *IPAMBlock { 279 b := IPAMBlock{} 280 281 b.Labels = map[string]string{ 282 IPPoolNameLabel: pool.Name, 283 } 284 b.Spec.CIDR = cidr.String() 285 b.Spec.ID = pool.ID() 286 b.Name = b.BlockName() 287 288 numAddresses := b.NumAddresses() 289 b.Spec.Allocations = make([]*int, numAddresses) 290 b.Spec.Unallocated = make([]int, numAddresses) 291 292 // Initialize unallocated ordinals. 293 for i := 0; i < numAddresses; i++ { 294 b.Spec.Unallocated[i] = i 295 } 296 297 if rsvdAttr != nil { 298 // Reserve IPs based on host reserved attributes. 299 // For example, with windows OS, the following IP addresses of the block are 300 // reserved. This is done by pre-allocating them during initialization 301 // time only. 302 // IPs : x.0, x.1, x.2 and x.bcastAddr (e.g. x.255 for /24 subnet) 303 304 // nil attributes 305 attrs := make(map[string]string) 306 attrs["note"] = rsvdAttr.Note 307 handleID := rsvdAttr.Handle 308 b.Spec.Unallocated = b.Spec.Unallocated[rsvdAttr.StartOfBlock : numAddresses-rsvdAttr.EndOfBlock] 309 attrIndex := len(b.Spec.Attributes) 310 for i := 0; i < rsvdAttr.StartOfBlock; i++ { 311 b.Spec.Allocations[i] = &attrIndex 312 } 313 for i := 1; i <= rsvdAttr.EndOfBlock; i++ { 314 b.Spec.Allocations[numAddresses-i] = &attrIndex 315 } 316 317 // Create slice of IPs and perform the allocations. 318 attr := AllocationAttribute{ 319 AttrPrimary: handleID, 320 AttrSecondary: attrs, 321 } 322 b.Spec.Attributes = append(b.Spec.Attributes, attr) 323 } 324 325 return &b 326 } 327 328 type ReservedAttr struct { 329 // Number of addresses reserved from start of the block. 330 StartOfBlock int 331 332 // Number of addresses reserved from end of the block. 333 EndOfBlock int 334 335 // Handle for reserved addresses. 336 Handle string 337 338 // A description about the reserves. 339 Note string 340 }