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  }