-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add gnmi_dump tool for debug and unit test #60
Changes from 3 commits
f750373
fd01aa9
5ffcc3a
9b3a841
52e5d89
ce50a22
7ce8590
b3e3ce4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package common_utils | ||
|
||
import ( | ||
"fmt" | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// Use share memory to dump GNMI internal counters, | ||
// GNMI server and gnmi_dump should use memKey to access the share memory, | ||
// memSize is 1024 bytes, so we can support 128 counters | ||
// memMode is 0x380, this value is O_RDWR|IPC_CREAT, | ||
// O_RDWR means: Owner can write and read the file, everyone else can't. | ||
// IPC_CREAT means: Create a shared memory segment if a shared memory identifier does not exist for memKey. | ||
var ( | ||
memKey = 7749 | ||
memSize = 1024 | ||
memMode = 0x380 | ||
) | ||
|
||
func SetMemCounters(counters *[len(CountersName)]uint64) error { | ||
shmid, _, err := syscall.Syscall(syscall.SYS_SHMGET, uintptr(memKey), uintptr(memSize), uintptr(memMode)) | ||
if int(shmid) == -1 { | ||
return fmt.Errorf("syscall error, err: %v\n", err) | ||
} | ||
|
||
shmaddr, _, err := syscall.Syscall(syscall.SYS_SHMAT, shmid, 0, 0) | ||
if int(shmaddr) == -1 { | ||
return fmt.Errorf("syscall error, err: %v\n", err) | ||
} | ||
defer syscall.Syscall(syscall.SYS_SHMDT, shmaddr, 0, 0) | ||
|
||
const size = unsafe.Sizeof(uint64(0)) | ||
addr := uintptr(unsafe.Pointer(shmaddr)) | ||
|
||
for i := 0; i < len(counters); i++ { | ||
*(*uint64)(unsafe.Pointer(addr)) = counters[i] | ||
addr += size | ||
} | ||
return nil | ||
} | ||
|
||
func GetMemCounters(counters *[len(CountersName)]uint64) error { | ||
shmid, _, err := syscall.Syscall(syscall.SYS_SHMGET, uintptr(memKey), uintptr(memSize), uintptr(memMode)) | ||
if int(shmid) == -1 { | ||
return fmt.Errorf("syscall error, err: %v\n", err) | ||
} | ||
|
||
shmaddr, _, err := syscall.Syscall(syscall.SYS_SHMAT, shmid, 0, 0) | ||
if int(shmaddr) == -1 { | ||
return fmt.Errorf("syscall error, err: %v\n", err) | ||
} | ||
defer syscall.Syscall(syscall.SYS_SHMDT, shmaddr, 0, 0) | ||
|
||
const size = unsafe.Sizeof(uint64(0)) | ||
addr := uintptr(unsafe.Pointer(shmaddr)) | ||
|
||
for i := 0; i < len(counters); i++ { | ||
counters[i] = *(*uint64)(unsafe.Pointer(addr)) | ||
addr += size | ||
} | ||
return nil | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"github.com/sonic-net/sonic-gnmi/common_utils" | ||
) | ||
|
||
const help = ` | ||
gnmi_dump is used to dump internal counters for debugging purpose, | ||
including GNMI request counter, GNOI request counter and DBUS request counter. | ||
` | ||
|
||
func main() { | ||
flag.Usage = func() { | ||
fmt.Print(help) | ||
} | ||
flag.Parse() | ||
var counters [len(common_utils.CountersName)]uint64 | ||
err := common_utils.GetMemCounters(&counters) | ||
if err != nil { | ||
fmt.Printf("Error: Fail to read counters, %v", err) | ||
return | ||
} | ||
fmt.Printf("Dump GNMI counters\n") | ||
for i := 0; i < len(common_utils.CountersName); i++ { | ||
fmt.Printf("%v---%v\n", common_utils.CountersName[i], counters[i]) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,6 +124,8 @@ func NewServer(config *Config, opts []grpc.ServerOption) (*Server, error) { | |
return nil, errors.New("config not provided") | ||
} | ||
|
||
common_utils.InitCounters() | ||
|
||
s := grpc.NewServer(opts...) | ||
reflection.Register(s) | ||
|
||
|
@@ -274,26 +276,32 @@ func (s *Server) checkEncodingAndModel(encoding gnmipb.Encoding, models []*gnmip | |
|
||
// Get implements the Get RPC in gNMI spec. | ||
func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetResponse, error) { | ||
common_utils.IncCounter("GNMI get") | ||
ctx, err := authenticate(s.config.UserAuth, ctx) | ||
if err != nil { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, err | ||
} | ||
|
||
if req.GetType() != gnmipb.GetRequest_ALL { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Errorf(codes.Unimplemented, "unsupported request type: %s", gnmipb.GetRequest_DataType_name[int32(req.GetType())]) | ||
} | ||
|
||
if err = s.checkEncodingAndModel(req.GetEncoding(), req.GetUseModels()); err != nil { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Error(codes.Unimplemented, err.Error()) | ||
} | ||
|
||
var target string | ||
prefix := req.GetPrefix() | ||
if prefix == nil { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Error(codes.Unimplemented, "No target specified in prefix") | ||
} else { | ||
target = prefix.GetTarget() | ||
if target == "" { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Error(codes.Unimplemented, "Empty target data not supported yet") | ||
} | ||
} | ||
|
@@ -315,11 +323,13 @@ func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetRe | |
} | ||
|
||
if err != nil { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Error(codes.NotFound, err.Error()) | ||
} | ||
notifications := make([]*gnmipb.Notification, len(paths)) | ||
spbValues, err := dc.Get(nil) | ||
if err != nil { | ||
common_utils.IncCounter("GNMI get fail") | ||
return nil, status.Error(codes.NotFound, err.Error()) | ||
} | ||
|
||
|
@@ -339,8 +349,14 @@ func (s *Server) Get(ctx context.Context, req *gnmipb.GetRequest) (*gnmipb.GetRe | |
} | ||
|
||
func (s *Server) Set(ctx context.Context, req *gnmipb.SetRequest) (*gnmipb.SetResponse, error) { | ||
common_utils.IncCounter("GNMI set") | ||
if s.config.EnableTranslibWrite == false { | ||
common_utils.IncCounter("GNMI set fail") | ||
return nil, grpc.Errorf(codes.Unimplemented, "GNMI is in read-only mode") | ||
} | ||
ctx, err := authenticate(s.config.UserAuth, ctx) | ||
if err != nil { | ||
common_utils.IncCounter("GNMI set fail") | ||
return nil, err | ||
} | ||
var results []*gnmipb.UpdateResult | ||
|
@@ -388,13 +404,11 @@ func (s *Server) Set(ctx context.Context, req *gnmipb.SetRequest) (*gnmipb.SetRe | |
/* Add to Set response results. */ | ||
results = append(results, &res) | ||
} | ||
if s.config.EnableTranslibWrite { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I move the check to line 353 to simplify logic for set fail, when EnableTranslibWrite is false, no need to authenticate and no need to update result. |
||
err = dc.Set(req.GetDelete(), req.GetReplace(), req.GetUpdate()) | ||
} else { | ||
return nil, grpc.Errorf(codes.Unimplemented, "Telemetry is in read-only mode") | ||
err = dc.Set(req.GetDelete(), req.GetReplace(), req.GetUpdate()) | ||
if err != nil { | ||
common_utils.IncCounter("GNMI set fail") | ||
} | ||
|
||
|
||
return &gnmipb.SetResponse{ | ||
Prefix: req.GetPrefix(), | ||
Response: results, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,6 +107,25 @@ func createServer(t *testing.T, port int64) *Server { | |
return s | ||
} | ||
|
||
func createReadServer(t *testing.T, port int64) *Server { | ||
certificate, err := testcert.NewCert() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this is a helper function it should be marked as such by calling |
||
if err != nil { | ||
t.Errorf("could not load server key pair: %s", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
} | ||
tlsCfg := &tls.Config{ | ||
ClientAuth: tls.RequestClientCert, | ||
Certificates: []tls.Certificate{certificate}, | ||
} | ||
|
||
opts := []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} | ||
cfg := &Config{Port: port, EnableTranslibWrite: false} | ||
s, err := NewServer(cfg, opts) | ||
if err != nil { | ||
t.Errorf("Failed to create gNMI server: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comment here. This should be |
||
} | ||
return s | ||
} | ||
|
||
func createAuthServer(t *testing.T, port int64) *Server { | ||
certificate, err := testcert.NewCert() | ||
if err != nil { | ||
|
@@ -118,7 +137,7 @@ func createAuthServer(t *testing.T, port int64) *Server { | |
} | ||
|
||
opts := []grpc.ServerOption{grpc.Creds(credentials.NewTLS(tlsCfg))} | ||
cfg := &Config{Port: port, UserAuth: AuthTypes{"password": true, "cert": true, "jwt": true}} | ||
cfg := &Config{Port: port, EnableTranslibWrite: true, UserAuth: AuthTypes{"password": true, "cert": true, "jwt": true}} | ||
s, err := NewServer(cfg, opts) | ||
if err != nil { | ||
t.Errorf("Failed to create gNMI server: %v", err) | ||
|
@@ -732,6 +751,102 @@ func TestGnmiSet(t *testing.T) { | |
s.s.Stop() | ||
} | ||
|
||
func TestGnmiSetReadOnly(t *testing.T) { | ||
s := createReadServer(t, 8081) | ||
go runServer(t, s) | ||
defer s.s.Stop() | ||
|
||
tlsConfig := &tls.Config{InsecureSkipVerify: true} | ||
opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} | ||
|
||
targetAddr := "127.0.0.1:8081" | ||
conn, err := grpc.Dial(targetAddr, opts...) | ||
if err != nil { | ||
t.Fatalf("Dialing to %q failed: %v", targetAddr, err) | ||
} | ||
defer conn.Close() | ||
|
||
gClient := pb.NewGNMIClient(conn) | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
|
||
req := &pb.SetRequest{} | ||
_, err = gClient.Set(ctx, req) | ||
gotRetStatus, ok := status.FromError(err) | ||
if !ok { | ||
t.Fatal("got a non-grpc error from grpc call") | ||
} | ||
wantRetCode := codes.Unimplemented | ||
if gotRetStatus.Code() != wantRetCode { | ||
t.Log("err: ", err) | ||
t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), wantRetCode) | ||
} | ||
} | ||
|
||
func TestGnmiSetAuthFail(t *testing.T) { | ||
s := createAuthServer(t, 8081) | ||
go runServer(t, s) | ||
defer s.s.Stop() | ||
|
||
tlsConfig := &tls.Config{InsecureSkipVerify: true} | ||
opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} | ||
|
||
targetAddr := "127.0.0.1:8081" | ||
conn, err := grpc.Dial(targetAddr, opts...) | ||
if err != nil { | ||
t.Fatalf("Dialing to %q failed: %v", targetAddr, err) | ||
} | ||
defer conn.Close() | ||
|
||
gClient := pb.NewGNMIClient(conn) | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
|
||
req := &pb.SetRequest{} | ||
_, err = gClient.Set(ctx, req) | ||
gotRetStatus, ok := status.FromError(err) | ||
if !ok { | ||
t.Fatal("got a non-grpc error from grpc call") | ||
} | ||
wantRetCode := codes.Unauthenticated | ||
if gotRetStatus.Code() != wantRetCode { | ||
t.Log("err: ", err) | ||
t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), wantRetCode) | ||
} | ||
} | ||
|
||
func TestGnmiGetAuthFail(t *testing.T) { | ||
s := createAuthServer(t, 8081) | ||
go runServer(t, s) | ||
defer s.s.Stop() | ||
|
||
tlsConfig := &tls.Config{InsecureSkipVerify: true} | ||
opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))} | ||
|
||
targetAddr := "127.0.0.1:8081" | ||
conn, err := grpc.Dial(targetAddr, opts...) | ||
if err != nil { | ||
t.Fatalf("Dialing to %q failed: %v", targetAddr, err) | ||
} | ||
defer conn.Close() | ||
|
||
gClient := pb.NewGNMIClient(conn) | ||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
defer cancel() | ||
|
||
req := &pb.GetRequest{} | ||
_, err = gClient.Get(ctx, req) | ||
gotRetStatus, ok := status.FromError(err) | ||
if !ok { | ||
t.Fatal("got a non-grpc error from grpc call") | ||
} | ||
wantRetCode := codes.Unauthenticated | ||
if gotRetStatus.Code() != wantRetCode { | ||
t.Log("err: ", err) | ||
t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), wantRetCode) | ||
} | ||
} | ||
|
||
func runGnmiTestGet(t *testing.T, namespace string) { | ||
//t.Log("Start gNMI client") | ||
tlsConfig := &tls.Config{InsecureSkipVerify: true} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use string as an index? If an enum were used then there would be no need for the loop in line 81, right?