Skip to content

Commit c47918b

Browse files
authored
Add skip_deprecated and skip_obsolete flags to ygot (#1037)
* Add SkipDeprecated and SkipObsolete flags to ygot/ygen * Extend to individual deprecated/obsolete leaf fields within containers * Add helper functions isDeprecated(yang.Node) and isObsolete(yang.Node)
1 parent 3a953aa commit c47918b

File tree

5 files changed

+343
-12
lines changed

5 files changed

+343
-12
lines changed

generator/generator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ var (
8080
enumOrgPrefixesToTrim []string
8181
ignoreUnsupportedStatements = flag.Bool("ignore_unsupported", false, "If set to true, unsupported YANG statements are ignored.")
8282
ignoreDeviateNotsupported = flag.Bool("ignore_deviate_notsupported", false, "If set to true, 'deviate not-supported' YANG statements are ignored, thus target nodes are retained in the generated code.")
83+
skipDeprecated = flag.Bool("skip_deprecated", false, "If set to true, YANG fields with status 'deprecated' are excluded from the generated code.")
84+
skipObsolete = flag.Bool("skip_obsolete", false, "If set to true, YANG fields with status 'obsolete' are excluded from the generated code.")
8385

8486
// Flags used for GoStruct generation only.
8587
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.")
@@ -351,6 +353,8 @@ func main() {
351353
EnumOrgPrefixesToTrim: enumOrgPrefixesToTrim,
352354
UseDefiningModuleForTypedefEnumNames: *useDefiningModuleForTypedefEnumNames,
353355
EnumerationsUseUnderscores: true,
356+
SkipDeprecated: *skipDeprecated,
357+
SkipObsolete: *skipObsolete,
354358
},
355359
},
356360
gogen.GoOpts{

ygen/codegen.go

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,48 @@ import (
3636
gpb "github.com/openconfig/gnmi/proto/gnmi"
3737
)
3838

39+
// isDeprecated checks if a YANG node has status "deprecated".
40+
func isDeprecated(node yang.Node) bool {
41+
if node == nil {
42+
return false
43+
}
44+
var status *yang.Value
45+
switch n := node.(type) {
46+
case *yang.Container:
47+
status = n.Status
48+
case *yang.Leaf:
49+
status = n.Status
50+
case *yang.LeafList:
51+
status = n.Status
52+
case *yang.List:
53+
status = n.Status
54+
case *yang.Typedef:
55+
status = n.Status
56+
}
57+
return status != nil && status.Name == "deprecated"
58+
}
59+
60+
// isObsolete checks if a YANG node has status "obsolete".
61+
func isObsolete(node yang.Node) bool {
62+
if node == nil {
63+
return false
64+
}
65+
var status *yang.Value
66+
switch n := node.(type) {
67+
case *yang.Container:
68+
status = n.Status
69+
case *yang.Leaf:
70+
status = n.Status
71+
case *yang.LeafList:
72+
status = n.Status
73+
case *yang.List:
74+
status = n.Status
75+
case *yang.Typedef:
76+
status = n.Status
77+
}
78+
return status != nil && status.Name == "obsolete"
79+
}
80+
3981
// ParseOpts contains parsing configuration for a given schema.
4082
type ParseOpts struct {
4183
// IgnoreUnsupportedStatements ignores unsupported YANG statements when
@@ -112,6 +154,12 @@ type TransformationOpts struct {
112154
// EnumerationsUseUnderscores specifies whether enumeration names
113155
// should use underscores between path segments.
114156
EnumerationsUseUnderscores bool
157+
// SkipDeprecated specifies whether YANG fields with status "deprecated"
158+
// should be excluded from the generated code output.
159+
SkipDeprecated bool
160+
// SkipObsolete specifies whether YANG fields with status "obsolete"
161+
// should be excluded from the generated code output.
162+
SkipObsolete bool
115163
}
116164

117165
// yangEnum represents an enumerated type in YANG that is to be output in the
@@ -236,7 +284,7 @@ func mappedDefinitions(yangFiles, includePaths []string, opts IROptions) (*mappe
236284
// Need to transform the AST based on compression behaviour.
237285
genutil.TransformEntry(module, opts.TransformationOptions.CompressBehaviour)
238286

239-
errs = append(errs, findMappableEntities(module, dirs, enums, opts.ParseOptions.ExcludeModules, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.IgnoreUnsupportedStatements, modules)...)
287+
errs = append(errs, findMappableEntities(module, dirs, enums, opts.ParseOptions.ExcludeModules, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.IgnoreUnsupportedStatements, modules, opts.TransformationOptions)...)
240288
if module == nil {
241289
errs = append(errs, errors.New("found a nil module in the returned module set"))
242290
continue
@@ -301,7 +349,7 @@ func mappedDefinitions(yangFiles, includePaths []string, opts IROptions) (*mappe
301349
// mapped with path compression enabled. The set of modules that the current code generation
302350
// is processing is specified by the modules slice. This function returns slice of errors
303351
// encountered during processing.
304-
func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[string]*yang.Entry, excludeModules []string, compressPaths, ignoreUnsupportedStatements bool, modules []*yang.Entry) util.Errors {
352+
func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[string]*yang.Entry, excludeModules []string, compressPaths, ignoreUnsupportedStatements bool, modules []*yang.Entry, transformOpts TransformationOpts) util.Errors {
305353
// Skip entities who are defined within a module that we have been instructed
306354
// not to generate code for.
307355
for _, s := range excludeModules {
@@ -314,6 +362,16 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[
314362

315363
var errs util.Errors
316364
for _, ch := range util.Children(e) {
365+
// Skip children with deprecated status if SkipDeprecated is enabled
366+
if transformOpts.SkipDeprecated && isDeprecated(ch.Node) {
367+
continue // skip this entity
368+
}
369+
370+
// Skip children with obsolete status if SkipObsolete is enabled
371+
if transformOpts.SkipObsolete && isObsolete(ch.Node) {
372+
continue // skip this entity
373+
}
374+
317375
switch {
318376
case ch.IsLeaf(), ch.IsLeafList():
319377
// Leaves are not mapped as directories so do not map them unless we find
@@ -326,12 +384,12 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[
326384
// If this is a config or state container and we are compressing paths
327385
// then we do not want to map this container - but we do want to map its
328386
// children.
329-
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules))
387+
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules, transformOpts))
330388
case util.HasOnlyChild(ch) && util.Children(ch)[0].IsList() && compressPaths:
331389
// This is a surrounding container for a list, and we are compressing
332390
// paths, so we don't want to map it but again we do want to map its
333391
// children.
334-
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules))
392+
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules, transformOpts))
335393
case util.IsChoiceOrCase(ch):
336394
// Don't map for a choice or case node itself, and rather skip over it.
337395
// However, we must walk each branch to find the first container that
@@ -351,12 +409,12 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[
351409
if gch.IsContainer() || gch.IsList() {
352410
dirs[fmt.Sprintf("%s/%s", ch.Parent.Path(), gch.Name)] = gch
353411
}
354-
errs = util.AppendErrs(errs, findMappableEntities(gch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules))
412+
errs = util.AppendErrs(errs, findMappableEntities(gch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules, transformOpts))
355413
}
356414
case ch.IsContainer(), ch.IsList():
357415
dirs[ch.Path()] = ch
358416
// Recurse down the tree.
359-
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules))
417+
errs = util.AppendErrs(errs, findMappableEntities(ch, dirs, enums, excludeModules, compressPaths, ignoreUnsupportedStatements, modules, transformOpts))
360418
case ch.Kind == yang.AnyDataEntry:
361419
continue
362420
default:

ygen/codegen_test.go

Lines changed: 107 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ import (
2727
// into Go code from a YANG schema.
2828
func TestFindMappableEntities(t *testing.T) {
2929
tests := []struct {
30-
name string // name is an identifier for the test.
31-
in *yang.Entry // in is the yang.Entry corresponding to the YANG root element.
32-
inSkipModules []string // inSkipModules is a slice of strings indicating modules to be skipped.
33-
inModules []*yang.Entry // inModules is the set of modules that the code generation is for.
34-
inIgnoreUnsupportedStatements bool // inIgnoreUnsupportedStatements determines whether unsupported statements should error out.
30+
name string // name is an identifier for the test.
31+
in *yang.Entry // in is the yang.Entry corresponding to the YANG root element.
32+
inSkipModules []string // inSkipModules is a slice of strings indicating modules to be skipped.
33+
inModules []*yang.Entry // inModules is the set of modules that the code generation is for.
34+
inIgnoreUnsupportedStatements bool // inIgnoreUnsupportedStatements determines whether unsupported statements should error out.
35+
transformOpts TransformationOpts // transformOpts contains transformation options like SkipDeprecated/SkipObsolete
3536
// wantCompressed is a map keyed by the string "structs" or "enums" which contains a slice
3637
// of the YANG identifiers for the corresponding mappable entities that should be
3738
// found. wantCompressed is the set that are expected when compression is enabled.
@@ -395,6 +396,106 @@ func TestFindMappableEntities(t *testing.T) {
395396
wantUncompressed: map[string][]string{
396397
"structs": {"container"},
397398
"enums": {"choice-case-container-leaf", "choice-case2-leaf", "direct"}},
399+
}, {
400+
name: "no-filtering",
401+
in: &yang.Entry{
402+
Name: "module",
403+
Kind: yang.DirectoryEntry,
404+
Dir: map[string]*yang.Entry{
405+
"deprecated-container": {
406+
Name: "deprecated-container",
407+
Kind: yang.DirectoryEntry,
408+
Parent: &yang.Entry{Name: "module"},
409+
Node: &yang.Container{
410+
Status: &yang.Value{Name: "deprecated"},
411+
},
412+
},
413+
},
414+
},
415+
transformOpts: TransformationOpts{},
416+
wantCompressed: map[string][]string{
417+
"structs": {"deprecated-container"},
418+
"enums": {},
419+
},
420+
wantUncompressed: map[string][]string{
421+
"structs": {"deprecated-container"},
422+
"enums": {},
423+
},
424+
}, {
425+
name: "skip-deprecated",
426+
in: &yang.Entry{
427+
Name: "module",
428+
Kind: yang.DirectoryEntry,
429+
Dir: map[string]*yang.Entry{
430+
"deprecated-container": {
431+
Name: "deprecated-container",
432+
Kind: yang.DirectoryEntry,
433+
Parent: &yang.Entry{Name: "module"},
434+
Node: &yang.Container{
435+
Status: &yang.Value{Name: "deprecated"},
436+
},
437+
},
438+
},
439+
},
440+
transformOpts: TransformationOpts{SkipDeprecated: true},
441+
wantCompressed: map[string][]string{
442+
"structs": {},
443+
"enums": {},
444+
},
445+
wantUncompressed: map[string][]string{
446+
"structs": {},
447+
"enums": {},
448+
},
449+
}, {
450+
name: "skip-obsolete-with-deprecated",
451+
in: &yang.Entry{
452+
Name: "module",
453+
Kind: yang.DirectoryEntry,
454+
Dir: map[string]*yang.Entry{
455+
"deprecated-container": {
456+
Name: "deprecated-container",
457+
Kind: yang.DirectoryEntry,
458+
Parent: &yang.Entry{Name: "module"},
459+
Node: &yang.Container{
460+
Status: &yang.Value{Name: "deprecated"},
461+
},
462+
},
463+
},
464+
},
465+
transformOpts: TransformationOpts{SkipObsolete: true},
466+
wantCompressed: map[string][]string{
467+
"structs": {"deprecated-container"},
468+
"enums": {},
469+
},
470+
wantUncompressed: map[string][]string{
471+
"structs": {"deprecated-container"},
472+
"enums": {},
473+
},
474+
}, {
475+
name: "skip-obsolete-actual",
476+
in: &yang.Entry{
477+
Name: "module",
478+
Kind: yang.DirectoryEntry,
479+
Dir: map[string]*yang.Entry{
480+
"obsolete-container": {
481+
Name: "obsolete-container",
482+
Kind: yang.DirectoryEntry,
483+
Parent: &yang.Entry{Name: "module"},
484+
Node: &yang.Container{
485+
Status: &yang.Value{Name: "obsolete"},
486+
},
487+
},
488+
},
489+
},
490+
transformOpts: TransformationOpts{SkipObsolete: true},
491+
wantCompressed: map[string][]string{
492+
"structs": {},
493+
"enums": {},
494+
},
495+
wantUncompressed: map[string][]string{
496+
"structs": {},
497+
"enums": {},
498+
},
398499
}}
399500

400501
for _, tt := range tests {
@@ -407,7 +508,7 @@ func TestFindMappableEntities(t *testing.T) {
407508
structs := make(map[string]*yang.Entry)
408509
enums := make(map[string]*yang.Entry)
409510

410-
errs := findMappableEntities(tt.in, structs, enums, tt.inSkipModules, compress, tt.inIgnoreUnsupportedStatements, tt.inModules)
511+
errs := findMappableEntities(tt.in, structs, enums, tt.inSkipModules, compress, tt.inIgnoreUnsupportedStatements, tt.inModules, tt.transformOpts)
411512

412513
var err error
413514
switch {

ygen/directory.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,17 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory
221221
pd.Fields = make(map[string]*NodeDetails, len(dir.Fields))
222222
for _, fn := range GetOrderedFieldNames(dir) {
223223
field := dir.Fields[fn]
224+
225+
// Skip fields with deprecated status if SkipDeprecated is enabled
226+
if opts.TransformationOptions.SkipDeprecated && isDeprecated(field.Node) {
227+
continue // skip this field
228+
}
229+
230+
// Skip fields with obsolete status if SkipObsolete is enabled
231+
if opts.TransformationOptions.SkipObsolete && isObsolete(field.Node) {
232+
continue // skip this field
233+
}
234+
224235
shadowField, hasShadowField := dir.ShadowedFields[fn]
225236

226237
mp, mm, err := findMapPaths(dir, fn, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), false, opts.AbsoluteMapPaths)

0 commit comments

Comments
 (0)