Skip to content

Commit

Permalink
feat: add initial impl for List funcs
Browse files Browse the repository at this point in the history
Signed-off-by: Boris Glimcher <Boris.Glimcher@emc.com>
  • Loading branch information
glimchb committed Sep 7, 2023
1 parent 3108cdf commit 739af1c
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 24 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.o
docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.opiproject.org/bridges/testbridge"}' localhost:50151 opi_api.network.evpn-gw.v1alpha1.LogicalBridgeService.GetLogicalBridge
docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.opiproject.org/svis/testsvi"}' localhost:50151 opi_api.network.evpn-gw.v1alpha1.SviService.GetSvi
docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.opiproject.org/vrfs/testvrf"}' localhost:50151 opi_api.network.evpn-gw.v1alpha1.VrfService.GetVrf
#list
docker-compose exec opi-evpn-bridge grpcurl -plaintext localhost:50151 opi_api.network.evpn-gw.v1alpha1.BridgePortService.ListBridgePorts
docker-compose exec opi-evpn-bridge grpcurl -plaintext localhost:50151 opi_api.network.evpn-gw.v1alpha1.BridgePortService.ListLogicalBridges
docker-compose exec opi-evpn-bridge grpcurl -plaintext localhost:50151 opi_api.network.evpn-gw.v1alpha1.BridgePortService.ListSvis
docker-compose exec opi-evpn-bridge grpcurl -plaintext localhost:50151 opi_api.network.evpn-gw.v1alpha1.BridgePortService.ListVrfs
# delete
docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.opiproject.org/ports/testinterface"}' localhost:50151 opi_api.network.evpn-gw.v1alpha1.BridgePortService.DeleteBridgePort
docker-compose exec opi-evpn-bridge grpcurl -plaintext -d '{"name": "//network.opiproject.org/bridges/testbridge"}' localhost:50151 opi_api.network.evpn-gw.v1alpha1.LogicalBridgeService.DeleteLogicalBridge
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ services:
/entrypoint.sh call --json_input --json_output localhost:50151 GetSvi "{\"name\" : \"//network.opiproject.org/svis/yellow-vlan50\" }" && \
/entrypoint.sh call --json_input --json_output localhost:50151 GetBridgePort "{\"name\" : \"//network.opiproject.org/ports/eth1\" }" && \
/entrypoint.sh call --json_input --json_output localhost:50151 GetBridgePort "{\"name\" : \"//network.opiproject.org/ports/eth2\" }" && \
echo list && \
/entrypoint.sh call --json_input --json_output localhost:50151 ListVrfs "{}" && \
/entrypoint.sh call --json_input --json_output localhost:50151 ListLogicalBridges "{}" && \
/entrypoint.sh call --json_input --json_output localhost:50151 ListSvis "{}" && \
/entrypoint.sh call --json_input --json_output localhost:50151 ListBridgePorts "{}" && \
echo done'
host2-leaf2:
Expand Down
27 changes: 27 additions & 0 deletions pkg/evpn/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"
"log"
"net"
"sort"

"github.com/vishvananda/netlink"

Expand All @@ -25,6 +26,12 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)

func sortLogicalBridges(bridges []*pb.LogicalBridge) {
sort.Slice(bridges, func(i int, j int) bool {
return bridges[i].Name < bridges[j].Name
})
}

