Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IPAM] support reserved networks #2829

Draft
wants to merge 2 commits into
base: ipam
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apis/ipam/v1alpha1/network_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ type NetworkSpec struct {
// CIDR is the desired CIDR for the remote cluster.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="CIDR field is immutable"
CIDR networkingv1beta1.CIDR `json:"cidr"`
// PreAllocated is the number of IPs to pre-allocate (reserve) in the CIDR, starting from the first IP.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Reserved field is immutable"
PreAllocated uint `json:"preAllocated"`
}

// NetworkStatus defines the observed state of Network.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ spec:
x-kubernetes-validations:
- message: CIDR field is immutable
rule: self == oldSelf
preAllocated:
description: PreAllocated is the number of IPs to pre-allocate (reserve)
in the CIDR, starting from the first IP.
minimum: 0
type: integer
x-kubernetes-validations:
- message: Reserved field is immutable
rule: self == oldSelf
required:
- cidr
type: object
Expand Down
2 changes: 2 additions & 0 deletions deployments/liqo/templates/liqo-ipam-networks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ metadata:
ipam.liqo.io/network-type: external-cidr
spec:
cidr: {{ .Values.ipam.externalCIDR }}
preAllocated: 1 # the first IP of the external CIDR is reserved for the unknown source traffic
---
apiVersion: ipam.liqo.io/v1alpha1
kind: Network
Expand All @@ -53,4 +54,5 @@ metadata:
ipam.liqo.io/network-not-remapped: "true"
spec:
cidr: {{ $value }}
---
{{- end }}
6 changes: 3 additions & 3 deletions pkg/ipam/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (lipam *LiqoIPAM) IPRelease(_ context.Context, req *IPReleaseRequest) (*IPR

// NetworkAcquire acquires a network. If it is already reserved, it allocates and reserves a new free one with the same prefix length.
func (lipam *LiqoIPAM) NetworkAcquire(_ context.Context, req *NetworkAcquireRequest) (*NetworkAcquireResponse, error) {
remappedCidr, err := lipam.acquireNetwork(req.GetCidr(), req.GetImmutable())
remappedCidr, err := lipam.acquireNetwork(req.GetCidr(), uint(req.GetPreAllocated()), req.GetImmutable())
if err != nil {
return &NetworkAcquireResponse{}, err
}
Expand All @@ -100,14 +100,14 @@ func (lipam *LiqoIPAM) NetworkAcquire(_ context.Context, req *NetworkAcquireRequ

// NetworkRelease releases a network.
func (lipam *LiqoIPAM) NetworkRelease(_ context.Context, req *NetworkReleaseRequest) (*NetworkReleaseResponse, error) {
lipam.freeNetwork(req.GetCidr())
lipam.freeNetwork(network{cidr: req.GetCidr()})

return &NetworkReleaseResponse{}, nil
}

// NetworkIsAvailable checks if a network is available.
func (lipam *LiqoIPAM) NetworkIsAvailable(_ context.Context, req *NetworkAvailableRequest) (*NetworkAvailableResponse, error) {
available := lipam.isNetworkAvailable(req.GetCidr())
available := lipam.isNetworkAvailable(network{cidr: req.GetCidr()})

return &NetworkAvailableResponse{Available: available}, nil
}
104 changes: 57 additions & 47 deletions pkg/ipam/ipam.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/ipam/ipam.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ message IPReleaseResponse {
message NetworkAcquireRequest {
string cidr = 1;
bool immutable = 2; // If true, the network cannot be remapped. It will be allocated if available, or an error will be returned.
uint32 preAllocated = 3; // The number of IPs to pre-allocate (reserve) in the CIDR, starting from the first IP of the CIDR.
}

message NetworkAcquireResponse {
Expand Down
55 changes: 37 additions & 18 deletions pkg/ipam/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,39 @@ import (
)

type networkInfo struct {
cidr string
network
creationTimestamp time.Time
}

type network struct {
cidr string
preAllocated uint
}

func (n network) String() string {
return n.cidr
}

// reserveNetwork reserves a network, saving it in the cache.
func (lipam *LiqoIPAM) reserveNetwork(cidr string) error {
func (lipam *LiqoIPAM) reserveNetwork(nw network) error {
lipam.mutex.Lock()
defer lipam.mutex.Unlock()

// TODO: implement real network reserve logic
if lipam.cacheNetworks == nil {
lipam.cacheNetworks = make(map[string]networkInfo)
}
lipam.cacheNetworks[cidr] = networkInfo{
cidr: cidr,
lipam.cacheNetworks[nw.String()] = networkInfo{
network: nw,
creationTimestamp: time.Now(),
}

klog.Infof("Reserved network %q", cidr)
klog.Infof("Reserved network %q", nw)
return nil
}

// acquireNetwork acquires a network, eventually remapped if conflicts are found.
func (lipam *LiqoIPAM) acquireNetwork(cidr string, immutable bool) (string, error) {
func (lipam *LiqoIPAM) acquireNetwork(cidr string, preAllocated uint, immutable bool) (string, error) {
lipam.mutex.Lock()
defer lipam.mutex.Unlock()

Expand All @@ -56,39 +66,45 @@ func (lipam *LiqoIPAM) acquireNetwork(cidr string, immutable bool) (string, erro
if lipam.cacheNetworks == nil {
lipam.cacheNetworks = make(map[string]networkInfo)
}
lipam.cacheNetworks[cidr] = networkInfo{
cidr: cidr,
nw := network{
cidr: cidr,
preAllocated: preAllocated,
}
lipam.cacheNetworks[nw.String()] = networkInfo{
network: nw,
creationTimestamp: time.Now(),
}

klog.Infof("Acquired network %q", cidr)
return cidr, nil
klog.Infof("Acquired network %q", nw.cidr)
return nw.cidr, nil
}

// freeNetwork frees a network, removing it from the cache.
func (lipam *LiqoIPAM) freeNetwork(cidr string) {
func (lipam *LiqoIPAM) freeNetwork(nw network) {
lipam.mutex.Lock()
defer lipam.mutex.Unlock()

delete(lipam.cacheNetworks, cidr)
klog.Infof("Freed network %q", cidr)
// TODO: implement real network free logic
delete(lipam.cacheNetworks, nw.String())
klog.Infof("Freed network %q", nw.cidr)
}

// isNetworkAvailable checks if a network is available.
func (lipam *LiqoIPAM) isNetworkAvailable(cidr string) bool {
func (lipam *LiqoIPAM) isNetworkAvailable(nw network) bool {
lipam.mutex.Lock()
defer lipam.mutex.Unlock()

// TODO: implement real network availability check logic
if lipam.cacheNetworks == nil {
return true
}
_, ok := lipam.cacheNetworks[cidr]
_, ok := lipam.cacheNetworks[nw.String()]

return ok
}

func listNetworksOnCluster(ctx context.Context, cl client.Client) ([]string, error) {
var nets []string
func listNetworksOnCluster(ctx context.Context, cl client.Client) ([]network, error) {
var nets []network
var networks ipamv1alpha1.NetworkList
if err := cl.List(ctx, &networks); err != nil {
return nil, err
Expand All @@ -103,7 +119,10 @@ func listNetworksOnCluster(ctx context.Context, cl client.Client) ([]string, err
continue
}

nets = append(nets, cidr)
nets = append(nets, network{
cidr: cidr,
preAllocated: net.Spec.PreAllocated,
})
}

return nets, nil
Expand Down
6 changes: 3 additions & 3 deletions pkg/ipam/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,18 @@ func (lipam *LiqoIPAM) syncNetworks(ctx context.Context, expiredThreshold time.T

// Add networks that are present in the cluster but not in the cache.
for _, net := range clusterNetworks {
if _, inCache := lipam.cacheNetworks[net]; !inCache {
if _, inCache := lipam.cacheNetworks[net.String()]; !inCache {
if err := lipam.reserveNetwork(net); err != nil {
return err
}
}
setClusterNetworks[net] = struct{}{} // add network to the set
setClusterNetworks[net.String()] = struct{}{} // add network to the set
}

// Remove networks that are present in the cache but not in the cluster, and were added before the threshold.
for key := range lipam.cacheNetworks {
if _, inCluster := setClusterNetworks[key]; !inCluster && lipam.cacheNetworks[key].creationTimestamp.Before(expiredThreshold) {
lipam.freeNetwork(lipam.cacheNetworks[key].cidr)
lipam.freeNetwork(lipam.cacheNetworks[key].network)
}
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/ipam/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ var _ = Describe("Sync routine tests", func() {

addNetowrkToCache = func(ipamServer *LiqoIPAM, cidr string, creationTimestamp time.Time) {
ipamServer.cacheNetworks[cidr] = networkInfo{
cidr: cidr,
network: network{
cidr: cidr,
},
creationTimestamp: creationTimestamp,
}
}
Expand Down