Skip to content

Commit

Permalink
add vl3 chain elements and ipam service
Browse files Browse the repository at this point in the history
Signed-off-by: Denis Tingaikin <denis.tingajkin@xored.com>
  • Loading branch information
denis-tingaikin committed Mar 21, 2022
1 parent b88289b commit d3767c5
Show file tree
Hide file tree
Showing 18 changed files with 1,285 additions and 441 deletions.
127 changes: 127 additions & 0 deletions pkg/ipam/vl3ipam/server.go
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
}
191 changes: 191 additions & 0 deletions pkg/ipam/vl3ipam/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// 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_Allocat2(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_Allocat3(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)
}
}

func Test_vl3_IPAM_Allocat4(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", 40)

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,
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)

err = stream.Send(&ipam.PrefixRequest{
Type: ipam.Type_DELETE,
Prefix: "172.16.0.0/30",
})

require.NoError(t, err)
}
}
5 changes: 3 additions & 2 deletions pkg/networkservice/chains/client/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,18 @@ import (
"github.com/networkservicemesh/api/pkg/api/networkservice"
"google.golang.org/grpc"

"github.com/networkservicemesh/sdk/pkg/networkservice/common/clienturl"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/null"
)

type clientOptions struct {
name string
clientURL *url.URL
cc grpc.ClientConnInterface
additionalFunctionality []networkservice.NetworkServiceClient
authorizeClient networkservice.NetworkServiceClient
refreshClient networkservice.NetworkServiceClient
healClient networkservice.NetworkServiceClient
clientUrlClient networkservice.NetworkServiceClient
dialOptions []grpc.DialOption
dialTimeout time.Duration
}
Expand All @@ -51,7 +52,7 @@ func WithName(name string) Option {
// WithClientURL sets name for the client.
func WithClientURL(clientURL *url.URL) Option {
return Option(func(c *clientOptions) {
c.clientURL = clientURL
c.clientUrlClient = clienturl.NewClient(clientURL)
})
}

Expand Down
Loading

0 comments on commit d3767c5

Please sign in to comment.