// CreateLogicalBridge executes the creation of the LogicalBridge
func (s *Server) CreateLogicalBridge(_ context.Context, in *pb.CreateLogicalBridgeRequest) (*pb.LogicalBridge, error) {
log.Printf("CreateLogicalBridge: Received from client: %v", in)
Expand Down Expand Up @@ -237,3 +244,23 @@ func (s *Server) GetLogicalBridge(_ context.Context, in *pb.GetLogicalBridgeRequ
// TODO
return &pb.LogicalBridge{Name: in.Name, Spec: &pb.LogicalBridgeSpec{Vni: bridge.Spec.Vni, VlanId: bridge.Spec.VlanId}, Status: &pb.LogicalBridgeStatus{OperStatus: pb.LBOperStatus_LB_OPER_STATUS_UP}}, nil
}

// ListLogicalBridges lists logical bridges
func (s *Server) ListLogicalBridges(_ context.Context, in *pb.ListLogicalBridgesRequest) (*pb.ListLogicalBridgesResponse, error) {
log.Printf("ListLogicalBridges: Received from client: %v", in)
// check required fields
if err := fieldbehavior.ValidateRequiredFields(in); err != nil {
log.Printf("error: %v", err)
return nil, err
}
token := ""
Blobarray := []*pb.LogicalBridge{}
for _, bridge := range s.Bridges {
r := protoClone(bridge)
r.Status = &pb.LogicalBridgeStatus{OperStatus: pb.LBOperStatus_LB_OPER_STATUS_UP}
Blobarray = append(Blobarray, r)
}
// TODO: Limit results to offset and size and rememeber pagination
sortLogicalBridges(Blobarray)
return &pb.ListLogicalBridgesResponse{LogicalBridges: Blobarray, NextPageToken: token}, nil
}
87 changes: 79 additions & 8 deletions pkg/evpn/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ var (
},
},
}
testLogicalBridgeWithStatus = pb.LogicalBridge{
Name: testLogicalBridgeName,
Spec: testLogicalBridge.Spec,
Status: &pb.LogicalBridgeStatus{
OperStatus: pb.LBOperStatus_LB_OPER_STATUS_UP,
},
}
)

func Test_CreateLogicalBridge(t *testing.T) {
Expand Down Expand Up @@ -220,14 +227,9 @@ func Test_CreateLogicalBridge(t *testing.T) {
},
},
"successful call": {
id: testLogicalBridgeID,
in: &testLogicalBridge,
out: &pb.LogicalBridge{
Spec: testLogicalBridge.Spec,
Status: &pb.LogicalBridgeStatus{
OperStatus: pb.LBOperStatus_LB_OPER_STATUS_UP,
},
},
id: testLogicalBridgeID,
in: &testLogicalBridge,
out: &testLogicalBridgeWithStatus,
errCode: codes.OK,
errMsg: "",
exist: false,
Expand Down Expand Up @@ -631,3 +633,72 @@ func Test_GetLogicalBridge(t *testing.T) {
})
}
}

func Test_ListLogicalBridges(t *testing.T) {
tests := map[string]struct {
in string
out []*pb.LogicalBridge
errCode codes.Code
errMsg string
size int32
token string
}{
"example test": {
in: "",
out: []*pb.LogicalBridge{&testLogicalBridgeWithStatus},
errCode: codes.OK,
errMsg: "",
size: 0,
token: "",
},
}

// run tests
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
// start GRPC mockup server
ctx := context.Background()
mockNetlink := mocks.NewNetlink(t)
opi := NewServerWithArgs(mockNetlink)
conn, err := grpc.DialContext(ctx,
"",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(dialer(opi)))
if err != nil {
log.Fatal(err)
}
defer func(conn *grpc.ClientConn) {
err := conn.Close()
if err != nil {
log.Fatal(err)
}
}(conn)
client := pb.NewLogicalBridgeServiceClient(conn)

opi.Bridges[testLogicalBridgeName] = protoClone(&testLogicalBridge)
opi.Bridges[testLogicalBridgeName].Name = testLogicalBridgeName

request := &pb.ListLogicalBridgesRequest{PageSize: tt.size, PageToken: tt.token}
response, err := client.ListLogicalBridges(ctx, request)
if !equalProtoSlices(response.GetLogicalBridges(), tt.out) {
t.Error("response: expected", tt.out, "received", response.GetLogicalBridges())
}

// Empty NextPageToken indicates end of results list
if tt.size != 1 && response.GetNextPageToken() != "" {
t.Error("Expected end of results, received non-empty next page token", response.GetNextPageToken())
}

if er, ok := status.FromError(err); ok {
if er.Code() != tt.errCode {
t.Error("error code: expected", tt.errCode, "received", er.Code())
}
if er.Message() != tt.errMsg {
t.Error("error message: expected", tt.errMsg, "received", er.Message())
}
} else {
t.Error("expected grpc error status")
}
})
}
}
15 changes: 15 additions & 0 deletions pkg/evpn/evpn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"google.golang.org/grpc"
"google.golang.org/grpc/test/bufconn"
"google.golang.org/protobuf/proto"

