diff --git a/dataplane/saiserver/ports.go b/dataplane/saiserver/ports.go index d95d09d75..8d23c8dfc 100644 --- a/dataplane/saiserver/ports.go +++ b/dataplane/saiserver/ports.go @@ -19,6 +19,8 @@ import ( "fmt" "log/slog" "net" + "slices" + "strconv" "google.golang.org/grpc" "google.golang.org/protobuf/proto" @@ -323,6 +325,27 @@ func (port *port) CreatePort(ctx context.Context, req *saipb.CreatePortRequest) } port.mgr.StoreAttributes(id, attrs) + + // Update the switch's port list. + switchID, ok := port.mgr.GetSwitchID() + if !ok { + return nil, fmt.Errorf("failed to get switch ID") + } + switchAttrs := &saipb.SwitchAttribute{} + if err := port.mgr.PopulateAllAttributes(switchID, switchAttrs); err != nil { + return nil, fmt.Errorf("failed to get switch attributes: %v", err) + } + uSwitchID, err := strconv.ParseUint(switchID, 10, 64) + if err != nil { + return nil, err + } + portList := switchAttrs.GetPortList() + portList = append(portList, id) + port.mgr.StoreAttributes(uSwitchID, &saipb.SwitchAttribute{ + PortList: portList, + NumberOfActivePorts: proto.Uint32(uint32(len(portList))), + }) + nid, err := port.dataplane.ObjectNID(ctx, &fwdpb.ObjectNIDRequest{ ContextId: &fwdpb.ContextId{Id: port.dataplane.ID()}, ObjectId: &fwdpb.ObjectId{Id: fmt.Sprint(id)}, @@ -572,12 +595,37 @@ func (port *port) GetPortStats(ctx context.Context, req *saipb.GetPortStatsReque } func (port *port) RemovePort(ctx context.Context, req *saipb.RemovePortRequest) (*saipb.RemovePortResponse, error) { - deleteReq := &fwdpb.ObjectDeleteRequest{ + if _, err := port.dataplane.ObjectDelete(ctx, &fwdpb.ObjectDeleteRequest{ ContextId: &fwdpb.ContextId{Id: port.dataplane.ID()}, ObjectId: &fwdpb.ObjectId{Id: fmt.Sprint(req.GetOid())}, + }); err != nil { + return nil, err } - _, err := port.dataplane.ObjectDelete(ctx, deleteReq) - return &saipb.RemovePortResponse{}, err + + // Update the switch's port list. + switchID, ok := port.mgr.GetSwitchID() + if !ok { + return nil, fmt.Errorf("failed to get switch ID") + } + switchAttrs := &saipb.SwitchAttribute{} + if err := port.mgr.PopulateAllAttributes(switchID, switchAttrs); err != nil { + return nil, fmt.Errorf("failed to get switch attributes: %v", err) + } + uSwitchID, err := strconv.ParseUint(switchID, 10, 64) + if err != nil { + return nil, err + } + portList := switchAttrs.GetPortList() + portOid := req.GetOid() + + if i := slices.Index(portList, portOid); i != -1 { + portList = slices.Delete(portList, i, i+1) + } + port.mgr.StoreAttributes(uSwitchID, &saipb.SwitchAttribute{ + PortList: portList, + NumberOfActivePorts: proto.Uint32(uint32(len(portList))), + }) + return &saipb.RemovePortResponse{}, nil } func (port *port) Reset() { diff --git a/dataplane/saiserver/ports_test.go b/dataplane/saiserver/ports_test.go index 259621c26..6b853a41b 100644 --- a/dataplane/saiserver/ports_test.go +++ b/dataplane/saiserver/ports_test.go @@ -512,20 +512,27 @@ func TestRemovePort(t *testing.T) { }{{ desc: "success", req: &saipb.RemovePortRequest{ - Oid: 1, + Oid: 0, }, want: &fwdpb.ObjectDeleteRequest{ ContextId: &fwdpb.ContextId{Id: "foo"}, - ObjectId: &fwdpb.ObjectId{Id: "1"}, + ObjectId: &fwdpb.ObjectId{Id: ""}, }, }} for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { f := &fakeSwitchDataplane{} - c, mgr, stopFn := newTestPort(t, f, &dplaneopts.Options{PortType: fwdpb.PortType_PORT_TYPE_KERNEL}) + c, _, stopFn := newTestPort(t, f, &dplaneopts.Options{PortType: fwdpb.PortType_PORT_TYPE_KERNEL}) defer stopFn() - mgr.StoreAttributes(1, &saipb.CreatePortRequest{HwLaneList: []uint32{1}}) + // Create a port with a distinct oid from the switch. + createResp, err := c.CreatePort(context.Background(), &saipb.CreatePortRequest{Switch: 1, HwLaneList: []uint32{1}}) + if err != nil { + t.Fatalf("CreatePort failed: %v", err) + } + tt.req.Oid = createResp.Oid + tt.want.ObjectId.Id = fmt.Sprint(createResp.Oid) + _, gotErr := c.RemovePort(context.Background(), tt.req) if diff := errdiff.Check(gotErr, tt.wantErr); diff != "" { t.Fatalf("RemovePort() unexpected err: %s", diff) @@ -552,6 +559,7 @@ func newTestPort(t testing.TB, api switchDataplaneAPI, opts *dplaneopts.Options) mgr.StoreAttributes(swID, &saipb.SwitchAttribute{ DefaultStpInstId: proto.Uint64(101), }) + mgr.SetType(fmt.Sprint(swID), saipb.ObjectType_OBJECT_TYPE_SWITCH) resp, err := vlan.CreateVlan(context.Background(), &saipb.CreateVlanRequest{ Switch: swID, VlanId: proto.Uint32(DefaultVlanId),