Skip to content
This repository has been archived by the owner on Aug 31, 2022. It is now read-only.

Adding new data set to query Sonic OS version. #50

Merged
merged 3 commits into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 105 additions & 63 deletions gnmi_server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ package gnmi
import (
"crypto/tls"
"encoding/json"
"fmt"

testcert "github.com/Azure/sonic-telemetry/testdata/tls"
"github.com/go-redis/redis"
"github.com/golang/protobuf/proto"

"io/ioutil"
"os"
"os/exec"
"reflect"
"testing"
"time"

"github.com/kylelemons/godebug/pretty"
"github.com/openconfig/gnmi/client"
pb "github.com/openconfig/gnmi/proto/gnmi"
Expand All @@ -19,17 +28,11 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/status"
"io/ioutil"
"os"
"os/exec"
"reflect"
"testing"
"time"

// Register supported client types.
sdc "github.com/Azure/sonic-telemetry/sonic_data_client"
sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config"
gclient "github.com/jipanyang/gnmi/client/gnmi"

)

var clientTypes = []string{gclient.Type}
Expand Down Expand Up @@ -121,7 +124,7 @@ func runTestGet(t *testing.T, ctx context.Context, gClient pb.GNMIClient, pathTa
t.Log("err: ", err)
t.Fatalf("got return code %v, want %v", gotRetStatus.Code(), wantRetCode)
}

