Skip to content

Commit f9326f6

Browse files
authored
Add Go generation flag ignore_unsupported (#712)
* Add Go generation flag `ignore_unsupported` This allows generating from YANG files with unsupported statements such as `notification` without erroring out. * Add tests and logging * Fix integration test error message
1 parent 426d469 commit f9326f6

File tree

7 files changed

+477
-17
lines changed

7 files changed

+477
-17
lines changed

generator/generator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var (
7878
trimEnumOpenConfigPrefix = flag.Bool("trim_enum_openconfig_prefix", false, `If set to true when compressPaths=true, the organizational prefix "openconfig-" is trimmed from the module part of the name of enumerated names in the generated code`)
7979
includeDescriptions = flag.Bool("include_descriptions", false, "If set to true when generateSchema=true, the YANG descriptions will be included in the generated code artefact.")
8080
enumOrgPrefixesToTrim []string
81+
ignoreUnsupportedStatements = flag.Bool("ignore_unsupported", false, "If set to true, unsupported YANG statements are ignored.")
8182

8283
// Flags used for GoStruct generation only.
8384
generateFakeRoot = flag.Bool("generate_fakeroot", false, "If set to true, a fake element at the root of the data tree is generated. By default the fake root entity is named Device, its name can be controlled with the fakeroot_name flag.")
@@ -329,7 +330,8 @@ func main() {
329330
"",
330331
ygen.IROptions{
331332
ParseOptions: ygen.ParseOpts{
332-
ExcludeModules: modsExcluded,
333+
IgnoreUnsupportedStatements: *ignoreUnsupportedStatements,
334+
ExcludeModules: modsExcluded,
333335
YANGParseOptions: yang.Options{
334336
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
335337
},
@@ -439,6 +441,7 @@ func main() {
439441
FakeRootName: *fakeRootName,
440442
PathStructSuffix: *pathStructSuffix,
441443
ExcludeModules: modsExcluded,
444+
IgnoreUnsupportedStatements: *ignoreUnsupportedStatements,
442445
YANGParseOptions: yang.Options{
443446
IgnoreSubmoduleCircularDependencies: *ignoreCircDeps,
444447
},

gogen/codegen_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,49 @@ func TestSimpleStructs(t *testing.T) {
139139
},
140140
},
141141
wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt"),
142+
}, {
143+
name: "simple openconfig test with unsupported statements, don't tolerate",
144+
inFiles: []string{filepath.Join(datapath, "openconfig-simple-with-unsupported.yang")},
145+
inConfig: CodeGenerator{
146+
IROptions: ygen.IROptions{
147+
TransformationOptions: ygen.TransformationOpts{
148+
CompressBehaviour: genutil.PreferIntendedConfig,
149+
ShortenEnumLeafNames: true,
150+
EnumOrgPrefixesToTrim: []string{"openconfig"},
151+
UseDefiningModuleForTypedefEnumNames: true,
152+
EnumerationsUseUnderscores: true,
153+
},
154+
},
155+
GoOptions: GoOpts{
156+
GenerateSimpleUnions: true,
157+
GenerateLeafGetters: true,
158+
GeneratePopulateDefault: true,
159+
},
160+
},
161+
wantErrSubstring: "unsupported statement type (Notification)",
162+
}, {
163+
name: "simple openconfig test with unsupported statements, tolerate",
164+
inFiles: []string{filepath.Join(datapath, "openconfig-simple-with-unsupported.yang")},
165+
inConfig: CodeGenerator{
166+
IROptions: ygen.IROptions{
167+
ParseOptions: ygen.ParseOpts{
168+
IgnoreUnsupportedStatements: true,
169+
},
170+
TransformationOptions: ygen.TransformationOpts{
171+
CompressBehaviour: genutil.PreferIntendedConfig,
172+
ShortenEnumLeafNames: true,
173+
EnumOrgPrefixesToTrim: []string{"openconfig"},
174+
UseDefiningModuleForTypedefEnumNames: true,
175+
EnumerationsUseUnderscores: true,
176+
},
177+
},
178+
GoOptions: GoOpts{
179+
GenerateSimpleUnions: true,
180+
GenerateLeafGetters: true,
181+
GeneratePopulateDefault: true,
182+
},
183+
},
184+
wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-with-unsupported.formatted-txt"),
142185
}, {
143186
name: "OpenConfig leaf-list defaults test, with compression",
144187
inFiles: []string{filepath.Join(datapath, "openconfig-leaflist-default.yang")},
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
Package ocstructs is a generated package which contains definitions
3+
of structs which represent a YANG schema. The generated schema can be
4+
compressed by a series of transformations (compression was true
5+
in this case).
6+
7+
This package was generated by codegen-tests
8+
using the following YANG input files:
9+
- ../testdata/modules/openconfig-simple-with-unsupported.yang
10+
Imported modules were sourced from:
11+
*/
12+
package ocstructs
13+
14+
import (
15+
"encoding/json"
16+
"fmt"
17+
"reflect"
18+
19+
"github.com/openconfig/ygot/ygot"
20+
)
21+
22+
// Binary is a type that is used for fields that have a YANG type of
23+
// binary. It is used such that binary fields can be distinguished from
24+
// leaf-lists of uint8s (which are mapped to []uint8, equivalent to
25+
// []byte in reflection).
26+
type Binary []byte
27+
28+
// YANGEmpty is a type that is used for fields that have a YANG type of
29+
// empty. It is used such that empty fields can be distinguished from boolean fields
30+
// in the generated code.
31+
type YANGEmpty bool
32+
33+
// UnionInt8 is an int8 type assignable to unions of which it is a subtype.
34+
type UnionInt8 int8
35+
36+
// UnionInt16 is an int16 type assignable to unions of which it is a subtype.
37+
type UnionInt16 int16
38+
39+
// UnionInt32 is an int32 type assignable to unions of which it is a subtype.
40+
type UnionInt32 int32
41+
42+
// UnionInt64 is an int64 type assignable to unions of which it is a subtype.
43+
type UnionInt64 int64
44+
45+
// UnionUint8 is a uint8 type assignable to unions of which it is a subtype.
46+
type UnionUint8 uint8
47+
48+
// UnionUint16 is a uint16 type assignable to unions of which it is a subtype.
49+
type UnionUint16 uint16
50+
51+
// UnionUint32 is a uint32 type assignable to unions of which it is a subtype.
52+
type UnionUint32 uint32
53+
54+
// UnionUint64 is a uint64 type assignable to unions of which it is a subtype.
55+
type UnionUint64 uint64
56+
57+
// UnionFloat64 is a float64 type assignable to unions of which it is a subtype.
58+
type UnionFloat64 float64
59+
60+
// UnionString is a string type assignable to unions of which it is a subtype.
61+
type UnionString string
62+
63+
// UnionBool is a bool type assignable to unions of which it is a subtype.
64+
type UnionBool bool
65+
66+
// UnionUnsupported is an interface{} wrapper type for unsupported types. It is
67+
// assignable to unions of which it is a subtype.
68+
type UnionUnsupported struct {
69+
Value interface{}
70+
}
71+
72+
// Parent represents the /openconfig-simple/parent YANG schema element.
73+
type Parent struct {
74+
Child *Parent_Child `path:"child" module:"openconfig-simple"`
75+
}
76+
77+
// IsYANGGoStruct ensures that Parent implements the yang.GoStruct
78+
// interface. This allows functions that need to handle this struct to
79+
// identify it as being generated by ygen.
80+
func (*Parent) IsYANGGoStruct() {}
81+
82+
// PopulateDefaults recursively populates unset leaf fields in the Parent
83+
// with default values as specified in the YANG schema, instantiating any nil
84+
// container fields.
85+
func (t *Parent) PopulateDefaults() {
86+
if (t == nil) {
87+
return
88+
}
89+
ygot.BuildEmptyTree(t)
90+
t.Child.PopulateDefaults()
91+
}
92+
93+
// ΛBelongingModule returns the name of the module that defines the namespace
94+
// of Parent.
95+
func (*Parent) ΛBelongingModule() string {
96+
return "openconfig-simple"
97+
}
98+
99+
// Parent_Child represents the /openconfig-simple/parent/child YANG schema element.
100+
type Parent_Child struct {
101+
Four Binary `path:"config/four" module:"openconfig-simple/openconfig-simple"`
102+
One *string `path:"config/one" module:"openconfig-simple/openconfig-simple"`
103+
Three E_Child_Three `path:"config/three" module:"openconfig-simple/openconfig-simple"`
104+
Two *string `path:"state/two" module:"openconfig-simple/openconfig-simple"`
105+
}
106+
107+
// IsYANGGoStruct ensures that Parent_Child implements the yang.GoStruct
108+
// interface. This allows functions that need to handle this struct to
109+
// identify it as being generated by ygen.
110+
func (*Parent_Child) IsYANGGoStruct() {}
111+
112+
// GetFour retrieves the value of the leaf Four from the Parent_Child
113+
// struct. If the field is unset but has a default value in the YANG schema,
114+
// then the default value will be returned.
115+
// Caution should be exercised whilst using this method since when without a
116+
// default value, it will return the Go zero value if the field is explicitly
117+
// unset. If the caller explicitly does not care if Four is set, it can
118+
// safely use t.GetFour() to retrieve the value. In the case that the
119+
// caller has different actions based on whether the leaf is set or unset, it
120+
// should use 'if t.Four == nil' before retrieving the leaf's value.
121+
func (t *Parent_Child) GetFour() Binary {
122+
if t == nil || t.Four == nil {
123+
return nil
124+
}
125+
return t.Four
126+
}
127+
128+
// GetOne retrieves the value of the leaf One from the Parent_Child
129+
// struct. If the field is unset but has a default value in the YANG schema,
130+
// then the default value will be returned.
131+
// Caution should be exercised whilst using this method since when without a
132+
// default value, it will return the Go zero value if the field is explicitly
133+
// unset. If the caller explicitly does not care if One is set, it can
134+
// safely use t.GetOne() to retrieve the value. In the case that the
135+
// caller has different actions based on whether the leaf is set or unset, it
136+
// should use 'if t.One == nil' before retrieving the leaf's value.
137+
func (t *Parent_Child) GetOne() string {
138+
if t == nil || t.One == nil {
139+
return ""
140+
}
141+
return *t.One
142+
}
143+
144+
// GetThree retrieves the value of the leaf Three from the Parent_Child
145+
// struct. If the field is unset but has a default value in the YANG schema,
146+
// then the default value will be returned.
147+
// Caution should be exercised whilst using this method since when without a
148+
// default value, it will return the Go zero value if the field is explicitly
149+
// unset. If the caller explicitly does not care if Three is set, it can
150+
// safely use t.GetThree() to retrieve the value. In the case that the
151+
// caller has different actions based on whether the leaf is set or unset, it
152+
// should use 'if t.Three == nil' before retrieving the leaf's value.
153+
func (t *Parent_Child) GetThree() E_Child_Three {
154+
if t == nil || t.Three == 0 {
155+
return 0
156+
}
157+
return t.Three
158+
}
159+
160+
// GetTwo retrieves the value of the leaf Two from the Parent_Child
161+
// struct. If the field is unset but has a default value in the YANG schema,
162+
// then the default value will be returned.
163+
// Caution should be exercised whilst using this method since when without a
164+
// default value, it will return the Go zero value if the field is explicitly
165+
// unset. If the caller explicitly does not care if Two is set, it can
166+
// safely use t.GetTwo() to retrieve the value. In the case that the
167+
// caller has different actions based on whether the leaf is set or unset, it
168+
// should use 'if t.Two == nil' before retrieving the leaf's value.
169+
func (t *Parent_Child) GetTwo() string {
170+
if t == nil || t.Two == nil {
171+
return ""
172+
}
173+
return *t.Two
174+
}
175+
176+
// PopulateDefaults recursively populates unset leaf fields in the Parent_Child
177+
// with default values as specified in the YANG schema, instantiating any nil
178+
// container fields.
179+
func (t *Parent_Child) PopulateDefaults() {
180+
if (t == nil) {
181+
return
182+
}
183+
ygot.BuildEmptyTree(t)
184+
}
185+
186+
// ΛBelongingModule returns the name of the module that defines the namespace
187+
// of Parent_Child.
188+
func (*Parent_Child) ΛBelongingModule() string {
189+
return "openconfig-simple"
190+
}
191+
192+
// RemoteContainer represents the /openconfig-simple/remote-container YANG schema element.
193+
type RemoteContainer struct {
194+
ALeaf *string `path:"config/a-leaf" module:"openconfig-simple/openconfig-simple"`
195+
}
196+
197+
// IsYANGGoStruct ensures that RemoteContainer implements the yang.GoStruct
198+
// interface. This allows functions that need to handle this struct to
199+
// identify it as being generated by ygen.
200+
func (*RemoteContainer) IsYANGGoStruct() {}
201+
202+
// GetALeaf retrieves the value of the leaf ALeaf from the RemoteContainer
203+
// struct. If the field is unset but has a default value in the YANG schema,
204+
// then the default value will be returned.
205+
// Caution should be exercised whilst using this method since when without a
206+
// default value, it will return the Go zero value if the field is explicitly
207+
// unset. If the caller explicitly does not care if ALeaf is set, it can
208+
// safely use t.GetALeaf() to retrieve the value. In the case that the
209+
// caller has different actions based on whether the leaf is set or unset, it
210+
// should use 'if t.ALeaf == nil' before retrieving the leaf's value.
211+
func (t *RemoteContainer) GetALeaf() string {
212+
if t == nil || t.ALeaf == nil {
213+
return ""
214+
}
215+
return *t.ALeaf
216+
}
217+
218+
// PopulateDefaults recursively populates unset leaf fields in the RemoteContainer
219+
// with default values as specified in the YANG schema, instantiating any nil
220+
// container fields.
221+
func (t *RemoteContainer) PopulateDefaults() {
222+
if (t == nil) {
223+
return
224+
}
225+
ygot.BuildEmptyTree(t)
226+
}
227+
228+
// ΛBelongingModule returns the name of the module that defines the namespace
229+
// of RemoteContainer.
230+
func (*RemoteContainer) ΛBelongingModule() string {
231+
return "openconfig-simple"
232+
}
233+
234+
// E_Child_Three is a derived int64 type which is used to represent
235+
// the enumerated node Child_Three. An additional value named
236+
// Child_Three_UNSET is added to the enumeration which is used as
237+
// the nil value, indicating that the enumeration was not explicitly set by
238+
// the program importing the generated structures.
239+
type E_Child_Three int64
240+
241+
// IsYANGGoEnum ensures that Child_Three implements the yang.GoEnum
242+
// interface. This ensures that Child_Three can be identified as a
243+
// mapped type for a YANG enumeration.
244+
func (E_Child_Three) IsYANGGoEnum() {}
245+
246+
// ΛMap returns the value lookup map associated with Child_Three.
247+
func (E_Child_Three) ΛMap() map[string]map[int64]ygot.EnumDefinition { return ΛEnum; }
248+
249+
// String returns a logging-friendly string for E_Child_Three.
250+
func (e E_Child_Three) String() string {
251+
return ygot.EnumLogString(e, int64(e), "E_Child_Three")
252+
}
253+
254+
const (
255+
// Child_Three_UNSET corresponds to the value UNSET of Child_Three
256+
Child_Three_UNSET E_Child_Three = 0
257+
// Child_Three_ONE corresponds to the value ONE of Child_Three
258+
Child_Three_ONE E_Child_Three = 1
259+
// Child_Three_TWO corresponds to the value TWO of Child_Three
260+
Child_Three_TWO E_Child_Three = 2
261+
)
262+
263+
// ΛEnum is a map, keyed by the name of the type defined for each enum in the
264+
// generated Go code, which provides a mapping between the constant int64 value
265+
// of each value of the enumeration, and the string that is used to represent it
266+
// in the YANG schema. The map is named ΛEnum in order to avoid clash with any
267+
// valid YANG identifier.
268+
var ΛEnum = map[string]map[int64]ygot.EnumDefinition{
269+
"E_Child_Three": {
270+
1: {Name: "ONE"},
271+
2: {Name: "TWO"},
272+
},
273+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module openconfig-simple {
2+
prefix "ocs";
3+
namespace "urn:ocs";
4+
description
5+
"A simple test module with the OpenConfig structure.";
6+
7+
import openconfig-remote { prefix "ocr"; }
8+
9+
grouping parent-config {
10+
leaf one { type string; }
11+
leaf three {
12+
type enumeration {
13+
enum ONE;
14+
enum TWO;
15+
}
16+
}
17+
leaf four {
18+
type binary;
19+
}
20+
}
21+
22+
container parent {
23+
description
24+
"I am a parent container
25+
that has 4 children.";
26+
container child {
27+
container config {
28+
uses parent-config;
29+
}
30+
container state {
31+
config false;
32+
uses parent-config;
33+
leaf two { type string; }
34+
}
35+
}
36+
}
37+
38+
notification update {
39+
description "update notification";
40+
anyxml data {
41+
description "updated stuff";
42+
}
43+
}
44+
45+
uses ocr:a-grouping;
46+
}

0 commit comments

Comments
 (0)