Skip to content
Merged
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
26 changes: 25 additions & 1 deletion ygot/pathtranslate/pathtranslate.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (r *PathTranslator) PathElem(p []string) ([]*gnmipb.PathElem, error) {
// When keys are consumed, they are set as true in "used" slice.
used := make([]bool, len(p))

// Keeps the path elements seeen so far by appending with a separator.
// Keeps the path elements seen so far by appending with a separator.
var pathSoFar string

var res []*gnmipb.PathElem
Expand Down Expand Up @@ -114,3 +114,27 @@ func (r *PathTranslator) PathElem(p []string) ([]*gnmipb.PathElem, error) {

return res, nil
}

// SetWildcardKeys sets the keys of the given path elements based on stored rewrite rules,
// with the value set to "*". Elems are modified in place.
// It returns true if the path has been updated, and an error if any of the elems already has keys.
func (r *PathTranslator) SetWildcardKeys(elems []*gnmipb.PathElem) (bool, error) {
var pathSoFar string
var updated bool
for _, elem := range elems {
pathSoFar = pathSoFar + separator + elem.GetName()
keyNames, ok := r.rules[pathSoFar]
if !ok {
continue
}
if elem.GetKey() != nil {
return false, fmt.Errorf("path %v already has keys", elems)
}
elem.Key = map[string]string{}
for _, key := range keyNames {
elem.Key[key] = "*"
}
updated = true
}
return updated, nil
}
161 changes: 161 additions & 0 deletions ygot/pathtranslate/pathtranslate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,164 @@ func TestPathElem(t *testing.T) {
})
}
}

func TestSetWildcardKeys(t *testing.T) {
schemas := []*yang.Entry{
{Name: "root"},
{
Name: "simpleKeyedList",
Key: "k1",
Parent: &yang.Entry{
Name: "simpleKeyedLists",
Parent: &yang.Entry{
Name: "b",
Parent: &yang.Entry{
Name: "a",
Parent: &yang.Entry{Name: "root"},
},
},
},
},
{
Name: "structKeyedList",
Key: "k1 k2 k3",
Parent: &yang.Entry{Name: "structKeyedLists",
Parent: &yang.Entry{
Name: "simpleKeyedList",
Key: "k1",
Parent: &yang.Entry{
Name: "simpleKeyedLists",
Parent: &yang.Entry{
Name: "b",
Parent: &yang.Entry{
Name: "a",
Parent: &yang.Entry{Name: "root"},
},
},
},
},
},
},
}

tests := []struct {
inDesc string
path []*gnmipb.PathElem
wantPath []*gnmipb.PathElem
wantUpdated bool
wantErrSubstring string
}{
{
inDesc: "success empty path",
path: []*gnmipb.PathElem{},
wantPath: []*gnmipb.PathElem{},
wantUpdated: false,
},
{
inDesc: "success path with no keyed list(note, it doesn't exist in schema)",
path: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
},
wantPath: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
},
wantUpdated: false,
},
{
inDesc: "success path with keyed list at the end",
path: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList"},
},
wantPath: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList", Key: map[string]string{"k1": "*"}},
},
wantUpdated: true,
},
{
inDesc: "success path with keyed list followed by arbitrary elements",
path: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList"},
{Name: "arbitrary1"},
{Name: "arbitrary2"},
},
wantPath: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList", Key: map[string]string{"k1": "*"}},
{Name: "arbitrary1"},
{Name: "arbitrary2"},
},
wantUpdated: true,
},
{
inDesc: "success path with struct keyed list",
path: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList"},
{Name: "structKeyedLists"},
{Name: "structKeyedList"},
},
wantPath: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList", Key: map[string]string{"k1": "*"}},
{Name: "structKeyedLists"},
{Name: "structKeyedList", Key: map[string]string{"k1": "*", "k2": "*", "k3": "*"}},
},
wantUpdated: true,
},
{
inDesc: "fail when input path already has keys",
path: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList", Key: map[string]string{"k1": "key1"}},
{Name: "arbitrary"},
},
wantPath: []*gnmipb.PathElem{
{Name: "a"},
{Name: "b"},
{Name: "simpleKeyedLists"},
{Name: "simpleKeyedList", Key: map[string]string{"k1": "key1"}},
{Name: "arbitrary"},
},
wantUpdated: false,
wantErrSubstring: "already has keys",
},
}
r, err := NewPathTranslator(schemas)
if err != nil {
t.Errorf("failed to create path translator; %v", r)
}
for _, tc := range tests {
t.Run(tc.inDesc, func(t *testing.T) {
updated, err := r.SetWildcardKeys(tc.path)
if diff := errdiff.Substring(err, tc.wantErrSubstring); diff != "" {
t.Errorf("diff: %v", diff)
return
}
if updated != tc.wantUpdated {
t.Errorf("got matched %v, want %v", updated, tc.wantUpdated)
}
if !cmp.Equal(tc.path, tc.wantPath, cmp.Comparer(proto.Equal)) {
t.Errorf("got %v, want %v", tc.path, tc.wantPath)
}
})
}
}
Loading