diff --git a/gogen/genir_test.go b/gogen/genir_test.go index 8a27edd8..5545c510 100644 --- a/gogen/genir_test.go +++ b/gogen/genir_test.go @@ -1254,16 +1254,18 @@ func TestGenerateIR(t *testing.T) { IsFakeRoot: true, }, "/openconfig-complex/example-presence": { - Name: "ExamplePresence", - Type: ygen.Container, - Path: "/openconfig-complex/example-presence", - Fields: map[string]*ygen.NodeDetails{}, - PackageName: "", - ListKeys: nil, - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + Name: "ExamplePresence", + Type: ygen.Container, + Path: "/openconfig-complex/example-presence", + Fields: map[string]*ygen.NodeDetails{}, + PackageName: "", + ListKeys: nil, + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: true, + CompressedTelemetryAtomic: false, }, "/openconfig-complex/model": { Name: "Model", @@ -1675,12 +1677,14 @@ func TestGenerateIR(t *testing.T) { }, }, }, - ListKeyYANGNames: []string{"key"}, - PackageName: "", - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + ListKeyYANGNames: []string{"key"}, + PackageName: "", + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: false, + CompressedTelemetryAtomic: true, }, "/openconfig-complex/model/b/multi-key": { Name: "Model_MultiKey", @@ -1756,12 +1760,14 @@ func TestGenerateIR(t *testing.T) { LangType: &ygen.MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, }, }, - ListKeyYANGNames: []string{"key1", "key2"}, - PackageName: "", - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + ListKeyYANGNames: []string{"key1", "key2"}, + PackageName: "", + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: true, + CompressedTelemetryAtomic: false, }, "/openconfig-complex/model/c/unkeyed-list": { Name: "Model_UnkeyedList", @@ -1905,7 +1911,10 @@ func TestGenerateIR(t *testing.T) { }, }, }, - ModelData: []*gpb.ModelData{{Name: "openconfig-complex"}}, + ModelData: []*gpb.ModelData{ + {Name: "openconfig-complex"}, + {Name: "openconfig-extensions"}, + }, }, }} diff --git a/protogen/genir_test.go b/protogen/genir_test.go index 8a97a529..afd04010 100644 --- a/protogen/genir_test.go +++ b/protogen/genir_test.go @@ -90,16 +90,18 @@ func protoIR(nestedDirectories bool) *ygen.IR { PackageName: "", }, "/openconfig-complex/example-presence": { - Name: "ExamplePresence", - Type: ygen.Container, - Path: "/openconfig-complex/example-presence", - Fields: map[string]*ygen.NodeDetails{}, - PackageName: "", - ListKeys: nil, - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + Name: "ExamplePresence", + Type: ygen.Container, + Path: "/openconfig-complex/example-presence", + Fields: map[string]*ygen.NodeDetails{}, + PackageName: "", + ListKeys: nil, + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: true, + CompressedTelemetryAtomic: false, }, "/openconfig-complex/model": { Name: "Model", @@ -675,12 +677,14 @@ func protoIR(nestedDirectories bool) *ygen.IR { }, }, }, - ListKeyYANGNames: []string{"key"}, - PackageName: packageName, - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + ListKeyYANGNames: []string{"key"}, + PackageName: packageName, + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: false, + CompressedTelemetryAtomic: true, }, "/openconfig-complex/model/b/multi-key": { Name: "MultiKey", @@ -810,12 +814,14 @@ func protoIR(nestedDirectories bool) *ygen.IR { }, }, }, - ListKeyYANGNames: []string{"key1", "key2"}, - PackageName: packageName, - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", + ListKeyYANGNames: []string{"key1", "key2"}, + PackageName: packageName, + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + TelemetryAtomic: true, + CompressedTelemetryAtomic: false, }, "/openconfig-complex/model/c/unkeyed-list": { Name: "UnkeyedList", @@ -960,7 +966,10 @@ func protoIR(nestedDirectories bool) *ygen.IR { }, }, }, - ModelData: []*gpb.ModelData{{Name: "openconfig-complex"}}, + ModelData: []*gpb.ModelData{ + {Name: "openconfig-complex"}, + {Name: "openconfig-extensions"}, + }, } } diff --git a/testdata/modules/openconfig-complex.yang b/testdata/modules/openconfig-complex.yang index e7519248..ebb3e8d2 100644 --- a/testdata/modules/openconfig-complex.yang +++ b/testdata/modules/openconfig-complex.yang @@ -3,6 +3,8 @@ module openconfig-complex { namespace "urn:occomplex"; prefix "oc"; + import openconfig-extensions { prefix "oc-ext"; } + description "A complex test module that is used to verify code generation for a variety of different schema features"; @@ -127,11 +129,13 @@ module openconfig-complex { grouping top { container example-presence { + oc-ext:telemetry-atomic; presence "This is an example presence container"; } container model { container a { + oc-ext:telemetry-atomic; list single-key { key "key"; @@ -164,6 +168,7 @@ module openconfig-complex { container b { list multi-key { + oc-ext:telemetry-atomic; key "key1 key2"; // Test for irregular spacing diff --git a/testdata/modules/openconfig-enumcamelcase.yang b/testdata/modules/openconfig-enumcamelcase.yang index 46257c74..4b24300c 100644 --- a/testdata/modules/openconfig-enumcamelcase.yang +++ b/testdata/modules/openconfig-enumcamelcase.yang @@ -2,6 +2,8 @@ module openconfig-enumcamelcase { prefix "occ"; namespace "urn:occ"; + import openconfig-codegen-extensions { prefix "ocgenext"; } + ocgenext:camelcase-name "OpenConfigCamelCase"; container foo { diff --git a/ygen/directory.go b/ygen/directory.go index 163b61fa..17020a04 100644 --- a/ygen/directory.go +++ b/ygen/directory.go @@ -167,6 +167,7 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory if definingModule := yang.RootNode(dir.Entry.Node); definingModule != nil { definingModuleName = definingModule.Name } + pd := &ParsedDirectory{ Name: dir.Name, Path: util.SlicePathToString(dir.Path), @@ -191,6 +192,29 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory pd.Type = Container } + for i, entry := 0, dir.Entry; ; i++ { + exts, err := yang.MatchingEntryExtensions(entry, "openconfig-extensions", "telemetry-atomic") + if err != nil { + return nil, fmt.Errorf("cannot retrieve OpenConfig extensions: %v", err) + } + if len(exts) > 0 { + if i == 0 { + pd.TelemetryAtomic = true + } else { + pd.CompressedTelemetryAtomic = true + } + } + + if entry.Parent == nil { + // The very first element is the module -- skip it. + break + } + entry = entry.Parent + if _, ok := directory[entry.Path()]; ok { + break + } + } + pd.Fields = make(map[string]*NodeDetails, len(dir.Fields)) for _, fn := range GetOrderedFieldNames(dir) { field := dir.Fields[fn] diff --git a/ygen/ir.go b/ygen/ir.go index dea69277..1bfa5af7 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -443,6 +443,27 @@ type ParsedDirectory struct { // statement in YANG: // https://datatracker.ietf.org/doc/html/rfc7950#section-7.21.1 ConfigFalse bool + // TelemetryAtomic indicates that the node has been modified with the + // OpenConfig extension "telemetry-atomic". + // https://github.com/openconfig/public/blob/master/release/models/openconfig-extensions.yang#L154 + // + // For example in the relative path /subinterfaces/subinterface, this + // field be true if and only if the second element, /interface, is + // marked "telemetry-atomic" in the YANG schema. + TelemetryAtomic bool + // CompressedTelemetryAtomic indicates that a parent of the node which + // has been compressed out has been modified with the OpenConfig + // extension "telemetry-atomic". + // + // For example, /interfaces/interface/subinterfaces/subinterface may be + // a path where the /subinterfaces element within the relative path + // /subinterfaces/subinterface is marked "telemetry-atomic". In this + // case, this field will be marked true since the relative path from + // the parent ParsedDirectory contains a compressed-out element that's + // marked "telemetry-atomic". + // + // https://github.com/openconfig/public/blob/master/release/models/openconfig-extensions.yang#L154 + CompressedTelemetryAtomic bool } // OrderedFieldNames returns the YANG name of all fields belonging to the