From cbb763111e6355666c2d7c2dd1a90d5e9ae3935d Mon Sep 17 00:00:00 2001 From: Sachin Holla <51310506+sachinholla@users.noreply.github.com> Date: Thu, 28 Sep 2023 02:21:38 +0530 Subject: [PATCH] Debug grpc to fetch subscribe preferences of a path (#130) Added a new 'GetSubscribePreferences' grpc which accepts a list of yang paths and returns following subscription preferences for each: - OnChangeSupported (if on_change subscription will be accepted) - TargetDefinedMode (mode to which target_defined will map to) - WildcardSupported (if wildcard keys will be accepted) - MinSampleInterval (minimum sample_interval accepted, in nanos) Accepts only translib managed yang paths. Uses the existing translib API IsSubscribeSupported() to learn the preferences. This rpc is defined in a new protobuf sonic_debug.proto. It defines only one rpc 'GetSubscribePreferences' now. More debuggability actions can be added in future. --- Makefile | 18 ++ gnmi_server/debug.go | 108 +++++++++ gnmi_server/server.go | 1 + gnmi_server/server_test.go | 14 ++ gnmi_server/transl_sub_test.go | 166 +++++++++++++- proto/gnoi/sonic_debug.pb.go | 395 +++++++++++++++++++++++++++++++++ proto/gnoi/sonic_debug.proto | 39 ++++ 7 files changed, 731 insertions(+), 10 deletions(-) create mode 100644 gnmi_server/debug.go create mode 100644 proto/gnoi/sonic_debug.pb.go create mode 100644 proto/gnoi/sonic_debug.proto diff --git a/Makefile b/Makefile index d93164fb..e6c07b24 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,24 @@ endif swsscommon_wrap: make -C swsscommon +.SECONDEXPANSION: + +PROTOC_PATH := $(PATH):$(GOBIN) +PROTOC_OPTS := -I$(CURDIR)/vendor -I/usr/local/include -I/usr/include + +# Generate following go & grpc bindings using teh legacy protoc-gen-go +PROTO_GO_BINDINGS += proto/sonic_internal.pb.go +PROTO_GO_BINDINGS += proto/gnoi/sonic_debug.pb.go + +$(PROTO_GO_BINDINGS): $$(patsubst %.pb.go,%.proto,$$@) | $(GOBIN)/protoc-gen-go + PATH=$(PROTOC_PATH) protoc -I$(@D) $(PROTOC_OPTS) --go_out=plugins=grpc:$(@D) $< + +$(GOBIN)/protoc-gen-go: + cd $$(mktemp -d) && \ + $(GO) mod init protoc && \ + $(GO) install github.com/golang/protobuf/protoc-gen-go + + DBCONFG = $(DBDIR)/database_config.json ENVFILE = build/test/env.txt TESTENV = $(shell cat $(ENVFILE)) diff --git a/gnmi_server/debug.go b/gnmi_server/debug.go new file mode 100644 index 00000000..5239b72e --- /dev/null +++ b/gnmi_server/debug.go @@ -0,0 +1,108 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2023 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// 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 gnmi + +import ( + "sort" + "time" + + "github.com/Azure/sonic-mgmt-common/translib" + "github.com/Azure/sonic-mgmt-common/translib/path" + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/sonic-net/sonic-gnmi/common_utils" + spb_gnoi "github.com/sonic-net/sonic-gnmi/proto/gnoi" + "github.com/sonic-net/sonic-gnmi/transl_utils" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (srv *Server) GetSubscribePreferences(req *spb_gnoi.SubscribePreferencesReq, stream spb_gnoi.Debug_GetSubscribePreferencesServer) error { + ctx := stream.Context() + ctx, err := authenticate(srv.config.UserAuth, ctx) + if err != nil { + return err + } + + translPaths := make([]translib.IsSubscribePath, 0, len(req.GetPath())) + for i, p := range req.GetPath() { + reqPath, err := transl_utils.ConvertToURI(nil, p, + &path.AppendModulePrefix{}, &path.AddWildcardKeys{}) + if err != nil { + return status.Error(codes.InvalidArgument, "Unknown path: "+path.String(p)) + } + + translPaths = append(translPaths, translib.IsSubscribePath{ + ID: uint32(i), + Path: reqPath, + Mode: translib.TargetDefined, + }) + } + + rc, _ := common_utils.GetContext(ctx) + trResp, err := translib.IsSubscribeSupported(translib.IsSubscribeRequest{ + Paths: translPaths, + User: translib.UserRoles{Name: rc.Auth.User, Roles: rc.Auth.Roles}, + }) + if err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + + // When the path supports on_change but some of its subpaths do not, extra entries + // gets appended with same request ID. Group such entries by ID. + sort.Slice(trResp, func(i, j int) bool { + return trResp[i].ID < trResp[j].ID || + (trResp[i].ID == trResp[j].ID && trResp[i].Path < trResp[j].Path) + }) + + for _, r := range trResp { + pathStr, err := path.New(r.Path) + if err != nil { + return status.Error(codes.Internal, err.Error()) + } + pref := &spb_gnoi.SubscribePreference{ + Path: pathStr, + WildcardSupported: r.IsWildcardSupported, + OnChangeSupported: r.IsOnChangeSupported, + TargetDefinedMode: gnmipb.SubscriptionMode_SAMPLE, + MinSampleInterval: uint64(r.MinInterval) * uint64(time.Second), + } + if !r.IsSubPath && hasOnChangeDisabledSubpath(r.ID, trResp) { + pref.OnChangeSupported = false + } + if r.IsOnChangeSupported && r.PreferredType == translib.OnChange { + pref.TargetDefinedMode = gnmipb.SubscriptionMode_ON_CHANGE + } + + if err = stream.Send(pref); err != nil { + return err + } + } + + return nil +} + +func hasOnChangeDisabledSubpath(id uint32, allPrefs []*translib.IsSubscribeResponse) bool { + for _, p := range allPrefs { + if p.ID == id && p.IsSubPath && !p.IsOnChangeSupported { + return true + } + } + return false +} diff --git a/gnmi_server/server.go b/gnmi_server/server.go index 03798daf..4ae32c18 100644 --- a/gnmi_server/server.go +++ b/gnmi_server/server.go @@ -163,6 +163,7 @@ func NewServer(config *Config, opts []grpc.ServerOption) (*Server, error) { if srv.config.EnableTranslibWrite { spb_gnoi.RegisterSonicServiceServer(srv.s, srv) } + spb_gnoi.RegisterDebugServer(srv.s, srv) log.V(1).Infof("Created Server on %s, read-only: %t", srv.Address(), !srv.config.EnableTranslibWrite) return srv, nil } diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index 89100209..eb3a5d4f 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -99,6 +99,20 @@ func loadDBNotStrict(t *testing.T, rclient *redis.Client, mpi map[string]interfa } } +func createClient(t *testing.T, port int) *grpc.ClientConn { + t.Helper() + cred := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) + conn, err := grpc.Dial( + fmt.Sprintf("127.0.0.1:%d", port), + grpc.WithTransportCredentials(cred), + ) + if err != nil { + t.Fatalf("Dialing to :%d failed: %v", port, err) + } + t.Cleanup(func() { conn.Close() }) + return conn +} + func createServer(t *testing.T, port int64) *Server { t.Helper() certificate, err := testcert.NewCert() diff --git a/gnmi_server/transl_sub_test.go b/gnmi_server/transl_sub_test.go index 4998ae22..3f90a0b1 100644 --- a/gnmi_server/transl_sub_test.go +++ b/gnmi_server/transl_sub_test.go @@ -3,6 +3,7 @@ package gnmi import ( "crypto/tls" "fmt" + "io" "path/filepath" "reflect" "strings" @@ -18,11 +19,10 @@ import ( extnpb "github.com/openconfig/gnmi/proto/gnmi_ext" "github.com/openconfig/ygot/ygot" spb "github.com/sonic-net/sonic-gnmi/proto" + spb_gnoi "github.com/sonic-net/sonic-gnmi/proto/gnoi" dbconfig "github.com/sonic-net/sonic-gnmi/sonic_db_config" "golang.org/x/net/context" - "google.golang.org/grpc" "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/status" ) @@ -875,17 +875,11 @@ func doSet(t *testing.T, data ...interface{}) { } } - cred := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) - conn, err := grpc.Dial("127.0.0.1:8081", grpc.WithTransportCredentials(cred)) - if err != nil { - t.Fatalf("Could not create client: %v", err) - } - + client := gnmipb.NewGNMIClient(createClient(t, 8081)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - defer conn.Close() - _, err = gnmipb.NewGNMIClient(conn).Set(ctx, req) + _, err := client.Set(ctx, req) if err != nil { t.Fatalf("Set failed: %v", err) } @@ -935,3 +929,155 @@ func newBundleVersion(t *testing.T, version string) *extnpb.Extension { ext := &extnpb.RegisteredExtension{Id: spb.BUNDLE_VERSION_EXT, Msg: v} return &extnpb.Extension{Ext: &extnpb.Extension_RegisteredExt{RegisteredExt: ext}} } + +func TestDebugSubscribePreferences(t *testing.T) { + s := createServer(t, 8081) + go runServer(t, s) + defer s.s.Stop() + + ifTop := &spb_gnoi.SubscribePreference{ + Path: strToPath("/openconfig-interfaces:interfaces/interface[name=*]"), + OnChangeSupported: false, + TargetDefinedMode: ON_CHANGE, + WildcardSupported: true, + } + ifMtu := &spb_gnoi.SubscribePreference{ + Path: strToPath("/openconfig-interfaces:interfaces/interface[name=*]/config/mtu"), + OnChangeSupported: true, + TargetDefinedMode: ON_CHANGE, + WildcardSupported: true, + } + ifStat := &spb_gnoi.SubscribePreference{ + Path: strToPath("/openconfig-interfaces:interfaces/interface[name=*]/state/counters"), + OnChangeSupported: false, + TargetDefinedMode: SAMPLE, + WildcardSupported: true, + } + aclConfig := &spb_gnoi.SubscribePreference{ + Path: strToPath("/openconfig-acl:acl/acl-sets/acl-set[name=*][type=*]/config"), + OnChangeSupported: true, + TargetDefinedMode: ON_CHANGE, + WildcardSupported: true, + } + yanglib := &spb_gnoi.SubscribePreference{ + Path: strToPath("/ietf-yang-library:modules-state/module-set-id"), + OnChangeSupported: false, + TargetDefinedMode: SAMPLE, + WildcardSupported: false, + } + + t.Run("invalid_path", func(t *testing.T) { + _, err := getSubscribePreferences(t, nil) + if res, _ := status.FromError(err); res.Code() != codes.InvalidArgument { + t.Fatalf("Expecting InvalidArgument error; got %v", err) + } + }) + + t.Run("unknown_path", func(t *testing.T) { + _, err := getSubscribePreferences(t, strToPath("/unknown")) + if res, _ := status.FromError(err); res.Code() != codes.InvalidArgument { + t.Fatalf("Expecting InvalidArgument error; got %v", err) + } + }) + + t.Run("onchange_supported", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{ifMtu.Path}, + []*spb_gnoi.SubscribePreference{ifMtu}) + }) + + t.Run("onchange_unsupported", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{ifStat.Path}, + []*spb_gnoi.SubscribePreference{ifStat}) + }) + + t.Run("onchange_mixed", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{ifTop.Path}, + []*spb_gnoi.SubscribePreference{ifTop, ifStat}) + }) + + t.Run("nondb_path", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{yanglib.Path}, + []*spb_gnoi.SubscribePreference{yanglib}) + }) + + t.Run("unprefixed_path", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{strToPath("/acl/acl-sets/acl-set/config")}, + []*spb_gnoi.SubscribePreference{aclConfig}) + }) + + t.Run("multiple_paths", func(t *testing.T) { + verifySubscribePreferences(t, + []*gnmipb.Path{yanglib.Path, ifTop.Path, aclConfig.Path}, + []*spb_gnoi.SubscribePreference{yanglib, ifTop, ifStat, aclConfig}) + }) +} + +func TestDebugSubscribePreferences_dummy(t *testing.T) { + // Dummy testcase to increase code coverage !!! + f := func(_ ...interface{}) {} + for _, m := range []*spb_gnoi.SubscribePreferencesReq{nil, {}} { + f(m.String(), m.GetPath()) + f(m.Descriptor()) + } + for _, p := range []*spb_gnoi.SubscribePreference{nil, {}} { + f(p.String(), p.GetPath(), p.GetOnChangeSupported(), p.GetTargetDefinedMode(), p.GetWildcardSupported(), p.GetMinSampleInterval()) + f(p.Descriptor()) + } +} + +func getSubscribePreferences(t *testing.T, paths ...*gnmipb.Path) ([]*spb_gnoi.SubscribePreference, error) { + t.Helper() + client := spb_gnoi.NewDebugClient(createClient(t, 8081)) + stream, err := client.GetSubscribePreferences( + context.Background(), + &spb_gnoi.SubscribePreferencesReq{Path: paths}, + ) + if err != nil { + t.Fatalf("Could not invoke GetSubscribePreferences: %v", err) + } + + var prefs []*spb_gnoi.SubscribePreference + for { + if p, err := stream.Recv(); err == nil { + prefs = append(prefs, p) + } else if err == io.EOF { + break + } else { + return prefs, err + } + } + + return prefs, nil +} + +func verifySubscribePreferences(t *testing.T, paths []*gnmipb.Path, exp []*spb_gnoi.SubscribePreference) { + t.Helper() + resp, err := getSubscribePreferences(t, paths...) + if err != nil { + t.Fatalf("GetSubscribePreferences returned error: %v", err) + } + if len(resp) != len(exp) { + t.Fatalf("Expected: %s\nReceived: %s", prefsText(exp), prefsText(resp)) + } + for i, ex := range exp { + if ex.MinSampleInterval == 0 { + resp[i].MinSampleInterval = 0 // ignore MinSampleInterval for comparison + } + if !proto.Equal(ex, resp[i]) { + t.Fatalf("Expected: %s\nReceived: %s", prefsText(exp), prefsText(resp)) + } + } +} + +func prefsText(prefs []*spb_gnoi.SubscribePreference) string { + var s []string + for _, p := range prefs { + s = append(s, proto.MarshalTextString(p)) + } + return "[\n" + strings.Join(s, "\n") + "]" +} diff --git a/proto/gnoi/sonic_debug.pb.go b/proto/gnoi/sonic_debug.pb.go new file mode 100644 index 00000000..e7b06a24 --- /dev/null +++ b/proto/gnoi/sonic_debug.pb.go @@ -0,0 +1,395 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.6.1 +// source: sonic_debug.proto + +package gnoi_sonic + +import ( + context "context" + gnmi "github.com/openconfig/gnmi/proto/gnmi" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Request message for GetSubscribePreferences RPC +type SubscribePreferencesReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Retrieve subscribe preferences for these paths. + Path []*gnmi.Path `protobuf:"bytes,1,rep,name=path,proto3" json:"path,omitempty"` +} + +func (x *SubscribePreferencesReq) Reset() { + *x = SubscribePreferencesReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sonic_debug_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribePreferencesReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribePreferencesReq) ProtoMessage() {} + +func (x *SubscribePreferencesReq) ProtoReflect() protoreflect.Message { + mi := &file_sonic_debug_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribePreferencesReq.ProtoReflect.Descriptor instead. +func (*SubscribePreferencesReq) Descriptor() ([]byte, []int) { + return file_sonic_debug_proto_rawDescGZIP(), []int{0} +} + +func (x *SubscribePreferencesReq) GetPath() []*gnmi.Path { + if x != nil { + return x.Path + } + return nil +} + +// SubscribePreference holds subscription capability information for a path. +type SubscribePreference struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Resource path, whose subscribe preferences are indicated here. + Path *gnmi.Path `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // Indicates if ON_CHANGE subscription will be accepted for this path. + OnChangeSupported bool `protobuf:"varint,2,opt,name=on_change_supported,json=onChangeSupported,proto3" json:"on_change_supported,omitempty"` + // Indicates how TARGET_DEFINED subscription will be handled for this path. + // It is possible to have target_defined_mode=ON_CHANGE but on_change_supported=false + // when this container/list has both on_change supported and unsupported subpaths. + TargetDefinedMode gnmi.SubscriptionMode `protobuf:"varint,3,opt,name=target_defined_mode,json=targetDefinedMode,proto3,enum=gnmi.SubscriptionMode" json:"target_defined_mode,omitempty"` + // Indicates if wildcard keys are supported for this path. + WildcardSupported bool `protobuf:"varint,4,opt,name=wildcard_supported,json=wildcardSupported,proto3" json:"wildcard_supported,omitempty"` + // Minimum SAMPLE interval supported for this path, in nanoseconds. + MinSampleInterval uint64 `protobuf:"varint,5,opt,name=min_sample_interval,json=minSampleInterval,proto3" json:"min_sample_interval,omitempty"` +} + +func (x *SubscribePreference) Reset() { + *x = SubscribePreference{} + if protoimpl.UnsafeEnabled { + mi := &file_sonic_debug_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribePreference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribePreference) ProtoMessage() {} + +func (x *SubscribePreference) ProtoReflect() protoreflect.Message { + mi := &file_sonic_debug_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribePreference.ProtoReflect.Descriptor instead. +func (*SubscribePreference) Descriptor() ([]byte, []int) { + return file_sonic_debug_proto_rawDescGZIP(), []int{1} +} + +func (x *SubscribePreference) GetPath() *gnmi.Path { + if x != nil { + return x.Path + } + return nil +} + +func (x *SubscribePreference) GetOnChangeSupported() bool { + if x != nil { + return x.OnChangeSupported + } + return false +} + +func (x *SubscribePreference) GetTargetDefinedMode() gnmi.SubscriptionMode { + if x != nil { + return x.TargetDefinedMode + } + return gnmi.SubscriptionMode_TARGET_DEFINED +} + +func (x *SubscribePreference) GetWildcardSupported() bool { + if x != nil { + return x.WildcardSupported + } + return false +} + +func (x *SubscribePreference) GetMinSampleInterval() uint64 { + if x != nil { + return x.MinSampleInterval + } + return 0 +} + +var File_sonic_debug_proto protoreflect.FileDescriptor + +var file_sonic_debug_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x73, 0x6f, 0x6e, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6e, 0x6f, 0x69, 0x2e, 0x73, 0x6f, 0x6e, 0x69, 0x63, 0x1a, + 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2f, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x39, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, + 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x8c, 0x02, 0x0a, + 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x16, 0x2e, 0x67, 0x6e, 0x6d, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x12, + 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, + 0x72, 0x64, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6d, + 0x69, 0x6e, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6d, 0x69, 0x6e, 0x53, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x32, 0x6a, 0x0a, 0x05, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x12, 0x61, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, + 0x23, 0x2e, 0x67, 0x6e, 0x6f, 0x69, 0x2e, 0x73, 0x6f, 0x6e, 0x69, 0x63, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x67, 0x6e, 0x6f, 0x69, 0x2e, 0x73, 0x6f, 0x6e, 0x69, + 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x30, 0x01, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x3b, 0x67, 0x6e, + 0x6f, 0x69, 0x5f, 0x73, 0x6f, 0x6e, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_sonic_debug_proto_rawDescOnce sync.Once + file_sonic_debug_proto_rawDescData = file_sonic_debug_proto_rawDesc +) + +func file_sonic_debug_proto_rawDescGZIP() []byte { + file_sonic_debug_proto_rawDescOnce.Do(func() { + file_sonic_debug_proto_rawDescData = protoimpl.X.CompressGZIP(file_sonic_debug_proto_rawDescData) + }) + return file_sonic_debug_proto_rawDescData +} + +var file_sonic_debug_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_sonic_debug_proto_goTypes = []interface{}{ + (*SubscribePreferencesReq)(nil), // 0: gnoi.sonic.SubscribePreferencesReq + (*SubscribePreference)(nil), // 1: gnoi.sonic.SubscribePreference + (*gnmi.Path)(nil), // 2: gnmi.Path + (gnmi.SubscriptionMode)(0), // 3: gnmi.SubscriptionMode +} +var file_sonic_debug_proto_depIdxs = []int32{ + 2, // 0: gnoi.sonic.SubscribePreferencesReq.path:type_name -> gnmi.Path + 2, // 1: gnoi.sonic.SubscribePreference.path:type_name -> gnmi.Path + 3, // 2: gnoi.sonic.SubscribePreference.target_defined_mode:type_name -> gnmi.SubscriptionMode + 0, // 3: gnoi.sonic.Debug.GetSubscribePreferences:input_type -> gnoi.sonic.SubscribePreferencesReq + 1, // 4: gnoi.sonic.Debug.GetSubscribePreferences:output_type -> gnoi.sonic.SubscribePreference + 4, // [4:5] is the sub-list for method output_type + 3, // [3:4] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_sonic_debug_proto_init() } +func file_sonic_debug_proto_init() { + if File_sonic_debug_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sonic_debug_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribePreferencesReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sonic_debug_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribePreference); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sonic_debug_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_sonic_debug_proto_goTypes, + DependencyIndexes: file_sonic_debug_proto_depIdxs, + MessageInfos: file_sonic_debug_proto_msgTypes, + }.Build() + File_sonic_debug_proto = out.File + file_sonic_debug_proto_rawDesc = nil + file_sonic_debug_proto_goTypes = nil + file_sonic_debug_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// DebugClient is the client API for Debug service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type DebugClient interface { + // GetSubscribePreferences returns the subscription capability info for specific + // paths and their subpaths. + GetSubscribePreferences(ctx context.Context, in *SubscribePreferencesReq, opts ...grpc.CallOption) (Debug_GetSubscribePreferencesClient, error) +} + +type debugClient struct { + cc grpc.ClientConnInterface +} + +func NewDebugClient(cc grpc.ClientConnInterface) DebugClient { + return &debugClient{cc} +} + +func (c *debugClient) GetSubscribePreferences(ctx context.Context, in *SubscribePreferencesReq, opts ...grpc.CallOption) (Debug_GetSubscribePreferencesClient, error) { + stream, err := c.cc.NewStream(ctx, &_Debug_serviceDesc.Streams[0], "/gnoi.sonic.Debug/GetSubscribePreferences", opts...) + if err != nil { + return nil, err + } + x := &debugGetSubscribePreferencesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Debug_GetSubscribePreferencesClient interface { + Recv() (*SubscribePreference, error) + grpc.ClientStream +} + +type debugGetSubscribePreferencesClient struct { + grpc.ClientStream +} + +func (x *debugGetSubscribePreferencesClient) Recv() (*SubscribePreference, error) { + m := new(SubscribePreference) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// DebugServer is the server API for Debug service. +type DebugServer interface { + // GetSubscribePreferences returns the subscription capability info for specific + // paths and their subpaths. + GetSubscribePreferences(*SubscribePreferencesReq, Debug_GetSubscribePreferencesServer) error +} + +// UnimplementedDebugServer can be embedded to have forward compatible implementations. +type UnimplementedDebugServer struct { +} + +func (*UnimplementedDebugServer) GetSubscribePreferences(*SubscribePreferencesReq, Debug_GetSubscribePreferencesServer) error { + return status.Errorf(codes.Unimplemented, "method GetSubscribePreferences not implemented") +} + +func RegisterDebugServer(s *grpc.Server, srv DebugServer) { + s.RegisterService(&_Debug_serviceDesc, srv) +} + +func _Debug_GetSubscribePreferences_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SubscribePreferencesReq) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(DebugServer).GetSubscribePreferences(m, &debugGetSubscribePreferencesServer{stream}) +} + +type Debug_GetSubscribePreferencesServer interface { + Send(*SubscribePreference) error + grpc.ServerStream +} + +type debugGetSubscribePreferencesServer struct { + grpc.ServerStream +} + +func (x *debugGetSubscribePreferencesServer) Send(m *SubscribePreference) error { + return x.ServerStream.SendMsg(m) +} + +var _Debug_serviceDesc = grpc.ServiceDesc{ + ServiceName: "gnoi.sonic.Debug", + HandlerType: (*DebugServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetSubscribePreferences", + Handler: _Debug_GetSubscribePreferences_Handler, + ServerStreams: true, + }, + }, + Metadata: "sonic_debug.proto", +} diff --git a/proto/gnoi/sonic_debug.proto b/proto/gnoi/sonic_debug.proto new file mode 100644 index 00000000..c61d4342 --- /dev/null +++ b/proto/gnoi/sonic_debug.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +import "github.com/openconfig/gnmi/proto/gnmi/gnmi.proto"; + +package gnoi.sonic; + +option go_package = "./;gnoi_sonic"; + + +// Debug service defines RPCs to get debug information from telemetry server +service Debug { + + // GetSubscribePreferences returns the subscription capability info for specific + // paths and their subpaths. + rpc GetSubscribePreferences(SubscribePreferencesReq) returns (stream SubscribePreference); + +} + +// Request message for GetSubscribePreferences RPC +message SubscribePreferencesReq { + // Retrieve subscribe preferences for these paths. + repeated gnmi.Path path = 1; +} + +// SubscribePreference holds subscription capability information for a path. +message SubscribePreference { + // Resource path, whose subscribe preferences are indicated here. + gnmi.Path path = 1; + // Indicates if ON_CHANGE subscription will be accepted for this path. + bool on_change_supported = 2; + // Indicates how TARGET_DEFINED subscription will be handled for this path. + // It is possible to have target_defined_mode=ON_CHANGE but on_change_supported=false + // when this container/list has both on_change supported and unsupported subpaths. + gnmi.SubscriptionMode target_defined_mode = 3; + // Indicates if wildcard keys are supported for this path. + bool wildcard_supported = 4; + // Minimum SAMPLE interval supported for this path, in nanoseconds. + uint64 min_sample_interval = 5; +}