From d13597942ae4bfc1a243a1f740446cbcadf09827 Mon Sep 17 00:00:00 2001 From: steiler Date: Wed, 7 Aug 2024 09:06:51 +0200 Subject: [PATCH 1/3] Implement Augmentation for Uses keyword --- pkg/yang/entry.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go index fa50af1..c3c18b7 100644 --- a/pkg/yang/entry.go +++ b/pkg/yang/entry.go @@ -660,6 +660,16 @@ func ToEntry(n Node) (e *Entry) { // when the group is used in multiple locations and the // grouping has a leafref that references outside the group. e = ToEntry(g).dup() + + // process augments + if s.Augment != nil { + a := ToEntry(s.Augment) + a.Parent = e + a.Augments = append(a.Augments, e) + + e.Find(a.Name).merge(nil, a.Namespace(), a) + } + addExtraKeywordsToLeafEntry(n, e) return e } From 2836ef67e41da4bd5865b50405d2f57b41cb1ca9 Mon Sep 17 00:00:00 2001 From: steiler Date: Wed, 7 Aug 2024 12:36:58 +0200 Subject: [PATCH 2/3] multiple augments per uses --- pkg/yang/entry.go | 38 +++++++++++++++++++++++++++++++++++--- pkg/yang/modules.go | 12 ++++++++++++ pkg/yang/yang.go | 14 +++++++------- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go index c3c18b7..ca14702 100644 --- a/pkg/yang/entry.go +++ b/pkg/yang/entry.go @@ -661,12 +661,18 @@ func ToEntry(n Node) (e *Entry) { // grouping has a leafref that references outside the group. e = ToEntry(g).dup() + switch determineYangVersion(g) { + case YangVersion10: + if len(s.Augment) > 1 { + return newError(s, "multiple augments not allowed in yang version 1.0: %s", s.Name) + } + } + // process augments - if s.Augment != nil { - a := ToEntry(s.Augment) + for _, x := range s.Augment { + a := ToEntry(x) a.Parent = e a.Augments = append(a.Augments, e) - e.Find(a.Name).merge(nil, a.Namespace(), a) } @@ -1055,6 +1061,32 @@ func ToEntry(n Node) (e *Entry) { return e } +type YangVersion string + +const ( + YangVersion10 YangVersion = "1.0" + YangVersion11 YangVersion = "1.1" +) + +func determineYangVersion(n Node) YangVersion { + p := n.ParentNode() + if p != nil { + return determineYangVersion(p) + } + var m *Module + var ok bool + if m, ok = n.(*Module); ok { + switch m.YangVersion.asString() { + case string(YangVersion10): + return YangVersion10 + case string(YangVersion11): + return YangVersion11 + } + } + // default to 1.0 + return YangVersion10 +} + // addExtraKeywordsToLeafEntry stores the values for unimplemented keywords in leaf entries. func addExtraKeywordsToLeafEntry(n Node, e *Entry) { v := reflect.ValueOf(n).Elem() diff --git a/pkg/yang/modules.go b/pkg/yang/modules.go index ab543d2..03cdac6 100644 --- a/pkg/yang/modules.go +++ b/pkg/yang/modules.go @@ -371,6 +371,18 @@ func (ms *Modules) Process() []error { } } + // rerun the error checks, after augmentation happened + for _, m := range ms.Modules { + errs = append(errs, ToEntry(m).GetErrors()...) + } + for _, m := range ms.SubModules { + errs = append(errs, ToEntry(m).GetErrors()...) + } + + if len(errs) > 0 { + return errorSort(errs) + } + // Now fix up all the choice statements to add in the missing case // statements. for _, m := range ms.Modules { diff --git a/pkg/yang/yang.go b/pkg/yang/yang.go index efff44a..822426f 100644 --- a/pkg/yang/yang.go +++ b/pkg/yang/yang.go @@ -624,13 +624,13 @@ type Uses struct { Parent Node `yang:"Parent,nomerge" json:"-"` Extensions []*Statement `yang:"Ext" json:"-"` - Augment *Augment `yang:"augment" json:",omitempty"` - Description *Value `yang:"description" json:",omitempty"` - IfFeature []*Value `yang:"if-feature" json:"-"` - Refine []*Refine `yang:"refine" json:"-"` - Reference *Value `yang:"reference" json:"-"` - Status *Value `yang:"status" json:"-"` - When *Value `yang:"when" json:",omitempty"` + Augment []*Augment `yang:"augment" json:",omitempty"` + Description *Value `yang:"description" json:",omitempty"` + IfFeature []*Value `yang:"if-feature" json:"-"` + Refine []*Refine `yang:"refine" json:"-"` + Reference *Value `yang:"reference" json:"-"` + Status *Value `yang:"status" json:"-"` + When *Value `yang:"when" json:",omitempty"` } func (Uses) Kind() string { return "uses" } From 423be34af4b4df0aae55aac787227cf6a841646f Mon Sep 17 00:00:00 2001 From: steiler Date: Thu, 8 Aug 2024 10:03:01 +0200 Subject: [PATCH 3/3] adding test --- pkg/yang/entry_test.go | 254 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/pkg/yang/entry_test.go b/pkg/yang/entry_test.go index 2000238..1cc9c97 100644 --- a/pkg/yang/entry_test.go +++ b/pkg/yang/entry_test.go @@ -3871,6 +3871,260 @@ func TestLeafEntry(t *testing.T) { } } +func TestAugmentUses(t *testing.T) { + + tests := []struct { + name string + inModules map[string]string + WantErrors []string + pathExist [][]string + }{ + { + name: "Yang 1.0 fail multiple augment in use", + WantErrors: []string{"multiple augments not allowed in yang version 1.0: grouping-a"}, + inModules: map[string]string{ + "a.yang": ` + module mod-a { + yang-version 1.1; + namespace "urn:mod-a"; + prefix moda; + + include submod-a-one; + include submod-a-two; + } + `, + "b.yang": ` + submodule submod-a-one { + belongs-to mod-a { + prefix moda; + } + yang-version 1.0; + include submod-a-two; + + grouping grouping-a { + list range { + must 'end >= start' { + error-message + "'end' must be greater than or equal to 'start'."; + } + leaf start { + type uint16 { + range "1..10"; + } + } + leaf end { + type uint16 { + range "1..10"; + } + } + key "start end"; + } + container z { + + } + } + + augment "/moda:container-a" { + container augment-a { + uses grouping-a { + augment "range" { + choice timeout-type { + mandatory true; + case period { + leaf period { + type uint16 { + range "2..100"; + } + units second; + } + } + case boolean { + container boolean { + leaf enabled { + type boolean; + } + } + } + } + } + augment "z" { + leaf enableZ { + type boolean; + } + } + } + } + } + } + `, + "c.yang": ` + submodule submod-a-two { + belongs-to mod-a { + prefix moda; + } + yang-version 1.1; + + container container-a { + + container a { + } + + container b { + } + } + } + `, + }, + }, + { + name: "Yang 1.1 pass multiple augment in use", + pathExist: [][]string{ + {"container-a", "a"}, + {"container-a", "b"}, + {"container-a", "augment-a", "range", "start"}, + {"container-a", "augment-a", "z", "enableZ"}, + }, + inModules: map[string]string{ + "a.yang": ` + module mod-a { + yang-version 1.1; + namespace "urn:mod-a"; + prefix moda; + + include submod-a-one; + include submod-a-two; + } + `, + "b.yang": ` + submodule submod-a-one { + belongs-to mod-a { + prefix moda; + } + yang-version 1.1; + include submod-a-two; + + grouping grouping-a { + list range { + must 'end >= start' { + error-message + "'end' must be greater than or equal to 'start'."; + } + leaf start { + type uint16 { + range "1..10"; + } + } + leaf end { + type uint16 { + range "1..10"; + } + } + key "start end"; + } + container z { + + } + } + + augment "/moda:container-a" { + container augment-a { + uses grouping-a { + augment "range" { + choice timeout-type { + mandatory true; + case period { + leaf period { + type uint16 { + range "2..100"; + } + units second; + } + } + case boolean { + container boolean { + leaf enabled { + type boolean; + } + } + } + } + } + augment "z" { + leaf enableZ { + type boolean; + } + } + } + } + } + } + `, + "c.yang": ` + submodule submod-a-two { + belongs-to mod-a { + prefix moda; + } + yang-version 1.1; + + container container-a { + + container a { + } + + container b { + } + } + } + `, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ms := NewModules() + var errs []error + + for n, m := range tt.inModules { + if err := ms.Parse(m, n); err != nil { + errs = append(errs, err) + + } + } + + if len(errs) > 0 { + t.Fatalf("ms.Parse(), got unexpected error parsing input modules: %v", errs) + } + + errs = append(errs, ms.Process()...) + + var errsStr = strings.Builder{} + for _, e := range errs { + errsStr.WriteString(e.Error()) + } + + for _, errStr := range tt.WantErrors { + strings.Contains(errsStr.String(), errStr) + } + + var m *Entry + m, errs = ms.GetModule("mod-a") + x := m + for _, p := range tt.pathExist { + for _, pe := range p { + y, ok := x.Dir[pe] + if !ok { + t.Fatalf("expected module %s to contain path %s", m.Name, strings.Join(p, "/")) + } + x = y + } + x = m + } + + }) + } +} + func TestLess(t *testing.T) { sErrors := sortedErrors{ {"testfile0", errors.New("test error0")},