pe "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go"

Expand All @@ -39,6 +40,20 @@ func dialer(opi *Server) func(context.Context, string) (net.Conn, error) {
}
}

func equalProtoSlices[T proto.Message](x, y []T) bool {
if len(x) != len(y) {
return false
}

for i := 0; i < len(x); i++ {
if !proto.Equal(x[i], y[i]) {
return false
}
}

return true
}

func TestFrontEnd_NewServerWithArgs(t *testing.T) {
tests := map[string]struct {
nLink utils.Netlink
Expand Down
27 changes: 27 additions & 0 deletions pkg/evpn/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"log"
"path"
"sort"

// "github.com/vishvananda/netlink"

Expand All @@ -24,6 +25,12 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)

func sortBridgePorts(ports []*pb.BridgePort) {
sort.Slice(ports, func(i int, j int) bool {
return ports[i].Name < ports[j].Name
})
}

// CreateBridgePort executes the creation of the port
func (s *Server) CreateBridgePort(_ context.Context, in *pb.CreateBridgePortRequest) (*pb.BridgePort, error) {
log.Printf("CreateBridgePort: Received from client: %v", in)
Expand Down Expand Up @@ -262,3 +269,23 @@ func (s *Server) GetBridgePort(_ context.Context, in *pb.GetBridgePortRequest) (
// TODO
return &pb.BridgePort{Name: in.Name, Spec: &pb.BridgePortSpec{MacAddress: port.Spec.MacAddress}, Status: &pb.BridgePortStatus{OperStatus: pb.BPOperStatus_BP_OPER_STATUS_UP}}, nil
}

// ListBridgePorts lists logical bridges
func (s *Server) ListBridgePorts(_ context.Context, in *pb.ListBridgePortsRequest) (*pb.ListBridgePortsResponse, error) {
log.Printf("ListBridgePorts: Received from client: %v", in)
// check required fields
if err := fieldbehavior.ValidateRequiredFields(in); err != nil {
log.Printf("error: %v", err)
return nil, err
}
token := ""
Blobarray := []*pb.BridgePort{}
for _, port := range s.Ports {
r := protoClone(port)
r.Status = &pb.BridgePortStatus{OperStatus: pb.BPOperStatus_BP_OPER_STATUS_UP}
Blobarray = append(Blobarray, r)
}
// TODO: Limit results to offset and size and rememeber pagination
sortBridgePorts(Blobarray)
return &pb.ListBridgePortsResponse{BridgePorts: Blobarray, NextPageToken: token}, nil
}
87 changes: 79 additions & 8 deletions pkg/evpn/port_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ var (
LogicalBridges: []string{testLogicalBridgeName},
},
}
testBridgePortWithStatus = pb.BridgePort{
Name: testBridgePortName,
Spec: testBridgePort.Spec,
Status: &pb.BridgePortStatus{
OperStatus: pb.BPOperStatus_BP_OPER_STATUS_UP,
},
}
)

func Test_CreateBridgePort(t *testing.T) {
Expand Down Expand Up @@ -263,14 +270,9 @@ func Test_CreateBridgePort(t *testing.T) {
},
},
"successful call": {
id: testBridgePortID,
in: &testBridgePort,
out: &pb.BridgePort{
Spec: testBridgePort.Spec,
Status: &pb.BridgePortStatus{
OperStatus: pb.BPOperStatus_BP_OPER_STATUS_UP,
},
},
id: testBridgePortID,
in: &testBridgePort,
out: &testBridgePortWithStatus,
errCode: codes.OK,
errMsg: "",
exist: false,
Expand Down Expand Up @@ -664,3 +666,72 @@ func Test_GetBridgePort(t *testing.T) {
})
}
}

