/
group.go
171 lines (141 loc) · 4.71 KB
/
group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
package allocator
import (
"errors"
"net"
"github.com/sirupsen/logrus"
"github.com/cilium/cilium/pkg/ipam/types"
"github.com/cilium/cilium/pkg/lock"
"github.com/cilium/cilium/pkg/logging"
"github.com/cilium/cilium/pkg/logging/logfields"
)
var (
errPoolNotExists = errors.New("pool does not exist")
log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ipam-allocator")
)
// PoolGroupAllocator is an allocator to allocate from a group of subnets
type PoolGroupAllocator struct {
mutex lock.RWMutex
allocators map[types.PoolID]*PoolAllocator
}
// NewPoolGroupAllocator returns a new allocator able to allocate out of a group of pools.
func NewPoolGroupAllocator(subnets types.SubnetMap) (*PoolGroupAllocator, error) {
g := &PoolGroupAllocator{allocators: map[types.PoolID]*PoolAllocator{}}
// Create subnet allocators for all identified subnets
for _, subnet := range subnets {
if subnet.CIDR == nil {
continue
}
a, err := NewPoolAllocator(types.PoolID(subnet.ID), subnet.CIDR)
if err != nil {
return nil, err
}
g.allocators[types.PoolID(subnet.ID)] = a
}
return g, nil
}
// AddressIterator is the required interface to allow iterating over a
// structure which holds a set of addresses
type AddressIterator interface {
ForeachAddress(instanceID string, fn types.AddressIterator) error
}
// ReserveAddresses reserves all addresses returned by an AddressIterator.
// Invalid IPs or failures to allocate are logged
func (g *PoolGroupAllocator) ReserveAddresses(iterator AddressIterator) {
iterator.ForeachAddress("", func(instanceID, interfaceID, ipString, poolID string, address types.Address) error {
ip := net.ParseIP(ipString)
if ip != nil {
if err := g.Allocate(types.PoolID(poolID), ip); err != nil {
log.WithFields(logrus.Fields{"instance": instanceID, "interface": interfaceID, "ip": ipString}).
WithError(err).Warning("Unable to allocate IP in internal allocator")
}
} else {
log.WithFields(logrus.Fields{"instance": instanceID, "interface": interfaceID, "ip": ipString}).
Warning("Unable to parse IP")
}
return nil
})
}
// GetPoolQuota returns the number of available IPs in all IP pools
func (g *PoolGroupAllocator) GetPoolQuota() types.PoolQuotaMap {
pool := types.PoolQuotaMap{}
g.mutex.RLock()
for poolID, allocator := range g.allocators {
pool[poolID] = types.PoolQuota{AvailableIPs: allocator.Free()}
}
g.mutex.RUnlock()
return pool
}
// AllocateMany allocates multiple IP addresses. The operation succeeds if all
// IPs can be allocated. On failure, all IPs are released again.
func (g *PoolGroupAllocator) AllocateMany(poolID types.PoolID, num int) ([]net.IP, error) {
allocator := g.getAllocator(poolID)
if allocator == nil {
return nil, errPoolNotExists
}
return allocator.AllocateMany(num)
}
// Allocate allocates a paritcular IP in a particular pool
func (g *PoolGroupAllocator) Allocate(poolID types.PoolID, ip net.IP) error {
var allocator *PoolAllocator
switch poolID {
case types.PoolUnspec:
g.mutex.RLock()
for _, a := range g.allocators {
if a.AllocationCIDR.IPNet.Contains(ip) {
allocator = a
break
}
}
g.mutex.RUnlock()
default:
allocator = g.getAllocator(poolID)
}
if allocator == nil {
return errPoolNotExists
}
return allocator.Allocate(ip)
}
// ReleaseMany releases a slice of IP addresses. This function has no effect
func (g *PoolGroupAllocator) ReleaseMany(poolID types.PoolID, ips []net.IP) error {
allocator := g.getAllocator(poolID)
if allocator == nil {
return errPoolNotExists
}
allocator.ReleaseMany(ips)
return nil
}
// getAllocator returns the allocator for a subnet
func (g *PoolGroupAllocator) getAllocator(poolID types.PoolID) *PoolAllocator {
g.mutex.RLock()
defer g.mutex.RUnlock()
return g.allocators[poolID]
}
// FirstPoolWithAvailableQuota returns the first pool ID in the list of pools
// with available addresses. If any of the preferred pool IDs have available
// addresses, the first pool in that list is returned.
func (g *PoolGroupAllocator) FirstPoolWithAvailableQuota(preferredPoolIDs []types.PoolID) (types.PoolID, int) {
g.mutex.RLock()
defer g.mutex.RUnlock()
for _, p := range preferredPoolIDs {
if allocator := g.allocators[p]; allocator != nil {
if available := allocator.Free(); available > 0 {
return p, available
}
}
}
for poolID, allocator := range g.allocators {
if available := allocator.Free(); available > 0 {
return poolID, available
}
}
return types.PoolNotExists, 0
}
// PoolExists returns true if an allocation pool exists.
func (g *PoolGroupAllocator) PoolExists(poolID types.PoolID) bool {
g.mutex.RLock()
_, ok := g.allocators[poolID]
g.mutex.RUnlock()
return ok
}