diff --git a/dataplane/saiserver/BUILD b/dataplane/saiserver/BUILD index 0cd6e8ee8..a1a27c374 100644 --- a/dataplane/saiserver/BUILD +++ b/dataplane/saiserver/BUILD @@ -42,6 +42,7 @@ go_test( name = "saiserver_test", srcs = [ "acl_test.go", + "bridge_test.go", "hostif_test.go", "l2mc_test.go", "mirror_test.go", diff --git a/dataplane/saiserver/bridge_test.go b/dataplane/saiserver/bridge_test.go new file mode 100644 index 000000000..b9f33c968 --- /dev/null +++ b/dataplane/saiserver/bridge_test.go @@ -0,0 +1,122 @@ +// Copyright 2025 Google LLC +// +// 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 saiserver + +import ( + "context" + "testing" + + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/gnmi/errdiff" + + "github.com/openconfig/lemming/dataplane/saiserver/attrmgr" + + saipb "github.com/openconfig/lemming/dataplane/proto/sai" +) + +func TestBridgePort(t *testing.T) { + tests := []struct { + desc string + req *saipb.CreateBridgePortRequest + want *saipb.CreateBridgePortResponse + wantAttr *saipb.BridgePortAttribute + wantErr string + }{{ + desc: "success", + req: &saipb.CreateBridgePortRequest{ + Switch: 1, + Type: saipb.BridgePortType_BRIDGE_PORT_TYPE_PORT.Enum(), + PortId: proto.Uint64(10), + FdbLearningMode: saipb.BridgePortFdbLearningMode_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE.Enum(), + AdminState: proto.Bool(true), + }, + want: &saipb.CreateBridgePortResponse{ + Oid: 1, + }, + wantAttr: &saipb.BridgePortAttribute{ + Type: saipb.BridgePortType_BRIDGE_PORT_TYPE_PORT.Enum(), + PortId: proto.Uint64(10), + FdbLearningMode: saipb.BridgePortFdbLearningMode_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE.Enum(), + AdminState: proto.Bool(true), + }, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + conn, mgr, stopFn := newTestBridge(t) + defer stopFn() + c := saipb.NewBridgeClient(conn) + + got, gotErr := c.CreateBridgePort(context.TODO(), tt.req) + if diff := errdiff.Check(gotErr, tt.wantErr); diff != "" { + t.Fatalf("CreateBridgePort() unexpected err: %s w/ error %v", diff, gotErr) + } + if gotErr != nil { + return + } + if d := cmp.Diff(got, tt.want, protocmp.Transform()); d != "" { + t.Errorf("CreateBridgePort() failed: diff(-got,+want)\n:%s", d) + } + attr := &saipb.BridgePortAttribute{} + if err := mgr.PopulateAllAttributes("1", attr); err != nil { + t.Fatal(err) + } + if d := cmp.Diff(attr, tt.wantAttr, protocmp.Transform()); d != "" { + t.Errorf("CreateBridgePort() failed: diff(-got,+want)\n:%s", d) + } + + // Test Set Attribute + _, err := c.SetBridgePortAttribute(context.TODO(), &saipb.SetBridgePortAttributeRequest{ + Oid: got.Oid, + AdminState: proto.Bool(false), + }) + if err != nil { + t.Fatalf("SetBridgePortAttribute() failed: %v", err) + } + if err := mgr.PopulateAllAttributes("1", attr); err != nil { + t.Fatal(err) + } + if attr.GetAdminState() != false { + t.Errorf("SetBridgePortAttribute() failed: got %v, want false", attr.GetAdminState()) + } + + // Test Get Attribute + resp, err := c.GetBridgePortAttribute(context.TODO(), &saipb.GetBridgePortAttributeRequest{ + Oid: got.Oid, + AttrType: []saipb.BridgePortAttr{saipb.BridgePortAttr_BRIDGE_PORT_ATTR_ADMIN_STATE}, + }) + if err != nil { + t.Fatalf("GetBridgePortAttribute() failed: %v", err) + } + if resp.GetAttr().GetAdminState() != false { + t.Errorf("GetBridgePortAttribute() failed: got %v, want false", resp.GetAttr().GetAdminState()) + } + // Test Remove + if _, err := c.RemoveBridgePort(context.TODO(), &saipb.RemoveBridgePortRequest{Oid: got.Oid}); err != nil { + t.Fatalf("RemoveBridgePort() failed: %v", err) + } + }) + } +} + +func newTestBridge(t testing.TB) (grpc.ClientConnInterface, *attrmgr.AttrMgr, func()) { + conn, mgr, stopFn := newTestServer(t, func(mgr *attrmgr.AttrMgr, srv *grpc.Server) { + newBridge(mgr, nil, srv) + }) + return conn, mgr, stopFn +} diff --git a/dataplane/saiserver/routing.go b/dataplane/saiserver/routing.go index 10b3ea0c4..05d93916a 100644 --- a/dataplane/saiserver/routing.go +++ b/dataplane/saiserver/routing.go @@ -1147,20 +1147,64 @@ func newBridge(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Serve return b } -func (br *bridge) CreateBridge(context.Context, *saipb.CreateBridgeRequest) (*saipb.CreateBridgeResponse, error) { - id := br.mgr.NextID() +func (b *bridge) CreateBridge(ctx context.Context, req *saipb.CreateBridgeRequest) (*saipb.CreateBridgeResponse, error) { + id := b.mgr.NextID() attrs := &saipb.BridgeAttribute{ PortList: []uint64{}, UnknownUnicastFloodGroup: proto.Uint64(0), UnknownMulticastFloodGroup: proto.Uint64(0), BroadcastFloodGroup: proto.Uint64(0), } - br.mgr.StoreAttributes(id, attrs) + b.mgr.StoreAttributes(id, attrs) return &saipb.CreateBridgeResponse{ Oid: id, }, nil } +func (b *bridge) RemoveBridge(ctx context.Context, req *saipb.RemoveBridgeRequest) (*saipb.RemoveBridgeResponse, error) { + return &saipb.RemoveBridgeResponse{}, nil +} + +func (b *bridge) SetBridgeAttribute(ctx context.Context, req *saipb.SetBridgeAttributeRequest) (*saipb.SetBridgeAttributeResponse, error) { + return &saipb.SetBridgeAttributeResponse{}, nil +} + +func (b *bridge) GetBridgeAttribute(ctx context.Context, req *saipb.GetBridgeAttributeRequest) (*saipb.GetBridgeAttributeResponse, error) { + return &saipb.GetBridgeAttributeResponse{}, nil +} + +func (b *bridge) GetBridgeStats(ctx context.Context, req *saipb.GetBridgeStatsRequest) (*saipb.GetBridgeStatsResponse, error) { + return &saipb.GetBridgeStatsResponse{}, nil +} + +func (b *bridge) CreateBridgePort(ctx context.Context, req *saipb.CreateBridgePortRequest) (*saipb.CreateBridgePortResponse, error) { + oid := b.mgr.NextID() + adminState := req.GetAdminState() + attrs := &saipb.BridgePortAttribute{ + AdminState: proto.Bool(adminState), + } + b.mgr.StoreAttributes(oid, attrs) + return &saipb.CreateBridgePortResponse{ + Oid: oid, + }, nil +} + +func (b *bridge) RemoveBridgePort(ctx context.Context, req *saipb.RemoveBridgePortRequest) (*saipb.RemoveBridgePortResponse, error) { + return &saipb.RemoveBridgePortResponse{}, nil +} + +func (b *bridge) SetBridgePortAttribute(ctx context.Context, req *saipb.SetBridgePortAttributeRequest) (*saipb.SetBridgePortAttributeResponse, error) { + return &saipb.SetBridgePortAttributeResponse{}, nil +} + +func (b *bridge) GetBridgePortAttribute(ctx context.Context, req *saipb.GetBridgePortAttributeRequest) (*saipb.GetBridgePortAttributeResponse, error) { + return &saipb.GetBridgePortAttributeResponse{}, nil +} + +func (b *bridge) GetBridgePortStats(ctx context.Context, req *saipb.GetBridgePortStatsRequest) (*saipb.GetBridgePortStatsResponse, error) { + return &saipb.GetBridgePortStatsResponse{}, nil +} + type hash struct { saipb.UnimplementedHashServer mgr *attrmgr.AttrMgr