Skip to content

Commit bf33c2a

Browse files
feat(ipamd): move introspection API types to separate package
1 parent de312d0 commit bf33c2a

File tree

6 files changed

+553
-538
lines changed

6 files changed

+553
-538
lines changed

pkg/ipamd/api/v1/types.go

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package v1
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"time"
7+
)
8+
9+
const (
10+
// minENILifeTime is the shortest time before we consider deleting a newly created ENI
11+
minENILifeTime = 1 * time.Minute
12+
)
13+
14+
// IPAMKey is the IPAM primary key. Quoting CNI spec:
15+
//
16+
// Plugins that store state should do so using a primary key of
17+
// (network name, CNI_CONTAINERID, CNI_IFNAME).
18+
type IPAMKey struct {
19+
NetworkName string `json:"networkName"`
20+
ContainerID string `json:"containerID"`
21+
IfName string `json:"ifName"`
22+
}
23+
24+
// IsZero returns true if object is equal to the golang zero/null value.
25+
func (k IPAMKey) IsZero() bool {
26+
return k == IPAMKey{}
27+
}
28+
29+
// String() implements the fmt.Stringer interface.
30+
func (k IPAMKey) String() string {
31+
return fmt.Sprintf("%s/%s/%s", k.NetworkName, k.ContainerID, k.IfName)
32+
}
33+
34+
// IPAMMetadata is the metadata associated with IP allocations.
35+
type IPAMMetadata struct {
36+
K8SPodNamespace string `json:"k8sPodNamespace,omitempty"`
37+
K8SPodName string `json:"k8sPodName,omitempty"`
38+
}
39+
40+
// ENI represents a single ENI. Exported fields will be marshaled for introspection.
41+
type ENI struct {
42+
// CreateTime is excluded from marshalled representation, used only by internal datastore
43+
CreateTime time.Time `json:"-"`
44+
45+
// AWS ENI ID
46+
ID string
47+
// IsPrimary indicates whether ENI is a primary ENI
48+
IsPrimary bool
49+
// IsTrunk indicates whether this ENI is used to provide pods with dedicated ENIs
50+
IsTrunk bool
51+
// IsEFA indicates whether this ENI is tagged as an EFA
52+
IsEFA bool
53+
// DeviceNumber is the device number of ENI (0 means the primary ENI)
54+
DeviceNumber int
55+
// IPv4Addresses shows whether each address is assigned, the key is IP address, which must
56+
// be in dot-decimal notation with no leading zeros and no whitespace(eg: "10.1.0.253")
57+
// Key is the IP address - PD: "IP/28" and SIP: "IP/32"
58+
AvailableIPv4Cidrs map[string]*CidrInfo
59+
//IPv6CIDRs contains information tied to IPv6 Prefixes attached to the ENI
60+
IPv6Cidrs map[string]*CidrInfo
61+
}
62+
63+
// IsTooYoung returns true if the ENI hasn't been around long enough to be deleted.
64+
func (e *ENI) IsTooYoung() bool {
65+
return time.Since(e.CreateTime) < minENILifeTime
66+
}
67+
68+
// HasIPInCooling returns true if an IP address was unassigned recently.
69+
func (e *ENI) HasIPInCooling(ipCooldownPeriod time.Duration) bool {
70+
for _, assignedaddr := range e.AvailableIPv4Cidrs {
71+
for _, addr := range assignedaddr.IPAddresses {
72+
if addr.InCoolingPeriod(ipCooldownPeriod) {
73+
return true
74+
}
75+
}
76+
}
77+
return false
78+
}
79+
80+
// HasPods returns true if the ENI has pods assigned to it.
81+
func (e *ENI) HasPods() bool {
82+
return e.AssignedIPv4Addresses() != 0
83+
}
84+
85+
// AddressInfo contains information about an IP, Exported fields will be marshaled for introspection.
86+
type AddressInfo struct {
87+
Address string
88+
89+
IPAMKey IPAMKey
90+
IPAMMetadata IPAMMetadata
91+
AssignedTime time.Time
92+
UnassignedTime time.Time
93+
}
94+
95+
// CidrInfo
96+
type CidrInfo struct {
97+
// Either v4/v6 Host or LPM Prefix
98+
Cidr net.IPNet
99+
// Key is individual IP addresses from the Prefix - /32 (v4) or /128 (v6)
100+
IPAddresses map[string]*AddressInfo
101+
// true if Cidr here is an LPM prefix
102+
IsPrefix bool
103+
// IP Address Family of the Cidr
104+
AddressFamily string
105+
}
106+
107+
func (cidr *CidrInfo) Size() int {
108+
ones, bits := cidr.Cidr.Mask.Size()
109+
return (1 << (bits - ones))
110+
}
111+
112+
func (e *ENI) findAddressForSandbox(ipamKey IPAMKey) (*CidrInfo, *AddressInfo) {
113+
// Either v4 or v6 for now.
114+
// Check in V4 prefixes
115+
for _, availableCidr := range e.AvailableIPv4Cidrs {
116+
for _, addr := range availableCidr.IPAddresses {
117+
if addr.IPAMKey == ipamKey {
118+
return availableCidr, addr
119+
}
120+
}
121+
}
122+
123+
// Check in V6 prefixes
124+
for _, availableCidr := range e.IPv6Cidrs {
125+
for _, addr := range availableCidr.IPAddresses {
126+
if addr.IPAMKey == ipamKey {
127+
return availableCidr, addr
128+
}
129+
}
130+
}
131+
return nil, nil
132+
}
133+
134+
// AssignedIPv4Addresses is the number of IP addresses already assigned
135+
func (e *ENI) AssignedIPv4Addresses() int {
136+
count := 0
137+
for _, availableCidr := range e.AvailableIPv4Cidrs {
138+
count += availableCidr.AssignedIPAddressesInCidr()
139+
}
140+
return count
141+
}
142+
143+
// AssignedIPAddressesInCidr is the number of IP addresses already assigned in the IPv4 CIDR
144+
func (cidr *CidrInfo) AssignedIPAddressesInCidr() int {
145+
count := 0
146+
//SIP : This will run just once and count will be 0 if addr is not assigned or addr is not allocated yet(unused IP)
147+
//PD : This will return count of number /32 assigned in /28 CIDR.
148+
for _, addr := range cidr.IPAddresses {
149+
if addr.Assigned() {
150+
count++
151+
}
152+
}
153+
return count
154+
}
155+
156+
type CidrStats struct {
157+
AssignedIPs int
158+
CooldownIPs int
159+
}
160+
161+
// Gets number of assigned IPs and the IPs in cooldown from a given CIDR
162+
func (cidr *CidrInfo) GetIPStatsFromCidr(ipCooldownPeriod time.Duration) CidrStats {
163+
stats := CidrStats{}
164+
for _, addr := range cidr.IPAddresses {
165+
if addr.Assigned() {
166+
stats.AssignedIPs++
167+
} else if addr.InCoolingPeriod(ipCooldownPeriod) {
168+
stats.CooldownIPs++
169+
}
170+
}
171+
return stats
172+
}
173+
174+
// Assigned returns true iff the address is allocated to a pod/sandbox.
175+
func (addr AddressInfo) Assigned() bool {
176+
return !addr.IPAMKey.IsZero()
177+
}
178+
179+
// InCoolingPeriod checks whether an addr is in ipCooldownPeriod
180+
func (addr AddressInfo) InCoolingPeriod(ipCooldownPeriod time.Duration) bool {
181+
return time.Since(addr.UnassignedTime) <= ipCooldownPeriod
182+
}
183+
184+
// ENIPool is a collection of ENI, keyed by ENI ID
185+
type ENIPool map[string]*ENI
186+
187+
// AssignedIPv4Addresses is the number of IP addresses already assigned
188+
func (p *ENIPool) AssignedIPv4Addresses() int {
189+
count := 0
190+
for _, eni := range *p {
191+
count += eni.AssignedIPv4Addresses()
192+
}
193+
return count
194+
}
195+
196+
// FindAddressForSandbox returns ENI and AddressInfo or (nil, nil) if not found
197+
func (p *ENIPool) FindAddressForSandbox(ipamKey IPAMKey) (*ENI, *CidrInfo, *AddressInfo) {
198+
for _, eni := range *p {
199+
if availableCidr, addr := eni.findAddressForSandbox(ipamKey); addr != nil && availableCidr != nil {
200+
return eni, availableCidr, addr
201+
}
202+
}
203+
return nil, nil, nil
204+
}
205+
206+
// PodIPInfo contains pod's IP and the device number of the ENI
207+
type PodIPInfo struct {
208+
IPAMKey IPAMKey
209+
// IP is the IPv4 address of pod
210+
IP string
211+
// DeviceNumber is the device number of the ENI
212+
DeviceNumber int
213+
}
214+
215+
// ENIInfos contains ENI IP information
216+
type ENIInfos struct {
217+
// TotalIPs is the total number of IP addresses
218+
TotalIPs int
219+
// assigned is the number of IP addresses that has been assigned
220+
AssignedIPs int
221+
// ENIs contains ENI IP pool information
222+
ENIs map[string]ENI
223+
}
224+
225+
// CheckpointFormatVersion is the version stamp used on stored checkpoints.
226+
const CheckpointFormatVersion = "vpc-cni-ipam/1"
227+
228+
// CheckpointData is the format of stored checkpoints. Note this is
229+
// deliberately a "dumb" format since efficiency is less important
230+
// than version stability here.
231+
type CheckpointData struct {
232+
Version string `json:"version"`
233+
Allocations []CheckpointEntry `json:"allocations"`
234+
}
235+
236+
// CheckpointEntry is a "row" in the conceptual IPAM datastore, as stored
237+
// in checkpoints.
238+
type CheckpointEntry struct {
239+
IPAMKey
240+
IPv4 string `json:"ipv4,omitempty"`
241+
IPv6 string `json:"ipv6,omitempty"`
242+
AllocationTimestamp int64 `json:"allocationTimestamp"`
243+
Metadata IPAMMetadata `json:"metadata"`
244+
}
245+
246+
type DataStoreStats struct {
247+
// Total number of addresses allocated
248+
TotalIPs int
249+
// Total number of prefixes allocated
250+
TotalPrefixes int
251+
252+
// Number of assigned addresses
253+
AssignedIPs int
254+
// Number of addresses in cooldown
255+
CooldownIPs int
256+
}
257+
258+
func (stats *DataStoreStats) String() string {
259+
return fmt.Sprintf("Total IPs/Prefixes = %d/%d, AssignedIPs/CooldownIPs: %d/%d",
260+
stats.TotalIPs, stats.TotalPrefixes, stats.AssignedIPs, stats.CooldownIPs)
261+
}
262+
263+
func (stats *DataStoreStats) AvailableAddresses() int {
264+
return stats.TotalIPs - stats.AssignedIPs
265+
}

0 commit comments

Comments
 (0)