Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
54 changes: 51 additions & 3 deletions gnmi_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"math"
"reflect"
"strings"

"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -153,8 +154,6 @@ func gnmiParseUpdates(parseOrigin bool, prefix *gnmi.Path, updates []*gnmi.Updat
xpath string
tmpJXpaths = jnprXpathDetails{xPaths: map[string]interface{}{}}
jXpaths *jnprXpathDetails

err error
)

if prefixPath == "" {
Expand All @@ -181,10 +180,20 @@ func gnmiParseUpdates(parseOrigin bool, prefix *gnmi.Path, updates []*gnmi.Updat
[]string{gGnmiJuniperHeaderFieldName, gGnmiJuniperPublishTsFieldName})

if len(internalFields) == 0 {
xpathValue[xpath], err = gnmiParseValue(update.GetVal(), false, enableUint)
parsedVal, err := gnmiParseValue(update.GetVal(), false, enableUint)
if err != nil {
return nil, err
}
switch parsedVal.(type) {
case map[string]interface{}:
jsonXpaths := parsedVal.(map[string]interface{})
for k, v := range jsonXpaths {
gnmiPathWithJsonXpaths := xpath + k
xpathValue[gnmiPathWithJsonXpaths] = v
}
default:
xpathValue[xpath] = parsedVal
}
} else {
if _, ok := internalFields[gGnmiJuniperHeaderFieldName]; ok {
tmpJXpaths.hdrXpath = xpath
Expand Down Expand Up @@ -278,6 +287,38 @@ func gnmiParsePath(prefix string, pes []*gnmi.PathElem, kvpairs map[string]strin
return prefix, kvpairs, lookForOutput
}

func gnmiParseJsontoXpath(jsonData map[string]interface{}, currXpath string, jsonXpaths map[string]interface{}) error {
var (
err error
value interface{}
)
for k, dataValue := range jsonData {
xpath := currXpath + gXPathTokenPathSep + k
switch dataValue.(type) {
case map[string]interface{}:
gnmiParseJsontoXpath(dataValue.(map[string]interface{}), xpath, jsonXpaths)
case json.Number:
jsonNumber := dataValue.(json.Number)
if strings.Contains(jsonNumber.String(), ".") {
value, err = jsonNumber.Float64()
} else {
value, err = jsonNumber.Int64()
}
if err != nil {
errMsg := fmt.Sprintf("Parsing json number failed, error: %v, jsonNumber: %s", err, jsonNumber.String())
return errors.New(errMsg)
}
jsonXpaths[xpath] = value
case bool, string:
jsonXpaths[xpath] = dataValue
default:
errMsg := fmt.Sprintf("Not a number/bool/string, jsonValue: %v, type :%v", dataValue, reflect.TypeOf(dataValue))
return errors.New(errMsg)
}
}
return nil
}

// Convert gNMI value to data types that Influx Line Protocol supports.
func gnmiParseValue(gnmiValue *gnmi.TypedValue, ts bool, enableUint bool) (interface{}, error) {
var (
Expand Down Expand Up @@ -398,6 +439,13 @@ func gnmiParseValue(gnmiValue *gnmi.TypedValue, ts bool, enableUint bool) (inter

case bool, string:
value = decodedValue
case map[string]interface{}:
jsonXpaths := make(map[string]interface{})
err = gnmiParseJsontoXpath(decodedValue.(map[string]interface{}), "", jsonXpaths)
if err != nil {
return nil, err
}
return jsonXpaths, nil
default:
errMsg := fmt.Sprintf("Not a number/bool/string, jsonValue: %s", dst.String())
return nil, errors.New(errMsg)
Expand Down
98 changes: 98 additions & 0 deletions gnmi_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,104 @@ func TestGnmiParseUpdates(t *testing.T) {
},
enableUint: false,
},
{
name: "updates-valid-with-misc-complex-types-json_ietf",
err: false,
parseOrigin: false,
prefix: &gnmi.Path{
Origin: "",
Elem: []*gnmi.PathElem{
{Name: "interface"},
{Name: "interface", Key: map[string]string{"k1": "foo"}},
{Name: "subinterfaces"},
{Name: "subinterface", Key: map[string]string{"k1": "foo1", "k2": "bar1"}},
},
},
updates: []*gnmi.Update{
{
Path: &gnmi.Path{
Origin: "",
Elem: []*gnmi.PathElem{
{Name: "state"},
{Name: "mtu"},
},
},
Val: &gnmi.TypedValue{
Value: &gnmi.TypedValue_JsonVal{
JsonVal: []byte(`{
"cpu": {
"utilization": {
"state": {
"instant": 9,
"avg": 8,
"min": 8,
"max": 10,
"interval": "300000000000",
"min-time": "1709048542669532934",
"max-time": "1709048602669532934"
}
}
}
}`),
},
},
},
{
Path: &gnmi.Path{
Origin: "",
Elem: []*gnmi.PathElem{
{Name: "state"},
{Name: "mtu"},
},
},
Val: &gnmi.TypedValue{
Value: &gnmi.TypedValue_JsonVal{
JsonVal: []byte(`{"js1": {"js2": "js3"}}`),
},
},
},
{
Path: &gnmi.Path{
Origin: "",
Elem: []*gnmi.PathElem{
{Name: "state"},
{Name: "counters"},
{Name: "in-octets"},
},
},
Val: &gnmi.TypedValue{
Value: &gnmi.TypedValue_JsonVal{
JsonVal: []byte(`{"sum": {"max": "10"}}`),
},
},
},
},
parseOutput: &gnmiParseOutputT{
prefixPath: "/a/b/c/d",
kvpairs: map[string]string{"/a/b/@k1": "foo", "/a/b/c/d/@k1": "foo1", "/a/b/c/d/@k2": "bar1"},
xpaths: map[string]interface{}{},
},
output: &gnmiParseOutputT{
prefixPath: "/a/b/c/d",
kvpairs: map[string]string{
"/a/b/@k1": "foo",
"/a/b/c/d/@k1": "foo1",
"/a/b/c/d/@k2": "bar1",
},
xpaths: map[string]interface{}{
"/a/b/c/d/state/mtu/cpu/utilization/state/instant": int64(9),
"/a/b/c/d/state/mtu/cpu/utilization/state/avg": int64(8),
"/a/b/c/d/state/mtu/cpu/utilization/state/min": int64(8),
"/a/b/c/d/state/mtu/cpu/utilization/state/max": int64(10),
"/a/b/c/d/state/mtu/cpu/utilization/state/interval": "300000000000",
"/a/b/c/d/state/mtu/cpu/utilization/state/min-time": "1709048542669532934",
"/a/b/c/d/state/mtu/cpu/utilization/state/max-time": "1709048602669532934",
"/a/b/c/d/state/mtu/js1/js2": "js3",
"/a/b/c/d/state/counters/in-octets/sum/max": "10",
},
},
enableUint: false,
},
{
name: "updates-valid-no-prefix-with-origin--config--donotParseOrigin",
err: false,
Expand Down