// Check response value
if valTest {
var gotVal interface{}
Expand Down Expand Up @@ -389,7 +392,7 @@ func prepareDbTranslib(t *testing.T) {
rclient := getRedisClient(t)
rclient.FlushDB()
rclient.Close()

//Enable keysapce notification
os.Setenv("PATH", "/usr/bin:/sbin:/bin:/usr/local/bin")
cmd := exec.Command("redis-cli", "config", "set", "notify-keyspace-events", "KEA")
Expand Down Expand Up @@ -512,8 +515,6 @@ func TestGnmiSet(t *testing.T) {
s.s.Stop()
}



func TestGnmiGet(t *testing.T) {
//t.Log("Start server")
s := createServer(t)
Expand Down Expand Up @@ -578,14 +579,47 @@ func TestGnmiGet(t *testing.T) {
t.Fatalf("read file %v err: %v", fileName, err)
}

tds := []struct {
type testCase struct {
desc string
pathTarget string
textPbPath string
wantRetCode codes.Code
wantRespVal interface{}
valTest bool
}{{
testInit func()
}

// A helper function create test cases for 'osversion/build' queries.
createBuildVersionTestCase := func(desc string, wantedVersion string, versionFileContent string, fileReadErr error) testCase {
return testCase{
desc: desc,
pathTarget: "OTHERS",
textPbPath: `
macikgozwa marked this conversation as resolved.
Show resolved Hide resolved
elem: <name: "osversion" >
elem: <name: "build" >
`,
wantRetCode: codes.OK,
valTest: true,
wantRespVal: []byte(wantedVersion),
testInit: func() {
// Override file read function to mock file content.
sdc.ImplIoutilReadFile = func(filePath string) ([]byte, error) {
if filePath == sdc.SonicVersionFilePath {
if fileReadErr != nil {
return nil, fileReadErr
}
return []byte(versionFileContent), nil
}
return ioutil.ReadFile(filePath)
}

// Reset the cache so that the content gets loaded again.
sdc.InvalidateVersionFileStash()
},
}
}

tds := []testCase{{
desc: "Test non-existing path Target",
pathTarget: "MY_DB",
textPbPath: `
Expand Down Expand Up @@ -702,9 +736,17 @@ func TestGnmiGet(t *testing.T) {
wantRetCode: codes.OK,
wantRespVal: countersEthernetWildcardPfcwdByte,
},
createBuildVersionTestCase("get osversion/build", `{"build_version": "sonic.12345678.90"}`, "build_version: '12345678.90'\ndebian_version: '9.13'", nil),
createBuildVersionTestCase("get osversion/build file load error", `{"build_version": "sonic.NA"}`, "", fmt.Errorf("Cannot access '%v' ", sdc.SonicVersionFilePath)),
createBuildVersionTestCase("get osversion/build file parse error", `{"build_version": "sonic.NA"}`, "no a valid YAML content", nil),
createBuildVersionTestCase("get osversion/build different value", `{"build_version": "sonic.23456789.01"}`, "build_version: '23456789.01'\ndebian_version: '9.15'", nil),
}

for _, td := range tds {
if td.testInit != nil {
td.testInit()
}

t.Run(td.desc, func(t *testing.T) {
runTestGet(t, ctx, gClient, td.pathTarget, td.textPbPath, td.wantRetCode, td.wantRespVal, td.valTest)
})
Expand Down Expand Up @@ -745,56 +787,56 @@ func TestGnmiGetTranslib(t *testing.T) {
}{

//These tests only work on the real switch platform, since they rely on files in the /proc and another running service
// {
// desc: "Get OC Platform",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-platform:components" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System State",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "state" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System CPU",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "cpus" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System memory",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "memory" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System processes",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "processes" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC Platform",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-platform:components" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System State",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "state" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System CPU",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "cpus" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System memory",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "memory" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
// {
// desc: "Get OC System processes",
// pathTarget: "OC_YANG",
// textPbPath: `
// elem: <name: "openconfig-system:system" > elem: <name: "processes" >
// `,
// wantRetCode: codes.OK,
// wantRespVal: emptyRespVal,
// valTest: false,
// },
{
desc: "Get OC Interfaces",
pathTarget: "OC_YANG",
Expand Down
81 changes: 73 additions & 8 deletions sonic_data_client/non_db_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@ package client
import (
"encoding/json"
"fmt"
"io/ioutil"
"sync"
"time"

"gopkg.in/yaml.v2"

spb "github.com/Azure/sonic-telemetry/proto"
"github.com/Workiva/go-datastructures/queue"
linuxproc "github.com/c9s/goprocinfo/linux"
log "github.com/golang/glog"
gnmipb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/Workiva/go-datastructures/queue"
"sync"
"time"
)

// Non db client is to Handle
// <1> data not in SONiC redis db

const (
statsRingCap uint64 = 3000 // capacity of statsRing.

// SonicVersionFilePath is the path of build version YML file.
SonicVersionFilePath = "/etc/sonic/sonic_version.yml"
)

type dataGetFunc func() ([]byte, error)
Expand All @@ -32,14 +39,36 @@ type statsRing struct {
mu sync.RWMutex // Mutex for data protection
}

// SonicVersionInfo is a data model to serialize '/etc/sonic/sonic_version.yml'
type SonicVersionInfo struct {
BuildVersion string `yaml:"build_version" json:"build_version"`
}

// sonicVersionYmlStash caches the content of '/etc/sonic/sonic_version.yml'
// Assumed that the content of the file doesn't change during the lifetime of telemetry service.
type sonicVersionYmlStash struct {
once sync.Once // sync object to make sure file is loaded only once.
versionInfo SonicVersionInfo
}

// InvalidateVersionFileStash invalidates the cache that keeps version file content.
func InvalidateVersionFileStash() {
versionFileStash = sonicVersionYmlStash{}
}

var (
clientTrie *Trie
statsR statsRing

// path2DataFuncTbl is used to populate trie tree which is reponsible
versionFileStash sonicVersionYmlStash

// ImplIoutilReadFile points to the implementation of ioutil.ReadFile. Should be overridden by UTs only.
ImplIoutilReadFile func(string) ([]byte, error) = ioutil.ReadFile

// path2DataFuncTbl is used to populate trie tree which is responsible
// for getting data at the path specified
path2DataFuncTbl = []path2DataFunc{
{ // Get cpu utilizaation
{ // Get cpu utilization
path: []string{"OTHERS", "platform", "cpu"},
getFunc: dataGetFunc(getCpuUtil),
},
Expand All @@ -63,6 +92,10 @@ var (
path: []string{"OTHERS", "proc", "stat"},
getFunc: dataGetFunc(getProcStat),
},
{ // OS build version
path: []string{"OTHERS", "osversion", "build"},
getFunc: dataGetFunc(getBuildVersion),
},
}
)

Expand Down Expand Up @@ -246,6 +279,39 @@ func getProcStat() ([]byte, error) {
return b, nil
}

func getBuildVersion() ([]byte, error) {

// Load and parse the content of version file
versionFileStash.once.Do(func() {
versionFileStash.versionInfo.BuildVersion = "sonic.NA"

fileContent, err := ImplIoutilReadFile(SonicVersionFilePath)
if err != nil {
log.Errorf("Failed to read '%v', %v", SonicVersionFilePath, err)
macikgozwa marked this conversation as resolved.
Show resolved Hide resolved
return
}

err = yaml.Unmarshal(fileContent, &versionFileStash.versionInfo)
if err != nil {
log.Errorf("Failed to parse '%v', %v", SonicVersionFilePath, err)
return
}

// Prepend 'sonic.' to the build version string.
if versionFileStash.versionInfo.BuildVersion != "sonic.NA" {
versionFileStash.versionInfo.BuildVersion = "sonic." + versionFileStash.versionInfo.BuildVersion
}
})

b, err := json.Marshal(versionFileStash.versionInfo)
if err != nil {
log.V(2).Infof("%v", err)
return b, err
}
log.V(4).Infof("getBuildVersion, output %v", string(b))
return b, nil
}

func pollStats() {
for {
stat, err := linuxproc.ReadStat("/proc/stat")
Expand Down Expand Up @@ -411,10 +477,9 @@ func (c *NonDbClient) Close() error {
return nil
}

func (c *NonDbClient) Set(path *gnmipb.Path, t *gnmipb.TypedValue, flagop int) error {
func (c *NonDbClient) Set(path *gnmipb.Path, t *gnmipb.TypedValue, flagop int) error {
return nil
}
func (c *NonDbClient) Capabilities() ([]gnmipb.ModelData) {
func (c *NonDbClient) Capabilities() []gnmipb.ModelData {
return nil
}