temp commit
This commit is contained in:
408
vendor/github.com/projectcalico/libcalico-go/lib/ipam/ipam_block.go
generated
vendored
Normal file
408
vendor/github.com/projectcalico/libcalico-go/lib/ipam/ipam_block.go
generated
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
// Copyright (c) 2016-2019 Tigera, Inc. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ipam
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/projectcalico/libcalico-go/lib/apis/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/projectcalico/libcalico-go/lib/backend/model"
|
||||
cnet "github.com/projectcalico/libcalico-go/lib/net"
|
||||
)
|
||||
|
||||
// Wrap the backend AllocationBlock struct so that we can
|
||||
// attach methods to it.
|
||||
type allocationBlock struct {
|
||||
*model.AllocationBlock
|
||||
}
|
||||
|
||||
func newBlock(cidr cnet.IPNet) allocationBlock {
|
||||
ones, size := cidr.Mask.Size()
|
||||
numAddresses := 1 << uint(size-ones)
|
||||
b := model.AllocationBlock{}
|
||||
b.Allocations = make([]*int, numAddresses)
|
||||
b.Unallocated = make([]int, numAddresses)
|
||||
b.StrictAffinity = false
|
||||
b.CIDR = cidr
|
||||
|
||||
// Initialize unallocated ordinals.
|
||||
for i := 0; i < numAddresses; i++ {
|
||||
b.Unallocated[i] = i
|
||||
}
|
||||
|
||||
return allocationBlock{&b}
|
||||
}
|
||||
|
||||
func (b *allocationBlock) autoAssign(
|
||||
num int, handleID *string, host string, attrs map[string]string, affinityCheck bool) ([]cnet.IPNet, error) {
|
||||
|
||||
// Determine if we need to check for affinity.
|
||||
checkAffinity := b.StrictAffinity || affinityCheck
|
||||
if checkAffinity && b.Affinity != nil && !hostAffinityMatches(host, b.AllocationBlock) {
|
||||
// Affinity check is enabled but the host does not match - error.
|
||||
s := fmt.Sprintf("Block affinity (%s) does not match provided (%s)", *b.Affinity, host)
|
||||
return nil, errors.New(s)
|
||||
} else if b.Affinity == nil {
|
||||
log.Warnf("Attempting to assign IPs from block with no affinity: %v", b)
|
||||
if checkAffinity {
|
||||
// If we're checking strict affinity, we can't assign from a block with no affinity.
|
||||
return nil, fmt.Errorf("Attempt to assign from block %v with no affinity", b.CIDR)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the allocations until we find enough addresses.
|
||||
ordinals := []int{}
|
||||
for len(b.Unallocated) > 0 && len(ordinals) < num {
|
||||
ordinals = append(ordinals, b.Unallocated[0])
|
||||
b.Unallocated = b.Unallocated[1:]
|
||||
}
|
||||
|
||||
// Create slice of IPs and perform the allocations.
|
||||
ips := []cnet.IPNet{}
|
||||
_, mask, _ := cnet.ParseCIDR(b.CIDR.String())
|
||||
for _, o := range ordinals {
|
||||
attrIndex := b.findOrAddAttribute(handleID, attrs)
|
||||
b.Allocations[o] = &attrIndex
|
||||
ipNets := cnet.IPNet(*mask)
|
||||
ipNets.IP = cnet.IncrementIP(cnet.IP{b.CIDR.IP}, big.NewInt(int64(o))).IP
|
||||
ips = append(ips, ipNets)
|
||||
}
|
||||
|
||||
log.Debugf("Block %s returned ips: %v", b.CIDR.String(), ips)
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func (b *allocationBlock) assign(address cnet.IP, handleID *string, attrs map[string]string, host string) error {
|
||||
if b.StrictAffinity && b.Affinity != nil && !hostAffinityMatches(host, b.AllocationBlock) {
|
||||
// Affinity check is enabled but the host does not match - error.
|
||||
return errors.New("Block host affinity does not match")
|
||||
} else if b.Affinity == nil {
|
||||
log.Warnf("Attempting to assign IP from block with no affinity: %v", b)
|
||||
if b.StrictAffinity {
|
||||
// If we're checking strict affinity, we can't assign from a block with no affinity.
|
||||
return fmt.Errorf("Attempt to assign from block %v with no affinity", b.CIDR)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to an ordinal.
|
||||
ordinal, err := b.IPToOrdinal(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if already allocated.
|
||||
if b.Allocations[ordinal] != nil {
|
||||
return errors.New("Address already assigned in block")
|
||||
}
|
||||
|
||||
// Set up attributes.
|
||||
attrIndex := b.findOrAddAttribute(handleID, attrs)
|
||||
b.Allocations[ordinal] = &attrIndex
|
||||
|
||||
// Remove from unallocated.
|
||||
for i, unallocated := range b.Unallocated {
|
||||
if unallocated == ordinal {
|
||||
b.Unallocated = append(b.Unallocated[:i], b.Unallocated[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hostAffinityMatches checks if the provided host matches the provided affinity.
|
||||
func hostAffinityMatches(host string, block *model.AllocationBlock) bool {
|
||||
return *block.Affinity == "host:"+host
|
||||
}
|
||||
|
||||
func getHostAffinity(block *model.AllocationBlock) string {
|
||||
if block.Affinity != nil && strings.HasPrefix(*block.Affinity, "host:") {
|
||||
return strings.TrimPrefix(*block.Affinity, "host:")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b allocationBlock) numFreeAddresses() int {
|
||||
return len(b.Unallocated)
|
||||
}
|
||||
|
||||
func (b allocationBlock) empty() bool {
|
||||
return b.numFreeAddresses() == b.NumAddresses()
|
||||
}
|
||||
|
||||
func (b *allocationBlock) release(addresses []cnet.IP) ([]cnet.IP, map[string]int, error) {
|
||||
// Store return values.
|
||||
unallocated := []cnet.IP{}
|
||||
countByHandle := map[string]int{}
|
||||
|
||||
// Used internally.
|
||||
var ordinals []int
|
||||
delRefCounts := map[int]int{}
|
||||
attrsToDelete := []int{}
|
||||
|
||||
// De-duplicate addresses to ensure reference counting is correcet
|
||||
uniqueAddresses := make(map[string]struct{})
|
||||
for _, ip := range addresses {
|
||||
uniqueAddresses[ip.IP.String()] = struct{}{}
|
||||
}
|
||||
|
||||
// Determine the ordinals that need to be released and the
|
||||
// attributes that need to be cleaned up.
|
||||
log.Debugf("Releasing addresses from block: %v", uniqueAddresses)
|
||||
for ipStr := range uniqueAddresses {
|
||||
ip := cnet.MustParseIP(ipStr)
|
||||
// Convert to an ordinal.
|
||||
ordinal, err := b.IPToOrdinal(ip)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Debugf("Address %s is ordinal %d", ip, ordinal)
|
||||
|
||||
// Check if allocated.
|
||||
log.Debugf("Checking if allocated: %v", b.Allocations)
|
||||
attrIdx := b.Allocations[ordinal]
|
||||
if attrIdx == nil {
|
||||
log.Debugf("Asked to release address that was not allocated")
|
||||
unallocated = append(unallocated, ip)
|
||||
continue
|
||||
}
|
||||
ordinals = append(ordinals, ordinal)
|
||||
log.Debugf("%s is allocated, ordinals to release are now %v", ip, ordinals)
|
||||
|
||||
// Increment reference counting for attributes.
|
||||
cnt := 1
|
||||
if cur, exists := delRefCounts[*attrIdx]; exists {
|
||||
cnt = cur + 1
|
||||
}
|
||||
delRefCounts[*attrIdx] = cnt
|
||||
log.Debugf("delRefCounts: %v", delRefCounts)
|
||||
|
||||
// Increment count of addresses by handle if a handle
|
||||
// exists.
|
||||
log.Debugf("Looking up attribute with index %d", *attrIdx)
|
||||
handleID := b.Attributes[*attrIdx].AttrPrimary
|
||||
if handleID != nil {
|
||||
log.Debugf("HandleID is %s", *handleID)
|
||||
handleCount := 0
|
||||
if count, ok := countByHandle[*handleID]; !ok {
|
||||
handleCount = count
|
||||
}
|
||||
log.Debugf("Handle ref count is %d, incrementing", handleCount)
|
||||
handleCount += 1
|
||||
countByHandle[*handleID] = handleCount
|
||||
log.Debugf("countByHandle %v", countByHandle)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle cleaning up of attributes. We do this by
|
||||
// reference counting. If we're deleting the last reference to
|
||||
// a given attribute, then it needs to be cleaned up.
|
||||
refCounts := b.attributeRefCounts()
|
||||
log.Debugf("Cleaning up attributes, refCounts: %v", refCounts)
|
||||
for idx, refs := range delRefCounts {
|
||||
log.Debugf("Checking ref count index %d", idx)
|
||||
if refCounts[idx] == refs {
|
||||
attrsToDelete = append(attrsToDelete, idx)
|
||||
}
|
||||
}
|
||||
if len(attrsToDelete) != 0 {
|
||||
log.Debugf("Deleting attributes: %v", attrsToDelete)
|
||||
b.deleteAttributes(attrsToDelete, ordinals)
|
||||
}
|
||||
|
||||
// Release requested addresses.
|
||||
log.Debugf("Allocations: %v", b.Allocations)
|
||||
log.Debugf("Releasing ordinals: %v", ordinals)
|
||||
for _, ordinal := range ordinals {
|
||||
log.Debugf("Releasing ordinal %d", ordinal)
|
||||
b.Allocations[ordinal] = nil
|
||||
b.Unallocated = append(b.Unallocated, ordinal)
|
||||
}
|
||||
return unallocated, countByHandle, nil
|
||||
}
|
||||
|
||||
func (b *allocationBlock) deleteAttributes(delIndexes, ordinals []int) {
|
||||
newIndexes := make([]*int, len(b.Attributes))
|
||||
newAttrs := []model.AllocationAttribute{}
|
||||
y := 0 // Next free slot in the new attributes list.
|
||||
for x := range b.Attributes {
|
||||
if !intInSlice(x, delIndexes) {
|
||||
// Attribute at x is not being deleted. Build a mapping
|
||||
// of old attribute index (x) to new attribute index (y).
|
||||
log.Debugf("%d in %v", x, delIndexes)
|
||||
newIndex := y
|
||||
newIndexes[x] = &newIndex
|
||||
y += 1
|
||||
newAttrs = append(newAttrs, b.Attributes[x])
|
||||
}
|
||||
}
|
||||
b.Attributes = newAttrs
|
||||
|
||||
// Update attribute indexes for all allocations in this block.
|
||||
for i := 0; i < b.NumAddresses(); i++ {
|
||||
if b.Allocations[i] != nil {
|
||||
// Get the new index that corresponds to the old index
|
||||
// and update the allocation.
|
||||
newIndex := newIndexes[*b.Allocations[i]]
|
||||
b.Allocations[i] = newIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b allocationBlock) attributeRefCounts() map[int]int {
|
||||
refCounts := map[int]int{}
|
||||
for _, a := range b.Allocations {
|
||||
if a == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if count, ok := refCounts[*a]; !ok {
|
||||
// No entry for given attribute index.
|
||||
refCounts[*a] = 1
|
||||
} else {
|
||||
refCounts[*a] = count + 1
|
||||
}
|
||||
}
|
||||
return refCounts
|
||||
}
|
||||
|
||||
func (b allocationBlock) attributeIndexesByHandle(handleID string) []int {
|
||||
indexes := []int{}
|
||||
for i, attr := range b.Attributes {
|
||||
if attr.AttrPrimary != nil && *attr.AttrPrimary == handleID {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
return indexes
|
||||
}
|
||||
|
||||
func (b *allocationBlock) releaseByHandle(handleID string) int {
|
||||
attrIndexes := b.attributeIndexesByHandle(handleID)
|
||||
log.Debugf("Attribute indexes to release: %v", attrIndexes)
|
||||
if len(attrIndexes) == 0 {
|
||||
// Nothing to release.
|
||||
log.Debugf("No addresses assigned to handle '%s'", handleID)
|
||||
return 0
|
||||
}
|
||||
|
||||
// There are addresses to release.
|
||||
ordinals := []int{}
|
||||
var o int
|
||||
for o = 0; o < b.NumAddresses(); o++ {
|
||||
// Only check allocated ordinals.
|
||||
if b.Allocations[o] != nil && intInSlice(*b.Allocations[o], attrIndexes) {
|
||||
// Release this ordinal.
|
||||
ordinals = append(ordinals, o)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean and reorder attributes.
|
||||
b.deleteAttributes(attrIndexes, ordinals)
|
||||
|
||||
// Release the addresses.
|
||||
for _, o := range ordinals {
|
||||
b.Allocations[o] = nil
|
||||
b.Unallocated = append(b.Unallocated, o)
|
||||
}
|
||||
return len(ordinals)
|
||||
}
|
||||
|
||||
func (b allocationBlock) ipsByHandle(handleID string) []cnet.IP {
|
||||
ips := []cnet.IP{}
|
||||
attrIndexes := b.attributeIndexesByHandle(handleID)
|
||||
var o int
|
||||
for o = 0; o < b.NumAddresses(); o++ {
|
||||
if b.Allocations[o] != nil && intInSlice(*b.Allocations[o], attrIndexes) {
|
||||
ip := b.OrdinalToIP(o)
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
func (b allocationBlock) attributesForIP(ip cnet.IP) (map[string]string, error) {
|
||||
// Convert to an ordinal.
|
||||
ordinal, err := b.IPToOrdinal(ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if allocated.
|
||||
attrIndex := b.Allocations[ordinal]
|
||||
if attrIndex == nil {
|
||||
return nil, errors.New(fmt.Sprintf("IP %s is not currently assigned in block", ip))
|
||||
}
|
||||
return b.Attributes[*attrIndex].AttrSecondary, nil
|
||||
}
|
||||
|
||||
func (b *allocationBlock) findOrAddAttribute(handleID *string, attrs map[string]string) int {
|
||||
logCtx := log.WithField("attrs", attrs)
|
||||
if handleID != nil {
|
||||
logCtx = log.WithField("handle", *handleID)
|
||||
}
|
||||
attr := model.AllocationAttribute{handleID, attrs}
|
||||
for idx, existing := range b.Attributes {
|
||||
if reflect.DeepEqual(attr, existing) {
|
||||
log.Debugf("Attribute '%+v' already exists", attr)
|
||||
return idx
|
||||
}
|
||||
}
|
||||
|
||||
// Does not exist - add it.
|
||||
logCtx.Debugf("New allocation attribute: %#v", attr)
|
||||
attrIndex := len(b.Attributes)
|
||||
b.Attributes = append(b.Attributes, attr)
|
||||
return attrIndex
|
||||
}
|
||||
|
||||
func getBlockCIDRForAddress(addr cnet.IP, pool *v3.IPPool) cnet.IPNet {
|
||||
var mask net.IPMask
|
||||
if addr.Version() == 6 {
|
||||
// This is an IPv6 address.
|
||||
mask = net.CIDRMask(pool.Spec.BlockSize, 128)
|
||||
} else {
|
||||
// This is an IPv4 address.
|
||||
mask = net.CIDRMask(pool.Spec.BlockSize, 32)
|
||||
}
|
||||
masked := addr.Mask(mask)
|
||||
return cnet.IPNet{IPNet: net.IPNet{IP: masked, Mask: mask}}
|
||||
}
|
||||
|
||||
func getIPVersion(ip cnet.IP) int {
|
||||
if ip.To4() == nil {
|
||||
return 6
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
func largerThanOrEqualToBlock(blockCIDR cnet.IPNet, pool *v3.IPPool) bool {
|
||||
ones, _ := blockCIDR.Mask.Size()
|
||||
return ones <= pool.Spec.BlockSize
|
||||
}
|
||||
|
||||
func intInSlice(searchInt int, slice []int) bool {
|
||||
for _, v := range slice {
|
||||
if v == searchInt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user