diff --git a/patches/apply.sh b/patches/apply.sh index 8d76c6329..2e91b6dca 100755 --- a/patches/apply.sh +++ b/patches/apply.sh @@ -38,7 +38,7 @@ copy github.com/openconfig/gnmi v0.0.0-20200617225440-d2b4e6a45802 . # Apply patches -patch -d ${DEST_DIR}/github.com/openconfig -p1 < ${PATCH_DIR}/ygot/ygot.patch +patch -d ${DEST_DIR}/github.com/openconfig/ygot -p1 < ${PATCH_DIR}/ygot/ygot.patch patch -d ${DEST_DIR}/github.com/openconfig/goyang -p1 < ${PATCH_DIR}/goyang/goyang.patch diff --git a/patches/ygot/ygot.patch b/patches/ygot/ygot.patch index 6bb8fa79c..888befdbb 100644 --- a/patches/ygot/ygot.patch +++ b/patches/ygot/ygot.patch @@ -1,7 +1,8 @@ -diff -ruN ygot-dir-orig/ygot/generator/generator.go ygot-dir/ygot/generator/generator.go ---- ygot-dir-orig/ygot/generator/generator.go 2020-10-07 14:33:58.430343000 -0700 -+++ ygot-dir/ygot/generator/generator.go 2020-10-27 16:02:47.667985000 -0700 -@@ -109,7 +109,7 @@ +diff --git a/generator/generator.go b/generator/generator.go +index a1c7e5e..e191dc3 100644 +--- a/generator/generator.go ++++ b/generator/generator.go +@@ -109,7 +109,7 @@ func writeGoCodeSingleFile(w io.Writer, goCode *ygen.GeneratedGoCode) error { } // writeIfNotEmpty writes the string s to b if it has a non-zero length. @@ -10,10 +11,11 @@ diff -ruN ygot-dir-orig/ygot/generator/generator.go ygot-dir/ygot/generator/gene if len(s) != 0 { b.WriteString(s) } -diff -ruN ygot-dir-orig/ygot/genutil/common.go ygot-dir/ygot/genutil/common.go ---- ygot-dir-orig/ygot/genutil/common.go 2020-10-07 14:33:58.413742000 -0700 -+++ ygot-dir/ygot/genutil/common.go 2020-10-27 16:02:47.683147000 -0700 -@@ -18,9 +18,9 @@ +diff --git a/genutil/common.go b/genutil/common.go +index 6b9f251..7caee4a 100644 +--- a/genutil/common.go ++++ b/genutil/common.go +@@ -18,9 +18,9 @@ package genutil import ( "fmt" @@ -25,7 +27,7 @@ diff -ruN ygot-dir-orig/ygot/genutil/common.go ygot-dir/ygot/genutil/common.go "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" -@@ -42,7 +42,7 @@ +@@ -42,7 +42,7 @@ const ( ) // WriteIfNotEmpty writes the string s to b if it has a non-zero length. @@ -34,9 +36,10 @@ diff -ruN ygot-dir-orig/ygot/genutil/common.go ygot-dir/ygot/genutil/common.go if len(s) != 0 { b.WriteString(s) } -diff -ruN ygot-dir-orig/ygot/util/debug.go ygot-dir/ygot/util/debug.go ---- ygot-dir-orig/ygot/util/debug.go 2020-10-07 14:33:58.180210000 -0700 -+++ ygot-dir/ygot/util/debug.go 2020-10-27 16:02:47.497624000 -0700 +diff --git a/util/debug.go b/util/debug.go +index 225468e..5c5ea23 100644 +--- a/util/debug.go ++++ b/util/debug.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -47,7 +50,7 @@ diff -ruN ygot-dir-orig/ygot/util/debug.go ygot-dir/ygot/util/debug.go package util import ( -@@ -53,6 +56,14 @@ +@@ -53,6 +56,14 @@ func DbgPrint(v ...interface{}) { fmt.Println(globalIndent + out) } @@ -62,7 +65,7 @@ diff -ruN ygot-dir-orig/ygot/util/debug.go ygot-dir/ygot/util/debug.go // DbgSchema prints v if the package global variable debugSchema is set. // v has the same format as Printf. func DbgSchema(v ...interface{}) { -@@ -177,6 +188,9 @@ +@@ -177,6 +188,9 @@ func SchemaTypeStr(schema *yang.Entry) string { // YangTypeToDebugString returns a debug string representation of a YangType. func YangTypeToDebugString(yt *yang.YangType) string { @@ -72,9 +75,10 @@ diff -ruN ygot-dir-orig/ygot/util/debug.go ygot-dir/ygot/util/debug.go out := fmt.Sprintf("(TypeKind: %s", yang.TypeKindToName[yt.Kind]) if len(yt.Pattern) != 0 { out += fmt.Sprintf(", Pattern: %s", strings.Join(yt.Pattern, " or ")) -diff -ruN ygot-dir-orig/ygot/util/path.go ygot-dir/ygot/util/path.go ---- ygot-dir-orig/ygot/util/path.go 2020-10-07 14:33:58.191131000 -0700 -+++ ygot-dir/ygot/util/path.go 2020-10-27 16:02:47.508799000 -0700 +diff --git a/util/path.go b/util/path.go +index c5709dc..552a2e4 100644 +--- a/util/path.go ++++ b/util/path.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -85,7 +89,7 @@ diff -ruN ygot-dir-orig/ygot/util/path.go ygot-dir/ygot/util/path.go package util import ( -@@ -19,10 +22,13 @@ +@@ -19,10 +22,13 @@ import ( "fmt" "reflect" "strings" @@ -100,7 +104,7 @@ diff -ruN ygot-dir-orig/ygot/util/path.go ygot-dir/ygot/util/path.go // SchemaPaths returns all the paths in the path tag. func SchemaPaths(f reflect.StructField) ([][]string, error) { var out [][]string -@@ -49,25 +55,39 @@ +@@ -49,25 +55,39 @@ func SchemaPaths(f reflect.StructField) ([][]string, error) { // leafref; the schema *yang.Entry for the field is given by // schema.Dir["config"].Dir["a"]. func RelativeSchemaPath(f reflect.StructField) ([]string, error) { @@ -156,7 +160,7 @@ diff -ruN ygot-dir-orig/ygot/util/path.go ygot-dir/ygot/util/path.go } // SchemaTreePath returns the schema tree path of the supplied yang.Entry -@@ -215,6 +235,10 @@ +@@ -215,6 +235,10 @@ func FindLeafRefSchema(schema *yang.Entry, pathStr string) (*yang.Entry, error) refSchema = refSchema.Dir[pe] } @@ -167,9 +171,10 @@ diff -ruN ygot-dir-orig/ygot/util/path.go ygot-dir/ygot/util/path.go return refSchema, nil } -diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go ---- ygot-dir-orig/ygot/util/reflect.go 2020-10-07 14:33:58.196912000 -0700 -+++ ygot-dir/ygot/util/reflect.go 2020-10-27 16:02:47.512819000 -0700 +diff --git a/util/reflect.go b/util/reflect.go +index 6f9f3ea..457a245 100644 +--- a/util/reflect.go ++++ b/util/reflect.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -180,7 +185,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go package util import ( -@@ -191,8 +194,10 @@ +@@ -191,8 +194,10 @@ func InsertIntoSlice(parentSlice interface{}, value interface{}) error { // InsertIntoMap inserts value with key into parent which must be a map. func InsertIntoMap(parentMap interface{}, key interface{}, value interface{}) error { @@ -193,7 +198,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go v := reflect.ValueOf(parentMap) t := reflect.TypeOf(parentMap) -@@ -283,7 +288,7 @@ +@@ -283,7 +288,7 @@ func InsertIntoStruct(parentStruct interface{}, fieldName string, fieldValue int n = reflect.Zero(ft.Type) } @@ -202,7 +207,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go return fmt.Errorf("cannot assign value %v (type %T) to struct field %s (type %v) in struct %T", fieldValue, fieldValue, fieldName, ft.Type, parentStruct) } -@@ -452,14 +457,36 @@ +@@ -452,14 +457,36 @@ func DeepEqualDerefPtrs(a, b interface{}) bool { return cmp.Equal(aa, bb) } @@ -241,7 +246,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go p, err := RelativeSchemaPath(f) if err != nil { return nil, err -@@ -490,6 +517,7 @@ +@@ -490,6 +517,7 @@ func ChildSchema(schema *yang.Entry, f reflect.StructField) (*yang.Entry, error) } if foundSchema { DbgSchema(" - found\n") @@ -249,7 +254,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go return childSchema, nil } DbgSchema(" - not found\n") -@@ -505,11 +533,15 @@ +@@ -505,11 +533,15 @@ func ChildSchema(schema *yang.Entry, f reflect.StructField) (*yang.Entry, error) // path element i.e. choice1/case1/leaf1 path in the schema will have // struct tag `path:"leaf1"`. This implies that only paths with length // 1 are eligible for this matching. @@ -266,7 +271,7 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go for path, entry := range entries { splitPath := SplitPath(path) name := splitPath[len(splitPath)-1] -@@ -517,11 +549,13 @@ +@@ -517,11 +549,13 @@ func ChildSchema(schema *yang.Entry, f reflect.StructField) (*yang.Entry, error) if StripModulePrefix(name) == p[0] { DbgSchema(" - match\n") @@ -280,9 +285,10 @@ diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go return nil, nil } -diff -ruN ygot-dir-orig/ygot/ygen/codegen.go ygot-dir/ygot/ygen/codegen.go ---- ygot-dir-orig/ygot/ygen/codegen.go 2020-10-07 14:33:58.356199000 -0700 -+++ ygot-dir/ygot/ygen/codegen.go 2020-10-27 16:02:47.723601000 -0700 +diff --git a/ygen/codegen.go b/ygen/codegen.go +index 991705e..66f1fe3 100644 +--- a/ygen/codegen.go ++++ b/ygen/codegen.go @@ -15,6 +15,10 @@ // Package ygen contains a library to generate Go structs from a YANG model. // The Goyang parsing library is used to parse YANG. The output can consider @@ -294,7 +300,7 @@ diff -ruN ygot-dir-orig/ygot/ygen/codegen.go ygot-dir/ygot/ygen/codegen.go package ygen import ( -@@ -946,7 +950,7 @@ +@@ -946,7 +950,7 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[ dirs[ch.Path()] = ch // Recurse down the tree. errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, modules)) @@ -303,9 +309,10 @@ diff -ruN ygot-dir-orig/ygot/ygen/codegen.go ygot-dir/ygot/ygen/codegen.go continue default: errs = util.AppendErr(errs, fmt.Errorf("unknown type of entry %v in findMappableEntities for %s", e.Kind, e.Path())) -diff -ruN ygot-dir-orig/ygot/ygen/genstate.go ygot-dir/ygot/ygen/genstate.go ---- ygot-dir-orig/ygot/ygen/genstate.go 2020-10-07 14:33:58.365616000 -0700 -+++ ygot-dir/ygot/ygen/genstate.go 2020-10-27 16:02:47.741918000 -0700 +diff --git a/ygen/genstate.go b/ygen/genstate.go +index e39c478..b7766f0 100644 +--- a/ygen/genstate.go ++++ b/ygen/genstate.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -316,7 +323,7 @@ diff -ruN ygot-dir-orig/ygot/ygen/genstate.go ygot-dir/ygot/ygen/genstate.go package ygen import ( -@@ -83,6 +86,7 @@ +@@ -83,6 +86,7 @@ func newEnumGenState() *enumGenState { // noUnderscores boolean. func (s *enumGenState) enumeratedUnionEntry(e *yang.Entry, compressPaths, noUnderscores bool) ([]*yangEnum, error) { var es []*yangEnum @@ -324,7 +331,7 @@ diff -ruN ygot-dir-orig/ygot/ygen/genstate.go ygot-dir/ygot/ygen/genstate.go for _, t := range util.EnumeratedUnionTypes(e.Type.Type) { var en *yangEnum -@@ -111,20 +115,36 @@ +@@ -111,20 +115,36 @@ func (s *enumGenState) enumeratedUnionEntry(e *yang.Entry, compressPaths, noUnde } } @@ -372,10 +379,72 @@ diff -ruN ygot-dir-orig/ygot/ygen/genstate.go ygot-dir/ygot/ygen/genstate.go es = append(es, en) } -diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go ---- ygot-dir-orig/ygot/ygot/render.go 2020-10-07 14:33:58.224638000 -0700 -+++ ygot-dir/ygot/ygot/render.go 2020-10-27 16:02:47.544072000 -0700 -@@ -45,6 +45,11 @@ +diff --git a/ygen/schemaparse.go b/ygen/schemaparse.go +index f71e7e6..800ec60 100644 +--- a/ygen/schemaparse.go ++++ b/ygen/schemaparse.go +@@ -43,10 +43,28 @@ func buildJSONTree(ms []*yang.Entry, dn map[string]string, fakeroot *yang.Entry, + } + for _, m := range ms { + annotateChildren(m, dn) ++ ++ // a leaf with a different prefix ++ if len(m.Dir) == 0 { ++ if _, found := m.Annotation["modulename"]; found { ++ rootEntry.Dir[m.Name] = m ++ } ++ } ++ + for _, ch := range util.Children(m) { + if _, ex := rootEntry.Dir[ch.Name]; ex { + return nil, fmt.Errorf("overlapping root children for key %s", ch.Name) + } ++ ++ ch.Annotation["namespace"] = m.Namespace().Name ++ ch.Annotation["modulename"] = m.Prefix.Parent.NName() ++ ch.Annotation["revision"] = m.Prefix.Parent.(*yang.Module).Current() ++ if m.Prefix.Parent.(*yang.Module).Organization != nil { ++ ch.Annotation["organization"] = m.Prefix.Parent.(*yang.Module).Organization.Name ++ } else { ++ ch.Annotation["organization"] = "" ++ } ++ + rootEntry.Dir[ch.Name] = ch + } + } +@@ -101,6 +119,7 @@ func annotateChildren(e *yang.Entry, dn map[string]string) { + // corresponds to a YANG directory. + func annotateEntry(e *yang.Entry, dn map[string]string) { + e.Description = "" ++ e.Augmented = nil + if e.Annotation == nil { + e.Annotation = map[string]interface{}{} + } +@@ -110,6 +129,11 @@ func annotateEntry(e *yang.Entry, dn map[string]string) { + if e.IsDir() { + e.Annotation["schemapath"] = e.Path() + } ++ // add the modulename if the prefix is different from its parent ++ // and it belong to "module" only, i.e. filter out submodule ++ if e.Parent != nil && e.Namespace().Name != e.Parent.Namespace().Name && e.Prefix.Parent.Statement().Keyword == "module" { ++ e.Annotation["modulename"] = e.Prefix.Parent.NName() ++ } + } + + // WriteGzippedByteSlice takes an input slice of bytes, gzips it +diff --git a/ygot/render.go b/ygot/render.go +index 0d649c3..393e7fa 100644 +--- a/ygot/render.go ++++ b/ygot/render.go +@@ -20,6 +20,7 @@ import ( + "fmt" + "reflect" + "sort" ++ "strconv" + "strings" + + "github.com/golang/protobuf/proto" +@@ -45,6 +46,11 @@ type path struct { p *gnmiPath } @@ -387,7 +456,66 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go func (p *path) String() string { if p.p.isPathElemPath() { return proto.MarshalTextString(&gnmipb.Path{Elem: p.p.pathElemPath}) -@@ -870,19 +875,21 @@ +@@ -272,6 +278,8 @@ type GNMINotificationsConfig struct { + // of PathElem messages. This path format is used by gNMI 0.4.0 and + // above. Used if PathElem is set. + PathElemPrefix []*gnmipb.PathElem ++ // Encoding to be used for atomic values - like keyless lists ++ Encoding gnmipb.Encoding + } + + // TogNMINotifications takes an input GoStruct and renders it to slice of +@@ -297,7 +305,7 @@ func TogNMINotifications(s GoStruct, ts int64, cfg GNMINotificationsConfig) ([]* + return nil, err + } + +- msgs, err := leavesToNotifications(leaves, ts, pfx) ++ msgs, err := leavesToNotifications(leaves, ts, pfx, cfg.Encoding) + if err != nil { + return nil, err + } +@@ -379,6 +387,13 @@ func findUpdatedLeaves(leaves map[*path]interface{}, s GoStruct, parent *gnmiPat + } + case reflect.Slice: + if fval.Type().Elem().Kind() == reflect.Ptr { ++ // s is a container with single keyless list. Add the whole container ++ if sval.NumField() == 1 { ++ for _, p := range mapPaths { ++ leaves[&path{p}] = s ++ } ++ continue ++ } + // This is a keyless list - currently unsupported for mapping since there is + // not an explicit path that can be used. + errs.Add(fmt.Errorf("unimplemented: keyless list cannot be output: %v", mapPaths[0])) +@@ -591,7 +606,7 @@ func sliceToScalarArray(v []interface{}) (*gnmipb.ScalarArray, error) { + // likely to be suboptimal since it results in very large Notifications for particular + // structs. There should be some fragmentation of Updates across Notification messages + // in a future implementation. We return a slice to keep the API stable. +-func leavesToNotifications(leaves map[*path]interface{}, ts int64, pfx *gnmiPath) ([]*gnmipb.Notification, error) { ++func leavesToNotifications(leaves map[*path]interface{}, ts int64, pfx *gnmiPath, enc gnmipb.Encoding) ([]*gnmipb.Notification, error) { + n := &gnmipb.Notification{ + Timestamp: ts, + } +@@ -613,7 +628,7 @@ func leavesToNotifications(leaves map[*path]interface{}, ts int64, pfx *gnmiPath + return nil, err + } + +- val, err := EncodeTypedValue(v, gnmipb.Encoding_JSON) ++ val, err := EncodeTypedValue(v, enc) + if err != nil { + return nil, err + } +@@ -713,7 +728,7 @@ func marshalStruct(s GoStruct, enc gnmipb.Encoding) (*gnmipb.TypedValue, error) + return nil, err + } + +- js, err := json.MarshalIndent(j, "", " ") ++ js, err := json.Marshal(j) + if err != nil { + return nil, fmt.Errorf("cannot encode JSON, %v", err) + } +@@ -870,19 +885,21 @@ type RFC7951JSONConfig struct { // the module name should be appended to entities that are defined in a different // module to their parent. func ConstructIETFJSON(s GoStruct, args *RFC7951JSONConfig) (map[string]interface{}, error) { @@ -412,7 +540,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go } // jsonOutputConfig is used to determine how constructJSON should generate -@@ -902,7 +909,7 @@ +@@ -902,7 +919,7 @@ type jsonOutputConfig struct { // be produced and whether such module names are appended is controlled through the // supplied jsonOutputConfig. Returns an error if the GoStruct cannot be rendered // to JSON. @@ -421,7 +549,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go var errs errlist.List sval := reflect.ValueOf(s).Elem() -@@ -916,20 +923,51 @@ +@@ -916,20 +933,51 @@ func structJSON(s GoStruct, parentMod string, args jsonOutputConfig) (map[string field := sval.Field(i) fType := stype.Field(i) @@ -443,16 +571,12 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go - // within this context. - if chMod != parentMod { - appmod = chMod -- } -- // Update the parent module name to be used for subsequent -- // children. -- pmod = chMod + + var mapPaths []*gnmiPath + var err error + + if tagInfo, ok := tagPaths[fType.Tag]; !ok { -+ tagInfoObj := new (structTagInfo) ++ tagInfoObj := new(structTagInfo) + tagInfoObj.gPathList, err = structTagToLibPaths(fType, newStringSliceGNMIPath([]string{})) + if err != nil { + errs.Add(fmt.Errorf("%s: %v", fType.Name, err)) @@ -479,11 +603,14 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go + pmod = tagInfo.chModName + if tagInfo.chModName != parentMod { + appmod = tagInfo.chModName -+ } + } +- // Update the parent module name to be used for subsequent +- // children. +- pmod = chMod } var appendModName bool -@@ -937,19 +975,8 @@ +@@ -937,19 +985,8 @@ func structJSON(s GoStruct, parentMod string, args jsonOutputConfig) (map[string appendModName = true } @@ -504,7 +631,19 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { errs.Add(err) -@@ -1086,7 +1113,7 @@ +@@ -1058,7 +1095,10 @@ func structJSON(s GoStruct, parentMod string, args jsonOutputConfig) (map[string + // and float64 values are represented as strings. + func writeIETFScalarJSON(i interface{}) interface{} { + switch reflect.ValueOf(i).Kind() { +- case reflect.Uint64, reflect.Int64, reflect.Float64: ++ case reflect.Float64: ++ val, _ := i.(float64) ++ return strconv.FormatFloat(val, 'f', -1, 64) ++ case reflect.Uint64, reflect.Int64: + return fmt.Sprintf("%v", i) + } + return i +@@ -1086,7 +1126,7 @@ func keyValue(v reflect.Value, appendModuleName bool) (interface{}, error) { // constructs the representation for JSON marshalling that corresponds to it. // The module within which the map is defined is specified by the parentMod // argument. @@ -513,7 +652,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go var errs errlist.List mapKeyMap := map[string]reflect.Value{} // Order of elements determines the order in which keys will be processed. -@@ -1155,6 +1182,7 @@ +@@ -1155,6 +1195,7 @@ func mapJSON(field reflect.Value, parentMod string, args jsonOutputConfig) (inte default: return nil, fmt.Errorf("invalid JSON format specified: %v", args.jType) } @@ -521,7 +660,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go for _, kn := range mapKeys { k := mapKeyMap[kn] goStruct, ok := field.MapIndex(k).Interface().(GoStruct) -@@ -1163,7 +1191,7 @@ +@@ -1163,7 +1204,7 @@ func mapJSON(field reflect.Value, parentMod string, args jsonOutputConfig) (inte continue } @@ -530,7 +669,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { errs.Add(err) continue -@@ -1191,7 +1219,7 @@ +@@ -1191,7 +1232,7 @@ func mapJSON(field reflect.Value, parentMod string, args jsonOutputConfig) (inte // The module within which the value is defined is specified by the parentMod string, // and the type of JSON to be rendered controlled by the value of the jsonOutputConfig // provided. Returns an error if one occurs during the mapping process. @@ -539,7 +678,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go var value interface{} var errs errlist.List -@@ -1210,7 +1238,7 @@ +@@ -1210,7 +1251,7 @@ func jsonValue(field reflect.Value, parentMod string, args jsonOutputConfig) (in switch field.Kind() { case reflect.Map: var err error @@ -548,7 +687,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { errs.Add(err) } -@@ -1223,7 +1251,7 @@ +@@ -1223,7 +1264,7 @@ func jsonValue(field reflect.Value, parentMod string, args jsonOutputConfig) (in } var err error @@ -557,7 +696,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { errs.Add(err) } -@@ -1235,7 +1263,7 @@ +@@ -1235,7 +1276,7 @@ func jsonValue(field reflect.Value, parentMod string, args jsonOutputConfig) (in } case reflect.Slice: var err error @@ -566,7 +705,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { return nil, err } -@@ -1289,7 +1317,7 @@ +@@ -1289,7 +1330,7 @@ func jsonValue(field reflect.Value, parentMod string, args jsonOutputConfig) (in // GoStruct, a slice may be a binary field, leaf-list or an unkeyed list. The // parentMod is used to track the name of the parent module in the case that // module names should be appended. @@ -575,7 +714,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if field.Type().Name() == BinaryTypeName { // Handle the case that that we have a Binary ([]byte) value, // which must be returned as a JSON string. -@@ -1305,7 +1333,7 @@ +@@ -1305,7 +1346,7 @@ func jsonSlice(field reflect.Value, parentMod string, args jsonOutputConfig) (in if !ok { return nil, fmt.Errorf("invalid member of a slice, %s was not a valid GoStruct", c.Name()) } @@ -584,9 +723,10 @@ diff -ruN ygot-dir-orig/ygot/ygot/render.go ygot-dir/ygot/ygot/render.go if err != nil { return nil, err } -diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/struct_validation_map.go ---- ygot-dir-orig/ygot/ygot/struct_validation_map.go 2020-10-07 14:33:58.231818000 -0700 -+++ ygot-dir/ygot/ygot/struct_validation_map.go 2020-10-27 16:02:47.553165000 -0700 +diff --git a/ygot/struct_validation_map.go b/ygot/struct_validation_map.go +index d3e54b9..6d16641 100644 +--- a/ygot/struct_validation_map.go ++++ b/ygot/struct_validation_map.go @@ -19,6 +19,10 @@ // to return pointers to a type. // - Renders structs to other output formats such as JSON, or gNMI @@ -598,7 +738,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st package ygot import ( -@@ -336,31 +340,12 @@ +@@ -336,31 +340,12 @@ type EmitJSONConfig struct { // EmitJSON takes an input ValidatedGoStruct (produced by ygen with validation enabled) // and serialises it to a JSON string. By default, produces the Internal format JSON. func EmitJSON(s ValidatedGoStruct, opts *EmitJSONConfig) (string, error) { @@ -631,7 +771,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st if err != nil { return "", fmt.Errorf("JSON marshalling error: %v", err) } -@@ -525,7 +510,6 @@ +@@ -525,7 +510,6 @@ func copyStruct(dstVal, srcVal reflect.Value) error { for i := 0; i < srcVal.NumField(); i++ { srcField := srcVal.Field(i) dstField := dstVal.Field(i) @@ -639,7 +779,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st switch srcField.Kind() { case reflect.Ptr: if err := copyPtrField(dstField, srcField); err != nil { -@@ -555,7 +539,13 @@ +@@ -555,7 +539,13 @@ func copyStruct(dstVal, srcVal reflect.Value) error { dstField.Set(srcField) } default: @@ -654,7 +794,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st } } return nil -@@ -595,6 +585,7 @@ +@@ -595,6 +585,7 @@ func copyPtrField(dstField, srcField reflect.Value) error { return err } dstField.Set(d) @@ -662,7 +802,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st return nil } -@@ -608,6 +599,7 @@ +@@ -608,6 +599,7 @@ func copyPtrField(dstField, srcField reflect.Value) error { p := reflect.New(srcField.Type().Elem()) p.Elem().Set(srcField.Elem()) dstField.Set(p) @@ -670,7 +810,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st return nil } -@@ -636,6 +628,7 @@ +@@ -636,6 +628,7 @@ func copyInterfaceField(dstField, srcField reflect.Value) error { return err } dstField.Set(d) @@ -678,7 +818,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st return nil } -@@ -645,6 +638,7 @@ +@@ -645,6 +638,7 @@ func copyInterfaceField(dstField, srcField reflect.Value) error { // key is populated in srcField and dstField, their contents are merged if they // do not overlap, otherwise an error is returned. func copyMapField(dstField, srcField reflect.Value) error { @@ -686,7 +826,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st if !util.IsValueMap(srcField) { return fmt.Errorf("received a non-map type in src map field: %v", srcField.Kind()) } -@@ -664,8 +658,6 @@ +@@ -664,8 +658,6 @@ func copyMapField(dstField, srcField reflect.Value) error { } srcKeys := srcField.MapKeys() @@ -695,7 +835,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st nm := reflect.MakeMapWithSize(reflect.MapOf(m.key, m.value), srcField.Len()) mapsToMap := []struct { -@@ -673,20 +665,24 @@ +@@ -673,20 +665,24 @@ func copyMapField(dstField, srcField reflect.Value) error { field reflect.Value }{ {srcKeys, srcField}, @@ -723,7 +863,7 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st } if err := copyStruct(d.Elem(), v.Elem()); err != nil { -@@ -804,3 +800,12 @@ +@@ -804,3 +800,12 @@ func uniqueSlices(a, b reflect.Value) (bool, error) { } return true, nil } @@ -736,9 +876,10 @@ diff -ruN ygot-dir-orig/ygot/ygot/struct_validation_map.go ygot-dir/ygot/ygot/st + } + return nil, fmt.Errorf("No match found in the existingKeys map.") +} -diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container.go ---- ygot-dir-orig/ygot/ytypes/container.go 2020-10-07 14:33:58.269626000 -0700 -+++ ygot-dir/ygot/ytypes/container.go 2020-10-27 16:02:47.588487000 -0700 +diff --git a/ytypes/container.go b/ytypes/container.go +index 9864a2c..b3446fd 100644 +--- a/ytypes/container.go ++++ b/ytypes/container.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -749,7 +890,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container. package ytypes import ( -@@ -69,9 +72,13 @@ +@@ -69,9 +72,13 @@ func validateContainer(schema *yang.Entry, value ygot.GoStruct) util.Errors { case cschema != nil: // Regular named child. if errs := Validate(cschema, fieldValue); errs != nil { @@ -765,7 +906,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container. // Either an element in choice schema subtree, or bad field. // If the former, it will be found in the choice check below. extraFields[fieldName] = nil -@@ -100,6 +107,10 @@ +@@ -100,6 +107,10 @@ func validateContainer(schema *yang.Entry, value ygot.GoStruct) util.Errors { if len(extraFields) > 0 { errors = util.AppendErr(errors, fmt.Errorf("fields %v are not found in the container schema %s", stringMapSetToSlice(extraFields), schema.Name)) } @@ -776,7 +917,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container. return util.UniqueErrors(errors) } -@@ -138,6 +149,92 @@ +@@ -138,6 +149,92 @@ func unmarshalContainer(schema *yang.Entry, parent interface{}, jsonTree interfa return unmarshalStruct(schema, parent, jt, enc, opts...) } @@ -869,7 +1010,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container. // unmarshalStruct unmarshals a JSON tree into a struct. // schema is the YANG schema of the node corresponding to the struct being // unmarshalled into. -@@ -217,7 +314,10 @@ +@@ -217,7 +314,10 @@ func unmarshalStruct(schema *yang.Entry, parent interface{}, jsonTree map[string } } @@ -881,9 +1022,10 @@ diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container. return nil } -diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go ---- ygot-dir-orig/ygot/ytypes/leaf.go 2020-10-07 14:33:58.296548000 -0700 -+++ ygot-dir/ygot/ytypes/leaf.go 2020-10-27 16:02:47.605111000 -0700 +diff --git a/ytypes/leaf.go b/ytypes/leaf.go +index 1d4195a..55de85c 100644 +--- a/ytypes/leaf.go ++++ b/ytypes/leaf.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -894,7 +1036,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go package ytypes import ( -@@ -20,11 +23,13 @@ +@@ -20,11 +23,13 @@ import ( "math/big" "reflect" "strconv" @@ -908,7 +1050,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go gpb "github.com/openconfig/gnmi/proto/gnmi" ) -@@ -77,7 +82,7 @@ +@@ -77,7 +82,7 @@ func validateLeaf(inSchema *yang.Entry, value interface{}) util.Errors { switch ykind { case yang.Ybinary: @@ -917,7 +1059,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go case yang.Ybits: return nil // TODO(mostrowski): restore when representation is decided. -@@ -258,7 +263,7 @@ +@@ -258,7 +263,7 @@ func validateUnion(schema *yang.Entry, value interface{}) util.Errors { // during validation against each matching schema otherwise. func validateMatchingSchemas(schema *yang.Entry, value interface{}) util.Errors { var errors []error @@ -926,7 +1068,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go var kk []yang.TypeKind for _, s := range ss { kk = append(kk, s.Type.Kind) -@@ -289,17 +294,25 @@ +@@ -289,17 +294,25 @@ func validateMatchingSchemas(schema *yang.Entry, value interface{}) util.Errors // findMatchingSchemasInUnion returns all schemas in the given union type, // including those within nested unions, that match the Go type of value. // value must not be nil. @@ -954,7 +1096,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go ybt := yangBuiltinTypeToGoType(t.Kind) if reflect.ValueOf(value).Kind() == reflect.Ptr { ybt = ygot.ToPtr(yangBuiltinTypeToGoType(t.Kind)) -@@ -467,8 +480,11 @@ +@@ -467,8 +480,11 @@ func unmarshalUnion(schema *yang.Entry, parent interface{}, fieldName string, va default: return fmt.Errorf("got %v non-enum types and %v enum types for union schema %s for type %T, expect just one type in total", sks, ets, fieldName, parent) } @@ -968,7 +1110,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go if err != nil { return fmt.Errorf("could not unmarshal %v into type %s", value, yk) } -@@ -533,6 +549,8 @@ +@@ -533,6 +549,8 @@ func unmarshalUnion(schema *yang.Entry, parent interface{}, fieldName string, va for _, sk := range sks { util.DbgPrint("try to unmarshal into type %s", sk) sch := yangKindToLeafEntry(sk) @@ -977,7 +1119,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go gv, err := unmarshalScalar(parent, sch, fieldName, value, enc) if err == nil { return setFieldWithTypedValue(parentT, destUnionFieldV, destUnionFieldElemT, gv) -@@ -632,22 +650,24 @@ +@@ -632,22 +650,24 @@ func getUnionTypesNotEnums(schema *yang.Entry, yt *yang.YangType) ([]*yang.YangT // type) for a given schema, which must be for an enum type. t is the type of // the containing parent struct. func schemaToEnumTypes(schema *yang.Entry, t reflect.Type) ([]reflect.Type, error) { @@ -1007,7 +1149,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go return enumTypesMap[absoluteSchemaDataPath(schema)], nil } -@@ -770,6 +790,65 @@ +@@ -770,6 +790,65 @@ func sanitizeJSON(parent interface{}, schema *yang.Entry, fieldName string, valu return nil, fmt.Errorf("unmarshalScalar: unsupported type %v in schema node %s", ykind, schema.Name) } @@ -1073,7 +1215,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go // sanitizeGNMI decodes the GNMI TypedValue encoded value into a field of the // corresponding type in GoStruct. Parent is the parent struct containing the // field being unmarshaled. schema is *yang.Entry corresponding to the field. -@@ -780,6 +859,18 @@ +@@ -780,6 +859,18 @@ func sanitizeJSON(parent interface{}, schema *yang.Entry, fieldName string, valu func sanitizeGNMI(parent interface{}, schema *yang.Entry, fieldName string, tv *gpb.TypedValue, jsonTolerance bool) (interface{}, error) { ykind := schema.Type.Kind @@ -1092,16 +1234,17 @@ diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go var ok bool if ok = gNMIToYANGTypeMatches(ykind, tv, jsonTolerance); !ok { return nil, fmt.Errorf("failed to unmarshal %v into %v", tv.GetValue(), yang.TypeKindToName[ykind]) -@@ -875,4 +966,4 @@ +@@ -875,4 +966,4 @@ func isValueInterfacePtrToEnum(v reflect.Value) bool { v = v.Elem() return v.Kind() == reflect.Int64 -} +} \ No newline at end of file -diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go ---- ygot-dir-orig/ygot/ytypes/list.go 2020-10-07 14:33:58.308948000 -0700 -+++ ygot-dir/ygot/ytypes/list.go 2020-10-27 16:02:47.621524000 -0700 +diff --git a/ytypes/list.go b/ytypes/list.go +index ec52ee3..35f32f5 100644 +--- a/ytypes/list.go ++++ b/ytypes/list.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -1112,7 +1255,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go package ytypes import ( -@@ -22,6 +25,7 @@ +@@ -22,6 +25,7 @@ import ( "github.com/kylelemons/godebug/pretty" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/util" @@ -1120,7 +1263,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go ) // Refer to: https://tools.ietf.org/html/rfc6020#section-7.8. -@@ -217,6 +221,9 @@ +@@ -217,6 +221,9 @@ func validateListSchema(schema *yang.Entry) error { if len(schema.Key) == 0 { return fmt.Errorf("list %s with config set must have a key", schema.Name) } @@ -1130,7 +1273,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go keys := strings.Fields(schema.Key) keysMissing := make(map[string]bool) for _, v := range keys { -@@ -232,6 +239,7 @@ +@@ -232,6 +239,7 @@ func validateListSchema(schema *yang.Entry) error { } } @@ -1138,7 +1281,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go return nil } -@@ -317,6 +325,9 @@ +@@ -317,6 +325,9 @@ func unmarshalList(schema *yang.Entry, parent interface{}, jsonList interface{}, return fmt.Errorf("unmarshalList for %s parent type %T, has bad field type %v", listElementType, parent, listElementType) } @@ -1148,7 +1291,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go // Iterate over JSON list. Each JSON list element is a map with the field // name as the key. The JSON values must be unmarshaled and inserted into // the new struct list element. When all fields of the new element have been -@@ -330,16 +341,41 @@ +@@ -330,16 +341,41 @@ func unmarshalList(schema *yang.Entry, parent interface{}, jsonList interface{}, jt := le.(map[string]interface{}) newVal := reflect.New(listElementType.Elem()) util.DbgPrint("creating a new list element val of type %v", newVal.Type()) @@ -1193,7 +1336,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go err = util.InsertIntoMap(parent, newKey.Interface(), newVal.Interface()) case util.IsTypeSlicePtr(t): err = util.InsertIntoSlice(parent, newVal.Interface()) -@@ -350,7 +386,9 @@ +@@ -350,7 +386,9 @@ func unmarshalList(schema *yang.Entry, parent interface{}, jsonList interface{}, return err } } @@ -1204,7 +1347,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go return nil } -@@ -394,11 +432,91 @@ +@@ -394,11 +432,91 @@ func makeValForInsert(schema *yang.Entry, parent interface{}, keys map[string]st if util.IsValuePtr(fv) { ft = ft.Elem() } @@ -1298,7 +1441,16 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go return util.InsertIntoStruct(val.Interface(), fn, nv.Interface()) } -@@ -494,6 +612,9 @@ +@@ -468,6 +586,8 @@ func makeKeyForInsert(schema *yang.Entry, parentMap interface{}, newVal reflect. + kv, err := getKeyValue(newVal.Elem(), schema.Key) + if err != nil { + return reflect.ValueOf(nil), err ++ } else if kv == nil { ++ return reflect.ValueOf(nil), fmt.Errorf("List key leaf node %v is not present in the payload", schema.Key) + } + util.DbgPrint("key value is %v.", kv) + +@@ -494,6 +614,9 @@ func insertAndGetKey(schema *yang.Entry, root interface{}, keys map[string]strin } // TODO(yusufsn): When the key is a leafref, its target should be filled out. @@ -1308,9 +1460,10 @@ diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go mapVal, err := makeValForInsert(schema, root, keys) if err != nil { return nil, fmt.Errorf("failed to create map value for insert, root %T, keys %v: %v", root, keys, err) -diff -ruN ygot-dir-orig/ygot/ytypes/node.go ygot-dir/ygot/ytypes/node.go ---- ygot-dir-orig/ygot/ytypes/node.go 2020-10-07 14:33:58.312963000 -0700 -+++ ygot-dir/ygot/ytypes/node.go 2020-10-27 16:02:47.626175000 -0700 +diff --git a/ytypes/node.go b/ytypes/node.go +index bad8474..1bce0ba 100644 +--- a/ytypes/node.go ++++ b/ytypes/node.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -1321,7 +1474,15 @@ diff -ruN ygot-dir-orig/ygot/ytypes/node.go ygot-dir/ygot/ytypes/node.go package ytypes import ( -@@ -140,6 +143,16 @@ +@@ -25,6 +28,7 @@ import ( + "google.golang.org/grpc/status" + + gpb "github.com/openconfig/gnmi/proto/gnmi" ++ "strings" + ) + + // Type retrieveNodeArgs contains the set of parameters that changes +@@ -140,6 +144,16 @@ func retrieveNodeContainer(schema *yang.Entry, root interface{}, path *gpb.Path, if err := util.InitializeStructField(root, ft.Name); err != nil { return nil, status.Errorf(codes.Unknown, "failed to initialize struct field %s in %T, child schema %v, path %v", ft.Name, root, cschema, path) } @@ -1338,7 +1499,33 @@ diff -ruN ygot-dir-orig/ygot/ytypes/node.go ygot-dir/ygot/ytypes/node.go } // If delete is specified, and the path is exhausted, then we set the -@@ -319,6 +332,11 @@ +@@ -243,6 +257,13 @@ func retrieveNodeList(schema *yang.Entry, root interface{}, path, traversedPath + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "failed to convert %v to a string, path %v: %v", kv, path, err) + } ++ ++ if strings.Contains(keyAsString, "\\") { ++ keyPath, err := ygot.StringToStructuredPath(schema.Name+"["+schema.Key+"="+keyAsString+"]") ++ if err != nil { return nil, status.Errorf(codes.InvalidArgument, "failed to convert %v to a string, path %v: %v", kv, path, err) } ++ if len(keyPath.Elem) == 1 { keyAsString = keyPath.Elem[0].Key[schema.Key] } ++ } ++ + if keyAsString == pathKey { + remainingPath := util.PopGNMIPath(path) + if args.delete && len(remainingPath.GetElem()) == 0 { +@@ -287,6 +308,11 @@ func retrieveNodeList(schema *yang.Entry, root interface{}, path, traversedPath + if err != nil { + return nil, status.Errorf(codes.Unknown, "failed to convert the field value to string, field %v: %v", fieldName, err) + } ++ if strings.Contains(keyAsString, "\\") { ++ keyPath, err := ygot.StringToStructuredPath(schema.Name+"["+schemaKey+"="+keyAsString+"]") ++ if err != nil { return nil, status.Errorf(codes.Unknown, "failed to convert the field value to string, field %v: %v", fieldName, err) } ++ if len(keyPath.Elem) == 1 { keyAsString = keyPath.Elem[0].Key[schemaKey] } ++ } + if !(args.handleWildcards && pathKey == "*") && pathKey != keyAsString { + match = false + break +@@ -319,6 +345,11 @@ func retrieveNodeList(schema *yang.Entry, root interface{}, path, traversedPath if err != nil { return nil, err } @@ -1350,9 +1537,10 @@ diff -ruN ygot-dir-orig/ygot/ytypes/node.go ygot-dir/ygot/ytypes/node.go nodes, err := retrieveNode(schema, rv.MapIndex(reflect.ValueOf(key)).Interface(), util.PopGNMIPath(path), appendElem(traversedPath, path.GetElem()[0]), args) if err != nil { return nil, err -diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_type.go ---- ygot-dir-orig/ygot/ytypes/string_type.go 2020-10-07 14:33:58.316659000 -0700 -+++ ygot-dir/ygot/ytypes/string_type.go 2020-10-27 16:02:47.629911000 -0700 +diff --git a/ytypes/string_type.go b/ytypes/string_type.go +index 4ae91e2..f91bd74 100644 +--- a/ytypes/string_type.go ++++ b/ytypes/string_type.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -1363,7 +1551,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_t package ytypes import ( -@@ -19,10 +22,14 @@ +@@ -19,10 +22,14 @@ import ( "fmt" "regexp" "unicode/utf8" @@ -1378,7 +1566,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_t // Refer to: https://tools.ietf.org/html/rfc6020#section-9.4. // validateString validates value, which must be a Go string type, against the -@@ -48,10 +55,23 @@ +@@ -48,10 +55,23 @@ func validateString(schema *yang.Entry, value interface{}) error { // Check that the value satisfies any regex patterns. for _, p := range schema.Type.Pattern { @@ -1405,7 +1593,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_t // fixYangRegexp adds ^(...)$ around the pattern - the result is // equivalent to a full match of whole string. if !r.MatchString(stringVal) { -@@ -105,13 +125,33 @@ +@@ -105,13 +125,33 @@ func validateStringSchema(schema *yang.Entry) error { return fmt.Errorf("string schema %s has wrong type %v", schema.Name, schema.Type.Kind) } @@ -1443,9 +1631,10 @@ diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_t } // fixYangRegexp takes a pattern regular expression from a YANG module and -diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal.go ---- ygot-dir-orig/ygot/ytypes/unmarshal.go 2020-10-07 14:33:58.323984000 -0700 -+++ ygot-dir/ygot/ytypes/unmarshal.go 2020-10-27 16:02:47.638332000 -0700 +diff --git a/ytypes/unmarshal.go b/ytypes/unmarshal.go +index 864562f..2eea6d2 100644 +--- a/ytypes/unmarshal.go ++++ b/ytypes/unmarshal.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -1456,7 +1645,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal. package ytypes import ( -@@ -39,6 +42,13 @@ +@@ -39,6 +42,13 @@ type IgnoreExtraFields struct{} // IsUnmarshalOpt marks IgnoreExtraFields as a valid UnmarshalOpt. func (*IgnoreExtraFields) IsUnmarshalOpt() {} @@ -1470,7 +1659,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal. // Unmarshal recursively unmarshals JSON data tree in value into the given // parent, using the given schema. Any values already in the parent that are // not present in value are preserved. If provided schema is a leaf or leaf -@@ -81,7 +91,10 @@ +@@ -81,7 +91,10 @@ func unmarshalGeneric(schema *yang.Entry, parent interface{}, value interface{}, if schema == nil { return fmt.Errorf("nil schema for parent type %T, value %v (%T)", parent, value, value) } @@ -1482,7 +1671,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal. if enc == GNMIEncoding && !(schema.IsLeaf() || schema.IsLeafList()) { return errors.New("unmarshalling a non leaf node isn't supported in GNMIEncoding mode") -@@ -112,3 +125,14 @@ +@@ -112,3 +125,14 @@ func hasIgnoreExtraFields(opts []UnmarshalOpt) bool { } return false } @@ -1497,9 +1686,10 @@ diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal. + } + return false +} -diff -ruN ygot-dir-orig/ygot/ytypes/validate.go ygot-dir/ygot/ytypes/validate.go ---- ygot-dir-orig/ygot/ytypes/validate.go 2020-10-07 14:33:58.340787000 -0700 -+++ ygot-dir/ygot/ytypes/validate.go 2020-10-27 16:02:47.656540000 -0700 +diff --git a/ytypes/validate.go b/ytypes/validate.go +index a99f449..1ee0c17 100644 +--- a/ytypes/validate.go ++++ b/ytypes/validate.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. @@ -1510,7 +1700,7 @@ diff -ruN ygot-dir-orig/ygot/ytypes/validate.go ygot-dir/ygot/ytypes/validate.go package ytypes import ( -@@ -74,7 +77,7 @@ +@@ -74,7 +77,7 @@ func Validate(schema *yang.Entry, value interface{}, opts ...ygot.ValidationOpti errs = ValidateLeafRefData(schema, value, leafrefOpt) }