Skip to content

Add fields to IR and IROptions used by settting the path origin from the YANG module name or a specified name. #1030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions ygen/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
176 changes: 176 additions & 0 deletions ygen/directory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
7 changes: 7 additions & 0 deletions ygen/genir.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ygen/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,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
Expand Down
Loading