-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add vl3 chain elements and IPAM service (#1247)
* add vl3 chain elements and vl3ipam Signed-off-by: Denis Tingaikin <[email protected]> * fix typos Signed-off-by: Denis Tingaikin <[email protected]>
- Loading branch information
1 parent
b88289b
commit 57746e0
Showing
17 changed files
with
1,244 additions
and
439 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright (c) 2022 Cisco and/or its affiliates. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// 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 vl3ipam provides implementation of api/pkg/api/ipam.IPAMServer for vL3 scenario. | ||
package vl3ipam | ||
|
||
import ( | ||
"net" | ||
"sync" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/ipam" | ||
"github.com/pkg/errors" | ||
|
||
"github.com/networkservicemesh/sdk/pkg/tools/ippool" | ||
) | ||
|
||
// ErrUndefined means that operation is not supported | ||
var ErrUndefined = errors.New("request type is undefined") | ||
|
||
// ErrOutOfRange means that ip pool of IPAM is empty | ||
var ErrOutOfRange = errors.New("prefix is out of range or already in use") | ||
|
||
type vl3IPAMServer struct { | ||
pool *ippool.IPPool | ||
excludedPrefixes []string | ||
poolMutex sync.Mutex | ||
initalSize uint8 | ||
} | ||
|
||
// NewIPAMServer creates a new ipam.IPAMServer handler for grpc.Server | ||
func NewIPAMServer(prefix string, initialNSEPrefixSize uint8) ipam.IPAMServer { | ||
return &vl3IPAMServer{ | ||
pool: ippool.NewWithNetString(prefix), | ||
initalSize: initialNSEPrefixSize, | ||
} | ||
} | ||
|
||
var _ ipam.IPAMServer = (*vl3IPAMServer)(nil) | ||
|
||
func (s *vl3IPAMServer) ManagePrefixes(prefixServer ipam.IPAM_ManagePrefixesServer) error { | ||
var pool = s.pool | ||
var mutex = &s.poolMutex | ||
var clientsPrefixes []string | ||
var err error | ||
|
||
for err == nil { | ||
var r *ipam.PrefixRequest | ||
|
||
r, err = prefixServer.Recv() | ||
if err != nil { | ||
break | ||
} | ||
|
||
switch r.Type { | ||
case ipam.Type_UNDEFINED: | ||
return ErrUndefined | ||
|
||
case ipam.Type_ALLOCATE: | ||
var resp ipam.PrefixResponse | ||
mutex.Lock() | ||
for _, excludePrefix := range r.ExcludePrefixes { | ||
pool.ExcludeString(excludePrefix) | ||
} | ||
resp.Prefix = r.Prefix | ||
if resp.Prefix == "" || !pool.ContainsNetString(resp.Prefix) { | ||
var ip net.IP | ||
ip, err = pool.Pull() | ||
if err != nil { | ||
mutex.Unlock() | ||
break | ||
} | ||
ipNet := &net.IPNet{ | ||
IP: ip, | ||
Mask: net.CIDRMask( | ||
int(s.initalSize), | ||
len(ip)*8, | ||
), | ||
} | ||
resp.Prefix = ipNet.String() | ||
} | ||
s.excludedPrefixes = append(s.excludedPrefixes, r.Prefix) | ||
clientsPrefixes = append(clientsPrefixes, resp.Prefix) | ||
pool.ExcludeString(resp.Prefix) | ||
mutex.Unlock() | ||
resp.ExcludePrefixes = r.ExcludePrefixes | ||
resp.ExcludePrefixes = append(resp.ExcludePrefixes, s.excludedPrefixes...) | ||
err = prefixServer.Send(&resp) | ||
|
||
case ipam.Type_DELETE: | ||
for i, p := range clientsPrefixes { | ||
if p != r.Prefix { | ||
continue | ||
} | ||
mutex.Lock() | ||
pool.AddNetString(p) | ||
mutex.Unlock() | ||
clientsPrefixes = append(clientsPrefixes[:i], clientsPrefixes[i+1:]...) | ||
break | ||
} | ||
} | ||
} | ||
|
||
s.poolMutex.Lock() | ||
for _, prefix := range clientsPrefixes { | ||
pool.AddNetString(prefix) | ||
} | ||
s.poolMutex.Unlock() | ||
|
||
if prefixServer.Context().Err() != nil { | ||
return nil | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright (c) 2022 Cisco and/or its affiliates. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// 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 vl3ipam_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/url" | ||
"testing" | ||
"time" | ||
|
||
"github.com/networkservicemesh/api/pkg/api/ipam" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/goleak" | ||
"google.golang.org/grpc" | ||
|
||
"github.com/networkservicemesh/sdk/pkg/ipam/vl3ipam" | ||
"github.com/networkservicemesh/sdk/pkg/tools/grpcutils" | ||
) | ||
|
||
func newVL3IPAMServer(ctx context.Context, t *testing.T, prefix string, initialSize uint8) url.URL { | ||
var s = grpc.NewServer() | ||
ipam.RegisterIPAMServer(s, vl3ipam.NewIPAMServer(prefix, initialSize)) | ||
|
||
var serverAddr url.URL | ||
|
||
require.Len(t, grpcutils.ListenAndServe(ctx, &serverAddr, s), 0) | ||
|
||
return serverAddr | ||
} | ||
|
||
func newVL3IPAMClient(ctx context.Context, t *testing.T, connectTO *url.URL) ipam.IPAMClient { | ||
var cc, err = grpc.DialContext(ctx, grpcutils.URLToTarget(connectTO), grpc.WithInsecure()) | ||
require.NoError(t, err) | ||
|
||
go func() { | ||
<-ctx.Done() | ||
_ = cc.Close() | ||
}() | ||
|
||
return ipam.NewIPAMClient(cc) | ||
} | ||
|
||
func Test_vl3_IPAM_Allocate(t *testing.T) { | ||
t.Cleanup(func() { | ||
goleak.VerifyNone(t) | ||
}) | ||
|
||
var ctx, cancel = context.WithTimeout(context.Background(), time.Second*5) | ||
defer cancel() | ||
|
||
connectTO := newVL3IPAMServer(ctx, t, "172.16.0.0/16", 24) | ||
|
||
for i := 0; i < 10; i++ { | ||
c := newVL3IPAMClient(ctx, t, &connectTO) | ||
|
||
var stream, err = c.ManagePrefixes(ctx) | ||
|
||
require.NoError(t, err, i) | ||
|
||
err = stream.Send(&ipam.PrefixRequest{ | ||
Type: ipam.Type_ALLOCATE, | ||
}) | ||
|
||
require.NoError(t, err) | ||
|
||
resp, err := stream.Recv() | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, fmt.Sprintf("172.16.%v.0/24", i), resp.Prefix, i) | ||
require.NotEmpty(t, resp.ExcludePrefixes) | ||
} | ||
} | ||
|
||
func Test_vl3_IPAM_Allocate2(t *testing.T) { | ||
t.Cleanup(func() { | ||
goleak.VerifyNone(t) | ||
}) | ||
|
||
var ctx, cancel = context.WithTimeout(context.Background(), time.Second*5) | ||
defer cancel() | ||
connectTO := newVL3IPAMServer(ctx, t, "173.16.0.0/16", 24) | ||
|
||
for i := 0; i < 10; i++ { | ||
clientCTX, cancel := context.WithCancel(ctx) | ||
c := newVL3IPAMClient(clientCTX, t, &connectTO) | ||
|
||
var stream, err = c.ManagePrefixes(clientCTX) | ||
require.NoError(t, err, i) | ||
|
||
err = stream.Send(&ipam.PrefixRequest{ | ||
Type: ipam.Type_ALLOCATE, | ||
}) | ||
|
||
require.NoError(t, err) | ||
|
||
resp, err := stream.Recv() | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, "173.16.0.0/24", resp.Prefix, i) | ||
require.NotEmpty(t, resp.ExcludePrefixes, i) | ||
cancel() | ||
time.Sleep(time.Millisecond * 50) | ||
} | ||
} | ||
|
||
func Test_vl3_IPAM_Allocate3(t *testing.T) { | ||
t.Cleanup(func() { | ||
goleak.VerifyNone(t) | ||
}) | ||
|
||
var ctx, cancel = context.WithTimeout(context.Background(), time.Second*5) | ||
defer cancel() | ||
|
||
connectTO := newVL3IPAMServer(ctx, t, "172.16.0.0/16", 24) | ||
|
||
for i := 0; i < 10; i++ { | ||
clientCTX, cancel := context.WithCancel(ctx) | ||
c := newVL3IPAMClient(clientCTX, t, &connectTO) | ||
|
||
var stream, err = c.ManagePrefixes(clientCTX) | ||
require.NoError(t, err, i) | ||
|
||
err = stream.Send(&ipam.PrefixRequest{ | ||
Type: ipam.Type_ALLOCATE, | ||
Prefix: "172.16.0.0/30", | ||
}) | ||
|
||
require.NoError(t, err) | ||
|
||
resp, err := stream.Recv() | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, "172.16.0.0/30", resp.Prefix, i) | ||
require.NotEmpty(t, resp.ExcludePrefixes, i) | ||
cancel() | ||
time.Sleep(time.Millisecond * 50) | ||
} | ||
} |
Oops, something went wrong.