From 99b8043477ec1d954a4df32d54e828bd98189de9 Mon Sep 17 00:00:00 2001 From: keogaki Date: Mon, 14 Apr 2025 15:25:13 +0900 Subject: [PATCH 1/5] Add fields to IR and IROptions structs to support ygnmi/generator command option to set the path origin from the YANG module name or an arbitrarily specified name. --- ygen/genir.go | 9 +++++++++ ygen/ir.go | 3 +++ 2 files changed, 12 insertions(+) diff --git a/ygen/genir.go b/ygen/genir.go index bd920d0b..0b303ae3 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -53,6 +53,13 @@ type IROptions struct { // to true. // NOTE: This flag will be removed by v1 release. AppendEnumSuffixForSimpleUnionEnums bool + + // UseModuleNameAsPathOrigin specifies whether the YANG module name is + // set to the origin for generated gNMI paths when producing the IR. + UseModuleNameAsPathOrigin bool + + // PathOriginName specifies the orign name for generated gNMI paths when producing the IR. + PathOriginName string } // GenerateIR creates the ygen intermediate representation for a set of @@ -184,5 +191,7 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR opts: opts, fakeroot: rootEntry, parsedModules: mdef.modules, + UseModuleNameAsPathOrigin: opts.UseModuleNameAsPathOrigin, + PathOriginName: opts.PathOriginName, }, nil } diff --git a/ygen/ir.go b/ygen/ir.go index 8de84fa6..ee411ef9 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -312,6 +312,9 @@ type IR struct { // fakeroot stores the fake root's AST node for creating a serialized // version of the AST if needed. fakeroot *yang.Entry + + UseModuleNameAsPathOrigin bool + PathOriginName string } // OrderedDirectoryPaths returns the absolute YANG paths of all ParsedDirectory From 8712b9935843cd8ad44649428ffa311a0dcf0144 Mon Sep 17 00:00:00 2001 From: keogaki Date: Tue, 15 Apr 2025 13:58:04 +0900 Subject: [PATCH 2/5] fix some indents --- ygen/genir.go | 14 +++++++------- ygen/ir.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ygen/genir.go b/ygen/genir.go index 0b303ae3..38135330 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -185,13 +185,13 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR } return &IR{ - Directories: dirDets, - Enums: enumDefinitionMap, - ModelData: mdef.modelData, - opts: opts, - fakeroot: rootEntry, - parsedModules: mdef.modules, + Directories: dirDets, + Enums: enumDefinitionMap, + ModelData: mdef.modelData, + opts: opts, + fakeroot: rootEntry, + parsedModules: mdef.modules, UseModuleNameAsPathOrigin: opts.UseModuleNameAsPathOrigin, - PathOriginName: opts.PathOriginName, + PathOriginName: opts.PathOriginName, }, nil } diff --git a/ygen/ir.go b/ygen/ir.go index ee411ef9..145d126b 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -314,7 +314,7 @@ type IR struct { fakeroot *yang.Entry UseModuleNameAsPathOrigin bool - PathOriginName string + PathOriginName string } // OrderedDirectoryPaths returns the absolute YANG paths of all ParsedDirectory From 5b6938503a8521e08465aebad825c06a676e98af Mon Sep 17 00:00:00 2001 From: keogaki Date: Tue, 15 Apr 2025 15:21:31 +0900 Subject: [PATCH 3/5] fix some indents again --- ygen/genir.go | 14 +++++++------- ygen/ir.go | 6 +++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ygen/genir.go b/ygen/genir.go index 38135330..7c2f8f88 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -185,13 +185,13 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR } return &IR{ - Directories: dirDets, - Enums: enumDefinitionMap, - ModelData: mdef.modelData, - opts: opts, - fakeroot: rootEntry, - parsedModules: mdef.modules, + Directories: dirDets, + Enums: enumDefinitionMap, + ModelData: mdef.modelData, + opts: opts, + fakeroot: rootEntry, + parsedModules: mdef.modules, UseModuleNameAsPathOrigin: opts.UseModuleNameAsPathOrigin, - PathOriginName: opts.PathOriginName, + PathOriginName: opts.PathOriginName, }, nil } diff --git a/ygen/ir.go b/ygen/ir.go index 145d126b..be2ec43a 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -313,8 +313,12 @@ type IR struct { // version of the AST if needed. fakeroot *yang.Entry + // UseModuleNameAsPathOrigin specifies whether the YANG module name is + // set to the origin for generated gNMI paths. UseModuleNameAsPathOrigin bool - PathOriginName string + + // PathOriginName specifies the orign name for generated gNMI paths. + PathOriginName string } // OrderedDirectoryPaths returns the absolute YANG paths of all ParsedDirectory From 01d81a2d7969babd4625c49998c4599f8f8153b2 Mon Sep 17 00:00:00 2001 From: keogaki Date: Thu, 17 Apr 2025 13:57:15 +0900 Subject: [PATCH 4/5] Modify to parse the origin name information during IR generation --- ygen/directory.go | 8 ++++++++ ygen/genir.go | 14 ++++++-------- ygen/ir.go | 9 ++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ygen/directory.go b/ygen/directory.go index a2fef10e..0fa66636 100644 --- a/ygen/directory.go +++ b/ygen/directory.go @@ -276,6 +276,14 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory if hasShadowField { nd.YANGDetails.ShadowSchemaPath = util.SchemaTreePathNoModule(shadowField) } + // If IROptions.PathOriginName has a value, the value is set to the Origin of the node. + // Else if IROptions.UseModuleNameAsPathOrigin of the node is true, + // YANG module name is set to the Origin of the node. + if opts.PathOriginName != "" { + nd.PathOriginName = opts.PathOriginName + } else if opts.UseModuleNameAsPathOrigin { + nd.PathOriginName = nd.YANGDetails.RootElementModule + } switch { case field.IsLeaf(), field.IsLeafList(): diff --git a/ygen/genir.go b/ygen/genir.go index 7c2f8f88..4d67fa82 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -185,13 +185,11 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR } return &IR{ - Directories: dirDets, - Enums: enumDefinitionMap, - ModelData: mdef.modelData, - opts: opts, - fakeroot: rootEntry, - parsedModules: mdef.modules, - UseModuleNameAsPathOrigin: opts.UseModuleNameAsPathOrigin, - PathOriginName: opts.PathOriginName, + Directories: dirDets, + Enums: enumDefinitionMap, + ModelData: mdef.modelData, + opts: opts, + fakeroot: rootEntry, + parsedModules: mdef.modules, }, nil } diff --git a/ygen/ir.go b/ygen/ir.go index be2ec43a..38bc1852 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -312,13 +312,6 @@ type IR struct { // fakeroot stores the fake root's AST node for creating a serialized // version of the AST if needed. fakeroot *yang.Entry - - // UseModuleNameAsPathOrigin specifies whether the YANG module name is - // set to the origin for generated gNMI paths. - UseModuleNameAsPathOrigin bool - - // PathOriginName specifies the orign name for generated gNMI paths. - PathOriginName string } // OrderedDirectoryPaths returns the absolute YANG paths of all ParsedDirectory @@ -595,6 +588,8 @@ type NodeDetails struct { // Specifically, this field is set by the // LangMapperExt.PopulateFieldFlags function. Flags map[string]string + // PathOriginName specifies the origin name for the generated gNMI path. + PathOriginName string } // NodeType describes the different types of node that can From f59371e836bebf9ecb1dd41b5b566f9c2c3c2b75 Mon Sep 17 00:00:00 2001 From: keogaki Date: Wed, 23 Apr 2025 16:03:58 +0900 Subject: [PATCH 5/5] Add a test code --- ygen/directory_test.go | 176 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/ygen/directory_test.go b/ygen/directory_test.go index 52b22163..49b062bb 100644 --- a/ygen/directory_test.go +++ b/ygen/directory_test.go @@ -15,11 +15,14 @@ package ygen import ( + "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/openconfig/gnmi/errdiff" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/yangschema" ) // errToString returns the string representation of err and the empty string if @@ -702,3 +705,176 @@ func TestFindMapPaths(t *testing.T) { }) } } + +type mockLangMapper struct{} + +func (*mockLangMapper) FieldName(e *yang.Entry) (string, error) { return "", nil } + +func (*mockLangMapper) DirectoryName(e *yang.Entry, complessBehavior genutil.CompressBehaviour) (string, error) { + return "", nil +} + +func (*mockLangMapper) KeyLeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { + return nil, nil +} + +func (*mockLangMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { return nil, nil } + +func (*mockLangMapper) PackageName(e *yang.Entry, compressBehavior genutil.CompressBehaviour, nested bool) (string, error) { + return "", nil +} + +func (*mockLangMapper) InjectEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error { + return nil +} + +func (*mockLangMapper) InjectSchemaTree(entries []*yang.Entry) error { return nil } + +func (*mockLangMapper) PopulateEnumFlags(EnumeratedYANGType, *yang.YangType) map[string]string { + return nil +} + +func (*mockLangMapper) PopulateFieldFlags(NodeDetails, *yang.Entry) map[string]string { return nil } + +func (*mockLangMapper) setEnumSet(*enumSet) {} + +func (*mockLangMapper) setSchemaTree(*yangschema.Tree) {} + +func TestGetOrderedDirDetailsPathOrigin(t *testing.T) { + ms := compileModules(t, map[string]string{ + "a-module": ` + module a-module { + prefix "m"; + namespace "urn:m"; + + container a-container { + leaf field-a { + type string; + } + } + + container b-container { + container config { + leaf field-b { + type string; + } + } + container state { + leaf field-b { + type string; + } + } + + container c-container { + leaf field-d { + type string; + } + } + } + } + `, + }) + + tests := []struct { + name string + inDirectory map[string]*Directory + inSchemaTree *yangschema.Tree + inOpts IROptions + wantPathOriginName map[string]string + wantErr bool + wantErrorSubstrings []string + }{{ + name: "PathOriginName is set", + inDirectory: map[string]*Directory{ + "/a-module/a-container": { + Name: "AContainer", + Entry: findEntry(t, ms, "a-module", "a-container/field-a"), + Fields: map[string]*yang.Entry{ + "field-a": findEntry(t, ms, "a-module", "a-container/field-a"), + }, + Path: []string{"", "a-module", "a-container"}, + }, + }, + inSchemaTree: &yangschema.Tree{}, + inOpts: IROptions{ + PathOriginName: "explicit-origin", + }, + wantPathOriginName: map[string]string{ + "/a-module/a-container/field-a": "explicit-origin", + }, + }, { + name: "UseModuleNameAsPathOrigin is true", + inDirectory: map[string]*Directory{ + "/a-module/a-container": { + Name: "AContainer", + Entry: findEntry(t, ms, "a-module", "a-container/field-a"), + Fields: map[string]*yang.Entry{ + "field-a": findEntry(t, ms, "a-module", "a-container/field-a"), + }, + Path: []string{"", "a-module", "a-container"}, + }, + }, + inSchemaTree: &yangschema.Tree{}, + inOpts: IROptions{ + UseModuleNameAsPathOrigin: true, + }, + wantPathOriginName: map[string]string{ + "/a-module/a-container/field-a": "a-module", + }, + }, { + name: "PathOriginName and UseModuleNameAsPathOrigin are not set", + inDirectory: map[string]*Directory{ + "/a-module/a-container": { + Name: "AContainer", + Entry: findEntry(t, ms, "a-module", "a-container/field-a"), + Fields: map[string]*yang.Entry{ + "field-a": findEntry(t, ms, "a-module", "a-container/field-a"), + }, + Path: []string{"", "a-module", "a-container"}, + }, + }, + inSchemaTree: &yangschema.Tree{}, + inOpts: IROptions{}, + wantPathOriginName: map[string]string{ + "/a-module/a-container/field-a": "", + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + langMapper := &mockLangMapper{} + got, err := getOrderedDirDetails(langMapper, tt.inDirectory, tt.inSchemaTree, tt.inOpts) + + if tt.wantErr { + if err == nil { + t.Fatalf("getOrderedDirDetails() got no error, want error containing: %v", tt.wantErrorSubstrings) + } + for _, want := range tt.wantErrorSubstrings { + if !strings.Contains(err.Error(), want) { + t.Errorf("getOrderedDirDetails() got error: %v, want error containing: %q", err, want) + } + } + return + } + + if err != nil { + t.Fatalf("getOrderedDirDetails() unexpected error: %v", err) + } + + if diff := cmp.Diff(tt.wantPathOriginName, getPathOriginNames(got)); diff != "" { + t.Errorf("getOrderedDirDetails() PathOriginName mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func getPathOriginNames(dirs map[string]*ParsedDirectory) map[string]string { + origins := make(map[string]string) + for path, dir := range dirs { + for _, field := range dir.Fields { + origins[path] = field.PathOriginName + break // The PathOriginName of the first field in each ParsedDirectory is verified. + } + } + return origins +}