Skip to content

Commit

Permalink
Merge pull request #26 from ythadhani/typedef_extensions
Browse files Browse the repository at this point in the history
fetching extensions defined within Typedefs
  • Loading branch information
ythadhani authored Mar 20, 2024
2 parents e753b30 + c542fab commit 0d0e26c
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 44 deletions.
94 changes: 65 additions & 29 deletions genutil/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,32 +194,33 @@ func TranslateToCompressBehaviour(compressPaths, excludeState, preferOperational
// return value.
//
// For example, if we have a YANG tree:
// /interface (list)
// /interface/config (container)
// /interface/config/admin-state (leaf)
// /interface/state (container)
// /interface/state/admin-state (leaf)
// /interface/state/oper-state (leaf)
// /interface/state/counters (container)
// /interface/state/counters/in-pkts (leaf)
// /interface/ethernet/config (container)
// /interface/ethernet/config/mac-address (leaf)
// /interface/ethernet/state (state)
// /interface/ethernet/state/mac-address (leaf)
// /interface/subinterfaces (container)
// /interface/subinterfaces/subinterface (list)
//
// /interface (list)
// /interface/config (container)
// /interface/config/admin-state (leaf)
// /interface/state (container)
// /interface/state/admin-state (leaf)
// /interface/state/oper-state (leaf)
// /interface/state/counters (container)
// /interface/state/counters/in-pkts (leaf)
// /interface/ethernet/config (container)
// /interface/ethernet/config/mac-address (leaf)
// /interface/ethernet/state (state)
// /interface/ethernet/state/mac-address (leaf)
// /interface/subinterfaces (container)
// /interface/subinterfaces/subinterface (list)
//
// With compression disabled, then each directly connected child of a container should have
// code generated for it - so therefore we end up with:
//
// /interface: config, state, ethernet, subinterfaces
// /interface/config: admin-state
// /interface/state: admin-state, oper-state, counters
// /interface/state/counters: in-pkts
// /interface/ethernet: config, state
// /interface/ethernet/config: mac-address
// /interface/ethernet/state: mac-address
// /interface/subinterfaces: subinterface
// /interface: config, state, ethernet, subinterfaces
// /interface/config: admin-state
// /interface/state: admin-state, oper-state, counters
// /interface/state/counters: in-pkts
// /interface/ethernet: config, state
// /interface/ethernet/config: mac-address
// /interface/ethernet/state: mac-address
// /interface/subinterfaces: subinterface
//
// This is simply achieved by examining the directory provided by goyang (e.Dir)
// and extracting the direct children that exist. These are appended to the directChildren
Expand All @@ -229,13 +230,13 @@ func TranslateToCompressBehaviour(compressPaths, excludeState, preferOperational
// rules. In this case, the following "look-aheads" are implemented:
//
// 1. The 'config' and 'state' containers under a directory are removed. This is because
// OpenConfig duplicates nodes under config and state to represent intended versus applied
// configuration. In the compressed schema then we need to drop one of these configuration
// leaves (those leaves that are defined as the set under the 'state' container that also
// exist within the 'config' container), and compressBehaviour specifies which one to drop.
// The logic implemented is to recurse into the config container, and select these leaves as
// direct children of the original parent. Any leaves that do not exist in the 'config'
// container but do within 'state' are operation state leaves, and hence are also mapped.
// OpenConfig duplicates nodes under config and state to represent intended versus applied
// configuration. In the compressed schema then we need to drop one of these configuration
// leaves (those leaves that are defined as the set under the 'state' container that also
// exist within the 'config' container), and compressBehaviour specifies which one to drop.
// The logic implemented is to recurse into the config container, and select these leaves as
// direct children of the original parent. Any leaves that do not exist in the 'config'
// container but do within 'state' are operation state leaves, and hence are also mapped.
//
// Above, this means that /interfaces/interface has the admin-state and oper-state as direct
// children.
Expand Down Expand Up @@ -266,8 +267,10 @@ func TranslateToCompressBehaviour(compressPaths, excludeState, preferOperational
// It should be noted that special handling is required for choice and case - because these are
// directories within the resulting schema, but they are not data tree nodes. So for example,
// we can have:
//
// /container/choice/case-one/leaf-a
// /container/choice/case-two/leaf-b
//
// In this tree, "choice" and "case-one" (which are choice and case nodes respectively) are not
// valid data tree elements, so we recurse down both of the branches of "choice" to return leaf-a
// and leaf-b. Since choices can be nested (/choice-a/choice-b/choice-c/case-a), and can have
Expand Down Expand Up @@ -518,3 +521,36 @@ func TransformEntry(e *yang.Entry, compressBehaviour CompressBehaviour) util.Err
}
return errs
}

func GetTypeExtensions(typ *yang.Type, prefix string) (extensions []*yang.Statement) {
return getTypeExtensionsForPrefix(typ, "")
}

func getTypeExtensionsForPrefix(typ *yang.Type, prefix string) (extensions []*yang.Statement) {
if len(typ.Extensions) != 0 {
extensions = append(extensions, typ.Extensions...)
}
if typ.YangType != nil && !util.IsYANGBaseType(typ.YangType) {
// find extensions within Typedefs (if any)
prefixName, typedefName, err := util.SplitModuleAndNodeNames(typ.Name)
if err == nil {
if prefixName == "" {
prefixName = prefix
}
mod := yang.FindModuleByPrefix(typ, prefixName)
if mod != nil {
for _, typedef := range mod.Typedef {
if typedef.Name == typedefName {
extensions = append(extensions, typedef.Extensions...)
}
}
}
// The type of a Typedef could be a Typedef itself.
// Recurse until built-in type is encountered.
if typ.YangType.Base != nil {
extensions = append(extensions, getTypeExtensionsForPrefix(typ.YangType.Base, prefixName)...)
}
}
}
return extensions
}
42 changes: 32 additions & 10 deletions util/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ func ShadowSchemaPaths(f reflect.StructField) [][]string {

// RelativeSchemaPath returns a path to the schema for the struct field f.
// Paths are embedded in the "path" struct tag and can be either simple:
// e.g. "path:a"
//
// e.g. "path:a"
//
// or composite (if path compression is used) e.g.
// e.g. "path:config/a|a"
//
// e.g. "path:config/a|a"
//
// In the latter case, this function returns {"config", "a"}, because only the
// longer path exists in the data tree and we want the schema for that node.
// This case is found in OpenConfig leaf-ref cases where the key of a list is a
Expand All @@ -73,9 +77,13 @@ func RelativeSchemaPath(f reflect.StructField) ([]string, error) {
//
// Paths are embedded in the "shadow-path" and "path" struct tags and can be
// either simple:
// e.g. "path:a"
//
// e.g. "path:a"
//
// or composite (if path compression is used) e.g.
// e.g. "path:config/a|a"
//
// e.g. "path:config/a|a"
//
// In the latter case, this function returns {"config", "a"}, because only the
// longer path exists in the data tree and we want the schema for that node.
// This case is found in OpenConfig leaf-ref cases where the key of a list is a
Expand All @@ -87,9 +95,13 @@ func RelativeSchemaPathPreferShadow(f reflect.StructField) ([]string, error) {

// relativeSchemaPath returns a path to the schema for the struct field f.
// Paths are embedded in the "path" struct tag and can be either simple:
// e.g. "path:a"
//
// e.g. "path:a"
//
// or composite (if path compression is used) e.g.
// e.g. "path:config/a|a"
//
// e.g. "path:config/a|a"
//
// In the latter case, this function returns {"config", "a"}, because only the
// longer path exists in the data tree and we want the schema for that node.
// This case is found in OpenConfig leaf-ref cases where the key of a list is a
Expand Down Expand Up @@ -244,10 +256,10 @@ func removeXPATHPredicates(s string) (string, error) {
// FindLeafRefSchema returns a schema Entry at the path pathStr relative to
// schema if it exists, or an error otherwise.
// pathStr has either:
// - the relative form "../a/b/../b/c", where ".." indicates the parent of the
// node, or
// - the absolute form "/a/b/c", which indicates the absolute path from the
// root of the schema tree.
// - the relative form "../a/b/../b/c", where ".." indicates the parent of the
// node, or
// - the absolute form "/a/b/c", which indicates the absolute path from the
// root of the schema tree.
func FindLeafRefSchema(schema *yang.Entry, pathStr string) (*yang.Entry, error) {
if pathStr == "" {
return nil, fmt.Errorf("leafref schema %s has empty path", schema.Name)
Expand Down Expand Up @@ -340,6 +352,16 @@ func StripModulePrefix(name string) string {
}
}

func SplitModuleAndNodeNames(moduleAndNodeNames string) (string, string, error) {
ps := strings.Split(moduleAndNodeNames, ":")
if len(ps) == 2 {
return ps[0], ps[1], nil
} else if len(ps) == 1 {
return "", ps[0], nil
}
return "", "", fmt.Errorf("path element did not form a valid name (name, prefix:name): %v", moduleAndNodeNames)
}

// ReplacePathSuffix replaces the non-prefix part of a prefixed path name, or
// the whole path name otherwise.
// e.g. If replacing foo -> bar
Expand Down
9 changes: 4 additions & 5 deletions ygen/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,21 +317,20 @@ func generateExtensionTags(field *yang.Entry) string {
// Revisit once goyang is updated.
if field.IsLeaf() {
leafNode := field.Node.(*yang.Leaf)
if len(leafNode.Type.Extensions) != 0 {
extensions = append(extensions, leafNode.Type.Extensions...)
}
extensions = append(extensions, genutil.GetTypeExtensions(leafNode.Type, "")...)
}

uniqueExtKeywordArgs := map[uniqueExtParams]struct{}{}
if len(extensions) != 0 {
buf.WriteString(` extensions:"`)
for iter, extension := range extensions {
e := uniqueExtParams{keyword: extension.Keyword, argument: extension.Argument}
kwd := util.StripModulePrefix(extension.Keyword)
e := uniqueExtParams{keyword: kwd, argument: extension.Argument}
if _, present := uniqueExtKeywordArgs[e]; present {
continue
}
uniqueExtKeywordArgs[e] = struct{}{}
buf.WriteString(extension.Keyword)
buf.WriteString(kwd)
if extension.HasArgument {
buf.WriteString(fmt.Sprintf(",%s", extension.Argument))
}
Expand Down

0 comments on commit 0d0e26c

Please sign in to comment.