func Test_ListBridgePorts(t *testing.T) {
tests := map[string]struct {
in string
out []*pb.BridgePort
errCode codes.Code
errMsg string
size int32
token string
}{
"example test": {
in: "",
out: []*pb.BridgePort{&testBridgePortWithStatus},
errCode: codes.OK,
errMsg: "",
size: 0,
token: "",
},
}

// run tests
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
// start GRPC mockup server
ctx := context.Background()
mockNetlink := mocks.NewNetlink(t)
opi := NewServerWithArgs(mockNetlink)
conn, err := grpc.DialContext(ctx,
"",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(dialer(opi)))
if err != nil {
log.Fatal(err)
}
defer func(conn *grpc.ClientConn) {
err := conn.Close()
if err != nil {
log.Fatal(err)
}
}(conn)
client := pb.NewBridgePortServiceClient(conn)

opi.Ports[testBridgePortName] = protoClone(&testBridgePort)
opi.Ports[testBridgePortName].Name = testBridgePortName

request := &pb.ListBridgePortsRequest{PageSize: tt.size, PageToken: tt.token}
response, err := client.ListBridgePorts(ctx, request)
if !equalProtoSlices(response.GetBridgePorts(), tt.out) {
t.Error("response: expected", tt.out, "received", response.GetBridgePorts())
}

// Empty NextPageToken indicates end of results list
if tt.size != 1 && response.GetNextPageToken() != "" {
t.Error("Expected end of results, received non-empty next page token", response.GetNextPageToken())
}

if er, ok := status.FromError(err); ok {
if er.Code() != tt.errCode {
t.Error("error code: expected", tt.errCode, "received", er.Code())
}
if er.Message() != tt.errMsg {
t.Error("error message: expected", tt.errMsg, "received", er.Message())
}
} else {
t.Error("expected grpc error status")
}
})
}
}
27 changes: 27 additions & 0 deletions pkg/evpn/svi.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"log"
"net"
"path"
"sort"

"github.com/vishvananda/netlink"

Expand All @@ -26,6 +27,12 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)

func sortSvis(svis []*pb.Svi) {
sort.Slice(svis, func(i int, j int) bool {
return svis[i].Name < svis[j].Name
})
}

// CreateSvi executes the creation of the VLAN
func (s *Server) CreateSvi(_ context.Context, in *pb.CreateSviRequest) (*pb.Svi, error) {
log.Printf("CreateSvi: Received from client: %v", in)
Expand Down Expand Up @@ -295,3 +302,23 @@ func (s *Server) GetSvi(_ context.Context, in *pb.GetSviRequest) (*pb.Svi, error
// TODO
return &pb.Svi{Name: in.Name, Spec: &pb.SviSpec{MacAddress: obj.Spec.MacAddress, EnableBgp: obj.Spec.EnableBgp, RemoteAs: obj.Spec.RemoteAs}, Status: &pb.SviStatus{OperStatus: pb.SVIOperStatus_SVI_OPER_STATUS_UP}}, nil
}

// ListSvis lists logical bridges
func (s *Server) ListSvis(_ context.Context, in *pb.ListSvisRequest) (*pb.ListSvisResponse, error) {
log.Printf("ListSvis: Received from client: %v", in)
// check required fields
if err := fieldbehavior.ValidateRequiredFields(in); err != nil {
log.Printf("error: %v", err)
return nil, err
}
token := ""
Blobarray := []*pb.Svi{}
for _, svi := range s.Svis {
r := protoClone(svi)
r.Status = &pb.SviStatus{OperStatus: pb.SVIOperStatus_SVI_OPER_STATUS_UP}
Blobarray = append(Blobarray, r)
}
// TODO: Limit results to offset and size and rememeber pagination
sortSvis(Blobarray)
return &pb.ListSvisResponse{Svis: Blobarray, NextPageToken: token}, nil
}
Loading

0 comments on commit 739af1c

Please sign in to comment.