Skip to content

Commit 3a953aa

Browse files
keogakirobshakir
andauthored
Add fields to IR and IROptions used by settting the path origin from the YANG module name or a specified name. (#1030)
* 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. * fix some indents * fix some indents again * Modify to parse the origin name information during IR generation * Add a test code * move origin name field to YANGNodeDetails * fix test cases in genir_test.go * fix as review comments on May 23rd --------- Co-authored-by: Rob Shakir <[email protected]>
1 parent 863d894 commit 3a953aa

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

ygen/directory.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,19 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory
276276
if hasShadowField {
277277
nd.YANGDetails.ShadowSchemaPath = util.SchemaTreePathNoModule(shadowField)
278278
}
279+
// If IROptions.PathOriginName has a value, the value is set to the Origin of the node.
280+
// Else if IROptions.UseModuleNameAsPathOrigin of the node is true,
281+
// YANG module name is set to the Origin of the node.
282+
// Else "" is set to the Origin of the node.
283+
switch {
284+
case opts.PathOriginName != "":
285+
nd.YANGDetails.Origin = opts.PathOriginName
286+
case opts.UseModuleNameAsPathOrigin:
287+
nd.YANGDetails.Origin = nd.YANGDetails.BelongingModule
288+
default:
289+
// TODO: read the origin from the relevant YANG extension.
290+
nd.YANGDetails.Origin = ""
291+
}
279292

280293
switch {
281294
case field.IsLeaf(), field.IsLeafList():

ygen/directory_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
package ygen
1616

1717
import (
18+
"strings"
1819
"testing"
1920

2021
"github.com/google/go-cmp/cmp"
2122
"github.com/openconfig/gnmi/errdiff"
2223
"github.com/openconfig/goyang/pkg/yang"
24+
"github.com/openconfig/ygot/genutil"
25+
"github.com/openconfig/ygot/yangschema"
2326
)
2427

2528
// errToString returns the string representation of err and the empty string if
@@ -702,3 +705,176 @@ func TestFindMapPaths(t *testing.T) {
702705
})
703706
}
704707
}
708+
709+
type mockLangMapper struct{}
710+
711+
func (*mockLangMapper) FieldName(_ *yang.Entry) (string, error) { return "", nil }
712+
713+
func (*mockLangMapper) DirectoryName(_ *yang.Entry, _ genutil.CompressBehaviour) (string, error) {
714+
return "", nil
715+
}
716+
717+
func (*mockLangMapper) KeyLeafType(_ *yang.Entry, _ IROptions) (*MappedType, error) {
718+
return nil, nil
719+
}
720+
721+
func (*mockLangMapper) LeafType(_ *yang.Entry, _ IROptions) (*MappedType, error) { return nil, nil }
722+
723+
func (*mockLangMapper) PackageName(_ *yang.Entry, _ genutil.CompressBehaviour, _ bool) (string, error) {
724+
return "", nil
725+
}
726+
727+
func (*mockLangMapper) InjectEnumSet(_ map[string]*yang.Entry, _, _, _, _, _, _ bool, _ []string) error {
728+
return nil
729+
}
730+
731+
func (*mockLangMapper) InjectSchemaTree(_ []*yang.Entry) error { return nil }
732+
733+
func (*mockLangMapper) PopulateEnumFlags(EnumeratedYANGType, *yang.YangType) map[string]string {
734+
return nil
735+
}
736+
737+
func (*mockLangMapper) PopulateFieldFlags(NodeDetails, *yang.Entry) map[string]string { return nil }
738+
739+
func (*mockLangMapper) setEnumSet(*enumSet) {}
740+
741+
func (*mockLangMapper) setSchemaTree(*yangschema.Tree) {}
742+
743+
func TestGetOrderedDirDetailsPathOrigin(t *testing.T) {
744+
ms := compileModules(t, map[string]string{
745+
"a-module": `
746+
module a-module {
747+
prefix "m";
748+
namespace "urn:m";
749+
750+
container a-container {
751+
leaf field-a {
752+
type string;
753+
}
754+
}
755+
756+
container b-container {
757+
container config {
758+
leaf field-b {
759+
type string;
760+
}
761+
}
762+
container state {
763+
leaf field-b {
764+
type string;
765+
}
766+
}
767+
768+
container c-container {
769+
leaf field-d {
770+
type string;
771+
}
772+
}
773+
}
774+
}
775+
`,
776+
})
777+
778+
tests := []struct {
779+
name string
780+
inDirectory map[string]*Directory
781+
inSchemaTree *yangschema.Tree
782+
inOpts IROptions
783+
wantPathOriginName map[string]string
784+
wantErr bool
785+
wantErrorSubstrings []string
786+
}{{
787+
name: "PathOriginName is set",
788+
inDirectory: map[string]*Directory{
789+
"/a-module/a-container": {
790+
Name: "AContainer",
791+
Entry: findEntry(t, ms, "a-module", "a-container/field-a"),
792+
Fields: map[string]*yang.Entry{
793+
"field-a": findEntry(t, ms, "a-module", "a-container/field-a"),
794+
},
795+
Path: []string{"", "a-module", "a-container"},
796+
},
797+
},
798+
inSchemaTree: &yangschema.Tree{},
799+
inOpts: IROptions{
800+
PathOriginName: "explicit-origin",
801+
},
802+
wantPathOriginName: map[string]string{
803+
"/a-module/a-container/field-a": "explicit-origin",
804+
},
805+
}, {
806+
name: "UseModuleNameAsPathOrigin is true",
807+
inDirectory: map[string]*Directory{
808+
"/a-module/a-container": {
809+
Name: "AContainer",
810+
Entry: findEntry(t, ms, "a-module", "a-container/field-a"),
811+
Fields: map[string]*yang.Entry{
812+
"field-a": findEntry(t, ms, "a-module", "a-container/field-a"),
813+
},
814+
Path: []string{"", "a-module", "a-container"},
815+
},
816+
},
817+
inSchemaTree: &yangschema.Tree{},
818+
inOpts: IROptions{
819+
UseModuleNameAsPathOrigin: true,
820+
},
821+
wantPathOriginName: map[string]string{
822+
"/a-module/a-container/field-a": "a-module",
823+
},
824+
}, {
825+
name: "PathOriginName and UseModuleNameAsPathOrigin are not set",
826+
inDirectory: map[string]*Directory{
827+
"/a-module/a-container": {
828+
Name: "AContainer",
829+
Entry: findEntry(t, ms, "a-module", "a-container/field-a"),
830+
Fields: map[string]*yang.Entry{
831+
"field-a": findEntry(t, ms, "a-module", "a-container/field-a"),
832+
},
833+
Path: []string{"", "a-module", "a-container"},
834+
},
835+
},
836+
inSchemaTree: &yangschema.Tree{},
837+
inOpts: IROptions{},
838+
wantPathOriginName: map[string]string{
839+
"/a-module/a-container/field-a": "",
840+
},
841+
}}
842+
843+
for _, tt := range tests {
844+
t.Run(tt.name, func(t *testing.T) {
845+
langMapper := &mockLangMapper{}
846+
got, err := getOrderedDirDetails(langMapper, tt.inDirectory, tt.inSchemaTree, tt.inOpts)
847+
848+
if tt.wantErr {
849+
if err == nil {
850+
t.Fatalf("getOrderedDirDetails() got no error, want error containing: %v", tt.wantErrorSubstrings)
851+
}
852+
for _, want := range tt.wantErrorSubstrings {
853+
if !strings.Contains(err.Error(), want) {
854+
t.Errorf("getOrderedDirDetails() got error: %v, want error containing: %q", err, want)
855+
}
856+
}
857+
return
858+
}
859+
860+
if err != nil {
861+
t.Fatalf("getOrderedDirDetails() unexpected error: %v", err)
862+
}
863+
864+
if diff := cmp.Diff(tt.wantPathOriginName, getPathOriginNames(got)); diff != "" {
865+
t.Errorf("getOrderedDirDetails() PathOriginName mismatch (-want +got):\n%s", diff)
866+
}
867+
})
868+
}
869+
}
870+
871+
func getPathOriginNames(dirs map[string]*ParsedDirectory) map[string]string {
872+
origins := make(map[string]string)
873+
for path, dir := range dirs {
874+
for _, field := range dir.Fields {
875+
origins[path] = field.YANGDetails.Origin
876+
break // The PathOriginName of the first field in each ParsedDirectory is verified.
877+
}
878+
}
879+
return origins
880+
}

ygen/genir.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ type IROptions struct {
5353
// to true.
5454
// NOTE: This flag will be removed by v1 release.
5555
AppendEnumSuffixForSimpleUnionEnums bool
56+
57+
// UseModuleNameAsPathOrigin specifies whether the YANG module name is
58+
// set to the origin for generated gNMI paths when producing the IR.
59+
UseModuleNameAsPathOrigin bool
60+
61+
// PathOriginName specifies the orign name for generated gNMI paths when producing the IR.
62+
PathOriginName string
5663
}
5764

5865
// GenerateIR creates the ygen intermediate representation for a set of

ygen/ir.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,8 @@ type YANGNodeDetails struct {
709709
// statement in YANG:
710710
// https://datatracker.ietf.org/doc/html/rfc7950#section-7.21.1
711711
ConfigFalse bool
712+
// Origin specifies the origin name for the generated gNMI path.
713+
Origin string
712714
}
713715

714716
// EnumeratedValueType is used to indicate the source YANG type

0 commit comments

Comments
 (0)