diff --git a/Makefile b/Makefile index 39dc969576..29d8c73b7c 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,21 @@ -####################################################################### -# -# Copyright 2019 Broadcom. All rights reserved. -# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -# -####################################################################### +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ .PHONY: all clean cleanall codegen rest-server rest-clean yamlGen cli @@ -64,6 +76,7 @@ cvl: go-deps $(MAKE) -C src/cvl $(MAKE) -C src/cvl/schema $(MAKE) -C src/cvl/testdata/schema + cvl-test: $(MAKE) -C src/cvl gotest @@ -94,15 +107,26 @@ cp $(TOPDIR)/ygot-modified-files/schema.go $(BUILD_GOPATH)/src/github.com/openco cp $(TOPDIR)/ygot-modified-files/unmarshal.go $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ytypes/unmarshal.go; \ cp $(TOPDIR)/ygot-modified-files/validate.go $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ytypes/validate.go; \ cp $(TOPDIR)/ygot-modified-files/reflect.go $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ytypes/../util/reflect.go; \ -$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ygot +cp $(TOPDIR)/goyang-modified-files/README.md $(BUILD_GOPATH)/src/github.com/openconfig/goyang/README.md; \ +cp $(TOPDIR)/goyang-modified-files/yang.go $(BUILD_GOPATH)/src/github.com/openconfig/goyang/yang.go; \ +cp $(TOPDIR)/goyang-modified-files/annotate.go $(BUILD_GOPATH)/src/github.com/openconfig/goyang/annotate.go; \ +cp $(TOPDIR)/goyang-modified-files/entry.go $(BUILD_GOPATH)/src/github.com/openconfig/goyang/pkg/yang/entry.go; \ +$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ygot; \ +$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/openconfig/goyang install: $(INSTALL) -D $(REST_BIN) $(DESTDIR)/usr/sbin/rest_server $(INSTALL) -D $(CERTGEN_BIN) $(DESTDIR)/usr/sbin/generate_cert $(INSTALL) -d $(DESTDIR)/usr/sbin/schema/ $(INSTALL) -d $(DESTDIR)/usr/sbin/lib/ + $(INSTALL) -d $(DESTDIR)/usr/models/yang/ $(INSTALL) -D $(TOPDIR)/src/cvl/schema/*.yin $(DESTDIR)/usr/sbin/schema/ $(INSTALL) -D $(TOPDIR)/src/cvl/testdata/schema/*.yin $(DESTDIR)/usr/sbin/schema/ + $(INSTALL) -D $(TOPDIR)/src/cvl/schema/*.yang $(DESTDIR)/usr/models/yang/ + $(INSTALL) -D $(TOPDIR)/models/yang/*.yang $(DESTDIR)/usr/models/yang/ + $(INSTALL) -D $(TOPDIR)/config/transformer/models_list $(DESTDIR)/usr/models/yang/ + $(INSTALL) -D $(TOPDIR)/models/yang/common/*.yang $(DESTDIR)/usr/models/yang/ + $(INSTALL) -D $(TOPDIR)/models/yang/annotations/*.yang $(DESTDIR)/usr/models/yang/ cp -rf $(TOPDIR)/build/rest_server/dist/ui/ $(DESTDIR)/rest_ui/ cp -rf $(TOPDIR)/build/cli $(DESTDIR)/usr/sbin/ cp -rf $(TOPDIR)/build/swagger_client_py/ $(DESTDIR)/usr/sbin/lib/ diff --git a/config/transformer/models_list b/config/transformer/models_list new file mode 100644 index 0000000000..bc192c902a --- /dev/null +++ b/config/transformer/models_list @@ -0,0 +1,4 @@ +#List yang models transformer need to load + +openconfig-acl.yang +openconfig-acl-annot.yang diff --git a/go_server.sh b/go_server.sh index de3be2c253..4c13c1aa2f 100755 --- a/go_server.sh +++ b/go_server.sh @@ -1,5 +1,24 @@ #!/usr/bin/env bash +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + set -e TOPDIR=$PWD diff --git a/goyang-modified-files/README.md b/goyang-modified-files/README.md new file mode 100644 index 0000000000..805adb59b7 --- /dev/null +++ b/goyang-modified-files/README.md @@ -0,0 +1,50 @@ +# goyang +YANG parser and compiler for Go programs. + +The yang package (pkg/yang) is used to convert a YANG schema into either an +in memory abstract syntax trees (ast) or more fully resolved, in memory, "Entry" +trees. An Entry tree consists only of Entry structures and has had +augmentation, imports, and includes all applied. + +goyang is a sample program that uses the yang (pkg/yang) package. + +goyang uses the yang package to create an in-memory tree representation of +schemas defined in YANG and then dumps out the contents in several forms. +The forms include: + +* tree - a simple tree representation +* types - list understood types extracted from the schema +* annotate - a template file to annotate the yang modules + +The yang package, and the goyang program, are not complete and are a work in +progress. + +For more complex output types, such as Go structs, and protobuf messages +please use the [openconfig/ygot](https://github.com/openconfig/ygot) package, +which uses this package as its backend. + +### Getting started + +To build goyang, ensure you have go language tools installed +(available at [golang.org](https://golang.org/dl)) and that the `GOPATH` +environment variable is set to your Go workspace. + +1. `go get github.com/openconfig/goyang` + * This will download goyang code and dependencies into the src +subdirectory in your workspace. + +2. `cd /src/github.com/openconfig/goyang` + +3. `go build` + + * This will build the goyang binary and place it in the bin +subdirectory in your workspace. + +### Contributing to goyang + +goyang is still a work-in-progress and we welcome contributions. Please see +the `CONTRIBUTING` file for information about how to contribute to the codebase. + +### Disclaimer + +This is not an official Google product. diff --git a/goyang-modified-files/annotate.go b/goyang-modified-files/annotate.go new file mode 100644 index 0000000000..e86d441fad --- /dev/null +++ b/goyang-modified-files/annotate.go @@ -0,0 +1,387 @@ +// Copyright 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "io" + "strings" + + "github.com/openconfig/goyang/pkg/yang" +) + +var allimports = make(map[string]string) +var modules = make(map[string]*yang.Module) +var allmodules = make(map[string]*yang.Module) + +func init() { + register(&formatter{ + name: "annotate", + f: genAnnotate, + utilf: getFile, + help: "generate template file for yang annotations", + }) +} + +// Get the modules for which annotation file needs to be generated +func getFile(files []string, mods map[string]*yang.Module) { + allmodules = mods + for _, name := range files { + slash := strings.Split(name, "/") + modname := slash[len(slash)-1] + modname = strings.TrimSuffix(modname, ".yang"); + /* Save the yang.Module entries we are interested in */ + modules[modname] = mods[modname] + } +} + +func genAnnotate(w io.Writer, entries []*yang.Entry) { + /* Get all the imported modules in the entries */ + GetAllImports(entries) + for _, e := range entries { + if _, ok := modules[e.Name]; ok { + var path string = "" + generate(w, e, path) + // { Add closing brace for each module + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + } + } +} + +// generate writes to stdoutput a template annotation file entry for the selected modules. +func generate(w io.Writer, e *yang.Entry, path string) { + if e.Parent == nil { + if e.Name != "" { + fmt.Fprintf(w, "module %s-annot {\n", e.Name) //} + fmt.Fprintln(w) + fmt.Fprintf(w, " yang-version \"%s\";\n", getYangVersion(e.Name, modules)) + fmt.Fprintln(w) + fmt.Fprintf(w, " namespace \"http://openconfig.net/yang/annotation\";\n") + if e.Prefix != nil { + fmt.Fprintf(w, " prefix \"%s-annot\";\n", e.Prefix.Name) + } + fmt.Fprintln(w) + + var imports = make(map[string]string) + imports = getImportModules(e.Name, modules) + for k := range imports { + if e.Name != k { + fmt.Fprintf(w, " import %s { prefix %s; }\n", k, allimports[k]) + } + } + + fmt.Fprintln(w) + } + } + + name := e.Name + if e.Prefix != nil { + name = e.Prefix.Name + ":" + name + } + + delim := "" + if path != "" { + delim = "/" + } + path = path + delim + name + + printDeviation(w, path) + + var names []string + for k := range e.Dir { + names = append(names, k) + } + + if (e.Node.Kind() == "module") { + if len(e.Node.(*yang.Module).Augment) > 0 { + for _,a := range e.Node.(*yang.Module).Augment { + path = a.Name + handleAugments(w, a, e.Node.(*yang.Module).Grouping, e.Prefix.Name, path) + } + } + } + + for _, k := range names { + generate(w, e.Dir[k], path) + } + +} + +func printDeviation(w io.Writer, path string){ + fmt.Fprintf(w, " deviation %s {\n", path) + fmt.Fprintf(w, " deviate add {\n") + fmt.Fprintf(w, " }\n") + fmt.Fprintf(w, " }\n") + fmt.Fprintln(w) +} + + +// Save to map all imported modules +func GetAllImports(entries []*yang.Entry) { + for _, e := range entries { + allimports[e.Name] = e.Prefix.Name + } +} + +func GetModuleFromPrefix(prefix string) string { + for m, p := range allimports { + if prefix == p { + return m + } + } + return "" +} + +//Get Yang version from the yang.Modules +func getYangVersion(modname string, mods map[string]*yang.Module) string { + if (mods[modname].YangVersion != nil) { + return mods[modname].YangVersion.Name + } + return "" + +} + +// Get imported modules for a given module from yang.Module +func getImportModules(modname string, mods map[string]*yang.Module) map[string]string { + imports := map[string]string{} + if (mods[modname].Import != nil) { + for _, imp := range mods[modname].Import { + imports[imp.Name] = imp.Prefix.Name + } + } + return imports +} + +func handleAugments(w io.Writer, a *yang.Augment, grp []*yang.Grouping, prefix string, path string) { + for _, u := range a.Uses { + grpN := u.Name + for _, g := range grp { + if grpN == g.Name { + if len(g.Container) > 0 { + handleContainer(w, g.Container, grp, prefix, path) + } + if len(g.List) > 0 { + handleList(w, g.List, grp, prefix, path) + } + if len(g.LeafList) > 0 { + handleLeafList(w, g.LeafList, prefix, path) + } + if len(g.Leaf) > 0 { + handleLeaf(w, g.Leaf, prefix, path) + } + if len(g.Choice) > 0 { + handleChoice(w, g.Choice, grp, prefix, path) + } + if len(g.Uses) > 0 { + handleUses(w, g.Uses, grp, prefix, path) + } + } + } + } + +} + +func handleUses(w io.Writer, u []*yang.Uses, grp []*yang.Grouping, prefix string, path string) { + for _, u := range u { + grpN := u.Name + if strings.Contains(grpN, ":") { + tokens := strings.Split(grpN, ":") + nprefix := tokens[0] + grpN = tokens[1] + mod := GetModuleFromPrefix(nprefix) + grp = allmodules[mod].Grouping + prefix = nprefix + } + for _, g := range grp { + if grpN == g.Name { + if len(g.Container) > 0 { + handleContainer(w, g.Container, grp, prefix, path) + } + if len(g.List) > 0 { + handleList(w, g.List, grp, prefix, path) + } + if len(g.LeafList) > 0 { + handleLeafList(w, g.LeafList, prefix, path) + } + if len(g.Leaf) > 0 { + handleLeaf(w, g.Leaf, prefix, path) + } + if len(g.Choice) > 0 { + handleChoice(w, g.Choice, grp, prefix, path) + } + if len(g.Uses) > 0 { + handleUses(w, g.Uses, grp, prefix, path) + } + + } + } + } + +} + +func handleContainer(w io.Writer, ctr []*yang.Container, grp []*yang.Grouping, prefix string, path string) { + for _, c := range ctr { + npath := path + "/" + prefix + ":" + c.Name + printDeviation(w, npath) + if len(c.Container) > 0 { + handleContainer(w, c.Container, grp, prefix, npath) + } + if len(c.List) > 0 { + handleList(w, c.List, grp, prefix, npath) + } + if len(c.LeafList) > 0 { + handleLeafList(w, c.LeafList, prefix, npath) + } + if len(c.Leaf) > 0 { + handleLeaf(w, c.Leaf, prefix, npath) + } + if len(c.Choice) > 0 { + handleChoice(w, c.Choice, grp, prefix, npath) + } + if len(c.Grouping) > 0 { + handleGrouping(w, c.Grouping, grp, prefix, npath) + } + if len(c.Uses) > 0 { + handleUses(w, c.Uses, grp, prefix, npath) + } + } +} + +func handleList(w io.Writer, lst []*yang.List, grp []*yang.Grouping, prefix string, path string) { + for _, l := range lst { + npath := path + "/" + prefix + ":" + l.Name + printDeviation(w, npath) + if len(l.Container) > 0 { + handleContainer(w, l.Container, grp, prefix, npath) + } + if len(l.List) > 0 { + handleList(w, l.List, grp, prefix, npath) + } + if len(l.LeafList) > 0 { + handleLeafList(w, l.LeafList, prefix, npath) + } + if len(l.Leaf) > 0 { + handleLeaf(w, l.Leaf, prefix, npath) + } + if len(l.Choice) > 0 { + handleChoice(w, l.Choice, grp, prefix, npath) + } + if len(l.Grouping) > 0 { + handleGrouping(w, l.Grouping, grp, prefix, npath) + } + if len(l.Uses) > 0 { + handleUses(w, l.Uses, grp, prefix, npath) + } + + } +} + +func handleGrouping(w io.Writer, grp []*yang.Grouping, grptop []*yang.Grouping, prefix string, path string) { + for _, g := range grp { + npath := path + "/" + prefix + ":" + g.Name + printDeviation(w, npath) + if len(g.Container) > 0 { + handleContainer(w, g.Container, grptop, prefix, npath) + } + if len(g.List) > 0 { + handleList(w, g.List, grptop, prefix, npath) + } + if len(g.LeafList) > 0 { + handleLeafList(w, g.LeafList, prefix, npath) + } + if len(g.Leaf) > 0 { + handleLeaf(w, g.Leaf, prefix, npath) + } + if len(g.Choice) > 0 { + handleChoice(w, g.Choice, grptop, prefix, npath) + } + if len(g.Grouping) > 0 { + handleGrouping(w, g.Grouping, grptop, prefix, npath) + } + if len(g.Uses) > 0 { + handleUses(w, g.Uses, grptop, prefix, npath) + } + + } +} + +func handleLeaf (w io.Writer, lf []*yang.Leaf, prefix string, path string) { + if len(lf) > 0 { + for _, l := range lf { + npath := path + "/" + prefix + ":" + l.Name + printDeviation(w, npath) + } + } + +} + +func handleLeafList (w io.Writer, llst []*yang.LeafList, prefix string, path string) { + if len(llst) > 0 { + for _, l := range llst { + npath := path + "/" + prefix + ":" + l.Name + printDeviation(w, npath) + } + } +} + +func handleChoice (w io.Writer, ch []*yang.Choice, grp []*yang.Grouping, prefix string, path string) { + for _, c := range ch { + npath := path + "/" + prefix + ":" + c.Name + printDeviation(w, npath) + if len(c.Container) > 0 { + handleContainer(w, c.Container, grp, prefix, npath) + } + if len(c.List) > 0 { + handleList(w, c.List, grp, prefix, npath) + } + if len(c.LeafList) > 0 { + handleLeafList(w, c.LeafList, prefix, npath) + } + if len(c.Leaf) > 0 { + handleLeaf(w, c.Leaf, prefix, npath) + } + if len(c.Case) > 0 { + handleCase(w, c.Case, grp, prefix, npath) + } + } +} + +func handleCase (w io.Writer, ch []*yang.Case, grp []*yang.Grouping, prefix string, path string) { + for _, c := range ch { + npath := path + "/" + prefix + ":" + c.Name + printDeviation(w, npath) + if len(c.Container) > 0 { + handleContainer(w, c.Container, grp, prefix, npath) + } + if len(c.List) > 0 { + handleList(w, c.List, grp, prefix, npath) + } + if len(c.LeafList) > 0 { + handleLeafList(w, c.LeafList, prefix, npath) + } + if len(c.Leaf) > 0 { + handleLeaf(w, c.Leaf, prefix, npath) + } + if len(c.Choice) > 0 { + handleChoice(w, c.Choice, grp, prefix, npath) + } + if len(c.Uses) > 0 { + handleUses(w, c.Uses, grp, prefix, npath) + } + + } +} + diff --git a/goyang-modified-files/entry.go b/goyang-modified-files/entry.go new file mode 100644 index 0000000000..6b7870f924 --- /dev/null +++ b/goyang-modified-files/entry.go @@ -0,0 +1,1453 @@ +// Copyright 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package yang + +// The file contains the code to convert an AST (Node) tree into an Entry tree +// via the ToEntry function. The entry tree, once fully resolved, is the +// product of this package. The tree should have all types and references +// resolved. +// +// TODO(borman): handle types, leafrefs, and extensions + +import ( + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/openconfig/goyang/pkg/indent" +) + +// A TriState may be true, false, or unset +type TriState int + +// The possible states of a TriState. +const ( + TSUnset = TriState(iota) + TSTrue + TSFalse +) + +// Value returns the value of t as a boolean. Unset is returned as false. +func (t TriState) Value() bool { + return t == TSTrue +} + +// String displays t as a string. +func (t TriState) String() string { + switch t { + case TSUnset: + return "unset" + case TSTrue: + return "true" + case TSFalse: + return "false" + default: + return fmt.Sprintf("ts-%d", t) + } +} + +// An Entry represents a single node (directory or leaf) created from the +// AST. Directory entries have a non-nil Dir entry. Leaf nodes have a nil +// Dir entry. If Errors is not nil then the only other valid field is Node. +type Entry struct { + Parent *Entry `json:"-"` + Node Node `json:"-"` // the base node this Entry was derived from. + Name string // our name, same as the key in our parent Dirs + Description string `json:",omitempty"` // description from node, if any + Default string `json:",omitempty"` // default from node, if any + Units string `json:",omitempty"` // units associated with the type, if any + Errors []error `json:"-"` // list of errors encountered on this node + Kind EntryKind // kind of Entry + Config TriState // config state of this entry, if known + Prefix *Value `json:",omitempty"` // prefix to use from this point down + Mandatory TriState `json:",omitempty"` // whether this entry is mandatory in the tree + + // Fields associated with directory nodes + Dir map[string]*Entry `json:",omitempty"` + DirOKeys []string // Ordered Keys list in Dir + Key string `json:",omitempty"` // Optional key name for lists (i.e., maps) + + // Fields associated with leaf nodes + Type *YangType `json:",omitempty"` + Exts []*Statement `json:",omitempty"` // extensions found + + // Fields associated with list nodes (both lists and leaf-lists) + ListAttr *ListAttr `json:",omitempty"` + + RPC *RPCEntry `json:",omitempty"` // set if we are an RPC + + // Identities that are defined in this context, this is set if the Entry + // is a module only. + Identities []*Identity `json:",omitempty"` + + Augments []*Entry `json:",omitempty"` // Augments defined in this entry. + Augmented []*Entry `json:",omitempty"` // Augments merged into this entry. + Deviations []*DeviatedEntry `json:"-"` // Deviations associated with this entry. + Deviate map[deviationType][]*Entry `json:"-"` + Uses []*UsesStmt `json:",omitempty"` // Uses merged into this entry. + + // Extra maps all the unsupported fields to their values + Extra map[string][]interface{} `json:"-"` + + // Annotation stores annotated values, and is not populated by this + // library but rather can be used by calling code where additional + // information should be stored alongside the Entry. + Annotation map[string]interface{} `json:",omitempty"` + + // namespace stores the namespace of the Entry if it overrides the + // root namespace within the schema tree. This is the case where an + // entry is augmented into the tree, and it retains the namespace of + // the augmenting entity per RFC6020 Section 7.15.2. The namespace + // of the Entry should be accessed using the Namespace function. + namespace *Value +} + +// An RPCEntry contains information related to an RPC Node. +type RPCEntry struct { + Input *Entry + Output *Entry +} + +// A ListAttr is associated with an Entry that represents a List node +type ListAttr struct { + MinElements *Value // leaf-list or list MUST have at least min-elements + MaxElements *Value // leaf-list or list has at most max-elements + OrderedBy *Value // order of entries determined by "system" or "user" +} + +// A UsesStmt associates a *Uses with its referenced grouping *Entry +type UsesStmt struct { + Uses *Uses + Grouping *Entry +} + +// Modules returns the Modules structure that e is part of. This is needed +// when looking for rooted nodes not part of this Entry tree. +func (e *Entry) Modules() *Modules { + for e.Parent != nil { + e = e.Parent + } + return e.Node.(*Module).modules +} + +// IsDir returns true if e is a directory. +func (e *Entry) IsDir() bool { + return e.Dir != nil +} + +// IsLeaf returns true if e is a leaf i.e. is not a container, list, leaf-list, +// choice or case statement. +func (e *Entry) IsLeaf() bool { + return !e.IsDir() && e.Kind == LeafEntry && e.ListAttr == nil +} + +// IsLeafList returns true if e is a leaf-list. +func (e *Entry) IsLeafList() bool { + return !e.IsDir() && e.Kind == LeafEntry && e.ListAttr != nil +} + +// IsList returns true if e is a list. +func (e *Entry) IsList() bool { + return e.IsDir() && e.ListAttr != nil +} + +// IsContainer returns true if e is a container. +func (e *Entry) IsContainer() bool { + return e.Kind == DirectoryEntry && e.ListAttr == nil +} + +// IsChoice returns true if the entry is a choice node within the schema. +func (e *Entry) IsChoice() bool { + return e.Kind == ChoiceEntry +} + +// IsCase returns true if the entry is a case node within the schema. +func (e *Entry) IsCase() bool { + return e.Kind == CaseEntry +} + +// Print prints e to w in human readable form. +func (e *Entry) Print(w io.Writer) { + if e.Description != "" { + fmt.Fprintln(w) + fmt.Fprintln(indent.NewWriter(w, "// "), e.Description) + } + if e.ReadOnly() { + fmt.Fprintf(w, "RO: ") + } else { + fmt.Fprintf(w, "rw: ") + } + if e.Type != nil { + fmt.Fprintf(w, "%s ", e.Type.Name) + } + switch { + case e.Dir == nil && e.ListAttr != nil: + fmt.Fprintf(w, "[]%s\n", e.Name) + return + case e.Dir == nil: + fmt.Fprintf(w, "%s\n", e.Name) + return + case e.ListAttr != nil: + fmt.Fprintf(w, "[%s]%s {\n", e.Key, e.Name) //} + default: + fmt.Fprintf(w, "%s {\n", e.Name) //} + } + var names []string + for k := range e.Dir { + names = append(names, k) + } + sort.Strings(names) + for _, k := range names { + e.Dir[k].Print(indent.NewWriter(w, " ")) + } + // { to match the brace below to keep brace matching working + fmt.Fprintln(w, "}") +} + +// An EntryKind is the kind of node an Entry is. All leaf nodes are of kind +// LeafEntry. A LeafList is also considered a leaf node. All other kinds are +// directory nodes. +type EntryKind int + +// Enumeration of the types of entries. +const ( + LeafEntry = EntryKind(iota) + DirectoryEntry + AnyDataEntry + AnyXMLEntry + CaseEntry + ChoiceEntry + InputEntry + NotificationEntry + OutputEntry + DeviateEntry +) + +// EntryKindToName maps EntryKind to their names +var EntryKindToName = map[EntryKind]string{ + LeafEntry: "Leaf", + DirectoryEntry: "Directory", + AnyDataEntry: "AnyData", + AnyXMLEntry: "AnyXML", + CaseEntry: "Case", + ChoiceEntry: "Choice", + InputEntry: "Input", + NotificationEntry: "Notification", + OutputEntry: "Output", + DeviateEntry: "Deviate", +} + +func (k EntryKind) String() string { + if s := EntryKindToName[k]; s != "" { + return s + } + return fmt.Sprintf("unknown-entry-%d", k) +} + +// newDirectory returns an empty directory Entry. +func newDirectory(n Node) *Entry { + return &Entry{ + Kind: DirectoryEntry, + Dir: make(map[string]*Entry), + DirOKeys: make([]string, 0), + Node: n, + Name: n.NName(), + Extra: map[string][]interface{}{}, + } +} + +// newLeaf returns an empty leaf Entry. +func newLeaf(n Node) *Entry { + return &Entry{ + Kind: LeafEntry, + Node: n, + Name: n.NName(), + Extra: map[string][]interface{}{}, + } +} + +// newError returns an error node using format and v to create the error +// contained in the node. The location of the error is prepended. +func newError(n Node, format string, v ...interface{}) *Entry { + e := &Entry{Node: n} + e.errorf("%s: "+format, append([]interface{}{Source(n)}, v...)...) + return e +} + +// errorf appends the entry constructed from string and v to the list of errors +// on e. +func (e *Entry) errorf(format string, v ...interface{}) { + e.Errors = append(e.Errors, fmt.Errorf(format, v...)) +} + +// addError appends err to the list of errors on e if err is not nil. +func (e *Entry) addError(err error) { + if err != nil { + e.Errors = append(e.Errors, err) + } +} + +// importErrors imports all the errors from c and its children into e. +func (e *Entry) importErrors(c *Entry) { + if c == nil { + return + } + for _, err := range c.Errors { + e.addError(err) + } + // TODO(borman): need to determine if the extensions have errors + // for _, ce := range e.Exts { + // e.importErrors(ce) + // } + for _, ce := range c.Dir { + e.importErrors(ce) + } +} + +// checkErrors calls f on every error found in the tree e and its children. +func (e *Entry) checkErrors(f func(error)) { + if e == nil { + return + } + for _, e := range e.Dir { + e.checkErrors(f) + } + for _, err := range e.Errors { + f(err) + } + // TODO(borman): need to determine if the extensions have errors + // for _, e := range e.Exts { + // e.checkErrors(f) + // } +} + +// GetErrors returns a sorted list of errors found in e. +func (e *Entry) GetErrors() []error { + // the seen map is used to eliminate duplicate errors. + // Some entries will be processed more than once + // (groupings in particular) and as such may cause + // duplication of errors. + seen := map[error]bool{} + var errs []error + e.checkErrors(func(err error) { + if !seen[err] { + errs = append(errs, err) + seen[err] = true + } + }) + return errorSort(errs) +} + +// asKind sets the kind of e to k and returns e. +func (e *Entry) asKind(k EntryKind) *Entry { + e.Kind = k + return e +} + +// add adds the directory entry key assigned to the provided value. +func (e *Entry) add(key string, value *Entry) *Entry { + value.Parent = e + if e.Dir[key] != nil { + e.errorf("%s: duplicate key from %s: %s", Source(e.Node), Source(value.Node), key) + return e + } + e.Dir[key] = value + e.DirOKeys = append(e.DirOKeys, key) + return e +} + +// delete removes the directory entry key from the entry. +func (e *Entry) delete(key string) { + if _, ok := e.Dir[key]; !ok { + e.errorf("%s: unknown child key %s", Source(e.Node), key) + } + delete(e.Dir, key) +} + +// GetWhenXPath returns the when XPath statement of e if able. +func (e *Entry) GetWhenXPath() (string, bool) { + switch n := e.Node.(type) { + case *Container: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *Leaf: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *LeafList: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *List: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *Choice: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *Case: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *AnyXML: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *AnyData: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + case *Augment: + if n.When != nil && n.When.Statement() != nil { + return n.When.Statement().Arg() + } + } + return "", false +} + +// entryCache is used to prevent unnecessary recursion into previously +// converted nodes. +var entryCache = map[Node]*Entry{} + +// mergedSubmodule is used to prevent re-parsing a submodule that has already +// been merged into a particular entity when circular dependencies are being +// ignored. The keys of the map are a string that is formed by concatenating +// the name of the including (sub)module and the included submodule. +var mergedSubmodule = map[string]bool{} + +var depth = 0 + +// deviationType specifies an enumerated value covering the different substmts +// to the deviate statement. +type deviationType int64 + +const ( + // DeviationUnset specifies that the argument was unset, which is invalid. + DeviationUnset deviationType = iota + // DeviationNotSupported corresponds to the not-supported deviate argument. + DeviationNotSupported + // DeviationAdd corresponds to the add deviate argument to the deviate stmt. + DeviationAdd + // DeviationReplace corresponds to the replace argument to the deviate stmt. + DeviationReplace + // DeviationDelete corresponds to the delete argument to the deviate stmt. + DeviationDelete +) + +var ( + // fromDeviation maps from an enumerated deviation type to the YANG keyword. + fromDeviation = map[deviationType]string{ + DeviationNotSupported: "not-supported", + DeviationAdd: "add", + DeviationReplace: "replace", + DeviationDelete: "delete", + DeviationUnset: "unknown", + } + + // toDeviation maps from the YANG keyword to an enumerated deviation typee. + toDeviation = map[string]deviationType{ + "not-supported": DeviationNotSupported, + "add": DeviationAdd, + "replace": DeviationReplace, + "delete": DeviationDelete, + } +) + +func (d deviationType) String() string { + return fromDeviation[d] +} + +// DeviatedEntry stores a wrapped Entry that corresponds to a deviation. +type DeviatedEntry struct { + Type deviationType // Type specifies the deviation type. + DeviatedPath string // DeviatedPath corresponds to the path that is being deviated. + // Entry is the embedded Entry storing the deviations that are made. Fields + // are set to the value in the schema after the deviation has been applied. + *Entry +} + +// ToEntry expands node n into a directory Entry. Expansion is based on the +// YANG tags in the structure behind n. ToEntry must only be used +// with nodes that are directories, such as top level modules and sub-modules. +// ToEntry never returns nil. Any errors encountered are found in the Errors +// fields of the returned Entry and its children. Use GetErrors to determine +// if there were any errors. +func ToEntry(n Node) (e *Entry) { + if n == nil { + err := errors.New("ToEntry called with nil") + return &Entry{ + Node: &ErrorNode{Error: err}, + Errors: []error{err}, + } + } + if e := entryCache[n]; e != nil { + return e + } + defer func() { + entryCache[n] = e + }() + + // Copy in the extensions from our Node, if any. + defer func(n Node) { + if e != nil { + for _, ext := range n.Exts() { + e.Exts = append(e.Exts, ext) + } + } + }(n) + + // tristateValue returns TSTrue if i contains the value of true, TSFalse + // if it contains the value of false, and TSUnset if i does not have + // a set value (for instance, i is nil). An error is returned if i + // contains a value other than true or false. + tristateValue := func(i interface{}) (TriState, error) { + if v, ok := i.(*Value); ok && v != nil { + switch v.Name { + case "true": + return TSTrue, nil + case "false": + return TSFalse, nil + default: + return TSUnset, fmt.Errorf("%s: invalid config value: %s", Source(n), v.Name) + } + } + return TSUnset, nil + } + + var err error + // Handle non-directory nodes (leaf, leafref, and oddly enough, uses). + switch s := n.(type) { + case *Leaf: + e := newLeaf(n) + if errs := s.Type.resolve(); errs != nil { + e.Errors = errs + } + if s.Description != nil { + e.Description = s.Description.Name + } + if s.Default != nil { + e.Default = s.Default.Name + } + e.Type = s.Type.YangType + entryCache[n] = e + e.Config, err = tristateValue(s.Config) + e.addError(err) + e.Prefix = getRootPrefix(e) + return e + case *LeafList: + // Create the equivalent leaf element that we are a list of. + // We can then just annotate it as a list rather than a leaf. + leaf := &Leaf{ + Name: s.Name, + Source: s.Source, + Parent: s.Parent, + Extensions: s.Extensions, + Config: s.Config, + Description: s.Description, + IfFeature: s.IfFeature, + Must: s.Must, + Reference: s.Reference, + Status: s.Status, + Type: s.Type, + Units: s.Units, + When: s.When, + } + + e := ToEntry(leaf) + e.ListAttr = &ListAttr{ + MinElements: s.MinElements, + MaxElements: s.MaxElements, + OrderedBy: s.OrderedBy, + } + e.Prefix = getRootPrefix(e) + return e + case *Uses: + g := FindGrouping(s, s.Name, map[string]bool{}) + if g == nil { + return newError(n, "unknown group: %s", s.Name) + } + // We need to return a duplicate so we resolve properly + // when the group is used in multiple locations and the + // grouping has a leafref that references outside the group. + return ToEntry(g).dup() + } + + e = newDirectory(n) + + // Special handling for individual Node types. Lists are like any other + // node except a List has a ListAttr. + // + // Nodes of identified special kinds have their Kind set here. + switch s := n.(type) { + case *List: + e.ListAttr = &ListAttr{ + MinElements: s.MinElements, + MaxElements: s.MaxElements, + OrderedBy: s.OrderedBy, + } + case *Choice: + e.Kind = ChoiceEntry + if s.Default != nil { + e.Default = s.Default.Name + } + case *Case: + e.Kind = CaseEntry + case *AnyData: + e.Kind = AnyDataEntry + case *AnyXML: + e.Kind = AnyXMLEntry + case *Input: + e.Kind = InputEntry + case *Output: + e.Kind = OutputEntry + case *Notification: + e.Kind = NotificationEntry + case *Deviate: + e.Kind = DeviateEntry + } + + // Use Elem to get the Value of structure that n is pointing to, not + // the Value of the pointer. + v := reflect.ValueOf(n).Elem() + t := v.Type() + found := false + + for i := t.NumField() - 1; i > 0; i-- { + f := t.Field(i) + yang := f.Tag.Get("yang") + if yang == "" { + continue + } + fv := v.Field(i) + name := strings.Split(yang, ",")[0] + switch name { + case "": + e.addError(fmt.Errorf("%s: nil statement", Source(n))) + case "config": + e.Config, err = tristateValue(fv.Interface()) + e.addError(err) + case "description": + if v := fv.Interface().(*Value); v != nil { + e.Description = v.Name + } + case "prefix": + if v := fv.Interface().(*Value); v != nil { + e.Prefix = v + } + case "action": + for _, r := range fv.Interface().([]*Action) { + e.add(r.Name, ToEntry(r)) + } + case "augment": + for _, a := range fv.Interface().([]*Augment) { + ne := ToEntry(a) + ne.Parent = e + e.Augments = append(e.Augments, ne) + } + case "anydata": + for _, a := range fv.Interface().([]*AnyData) { + e.add(a.Name, ToEntry(a)) + } + case "anyxml": + for _, a := range fv.Interface().([]*AnyXML) { + e.add(a.Name, ToEntry(a)) + } + case "case": + for _, a := range fv.Interface().([]*Case) { + e.add(a.Name, ToEntry(a)) + } + case "choice": + for _, a := range fv.Interface().([]*Choice) { + e.add(a.Name, ToEntry(a)) + } + case "container": + for _, a := range fv.Interface().([]*Container) { + e.add(a.Name, ToEntry(a)) + } + case "grouping": + for _, a := range fv.Interface().([]*Grouping) { + // We just want to parse the grouping to + // collect errors. + e.importErrors(ToEntry(a)) + } + case "import": + // Apparently import only makes types and such + // available. There is nothing else for us to do. + case "include": + for _, a := range fv.Interface().([]*Include) { + // Handle circular dependencies between submodules. This can occur in + // two ways: + // - Where submodule A imports submodule B, and vice versa then the + // whilst processing A we will also try and process A (learnt via + // B). The default case of the switch handles this case. + // - Where submodule A imports submodule B that imports C, which also + // imports A, then we need to check whether we already have merged + // the specified module during this parse attempt. We check this + // against a map of merged submodules. + // The key of the map used is a synthesised value which is formed by + // concatenating the name of this node and the included submodule, + // separated by a ":". + srcToIncluded := a.Module.Name + ":" + n.NName() + includedToSrc := n.NName() + ":" + a.Module.Name + + switch { + case mergedSubmodule[srcToIncluded]: + // We have already merged this module, so don't try and do it + // again. + continue + case !mergedSubmodule[includedToSrc] && a.Module.NName() != n.NName(): + // We have not merged A->B, and B != B hence go ahead and merge. + includedToParent := a.Module.Name + ":" + a.Module.BelongsTo.Name + if mergedSubmodule[includedToParent] { + // Don't try and re-import submodules that have already been imported + // into the top-level module. Note that this ensures that we get to the + // top the tree (whichever the actual module for the chain of + // submodules is). The tracking of the immediate parent is achieved + // through 'key', which ensures that we do not end up in loops + // walking through a sub-cycle of the include graph. + continue + } + mergedSubmodule[srcToIncluded] = true + mergedSubmodule[includedToParent] = true + e.merge(a.Module.Prefix, nil, ToEntry(a.Module)) + case ParseOptions.IgnoreSubmoduleCircularDependencies: + continue + default: + e.addError(fmt.Errorf("%s: has a circular dependency, importing %s", n.NName(), a.Module.NName())) + } + } + case "leaf": + for _, a := range fv.Interface().([]*Leaf) { + e.add(a.Name, ToEntry(a)) + } + case "leaf-list": + for _, a := range fv.Interface().([]*LeafList) { + e.add(a.Name, ToEntry(a)) + } + case "list": + for _, a := range fv.Interface().([]*List) { + e.add(a.Name, ToEntry(a)) + } + case "key": + if v := fv.Interface().(*Value); v != nil { + e.Key = v.Name + } + case "notification": + for _, a := range fv.Interface().([]*Notification) { + e.add(a.Name, ToEntry(a)) + } + case "rpc": + // TODO(borman): what do we do with these? + // seems fine to ignore them for now, we are + // just interested in the tree structure. + for _, r := range fv.Interface().([]*RPC) { + switch rpc := ToEntry(r); { + case rpc.RPC == nil: + // When "rpc" has no "input" or "output" children + rpc.RPC = &RPCEntry{} + fallthrough + default: + e.add(r.Name, rpc) + } + } + + case "input": + if i := fv.Interface().(*Input); i != nil { + if e.RPC == nil { + e.RPC = &RPCEntry{} + } + in := ToEntry(i) + in.Parent = e + e.RPC.Input = in + e.RPC.Input.Name = "input" + e.RPC.Input.Kind = InputEntry + } + case "output": + if o := fv.Interface().(*Output); o != nil { + if e.RPC == nil { + e.RPC = &RPCEntry{} + } + out := ToEntry(o) + out.Parent = e + e.RPC.Output = out + e.RPC.Output.Name = "output" + e.RPC.Output.Kind = OutputEntry + } + case "identity": + if i := fv.Interface().([]*Identity); i != nil { + e.Identities = i + } + case "uses": + for _, a := range fv.Interface().([]*Uses) { + grouping := ToEntry(a) + e.merge(nil, nil, grouping) + if ParseOptions.StoreUses { + e.Uses = append(e.Uses, &UsesStmt{a, grouping.shallowDup()}) + } + } + case "type": + // The type keyword is specific to deviate to change a type. Other type handling + // (e.g., leaf type resolution) is done outside of this case. + n, ok := n.(*Deviate) + if !ok { + e.addError(fmt.Errorf("unexpected type found, only valid under Deviate, is %T", n)) + continue + } + + if n.Type != nil { + if errs := n.Type.resolve(); errs != nil { + e.addError(fmt.Errorf("deviation has unresolvable type, %v", errs)) + continue + } + e.Type = n.Type.YangType + } + continue + // Keywords that do not need to be handled as an Entry as they are added + // to other dictionaries. + case "default": + if e.Kind == LeafEntry { + // default is handled separately for a leaf, but in a deviate statement + // we must deal with it here. + continue + } + d, ok := fv.Interface().(*Value) + if !ok { + e.addError(fmt.Errorf("%s: unexpected default type in %s:%s", Source(n), n.Kind(), n.NName())) + } + e.Default = d.asString() + case "typedef": + continue + case "deviation": + if a := fv.Interface().([]*Deviation); a != nil { + for _, d := range a { + e.Deviations = append(e.Deviations, &DeviatedEntry{ + Entry: ToEntry(d), + DeviatedPath: d.Statement().Argument, + }) + + for _, sd := range d.Deviate { + if sd.Type != nil { + sd.Type.resolve() + } + } + } + } + case "deviate": + if a := fv.Interface().([]*Deviate); a != nil { + for _, d := range a { + de := ToEntry(d) + + dt, ok := toDeviation[d.Statement().Argument] + if !ok { + e.addError(fmt.Errorf("%s: unknown deviation type in %s:%s", Source(n), n.Kind(), n.NName())) + continue + } + + if e.Deviate == nil { + e.Deviate = map[deviationType][]*Entry{} + } + + e.Deviate[dt] = append(e.Deviate[dt], de) + } + } + case "mandatory": + v, ok := fv.Interface().(*Value) + if !ok { + e.addError(fmt.Errorf("%s: did not get expected value type", Source(n))) + } + e.Mandatory, err = tristateValue(v) + e.addError(err) + case "max-elements", "min-elements": + if e.Kind != DeviateEntry { + continue + } + // we can get max-elements or min-elements in a deviate statement, so create the + // corresponding logic. + v, ok := fv.Interface().(*Value) + if !ok { + e.addError(fmt.Errorf("%s: max or min elements had wrong type, %s:%s", Source(n), n.Kind(), n.NName())) + continue + } + + if e.ListAttr == nil { + e.ListAttr = &ListAttr{} + } + + if name == "max-elements" { + e.ListAttr.MaxElements = v + } else { + e.ListAttr.MinElements = v + } + case "units": + v, ok := fv.Interface().(*Value) + if !ok { + e.addError(fmt.Errorf("%s: units had wrong type, %s:%s", Source(n), n.Kind(), n.NName())) + } + if v != nil { + e.Units = v.asString() + } + // TODO(borman): unimplemented keywords + case "belongs-to", + "contact", + "extension", + "feature", + "if-feature", + "must", + "namespace", + "ordered-by", + "organization", + "presence", + "reference", + "revision", + "status", + "unique", + "when", + "yang-version": + e.Extra[name] = append(e.Extra[name], fv.Interface()) + continue + + case "Ext", "Name", "Parent", "Statement": + // These are meta-keywords used internally + continue + default: + e.addError(fmt.Errorf("%s: unexpected statement: %s", Source(n), name)) + continue + + } + // We found at least one field. + found = true + } + if !found { + return newError(n, "%T: cannot be converted to a *Entry", n) + } + // If prefix isn't set, provide it based on our root node (module) + if e.Prefix == nil { + e.Prefix = getRootPrefix(e) + } + + return e +} + +// getRootPrefix returns the prefix of e's root node (module) +func getRootPrefix(e *Entry) *Value { + if m := RootNode(e.Node); m != nil { + return m.getPrefix() + } + return nil +} + +// Augment processes augments in e, return the number of augments processed +// and the augments skipped. If addErrors is true then missing augments will +// generate errors. +func (e *Entry) Augment(addErrors bool) (processed, skipped int) { + // Now process the augments we found + // NOTE(borman): is it possible this will fail if the augment refers + // to some removed sibling that has not been processed? Perhaps this + // should be done after the entire tree is built. Is it correct to + // assume augment paths are data tree paths and not schema tree paths? + // Augments can depend upon augments. We need to figure out how to + // order the augments (or just keep trying until we can make no further + // progress) + var sa []*Entry + for _, a := range e.Augments { + ae := a.Find(a.Name) + if ae == nil { + if addErrors { + e.errorf("%s: augment %s not found", Source(a.Node), a.Name) + } + skipped++ + sa = append(sa, a) + continue + } + // Augments do not have a prefix we merge in, just a node. + // We retain the namespace from the original context of the + // augment since the nodes have this namespace even though they + // are merged into another entry. + processed++ + ae.merge(nil, a.Namespace(), a) + ae.Augmented = append(ae.Augmented, a.shallowDup()) + } + e.Augments = sa + return processed, skipped +} + +// ApplyDeviate walks the deviations within the supplied entry, and applies them to the +// schema. +func (e *Entry) ApplyDeviate() []error { + var errs []error + appendErr := func(err error) { errs = append(errs, err) } + for _, d := range e.Deviations { + deviatedNode := e.Find(d.DeviatedPath) + if deviatedNode == nil { + appendErr(fmt.Errorf("cannot find target node to deviate, %s", d.DeviatedPath)) + continue + } + + for dt, dv := range d.Deviate { + for _, devSpec := range dv { + switch dt { + case DeviationAdd, DeviationReplace: + if devSpec.Config != TSUnset { + deviatedNode.Config = devSpec.Config + } + + if devSpec.Default != "" { + deviatedNode.Default = "" + } + + if devSpec.Mandatory != TSUnset { + deviatedNode.Mandatory = devSpec.Mandatory + } + + if devSpec.ListAttr != nil && devSpec.ListAttr.MinElements != nil { + if !deviatedNode.IsList() && !deviatedNode.IsLeafList() { + appendErr(fmt.Errorf("tried to deviate min-elements on a non-list type %s", deviatedNode.Kind)) + continue + } + deviatedNode.ListAttr.MinElements = devSpec.ListAttr.MinElements + } + + if devSpec.ListAttr != nil && devSpec.ListAttr.MaxElements != nil { + if !deviatedNode.IsList() && !deviatedNode.IsLeafList() { + appendErr(fmt.Errorf("tried to deviate max-elements on a non-list type %s", deviatedNode.Kind)) + continue + } + deviatedNode.ListAttr.MaxElements = devSpec.ListAttr.MaxElements + } + + if devSpec.Units != "" { + deviatedNode.Units = devSpec.Units + } + + if devSpec.Type != nil { + deviatedNode.Type = devSpec.Type + } + + case DeviationNotSupported: + dp := deviatedNode.Parent + if dp == nil { + appendErr(fmt.Errorf("%s: node %s does not have a valid parent, but deviate not-supported references one", Source(e.Node), e.Name)) + continue + } + dp.delete(deviatedNode.Name) + case DeviationDelete: + if devSpec.Config != TSUnset { + deviatedNode.Config = TSUnset + } + + if devSpec.Default == "" { + deviatedNode.Default = "" + } + + if devSpec.Mandatory != TSUnset { + devSpec.Mandatory = TSUnset + } + default: + appendErr(fmt.Errorf("invalid deviation type %s", dt)) + } + } + } + } + + return errs + +} + +// FixChoice inserts missing Case entries in a choice +func (e *Entry) FixChoice() { + if e.Kind == ChoiceEntry && len(e.Errors) == 0 { + for k, ce := range e.Dir { + if ce.Kind != CaseEntry { + ne := &Entry{ + Parent: e, + Node: &Case{ + Parent: ce.Node.ParentNode(), + Name: ce.Node.NName(), + Source: ce.Node.Statement(), + Extensions: ce.Node.Exts(), + }, + Name: ce.Name, + Kind: CaseEntry, + Config: ce.Config, + Prefix: ce.Prefix, + Dir: map[string]*Entry{ce.Name: ce}, + Extra: map[string][]interface{}{}, + } + ce.Parent = ne + e.Dir[k] = ne + e.DirOKeys = append(e.DirOKeys, k) + } + } + } + for _, ce := range e.Dir { + ce.FixChoice() + } +} + +// ReadOnly returns true if e is a read-only variable (config == false). +// If Config is unset in e, then false is returned if e has no parent, +// otherwise the value parent's ReadOnly is returned. +func (e *Entry) ReadOnly() bool { + switch { + case e == nil: + // We made it all the way to the root of the tree + return false + case e.Kind == OutputEntry: + return true + case e.Config == TSUnset: + return e.Parent.ReadOnly() + default: + return !e.Config.Value() + } +} + +// Find finds the Entry named by name relative to e. +func (e *Entry) Find(name string) *Entry { + if e == nil || name == "" { + return nil + } + parts := strings.Split(name, "/") + + // If parts[0] is "" then this path started with a / + // and we need to find our parent. + if parts[0] == "" { + for e.Parent != nil { + e = e.Parent + } + parts = parts[1:] + + // Since this module might use a different prefix that isn't + // the prefix that the module itself uses then we need to resolve + // the module into its local prefix to find it. + pfxMap := map[string]string{ + // Seed the map with the local module - we use GetPrefix just + // in case the module is a submodule. + e.Node.(*Module).GetPrefix(): e.Prefix.Name, + } + + // Add a map between the prefix used in the import statement, and + // the prefix that is used in the module itself. + for _, i := range e.Node.(*Module).Import { + // Resolve the module using the current module set, since we may + // not have populated the Module for the entry yet. + m, ok := e.Node.(*Module).modules.Modules[i.Name] + if !ok { + e.addError(fmt.Errorf("cannot find a module with name %s when looking at imports in %s", i.Name, e.Path())) + return nil + } + + pfxMap[i.Prefix.Name] = m.Prefix.Name + } + + if prefix, _ := getPrefix(parts[0]); prefix != "" { + pfx, ok := pfxMap[prefix] + if !ok { + // This is an undefined prefix within our context, so + // we can't do anything about resolving it. + e.addError(fmt.Errorf("invalid module prefix %s within module %s, defined prefix map: %v", prefix, e.Name, pfxMap)) + return nil + } + m, err := e.Modules().FindModuleByPrefix(pfx) + if err != nil { + e.addError(err) + return nil + } + if e.Node.(*Module) != m { + e = ToEntry(m) + } + } + } + + for _, part := range parts { + switch { + case e == nil: + return nil + case part == ".": + case part == "..": + e = e.Parent + case e.RPC != nil: + switch part { + case "input": + e = e.RPC.Input + case "output": + e = e.RPC.Output + } + default: + _, part = getPrefix(part) + switch part { + case ".": + case "", "..": + return nil + default: + e = e.Dir[part] + } + } + } + return e +} + +// Path returns the path to e. A nil Entry returns "". +func (e *Entry) Path() string { + if e == nil { + return "" + } + return e.Parent.Path() + "/" + e.Name +} + +// Namespace returns the YANG/XML namespace Value for e as mounted in the Entry +// tree (e.g., as placed by grouping statements). +// +// Per RFC6020 section 7.12, the namespace on elements in the tree due to a +// "uses" statement is that of the where the uses statement occurs, i.e., the +// user, rather than creator (grouping) of those elements, so we follow the +// usage (Entry) tree up to the parent before obtaining the (then adjacent) root +// node for its namespace Value. +func (e *Entry) Namespace() *Value { + // Make e the root parent entry + for ; e.Parent != nil; e = e.Parent { + if e.namespace != nil { + return e.namespace + } + } + + // Return the namespace of a valid root parent entry + if e != nil && e.Node != nil { + if root := RootNode(e.Node); root != nil { + return root.Namespace + } + } + + // Otherwise return an empty namespace Value (rather than nil) + return new(Value) +} + +// InstantiatingModule returns the YANG module which instanitated the Entry +// within the schema tree - using the same rules described in the documentation +// of the Namespace function. The namespace is resolved in the module name. This +// approach to namespacing is used when serialising YANG-modelled data to JSON as +// per RFC7951. +func (e *Entry) InstantiatingModule() (string, error) { + n := e.Namespace() + if n == nil { + return "", fmt.Errorf("entry %s had nil namespace", e.Name) + } + + ns, err := e.Modules().FindModuleByNamespace(n.Name) + if err != nil { + return "", fmt.Errorf("could not find module %s when retrieving namespace for %s", n.Name, e.Name) + } + return ns.Name, nil +} + +// shallowDup makes a shallow duplicate of e (only direct children are +// duplicated; grandchildren and deeper descedents are deleted). +func (e *Entry) shallowDup() *Entry { + // Warning: if we add any elements to Entry that should not be + // copied we will have to explicitly uncopy them. + ne := *e + + //Copy the ordered Dir keys to new entry + if len(e.DirOKeys) > 0 { + ne.DirOKeys = make([]string, 0) + for _, key := range e.DirOKeys { + ne.DirOKeys = append(ne.DirOKeys, key) + } + } + + // Now only copy direct children, clear their Dir, and fix up + // Parent pointers. + if e.Dir != nil { + ne.Dir = make(map[string]*Entry, len(e.Dir)) + for k, v := range e.Dir { + de := *v + de.Dir = nil + de.Parent = &ne + ne.Dir[k] = &de + } + } + return &ne +} + +// dup makes a deep duplicate of e. +func (e *Entry) dup() *Entry { + // Warning: if we add any elements to Entry that should not be + // copied we will have to explicitly uncopy them. + // It is possible we may want to do a deep copy on some other fields, + // such as Exts, Choice and Case, but it is not clear that we need + // to do that. + ne := *e + + //Copy the ordered Dir keys to new entry + if len(e.DirOKeys) > 0 { + ne.DirOKeys = make([]string, 0) + for _, key := range e.DirOKeys { + ne.DirOKeys = append(ne.DirOKeys, key) + } + } + + // Now recurse down to all of our children, fixing up Parent + // pointers as we go. + if e.Dir != nil { + ne.Dir = make(map[string]*Entry, len(e.Dir)) + for k, v := range e.Dir { + de := v.dup() + de.Parent = &ne + ne.Dir[k] = de + } + } + return &ne +} + +// merge merges a duplicate of oe.Dir into e.Dir, setting the prefix of each +// element to prefix, if not nil. It is an error if e and oe contain common +// elements. +func (e *Entry) merge(prefix *Value, namespace *Value, oe *Entry) { + e.importErrors(oe) + for k, v := range oe.Dir { + v := v.dup() + if prefix != nil { + v.Prefix = prefix + } + if namespace != nil { + v.namespace = namespace + } + if se := e.Dir[k]; se != nil { + er := newError(oe.Node, `Duplicate node %q in %q from: + %s: %s + %s: %s`, k, e.Name, Source(v.Node), v.Name, Source(se.Node), se.Name) + e.addError(er.Errors[0]) + } else { + v.Parent = e + e.Dir[k] = v + e.DirOKeys = append(e.DirOKeys, k) + } + } +} + +// nless returns -1 if a is less than b, 0 if a == b, and 1 if a > b. +// If a and b are both numeric, then nless compares them as numbers, +// otherwise they are compared lexicographically. +func nless(a, b string) int { + an, ae := strconv.Atoi(a) + bn, be := strconv.Atoi(b) + switch { + case ae == nil && be == nil: + switch { + case an < bn: + return -1 + case an > bn: + return 1 + default: + return 0 + } + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} + +type sError struct { + s string + err error +} + +type sortedErrors []sError + +func (s sortedErrors) Len() int { return len(s) } +func (s sortedErrors) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sortedErrors) Less(i, j int) bool { + fi := strings.SplitN(s[i].s, ":", 4) + fj := strings.SplitN(s[j].s, ":", 4) + if fi[0] < fj[0] { + return true + } + if fi[0] > fj[0] { + return false + } + + // compare compares field x to see which is less. + // numbers are compared as numbers. + compare := func(x int) int { + switch { + case len(fi) == x && len(fj) > x: + return -1 + case len(fj) == x && len(fi) > x: + return 1 + case len(fj) < x && len(fi) < x: + return 0 + } + return nless(fi[x], fj[x]) + } + for x := 1; x < 4; x++ { + switch compare(1) { + case -1: + return true + case 1: + return false + } + } + return false +} + +// errorSort sorts the strings in the errors slice assuming each line starts +// with file:line:col. Line and column number are sorted numerically. +// Duplicate errors are stripped. +func errorSort(errors []error) []error { + switch len(errors) { + case 0: + return nil + case 1: + return errors + } + elist := make(sortedErrors, len(errors)) + for x, err := range errors { + elist[x] = sError{err.Error(), err} + } + sort.Sort(elist) + errors = make([]error, len(errors)) + i := 0 + for _, err := range elist { + if i > 0 && reflect.DeepEqual(err.err, errors[i-1]) { + continue + } + errors[i] = err.err + i++ + } + return errors[:i] +} + +// DefaultValue returns the schema default value for e, if any. If the leaf +// has no explicit default, its type default (if any) will be used. +func (e *Entry) DefaultValue() string { + if len(e.Default) > 0 { + return e.Default + } else if typ := e.Type; typ != nil { + if leaf, ok := e.Node.(*Leaf); ok { + if leaf.Mandatory == nil || leaf.Mandatory.Name == "false" { + return typ.Default + } + } + } + return "" +} diff --git a/goyang-modified-files/yang.go b/goyang-modified-files/yang.go new file mode 100644 index 0000000000..515d1b33b0 --- /dev/null +++ b/goyang-modified-files/yang.go @@ -0,0 +1,216 @@ +// Copyright 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Program yang parses YANG files, displays errors, and possibly writes +// something related to the input on output. +// +// Usage: yang [--path DIR] [--format FORMAT] [FORMAT OPTIONS] [MODULE] [FILE ...] +// +// If MODULE is specified (an argument that does not end in .yang), it is taken +// as the name of the module to display. Any FILEs specified are read, and the +// tree for MODULE is displayed. If MODULE was not defined in FILEs (or no +// files were specified), then the file MODULES.yang is read as well. An error +// is displayed if no definition for MODULE was found. +// +// If MODULE is missing, then all base modules read from the FILEs are +// displayed. If there are no arguments then standard input is parsed. +// +// If DIR is specified, it is considered a comma separated list of paths +// to append to the search directory. If DIR appears as DIR/... then +// DIR and all direct and indirect subdirectories are checked. +// +// FORMAT, which defaults to "tree", specifes the format of output to produce. +// Use "goyang --help" for a list of available formats. +// +// FORMAT OPTIONS are flags that apply to a specific format. They must follow +// --format. +// +// THIS PROGRAM IS STILL JUST A DEVELOPMENT TOOL. +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "runtime/trace" + "sort" + "strings" + + "github.com/openconfig/goyang/pkg/indent" + "github.com/openconfig/goyang/pkg/yang" + "github.com/pborman/getopt" +) + +// Each format must register a formatter with register. The function f will +// be called once with the set of yang Entry trees generated. +type formatter struct { + name string + f func(io.Writer, []*yang.Entry) + utilf func([]string, map[string]*yang.Module) + help string + flags *getopt.Set +} + +var formatters = map[string]*formatter{} + +func register(f *formatter) { + formatters[f.name] = f +} + +// exitIfError writes errs to standard error and exits with an exit status of 1. +// If errs is empty then exitIfError does nothing and simply returns. +func exitIfError(errs []error) { + if len(errs) > 0 { + for _, err := range errs { + fmt.Fprintln(os.Stderr, err) + } + stop(1) + } +} + +var stop = os.Exit + +func main() { + var format string + formats := make([]string, 0, len(formatters)) + for k := range formatters { + formats = append(formats, k) + } + sort.Strings(formats) + + var traceP string + var help bool + var paths []string + getopt.ListVarLong(&paths, "path", 0, "comma separated list of directories to add to search path", "DIR[,DIR...]") + getopt.StringVarLong(&format, "format", 0, "format to display: "+strings.Join(formats, ", "), "FORMAT") + getopt.StringVarLong(&traceP, "trace", 0, "write trace into to TRACEFILE", "TRACEFILE") + getopt.BoolVarLong(&help, "help", '?', "display help") + getopt.BoolVarLong(&yang.ParseOptions.IgnoreSubmoduleCircularDependencies, "ignore-circdep", 0, "ignore circular dependencies between submodules") + getopt.SetParameters("[FORMAT OPTIONS] [SOURCE] [...]") + + if err := getopt.Getopt(func(o getopt.Option) bool { + if o.Name() == "--format" { + f, ok := formatters[format] + if !ok { + fmt.Fprintf(os.Stderr, "%s: invalid format. Choices are %s\n", format, strings.Join(formats, ", ")) + stop(1) + } + if f.flags != nil { + f.flags.VisitAll(func(o getopt.Option) { + getopt.AddOption(o) + }) + } + } + return true + }); err != nil { + fmt.Fprintln(os.Stderr, err) + getopt.PrintUsage(os.Stderr) + os.Exit(1) + } + + if traceP != "" { + fp, err := os.Create(traceP) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + trace.Start(fp) + stop = func(c int) { trace.Stop(); os.Exit(c) } + defer func() { trace.Stop() }() + } + + if help { + getopt.CommandLine.PrintUsage(os.Stderr) + fmt.Fprintf(os.Stderr, ` +SOURCE may be a module name or a .yang file. + +Formats: +`) + for _, fn := range formats { + f := formatters[fn] + fmt.Fprintf(os.Stderr, " %s - %s\n", f.name, f.help) + if f.flags != nil { + f.flags.PrintOptions(indent.NewWriter(os.Stderr, " ")) + } + fmt.Fprintln(os.Stderr) + } + stop(0) + } + + for _, path := range paths { + expanded, err := yang.PathsWithModules(path) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + yang.AddPath(expanded...) + } + + if format == "" { + format = "tree" + } + if _, ok := formatters[format]; !ok { + fmt.Fprintf(os.Stderr, "%s: invalid format. Choices are %s\n", format, strings.Join(formats, ", ")) + stop(1) + + } + + files := getopt.Args() + + ms := yang.NewModules() + + if len(files) == 0 { + data, err := ioutil.ReadAll(os.Stdin) + if err == nil { + err = ms.Parse(string(data), "") + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + stop(1) + } + } + + for _, name := range files { + if err := ms.Read(name); err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + } + + // Process the read files, exiting if any errors were found. + exitIfError(ms.Process()) + + // Keep track of the top level modules we read in. + // Those are the only modules we want to print below. + mods := map[string]*yang.Module{} + var names []string + + for _, m := range ms.Modules { + if mods[m.Name] == nil { + mods[m.Name] = m + names = append(names, m.Name) + } + } + sort.Strings(names) + entries := make([]*yang.Entry, len(names)) + for x, n := range names { + entries[x] = yang.ToEntry(mods[n]) + } + + if format == "annotate" { + formatters[format].utilf(files, mods) + } + formatters[format].f(os.Stdout, entries) +} diff --git a/models/Makefile b/models/Makefile index fa432ca938..8f542e905d 100644 --- a/models/Makefile +++ b/models/Makefile @@ -1,9 +1,21 @@ -####################################################################### -# -# Copyright 2019 Broadcom. All rights reserved. -# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -# -####################################################################### +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ TOPDIR := .. ABS_TOPDIR := $(abspath $(TOPDIR)) diff --git a/models/yang/Makefile b/models/yang/Makefile index 721feecb21..228fa75751 100644 --- a/models/yang/Makefile +++ b/models/yang/Makefile @@ -1,9 +1,21 @@ -####################################################################### -# -# Copyright 2019 Broadcom. All rights reserved. -# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -# -####################################################################### +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ TOPDIR := ../.. BUILD_DIR := $(TOPDIR)/build diff --git a/models/yang/annotations/openconfig-acl-annot.yang b/models/yang/annotations/openconfig-acl-annot.yang new file mode 100644 index 0000000000..28b98e9b4e --- /dev/null +++ b/models/yang/annotations/openconfig-acl-annot.yang @@ -0,0 +1,197 @@ +module openconfig-acl-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/annotation"; + prefix "oc-acl-annot"; + + import sonic-extensions { prefix sonic-ext; } + import openconfig-acl { prefix oc-acl; } + import openconfig-packet-match { prefix oc-pkt-match; } + + deviation /oc-acl:acl { + deviate add { + sonic-ext:post-transformer "acl_post_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set { + deviate add { + sonic-ext:table-name "ACL_TABLE"; + sonic-ext:key-delimiter "_"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:config/oc-acl:name { + deviate add { + sonic-ext:field-transformer "acl_set_name_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:config/oc-acl:type { + deviate add { + sonic-ext:field-transformer "acl_type_field_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:config/oc-acl:description { + deviate add { + sonic-ext:field-name "policy_desc"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:state/oc-acl:name { + deviate add { + sonic-ext:field-transformer "acl_set_name_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:state/oc-acl:type { + deviate add { + sonic-ext:field-transformer "acl_type_field_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:state/oc-acl:description { + deviate add { + sonic-ext:field-name "policy_desc"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry { + deviate add { + sonic-ext:table-name "ACL_RULE"; + sonic-ext:key-transformer "acl_entry_key_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-acl:ipv4 { + deviate add { + sonic-ext:get-validate "validate_ipv4"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-acl:ipv6 { + deviate add { + sonic-ext:get-validate "validate_ipv6"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-acl:config/oc-acl:sequence-id { + deviate add { + sonic-ext:field-transformer "acl_entry_sequenceid_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:config/oc-pkt-match:source-address { + deviate add { + sonic-ext:field-name "SRC_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:config/oc-pkt-match:destination-address { + deviate add { + sonic-ext:field-name "DST_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:config/oc-pkt-match:protocol { + deviate add { + sonic-ext:field-transformer "acl_ip_protocol_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:state/oc-pkt-match:source-address { + deviate add { + sonic-ext:field-name "SRC_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:state/oc-pkt-match:destination-address { + deviate add { + sonic-ext:field-name "DST_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv4/oc-pkt-match:state/oc-pkt-match:protocol { + deviate add { + sonic-ext:field-transformer "acl_ip_protocol_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv6/oc-pkt-match:config/oc-pkt-match:source-address { + deviate add { + sonic-ext:field-name "SRC_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv6/oc-pkt-match:config/oc-pkt-match:destination-address { + deviate add { + sonic-ext:field-name "DST_IP"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:ipv6/oc-pkt-match:config/oc-pkt-match:protocol { + deviate add { + sonic-ext:field-transformer "acl_ip_protocol_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:transport/oc-pkt-match:config/oc-pkt-match:source-port { + deviate add { + sonic-ext:field-transformer "acl_source_port_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:transport/oc-pkt-match:config/oc-pkt-match:destination-port { + deviate add { + sonic-ext:field-transformer "acl_destination_port_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:transport/oc-pkt-match:state/oc-pkt-match:source-port { + deviate add { + sonic-ext:field-transformer "acl_source_port_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:transport/oc-pkt-match:state/oc-pkt-match:destination-port { + deviate add { + sonic-ext:field-transformer "acl_destination_port_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:transport/oc-pkt-match:config/oc-pkt-match:tcp-flags { + deviate add { + sonic-ext:field-transformer "acl_tcp_flags_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-pkt-match:l2/oc-pkt-match:config/oc-pkt-match:ethertype { + deviate add { + sonic-ext:field-transformer "acl_l2_ethertype_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-acl:actions/oc-acl:config/oc-acl:forwarding-action { + deviate add { + sonic-ext:field-name "PACKET_ACTION"; + sonic-ext:field-transformer "acl_forwarding_action_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry/oc-acl:actions/oc-acl:state/oc-acl:forwarding-action { + deviate add { + sonic-ext:field-name "PACKET_ACTION"; + sonic-ext:field-transformer "acl_forwarding_action_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:interfaces { + deviate add { + sonic-ext:subtree-transformer "acl_port_bindings_xfmr"; + } + } + +} + diff --git a/models/yang/common/sonic-extensions.yang b/models/yang/common/sonic-extensions.yang new file mode 100644 index 0000000000..ff3c315b3d --- /dev/null +++ b/models/yang/common/sonic-extensions.yang @@ -0,0 +1,78 @@ +module sonic-extensions { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/sonic-ext"; + + prefix "sonic-ext"; + + // meta + organization "Sonic working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module provides extensions to the YANG language to allow + Sonic specific functionality and meta-data to be defined."; + + revision "2019-08-30" { + description + "Add extensions for redis DB mappings to identify the Redis DB name."; + } + + revision "2019-07-26" { + description + "Add extensionis for redis DB mappings for table, table-keys, table-fields and corresponding transformer methods."; + } + + + // extension statements + extension table-name { + argument "table-name"; + description "Db table name."; + } + + extension key-transformer { + argument "key-transformer-name"; + description "Db table key transformer name indicating that the list keys together form db table keys."; + } + + extension key-delimiter { + argument "key-delimiter-string"; + description "Db table key values delimiter."; + } + + extension field-name { + argument "field-name"; + description "Db table field name."; + } + + extension field-transformer { + argument "field-transformer-name"; + description "Db table field transformer name.This can be applied to either transform yang value to some different format + or choose a specific DB field based on the type of yang value."; + } + + extension subtree-transformer { + argument "subtree-transformer-name"; + description "Subtree/node level transformer name that will have db mappings for an entire yang subtree."; + } + + extension post-transformer { + argument "post-transformer-name"; + description "Transformer name that will perform post-translation tasks."; + } + + extension get-validate { + argument "get-validate-name"; + description "Validation callpoint used to validate a YANG node during data translation back to YANG as a response to GET."; + } + + extension redis-db-name { + argument "db-name"; + description "DB name that will indicate where data is stored. Eg: Config DB, App DB etc"; + } +} diff --git a/models/yang/openconfig-acl.yang b/models/yang/openconfig-acl.yang index fe8098889b..67c75ff47c 100644 --- a/models/yang/openconfig-acl.yang +++ b/models/yang/openconfig-acl.yang @@ -818,7 +818,6 @@ module openconfig-acl { container config { description "Global config data for ACLs"; - uses acl-config; } diff --git a/src/CLI/actioner/sonic-cli-if.py b/src/CLI/actioner/sonic-cli-if.py index 9ed5f27006..e858fbb071 100755 --- a/src/CLI/actioner/sonic-cli-if.py +++ b/src/CLI/actioner/sonic-cli-if.py @@ -44,6 +44,7 @@ def call_method(name, args): def generate_body(func, args): body = None + keypath = [] # Get the rules of all ACL table entries. if func.__name__ == 'patch_openconfig_interfaces_interfaces_interface_config_description': keypath = [ args[0] ] @@ -98,6 +99,11 @@ def run(func, args): keypath, body = generate_body(func, args) try: + # Temporary code for #show vlan command with dummy data + if func.__name__ == "get_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_state": + api_response = {'Vlan100': {'Ethernet20': 'tagged', 'Ethernet40': 'untagged'}} + show_cli_output(args[0], api_response) + return if body is not None: api_response = getattr(aa,func.__name__)(*keypath, body=body) else : diff --git a/src/CLI/clitree/cli-xml/interface.xml b/src/CLI/clitree/cli-xml/interface.xml index 3341170d76..0ded9aabfd 100644 --- a/src/CLI/clitree/cli-xml/interface.xml +++ b/src/CLI/clitree/cli-xml/interface.xml @@ -23,89 +23,150 @@ limitations under the License. http://www.dellemc.com/sonic/XMLSchema/clish.xsd" > - + - - + > + + - - - - - - - - - if test "${if-subcommands}" = "status"; then - python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface_status.j2 ${__full_line} - elif test "${if-subcommands}" = "counters"; then - python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface_counters.j2 ${__full_line} - else - if test "${phy-if-id}" = ""; then - python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface.j2 ${__full_line} - else - python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces_interface Ethernet${phy-if-id} show_interface_id.j2 ${__full_line} - fi - fi - - - + + + + + + + + + if test "${if-subcommands}" = "status"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface_status.j2 ${__full_line} + elif test "${if-subcommands}" = "counters"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface_counters.j2 ${__full_line} + else + if test "${phy-if-id}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces show_interface.j2 ${__full_line} + else + python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_interfaces_interfaces_interface Ethernet${phy-if-id} show_interface_id.j2 ${__full_line} + fi + fi + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py get_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_state show_vlan.j2 + + + + - + + + + - + + + + + + + + + + + + + - - - - - + + + + + + - - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} False - - - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} True - - - - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} ${desc} + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} ${desc} - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} "" + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} "" - - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${iface} ${mtu} + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${iface} ${mtu} - python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${iface} 9100 - - + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} False + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/renderer/templates/show_vlan.j2 b/src/CLI/renderer/templates/show_vlan.j2 new file mode 100644 index 0000000000..aabb216869 --- /dev/null +++ b/src/CLI/renderer/templates/show_vlan.j2 @@ -0,0 +1,23 @@ +{% set vars = {'vlanName': ""} %} +{% set vars = {'ifName': ""} %} +{% set vars = {'tagMode': ""} %} +{% set vars = {'tagLetter': ""} %} +{% if json_output -%} +Q: A - Access (Untagged), T - Tagged +{{'%-11s'|format("NUM")}}{{'%-2s'|format("Q Ports")}} +{% for vlanKey, memberEntries in json_output.items() %} +{% if vars.update({'vlanName':vlanKey}) %}{% endif %} +{% for ifKey, ifMode in memberEntries.items() %} +{% if vars.update({'ifName':ifKey}) %}{% endif %} +{% if vars.update({'tagMode':ifMode}) %}{% endif %} +{% if ifMode == "untagged" %} +{% if vars.update({'tagLetter':" U "}) %}{% endif %} +{% else %} +{% if vars.update({'tagLetter':" T "}) %}{% endif %} +{% endif %} +{{'%-10s'|format(vars.vlanName)}}{{'%-2s'|format(vars.tagLetter)}}{{'%-10s'|format(vars.ifName)}} +{% if vars.update({'vlanName':''}) %}{% endif %} +{% endfor %} +{% endfor %} +{% endif %} + diff --git a/src/cvl/Makefile b/src/cvl/Makefile index 54c3980818..9cb91d1f78 100644 --- a/src/cvl/Makefile +++ b/src/cvl/Makefile @@ -1,3 +1,22 @@ +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + all: precheck deps schema tests GO?=/usr/local/go/bin/go SRC_FILES=$(shell find . -name '*.go' | grep -v '_test.go' | grep -v '/tests/') diff --git a/src/cvl/cvl.go b/src/cvl/cvl.go index 4d44b35b7a..2b81dbcaac 100644 --- a/src/cvl/cvl.go +++ b/src/cvl/cvl.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package cvl import ( "fmt" diff --git a/src/cvl/cvl_api.go b/src/cvl/cvl_api.go index d4bfe7ee2b..faac5b3481 100644 --- a/src/cvl/cvl_api.go +++ b/src/cvl/cvl_api.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package cvl import ( diff --git a/src/cvl/cvl_luascript.go b/src/cvl/cvl_luascript.go index ac94c04db5..fd4c863ad1 100644 --- a/src/cvl/cvl_luascript.go +++ b/src/cvl/cvl_luascript.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package cvl import ( "github.com/go-redis/redis" diff --git a/src/cvl/cvl_test.go b/src/cvl/cvl_test.go index 1ac35ed30f..f08394ba34 100644 --- a/src/cvl/cvl_test.go +++ b/src/cvl/cvl_test.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package cvl_test import ( diff --git a/src/cvl/internal/util/util.go b/src/cvl/internal/util/util.go index b57f8f1141..7404500d04 100644 --- a/src/cvl/internal/util/util.go +++ b/src/cvl/internal/util/util.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package util import ( diff --git a/src/cvl/internal/yparser/yparser.go b/src/cvl/internal/yparser/yparser.go index dff83d01c7..0277088d62 100644 --- a/src/cvl/internal/yparser/yparser.go +++ b/src/cvl/internal/yparser/yparser.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package yparser /* Yang parser using libyang library */ diff --git a/src/cvl/jsondata_test.go b/src/cvl/jsondata_test.go index 65adaa8200..592ef71268 100644 --- a/src/cvl/jsondata_test.go +++ b/src/cvl/jsondata_test.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package cvl_test var json_edit_config_create_acl_table_dependent_data = []string{`{ diff --git a/src/cvl/schema/Makefile b/src/cvl/schema/Makefile index 1844920d34..8f1276b68b 100644 --- a/src/cvl/schema/Makefile +++ b/src/cvl/schema/Makefile @@ -1,3 +1,22 @@ +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + sonic_yang=../../../models/yang/sonic sonic_yang_common=../../../models/yang/sonic/common pyang_plugin_dir=../../../tools/pyang/pyang_plugins @@ -9,11 +28,11 @@ out_common=$(patsubst %.yang, %.yin, $(shell ls -1 $(sonic_yang_common)/*.yang | out_tree=$(patsubst %.yang, %.tree, $(src_files)) search_path=$(sonic_yang):$(sonic_yang_common):$(sonic_yang_common)/ietf - -all:schema +all: yamlGen allyangs.tree allyangs_tree.html schema schema: $(out) $(out_common) + schema-tree: $(out_tree) #Build YANG models @@ -24,6 +43,7 @@ schema-tree: $(out_tree) pyang -p $(search_path) --plugindir $(pyang_plugin_dir) \ -f yin-cvl $$devOpt $< -o `basename $@` + #Build common YANG models %.yin:$(sonic_yang_common)/%.yang @echo "Generating `basename $@` ..." @@ -31,6 +51,7 @@ schema-tree: $(out_tree) if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ pyang -p $(search_path) --plugindir $(pyang_plugin_dir) \ -f yin-cvl $$devOpt $< -o `basename $@` + %.tree:%.yang @echo "Generating `basename $@` ..." @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ diff --git a/src/cvl/tests/Makefile b/src/cvl/tests/Makefile index 64b8eb1c6c..ce4dc21f0f 100644 --- a/src/cvl/tests/Makefile +++ b/src/cvl/tests/Makefile @@ -1,3 +1,22 @@ +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + SRC_FILES=$(wildcard *.go) OUT=$(patsubst %.go, %, $(SRC_FILES)) TOPDIR := $(abspath ../../..) diff --git a/src/cvl/tests/cfg_validator.go b/src/cvl/tests/cfg_validator.go index b7be94fe89..f5a532da95 100644 --- a/src/cvl/tests/cfg_validator.go +++ b/src/cvl/tests/cfg_validator.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/cvl/tests/cv_acl.go b/src/cvl/tests/cv_acl.go index 0bb61e0478..cb12c0109b 100644 --- a/src/cvl/tests/cv_acl.go +++ b/src/cvl/tests/cv_acl.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/cvl/tests/cv_edit_op.go b/src/cvl/tests/cv_edit_op.go index 6eb65fa67f..a9035bcf51 100644 --- a/src/cvl/tests/cv_edit_op.go +++ b/src/cvl/tests/cv_edit_op.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/cvl/tests/cv_vlan.go b/src/cvl/tests/cv_vlan.go index d3e13c42dc..b230e86b5a 100644 --- a/src/cvl/tests/cv_vlan.go +++ b/src/cvl/tests/cv_vlan.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main diff --git a/src/rest/Makefile b/src/rest/Makefile index 1eb352a88d..4b3d0055ec 100644 --- a/src/rest/Makefile +++ b/src/rest/Makefile @@ -1,9 +1,21 @@ -####################################################################### -# -# Copyright 2019 Broadcom. All rights reserved. -# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -# -####################################################################### +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ TOPDIR ?= ../.. ABS_TOPDIR ?= $(abspath $(TOPDIR)) @@ -24,6 +36,7 @@ REST_TEST_SRCS = $(shell find . -name '*_test.go') # Source files affecting REST server REST_SRCS := $(ALL_GO_SRCS) \ $(shell find $(TOPDIR)/models/yang -name '*.yang' | sort) \ + $(shell find $(TOPDIR)/src/cvl/schema '*.yang' | sort) \ $(shell find $(TOPDIR)/models/openapi -name '*.yaml' | sort) REST_GOPATH := $(GOPATH):$(abspath $(REST_BUILD_DIR)/dist) diff --git a/src/rest/main/main.go b/src/rest/main/main.go index 1c69290d38..e44056c733 100644 --- a/src/rest/main/main.go +++ b/src/rest/main/main.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package main diff --git a/src/rest/main/main_test.go b/src/rest/main/main_test.go index 2ab6a40702..50ee1010da 100644 --- a/src/rest/main/main_test.go +++ b/src/rest/main/main_test.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/rest/server/context.go b/src/rest/server/context.go index 2a7b4eb292..883679e343 100644 --- a/src/rest/server/context.go +++ b/src/rest/server/context.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/context_test.go b/src/rest/server/context_test.go index d4635e9668..1df7fc3857 100644 --- a/src/rest/server/context_test.go +++ b/src/rest/server/context_test.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/error.go b/src/rest/server/error.go index 3b05140975..0335e70eac 100644 --- a/src/rest/server/error.go +++ b/src/rest/server/error.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/error_test.go b/src/rest/server/error_test.go index 9ae5857dc8..7dfdfce3df 100644 --- a/src/rest/server/error_test.go +++ b/src/rest/server/error_test.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/handler.go b/src/rest/server/handler.go index 7b8c1eb036..5596c310f9 100644 --- a/src/rest/server/handler.go +++ b/src/rest/server/handler.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/handler_test.go b/src/rest/server/handler_test.go index eac69616c4..16e1ca21f5 100644 --- a/src/rest/server/handler_test.go +++ b/src/rest/server/handler_test.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/pamAuth.go b/src/rest/server/pamAuth.go index d726d35229..4ad9f0180e 100644 --- a/src/rest/server/pamAuth.go +++ b/src/rest/server/pamAuth.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package server import ( diff --git a/src/rest/server/pamAuth_test.go b/src/rest/server/pamAuth_test.go index 3726d895ce..d3015ea09d 100644 --- a/src/rest/server/pamAuth_test.go +++ b/src/rest/server/pamAuth_test.go @@ -1,8 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// // // Test cases for REST Server PAM Authentication module. // diff --git a/src/rest/server/req_validate.go b/src/rest/server/req_validate.go index d5a394f867..4571d8a100 100644 --- a/src/rest/server/req_validate.go +++ b/src/rest/server/req_validate.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/rest/server/router.go b/src/rest/server/router.go index 6442213965..aa01d193e5 100644 --- a/src/rest/server/router.go +++ b/src/rest/server/router.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package server diff --git a/src/translib/acl_app.go b/src/translib/acl_app.go index f7e94af58f..0678cde7aa 100644 --- a/src/translib/acl_app.go +++ b/src/translib/acl_app.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package translib diff --git a/src/translib/acl_app_test.go b/src/translib/acl_app_test.go index 88065982b8..d1773866ac 100644 --- a/src/translib/acl_app_test.go +++ b/src/translib/acl_app_test.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package translib import ( diff --git a/src/translib/app_interface.go b/src/translib/app_interface.go index a64d568065..ef4992e371 100644 --- a/src/translib/app_interface.go +++ b/src/translib/app_interface.go @@ -1,8 +1,21 @@ -/////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom Inc. -// -/////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package translib defines the interface for all the app modules @@ -12,7 +25,6 @@ It exposes register function for all the app modules to register It stores all the app module information in a map and presents it to the tranlib infra when it asks for the same. - */ package translib @@ -125,12 +137,9 @@ func getAppModuleInfo(path string) (*appInfo, error) { return app, err } - errStr := "Unsupported path=" + path - - err = errors.New(errStr) - log.Error(errStr) - - var app *appInfo + /* If no specific app registered fallback to default/common app */ + log.Infof("No app module registered for path %s hence fallback to default/common app", path) + app := appMap["*"] return app, err } diff --git a/src/translib/app_utils.go b/src/translib/app_utils.go index 90875968f7..0fa8b66fd3 100644 --- a/src/translib/app_utils.go +++ b/src/translib/app_utils.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package translib diff --git a/src/translib/common_app.go b/src/translib/common_app.go new file mode 100644 index 0000000000..2ec8cf10bd --- /dev/null +++ b/src/translib/common_app.go @@ -0,0 +1,463 @@ +package translib + +import ( + "errors" + "fmt" + "strings" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "reflect" + "translib/db" + "translib/ocbinds" + "translib/tlerr" + "translib/transformer" + "encoding/json" +) + +var () + +type CommonApp struct { + pathInfo *PathInfo + ygotRoot *ygot.GoStruct + ygotTarget *interface{} + cmnAppTableMap map[string]map[string]db.Value + cmnAppOrdTbllist []string +} + +var cmnAppInfo = appInfo{appType: reflect.TypeOf(CommonApp{}), + ygotRootType: nil, + isNative: false, + tablesToWatch: nil} + +func init() { + + register_model_path := []string{"/sonic-", "*"} // register yang model path(s) to be supported via common app + for _, mdl_pth := range register_model_path { + err := register(mdl_pth, &cmnAppInfo) + + if err != nil { + log.Fatal("Register Common app module with App Interface failed with error=", err, "for path=", mdl_pth) + } + } + +} + +func (app *CommonApp) initialize(data appData) { + log.Info("initialize:path =", data.path) + pathInfo := NewPathInfo(data.path) + *app = CommonApp{pathInfo: pathInfo, ygotRoot: data.ygotRoot, ygotTarget: data.ygotTarget} + +} + +func (app *CommonApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCreate:path =", app.pathInfo.Path) + + keys, err = app.translateCRUDCommon(d, CREATE) + + return keys, err +} + +func (app *CommonApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateUpdate:path =", app.pathInfo.Path) + + keys, err = app.translateCRUDCommon(d, UPDATE) + + return keys, err +} + +func (app *CommonApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateReplace:path =", app.pathInfo.Path) + + keys, err = app.translateCRUDCommon(d, REPLACE) + + return keys, err +} + +func (app *CommonApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateDelete:path =", app.pathInfo.Path) + keys, err = app.translateCRUDCommon(d, DELETE) + + return keys, err +} + +func (app *CommonApp) translateGet(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateGet:path =", app.pathInfo.Path) + return err +} + +func (app *CommonApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + err := errors.New("Not supported") + notifInfo := notificationInfo{dbno: db.ConfigDB} + return nil, ¬ifInfo, err +} + +func (app *CommonApp) processCreate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + log.Info("processCreate:path =", app.pathInfo.Path) + targetType := reflect.TypeOf(*app.ygotTarget) + log.Infof("processCreate: Target object is a <%s> of Type: %s", targetType.Kind().String(), targetType.Elem().Name()) + if err = app.processCommon(d, CREATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + + return resp, err +} + +func (app *CommonApp) processUpdate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + log.Info("processUpdate:path =", app.pathInfo.Path) + if err = app.processCommon(d, UPDATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + + return resp, err +} + +func (app *CommonApp) processReplace(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + log.Info("processReplace:path =", app.pathInfo.Path) + if err = app.processCommon(d, REPLACE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *CommonApp) processDelete(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + log.Info("processDelete:path =", app.pathInfo.Path) + + if err = app.processCommon(d, DELETE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + + return resp, err +} + +func (app *CommonApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var err error + var payload []byte + log.Info("processGet:path =", app.pathInfo.Path) + + payload, err = transformer.GetAndXlateFromDB(app.pathInfo.Path, app.ygotRoot, dbs) + if err != nil { + log.Error("transformer.transformer.GetAndXlateFromDB failure. error:", err) + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + targetObj, _ := (*app.ygotTarget).(ygot.GoStruct) + if targetObj != nil { + err = ocbinds.Unmarshal(payload, targetObj) + if err != nil { + log.Error("ocbinds.Unmarshal() failed. error:", err) + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + } + + payload, err = generateGetResponsePayload(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device), app.ygotTarget) + if err != nil { + log.Error("generateGetResponsePayload() failed") + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + var dat map[string]interface{} + err = json.Unmarshal(payload, &dat) + + return GetResponse{Payload: payload}, err +} + +func (app *CommonApp) translateCRUDCommon(d *db.DB, opcode int) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + var tblsToWatch []*db.TableSpec + var OrdTblList []string + var moduleNm string + log.Info("translateCRUDCommon:path =", app.pathInfo.Path) + + /* retrieve schema table order for incoming module name request */ + moduleNm, err = transformer.GetModuleNmFromPath(app.pathInfo.Path) + if (err != nil) || (len(moduleNm) == 0) { + log.Error("GetModuleNmFromPath() failed") + return keys, err + } + log.Info("getModuleNmFromPath() returned module name = ", moduleNm) + OrdTblList, err = transformer.GetOrdDBTblList(moduleNm) + if (err != nil) || (len(OrdTblList) == 0) { + log.Error("GetOrdDBTblList() failed") + return keys, err + } + + log.Info("GetOrdDBTblList() returned ordered table list = ", OrdTblList) + app.cmnAppOrdTbllist = OrdTblList + + /* enhance this to handle dependent tables - need CVL to provide list of such tables for a given request */ + for _, tblnm := range OrdTblList { // OrdTblList already has has all tables corresponding to a module + tblsToWatch = append(tblsToWatch, &db.TableSpec{Name: tblnm}) + } + log.Info("Tables to watch", tblsToWatch) + + cmnAppInfo.tablesToWatch = tblsToWatch + + // translate yang to db + result, err := transformer.XlateToDb(app.pathInfo.Path, opcode, d, (*app).ygotRoot, (*app).ygotTarget) + fmt.Println(result) + log.Info("transformer.XlateToDb() returned", result) + + if err != nil { + log.Error(err) + return keys, err + } + if len(result) == 0 { + log.Error("XlatetoDB() returned empty map") + err = errors.New("transformer.XlatetoDB() returned empty map") + return keys, err + } + app.cmnAppTableMap = result + + keys, err = app.generateDbWatchKeys(d, false) + + return keys, err +} + +func (app *CommonApp) processCommon(d *db.DB, opcode int) error { + + var err error + + log.Info("Processing DB operation for ", app.cmnAppTableMap) + switch opcode { + case CREATE: + log.Info("CREATE case") + err = app.cmnAppCRUCommonDbOpn(d, opcode) + case UPDATE: + log.Info("UPDATE case") + err = app.cmnAppCRUCommonDbOpn(d, opcode) + case REPLACE: + log.Info("REPLACE case") + err = app.cmnAppCRUCommonDbOpn(d, opcode) + case DELETE: + log.Info("DELETE case") + err = app.cmnAppDelDbOpn(d, opcode) + } + if err != nil { + log.Info("Returning from processCommon() - fail") + } else { + log.Info("Returning from processCommon() - success") + } + return err +} + +func (app *CommonApp) cmnAppCRUCommonDbOpn(d *db.DB, opcode int) error { + var err error + var cmnAppTs *db.TableSpec + + /* currently ordered by schema table order needs to be discussed */ + for _, tblNm := range app.cmnAppOrdTbllist { + log.Info("In Yang to DB map returned from transformer looking for table = ", tblNm) + if tblVal, ok := app.cmnAppTableMap[tblNm]; ok { + cmnAppTs = &db.TableSpec{Name: tblNm} + log.Info("Found table entry in yang to DB map") + for tblKey, tblRw := range tblVal { + log.Info("Processing Table key and row ", tblKey, tblRw) + existingEntry, _ := d.GetEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}) + switch opcode { + case CREATE: + if existingEntry.IsPopulated() { + log.Info("Entry already exists hence return.") + return tlerr.AlreadyExists("Entry %s already exists", tblKey) + } else { + err = d.CreateEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}, tblRw) + if err != nil { + log.Error("CREATE case - d.CreateEntry() failure") + return err + } + } + case UPDATE: + if existingEntry.IsPopulated() { + log.Info("Entry already exists hence modifying it.") + /* Handle leaf-list merge + A leaf-list field in redis has "@" suffix as per swsssdk convention. + */ + resTblRw := db.Value{Field: map[string]string{}} + resTblRw = processLeafList(existingEntry, tblRw, UPDATE, d, tblNm, tblKey) + err = d.ModEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}, resTblRw) + if err != nil { + log.Error("UPDATE case - d.ModEntry() failure") + return err + } + } else { + // workaround to patch operation from CLI + log.Info("Create(pathc) an entry.") + err = d.CreateEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}, tblRw) + if err != nil { + log.Error("UPDATE case - d.CreateEntry() failure") + return err + } + } + case REPLACE: + if existingEntry.IsPopulated() { + log.Info("Entry already exists hence execute db.SetEntry") + err := d.SetEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}, tblRw) + if err != nil { + log.Error("REPLACE case - d.SetEntry() failure") + return err + } + } else { + log.Info("Entry doesn't exist hence create it.") + err = d.CreateEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}, tblRw) + if err != nil { + log.Error("REPLACE case - d.CreateEntry() failure") + return err + } + } + } + } + } + } + return err +} + +func (app *CommonApp) cmnAppDelDbOpn(d *db.DB, opcode int) error { + var err error + var cmnAppTs, dbTblSpec *db.TableSpec + + /* needs enhancements from CVL to give table dependencies, and grouping of related tables only + if such a case where the sonic yang has unrelated tables */ + for tblidx, tblNm := range app.cmnAppOrdTbllist { + log.Info("In Yang to DB map returned from transformer looking for table = ", tblNm) + if tblVal, ok := app.cmnAppTableMap[tblNm]; ok { + cmnAppTs = &db.TableSpec{Name: tblNm} + log.Info("Found table entry in yang to DB map") + if len(tblVal) == 0 { + log.Info("DELETE case - No table instances/rows found hence delete entire table = ", tblNm) + for idx := len(app.cmnAppOrdTbllist)-1; idx >= tblidx+1; idx-- { + log.Info("Since parent table is to be deleted, first deleting child table = ", app.cmnAppOrdTbllist[idx]) + dbTblSpec = &db.TableSpec{Name: app.cmnAppOrdTbllist[idx]} + err = d.DeleteTable(dbTblSpec) + if err != nil { + log.Warning("DELETE case - d.DeleteTable() failure for Table = ", app.cmnAppOrdTbllist[idx]) + return err + } + } + err = d.DeleteTable(cmnAppTs) + if err != nil { + log.Warning("DELETE case - d.DeleteTable() failure for Table = ", tblNm) + return err + } + log.Info("DELETE case - Deleted entire table = ", tblNm) + log.Info("Done processing all tables.") + break + + } + + for tblKey, tblRw := range tblVal { + if len(tblRw.Field) == 0 { + log.Info("DELETE case - no fields/cols to delete hence delete the entire row.") + log.Info("First, delete child table instances that correspond to parent table instance to be deleted = ", tblKey) + for idx := len(app.cmnAppOrdTbllist)-1; idx >= tblidx+1; idx-- { + dbTblSpec = &db.TableSpec{Name: app.cmnAppOrdTbllist[idx]} + keyPattern := tblKey + "|*" + log.Info("Key pattern to be matched for deletion = ", keyPattern) + err = d.DeleteKeys(dbTblSpec, db.Key{Comp: []string{keyPattern}}) + if err != nil { + log.Warning("DELETE case - d.DeleteTable() failure for Table = ", app.cmnAppOrdTbllist[idx]) + return err + } + log.Info("Deleted keys matching parent table key pattern for child table = ", app.cmnAppOrdTbllist[idx]) + + } + err = d.DeleteEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}) + if err != nil { + log.Warning("DELETE case - d.DeleteEntry() failure") + return err + } + log.Info("Finally deleted the parent table row with key = ", tblKey) + } else { + log.Info("DELETE case - fields/cols to delete hence delete only those fields.") + existingEntry, _ := d.GetEntry(cmnAppTs, db.Key{Comp: []string{tblKey}}) + if !existingEntry.IsPopulated() { + log.Info("Table Entry from which the fields are to be deleted does not exist") + return err + } + /*handle leaf-list merge*/ + resTblRw := processLeafList(existingEntry, tblRw, DELETE, d, tblNm, tblKey) + err := d.DeleteEntryFields(cmnAppTs, db.Key{Comp: []string{tblKey}}, resTblRw) + if err != nil { + log.Error("DELETE case - d.DeleteEntryFields() failure") + return err + } + } + + } + } + } /* end of ordered table list for loop */ + return err +} + +func (app *CommonApp) generateDbWatchKeys(d *db.DB, isDeleteOp bool) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + + return keys, err +} + +func processLeafList(existingEntry db.Value, tblRw db.Value, opcode int, d *db.DB, tblNm string, tblKey string) db.Value { + log.Info("process leaf-list Fields in table row.") + dbTblSpec := &db.TableSpec{Name: tblNm} + mergeTblRw := db.Value{Field: map[string]string{}} + for field, value := range tblRw.Field { + if strings.HasSuffix(field, "@") { + exstLst := existingEntry.GetList(field) + if len(exstLst) != 0 { + valueLst := strings.Split(value, ",") + for _, item := range valueLst { + if !contains(exstLst, item) { + if opcode == UPDATE { + exstLst = append(exstLst, item) + } + } else { + if opcode == DELETE { + exstLst = removeElement(exstLst, item) + } + + } + } + log.Infof("For field %v value after merge %v", field, exstLst) + if opcode == DELETE { + mergeTblRw.SetList(field, exstLst) + delete(tblRw.Field, field) + } + } + tblRw.SetList(field, exstLst) + } + } + /* delete specific item from leaf-list */ + if opcode == DELETE { + if mergeTblRw.Field == nil { + return tblRw + } + err := d.ModEntry(dbTblSpec, db.Key{Comp: []string{tblKey}}, mergeTblRw) + if err != nil { + log.Warning("DELETE case(merge leaf-list) - d.ModEntry() failure") + } + } + log.Infof("Returning Table Row %v", tblRw) + return tblRw +} + diff --git a/src/translib/db/db.go b/src/translib/db/db.go index 804718dc8d..451bc92383 100644 --- a/src/translib/db/db.go +++ b/src/translib/db/db.go @@ -1,7 +1,21 @@ -/* -Copyright 2019 Broadcom. All rights reserved. -The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -*/ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package db implements a wrapper over the go-redis/redis. diff --git a/src/translib/db/db_test.go b/src/translib/db/db_test.go index 7f0030537b..edf05b281c 100644 --- a/src/translib/db/db_test.go +++ b/src/translib/db/db_test.go @@ -1,7 +1,21 @@ -/* -Copyright 2019 Broadcom. All rights reserved. -The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -*/ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package db diff --git a/src/translib/db/map.go b/src/translib/db/map.go index 63551b31c2..9010cfb15f 100644 --- a/src/translib/db/map.go +++ b/src/translib/db/map.go @@ -1,7 +1,21 @@ -/* -Copyright 2019 Broadcom. All rights reserved. -The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -*/ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package db implements a wrapper over the go-redis/redis. diff --git a/src/translib/db/subscribe.go b/src/translib/db/subscribe.go index 145f6fee12..65ea09811e 100644 --- a/src/translib/db/subscribe.go +++ b/src/translib/db/subscribe.go @@ -1,7 +1,21 @@ -/* -Copyright 2019 Broadcom. All rights reserved. -The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -*/ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package db implements a wrapper over the go-redis/redis. diff --git a/src/translib/db/test/arloIssue29.go b/src/translib/db/test/arloIssue29.go index 5299f4bf82..d42613d664 100644 --- a/src/translib/db/test/arloIssue29.go +++ b/src/translib/db/test/arloIssue29.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + /* UT for https://github.com/project-arlo/sonic-mgmt-framework/issues/29 diff --git a/src/translib/db/test/testdb.go b/src/translib/db/test/testdb.go index 0756bac16e..c28b368e3a 100644 --- a/src/translib/db/test/testdb.go +++ b/src/translib/db/test/testdb.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/translib/db/test/testmap.go b/src/translib/db/test/testmap.go index 7a8c76f303..728b8842ac 100644 --- a/src/translib/db/test/testmap.go +++ b/src/translib/db/test/testmap.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/translib/db/test/testsubscribe.go b/src/translib/db/test/testsubscribe.go index 751fb3c75c..298bf3443a 100644 --- a/src/translib/db/test/testsubscribe.go +++ b/src/translib/db/test/testsubscribe.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/translib/nonyang_app.go.demo b/src/translib/nonyang_app.go.demo index 3e3a5f686e..17553ce328 100644 --- a/src/translib/nonyang_app.go.demo +++ b/src/translib/nonyang_app.go.demo @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package translib diff --git a/src/translib/ocbinds/oc.go b/src/translib/ocbinds/oc.go index 91b532c907..cb09edf1e3 100644 --- a/src/translib/ocbinds/oc.go +++ b/src/translib/ocbinds/oc.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package ocbinds -//go:generate sh -c "/usr/local/go/bin/go run $BUILD_GOPATH/src/github.com/openconfig/ygot/generator/generator.go -generate_fakeroot -output_file ocbinds.go -package_name ocbinds -generate_fakeroot -fakeroot_name=device -compress_paths=false -exclude_modules ietf-interfaces -path . $(find ../../../models/yang -name '*.yang' | sort)" +//go:generate sh -c "/usr/local/go/bin/go run $BUILD_GOPATH/src/github.com/openconfig/ygot/generator/generator.go -generate_fakeroot -output_file ocbinds.go -package_name ocbinds -generate_fakeroot -fakeroot_name=device -compress_paths=false -exclude_modules ietf-interfaces -path . $(find ../../../models/yang ../../../src/cvl/schema -name '*.yang' | sort)" diff --git a/src/translib/path_utils.go b/src/translib/path_utils.go index 8d12ecdfcd..06b64ac784 100644 --- a/src/translib/path_utils.go +++ b/src/translib/path_utils.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package translib @@ -13,7 +25,6 @@ import ( "reflect" "strconv" "strings" - "translib/ocbinds" log "github.com/golang/glog" @@ -177,3 +188,5 @@ func getObjectFieldName(targetUri *string, deviceObj *ocbinds.Device, ygotTarget } return "", errors.New("Target object not found") } + + diff --git a/src/translib/path_utils_test.go b/src/translib/path_utils_test.go index 5379c00afa..e8d9c19b63 100644 --- a/src/translib/path_utils_test.go +++ b/src/translib/path_utils_test.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package translib import ( diff --git a/src/translib/subscribe.go b/src/translib/subscribe.go index d48c9ac12d..a22e04c6e1 100644 --- a/src/translib/subscribe.go +++ b/src/translib/subscribe.go @@ -1,8 +1,21 @@ -/////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom Inc. -// -/////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package translib defines the functions to be used by the subscribe diff --git a/src/translib/test/acl-sonic/01_create_MyACL1_1Rule.json b/src/translib/test/acl-sonic/01_create_MyACL1_1Rule.json new file mode 100644 index 0000000000..c59e8e3269 --- /dev/null +++ b/src/translib/test/acl-sonic/01_create_MyACL1_1Rule.json @@ -0,0 +1,22 @@ +{ + "ACL_TABLE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "policy_desc": "Description for MyACL1" + } + ], + "ACL_RULE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "rulename": "RULE_1", + "PRIORITY": 65534, + "RULE_DESCRIPTION": "Description for MyACL1", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4", + "IP_PROTOCOL": 6, + "SRC_IP": "10.1.1.1/32", + "DST_IP": "20.2.2.2/32" + } + ] + +} diff --git a/src/translib/test/acl-sonic/01_create_MyACL1_1Rule_cmd.txt b/src/translib/test/acl-sonic/01_create_MyACL1_1Rule_cmd.txt new file mode 100644 index 0000000000..1153b1beb3 --- /dev/null +++ b/src/translib/test/acl-sonic/01_create_MyACL1_1Rule_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "create" -u "/sonic-acl:sonic-acl" -p "./acl-sonic/01_create_MyACL1_1Rule.json" -logtostderr diff --git a/src/translib/test/acl-sonic/01_delete_MyACL1_1Rule_cmd.txt b/src/translib/test/acl-sonic/01_delete_MyACL1_1Rule_cmd.txt new file mode 100644 index 0000000000..1813ec6ed0 --- /dev/null +++ b/src/translib/test/acl-sonic/01_delete_MyACL1_1Rule_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "delete" -u "/sonic-acl:sonic-acl/ACL_RULE[aclname=MyACL1_ACL_IPV4][rulename=RULE_1]" -logtostderr diff --git a/src/translib/test/acl-sonic/01_delete_MyACL1_cmd.txt b/src/translib/test/acl-sonic/01_delete_MyACL1_cmd.txt new file mode 100644 index 0000000000..59153ec876 --- /dev/null +++ b/src/translib/test/acl-sonic/01_delete_MyACL1_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "delete" -u "/sonic-acl:sonic-acl/ACL_TABLE[aclname=MyACL1_ACL_IPV4]" -logtostderr diff --git a/src/translib/test/acl-sonic/01_delete_all_Rules_cmd.txt b/src/translib/test/acl-sonic/01_delete_all_Rules_cmd.txt new file mode 100644 index 0000000000..120c24ece8 --- /dev/null +++ b/src/translib/test/acl-sonic/01_delete_all_Rules_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "delete" -u "/sonic-acl:sonic-acl" -logtostderr diff --git a/src/translib/test/acl-sonic/01_get_MyACL1_1Rule_cmd.txt b/src/translib/test/acl-sonic/01_get_MyACL1_1Rule_cmd.txt new file mode 100644 index 0000000000..84ca9596a6 --- /dev/null +++ b/src/translib/test/acl-sonic/01_get_MyACL1_1Rule_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "get" -u "/sonic-acl:sonic-acl/ACL_RULE[aclname=MyACL1_ACL_IPV4][rulename=RULE_1]" -logtostderr diff --git a/src/translib/test/acl-sonic/01_get_MyACL1_command.txt b/src/translib/test/acl-sonic/01_get_MyACL1_command.txt new file mode 100644 index 0000000000..c5bcaa74b5 --- /dev/null +++ b/src/translib/test/acl-sonic/01_get_MyACL1_command.txt @@ -0,0 +1,4 @@ +./translibtest -o "get" -u "/sonic-acl:sonic-acl/ACL_TABLE[aclname=MyACL1_ACL_IPV4]" -logtostderr + + + diff --git a/src/translib/test/acl-sonic/01_replace_MyACL1.json b/src/translib/test/acl-sonic/01_replace_MyACL1.json new file mode 100644 index 0000000000..0767ab34e6 --- /dev/null +++ b/src/translib/test/acl-sonic/01_replace_MyACL1.json @@ -0,0 +1,22 @@ +{ + "ACL_TABLE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "policy_desc": "Updated MyACL1", + "type": "L3" + } + ], + "ACL_RULE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "rulename": "RULE_1", + "PRIORITY": 65536, + "RULE_DESCRIPTION": "Description for MyACL1 Rule1", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4", + "IP_PROTOCOL": 6, + "SRC_IP": "10.1.1.1/32", + "DST_IP": "any" + } + ] +} diff --git a/src/translib/test/acl-sonic/01_replace_MyACL1_cmd.txt b/src/translib/test/acl-sonic/01_replace_MyACL1_cmd.txt new file mode 100644 index 0000000000..cff3a546b9 --- /dev/null +++ b/src/translib/test/acl-sonic/01_replace_MyACL1_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "replace" -u "/sonic-acl:sonic-acl" -p "./acl-sonic/01_replace_MyACL1.json" -logtostderr diff --git a/src/translib/test/acl-sonic/01_update_MyACL1.json b/src/translib/test/acl-sonic/01_update_MyACL1.json new file mode 100644 index 0000000000..5a4ec83182 --- /dev/null +++ b/src/translib/test/acl-sonic/01_update_MyACL1.json @@ -0,0 +1,33 @@ +{ + "ACL_TABLE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "policy_desc": "Updated MyACL1", + "type": "L3" + } + ], + "ACL_RULE": [ + { + "aclname": "MyACL1_ACL_IPV4", + "rulename": "RULE_1", + "PRIORITY": 65534, + "RULE_DESCRIPTION": "Description for MyACL1 Rule1", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4", + "IP_PROTOCOL": 6, + "SRC_IP": "11.1.1.1/32", + "DST_IP": "22.2.2.2/32" + }, + { + "aclname": "MyACL1_ACL_IPV4", + "rulename": "RULE_2", + "PRIORITY": 65536, + "RULE_DESCRIPTION": "Description for MyACL1 Rule2", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4", + "IP_PROTOCOL": 6, + "SRC_IP": "any", + "DST_IP": "any" + } + ] +} diff --git a/src/translib/test/acl-sonic/01_update_MyACL1_cmd.txt b/src/translib/test/acl-sonic/01_update_MyACL1_cmd.txt new file mode 100644 index 0000000000..e56bad6729 --- /dev/null +++ b/src/translib/test/acl-sonic/01_update_MyACL1_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "update" -u "/sonic-acl:sonic-acl" -p "./acl-sonic/01_update_MyACL1.json" -logtostderr diff --git a/src/translib/test/acl-sonic/02_create_MyACL3_MyACL4.json b/src/translib/test/acl-sonic/02_create_MyACL3_MyACL4.json new file mode 100644 index 0000000000..fbc6211df4 --- /dev/null +++ b/src/translib/test/acl-sonic/02_create_MyACL3_MyACL4.json @@ -0,0 +1,35 @@ +{ + "ACL_TABLE": [ + { + "aclname": "MyACL3_ACL_IPV4", + "policy_desc": "Description for MyACL3", + "stage": "EGRESS", + "type": "L3", + "ports": ["Ethernet0"] + }, + { + "aclname": "MyACL4_ACL_IPV4", + "policy_desc": "Description for MyACL4", + "stage": "EGRESS", + "type": "L3", + "ports": ["Ethernet4","Ethernet8"] + } + ], + "ACL_RULE": [ + { + "aclname": "MyACL3_ACL_IPV4", + "rulename": "RULE_1", + "PRIORITY": 65536, + "RULE_DESCRIPTION": "MyACL3_ACL_IPV4 rule description", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4", + "IP_PROTOCOL": 6, + "SRC_IP": "10.1.1.1/32", + "DST_IP": "20.2.2.2/32", + "L4_SRC_PORT_RANGE": "13000-14000", + "L4_DST_PORT_RANGE": "9000-12000", + "DSCP": 2 + } + ] + +} diff --git a/src/translib/test/acl-sonic/02_get_all_Rules_cmd.txt b/src/translib/test/acl-sonic/02_get_all_Rules_cmd.txt new file mode 100644 index 0000000000..c46ec3db33 --- /dev/null +++ b/src/translib/test/acl-sonic/02_get_all_Rules_cmd.txt @@ -0,0 +1 @@ +./translibtest -o "get" -u "/sonic-acl:sonic-acl" -logtostderr diff --git a/src/translib/test/translibtest.go b/src/translib/test/translibtest.go index 0d87d472d4..24450736b3 100644 --- a/src/translib/test/translibtest.go +++ b/src/translib/test/translibtest.go @@ -1,3 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package main import ( diff --git a/src/translib/tlerr/app_errors.go b/src/translib/tlerr/app_errors.go index dec7b03184..d695954326 100644 --- a/src/translib/tlerr/app_errors.go +++ b/src/translib/tlerr/app_errors.go @@ -1,9 +1,21 @@ -/////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom. All rights reserved. -// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -// -/////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package tlerr diff --git a/src/translib/tlerr/tlerr.go b/src/translib/tlerr/tlerr.go index a0c799b386..8ff1254301 100644 --- a/src/translib/tlerr/tlerr.go +++ b/src/translib/tlerr/tlerr.go @@ -1,7 +1,21 @@ -/* -Copyright 2019 Broadcom. All rights reserved. -The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -*/ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package tlerr defines the errors of the translib library. @@ -17,15 +31,15 @@ Hence, it cannot occur here. package tlerr import ( -// "fmt" - "golang.org/x/text/message" - "golang.org/x/text/language" + // "fmt" "cvl" -// "errors" -// "strings" + "golang.org/x/text/language" + "golang.org/x/text/message" + // "errors" + // "strings" ) -var p * message.Printer +var p *message.Printer func init() { p = message.NewPrinter(language.English) @@ -48,7 +62,7 @@ func (e TranslibDBNotInit) Error() string { } type TranslibRedisClientEntryNotExist struct { - Entry string + Entry string } func (e TranslibRedisClientEntryNotExist) Error() string { @@ -56,8 +70,8 @@ func (e TranslibRedisClientEntryNotExist) Error() string { } type TranslibCVLFailure struct { - Code int - CVLErrorInfo cvl.CVLErrorInfo + Code int + CVLErrorInfo cvl.CVLErrorInfo } func (e TranslibCVLFailure) Error() string { @@ -80,10 +94,10 @@ func (e TranslibDBSubscribeFail) Error() string { } type TranslibSyntaxValidationError struct { - StatusCode int // status code - ErrorStr error // error message + StatusCode int // status code + ErrorStr error // error message } func (e TranslibSyntaxValidationError) Error() string { - return p.Sprintf("%s", e.ErrorStr) + return p.Sprintf("%s", e.ErrorStr) } diff --git a/src/translib/transformer/transformer.go b/src/translib/transformer/transformer.go new file mode 100644 index 0000000000..e6649cff2c --- /dev/null +++ b/src/translib/transformer/transformer.go @@ -0,0 +1,139 @@ +package transformer + +import ( + "fmt" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" + "os" + "strings" + "bufio" + "path/filepath" + "io/ioutil" +) + +const YangPath = "/usr/models/yang/" // OpenConfig-*.yang and sonic yang models path + +var entries = map[string]*yang.Entry{} + +//Interface for xfmr methods +type xfmrInterface interface { + tableXfmr(s *ygot.GoStruct, t *interface{}) (string, error) + keyXfmr(s *ygot.GoStruct, t *interface{}) (string, error) + fieldXfmr(s *ygot.GoStruct, t *interface{}) (string, error) +} + +func reportIfError(errs []error) { + if len(errs) > 0 { + for _, err := range errs { + fmt.Fprintln(os.Stderr, err) + } + } +} + +func getOcModelsList () ([] string) { + var fileList [] string + file, err := os.Open(YangPath + "models_list") + if err != nil { + return fileList + } + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + fileEntry := scanner.Text() + if strings.HasPrefix(fileEntry, "#") != true { + _, err := os.Stat(YangPath + fileEntry) + if err != nil { + continue + } + fileList = append(fileList, fileEntry) + } + } + return fileList +} + +func getDefaultModelsList () ([] string) { + var files []string + fileInfo, err := ioutil.ReadDir(YangPath) + if err != nil { + return files + } + + for _, file := range fileInfo { + if strings.HasPrefix(file.Name(), "sonic-") && !strings.HasSuffix(file.Name(), "-dev.yang") && filepath.Ext(file.Name()) == ".yang" { + files = append(files, file.Name()) + } + } + return files +} + +func init() { + yangFiles := []string{} + ocList := getOcModelsList() + yangFiles = getDefaultModelsList() + yangFiles = append(yangFiles, ocList...) + fmt.Println("Yang model List:", yangFiles) + err := loadYangModules(yangFiles...) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } +} + +func loadYangModules(files ...string) error { + + var err error + + paths := []string{YangPath} + + for _, path := range paths { + expanded, err := yang.PathsWithModules(path) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + yang.AddPath(expanded...) + } + + ms := yang.NewModules() + + for _, name := range files { + if err := ms.Read(name); err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + } + + // Process the Yang files + reportIfError(ms.Process()) + + // Keep track of the top level modules we read in. + // Those are the only modules we want to print below. + mods := map[string]*yang.Module{} + var names []string + + for _, m := range ms.Modules { + if mods[m.Name] == nil { + mods[m.Name] = m + names = append(names, m.Name) + } + } + + sonic_entries := make([]*yang.Entry, len(names)) + oc_entries := make(map[string]*yang.Entry) + annot_entries := make([]*yang.Entry, len(names)) + + for _, n := range names { + if strings.Contains(n, "annot") { + annot_entries = append(annot_entries, yang.ToEntry(mods[n])) + } else if strings.Contains(n, "sonic") { + sonic_entries = append(sonic_entries, yang.ToEntry(mods[n])) + } else if oc_entries[n] == nil { + oc_entries[n] = yang.ToEntry(mods[n]) + } + } + + dbMapBuild(sonic_entries) + annotToDbMapBuild(annot_entries) + yangToDbMapBuild(oc_entries) + + return err +} diff --git a/src/translib/transformer/xfmr_acl.go b/src/translib/transformer/xfmr_acl.go new file mode 100644 index 0000000000..3f07baa4a0 --- /dev/null +++ b/src/translib/transformer/xfmr_acl.go @@ -0,0 +1,927 @@ +package transformer + +import ( + "bytes" + "errors" + "fmt" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "reflect" + "strconv" + "strings" + "translib/db" + "translib/ocbinds" + "translib/tlerr" +) + +func init() { + XlateFuncBind("YangToDb_acl_set_name_xfmr", YangToDb_acl_set_name_xfmr) + XlateFuncBind("DbToYang_acl_set_name_xfmr", DbToYang_acl_set_name_xfmr) + XlateFuncBind("YangToDb_acl_type_field_xfmr", YangToDb_acl_type_field_xfmr) + XlateFuncBind("DbToYang_acl_type_field_xfmr", DbToYang_acl_type_field_xfmr) + XlateFuncBind("YangToDb_acl_entry_key_xfmr", YangToDb_acl_entry_key_xfmr) + XlateFuncBind("DbToYang_acl_entry_key_xfmr", DbToYang_acl_entry_key_xfmr) + XlateFuncBind("YangToDb_acl_entry_sequenceid_xfmr", YangToDb_acl_entry_sequenceid_xfmr) + XlateFuncBind("DbToYang_acl_entry_sequenceid_xfmr", DbToYang_acl_entry_sequenceid_xfmr) + XlateFuncBind("YangToDb_acl_l2_ethertype_xfmr", YangToDb_acl_l2_ethertype_xfmr) + XlateFuncBind("DbToYang_acl_l2_ethertype_xfmr", DbToYang_acl_l2_ethertype_xfmr) + XlateFuncBind("YangToDb_acl_ip_protocol_xfmr", YangToDb_acl_ip_protocol_xfmr) + XlateFuncBind("DbToYang_acl_ip_protocol_xfmr", DbToYang_acl_ip_protocol_xfmr) + XlateFuncBind("YangToDb_acl_source_port_xfmr", YangToDb_acl_source_port_xfmr) + XlateFuncBind("DbToYang_acl_source_port_xfmr", DbToYang_acl_source_port_xfmr) + XlateFuncBind("YangToDb_acl_destination_port_xfmr", YangToDb_acl_destination_port_xfmr) + XlateFuncBind("DbToYang_acl_destination_port_xfmr", DbToYang_acl_destination_port_xfmr) + XlateFuncBind("YangToDb_acl_tcp_flags_xfmr", YangToDb_acl_tcp_flags_xfmr) + XlateFuncBind("DbToYang_acl_tcp_flags_xfmr", DbToYang_acl_tcp_flags_xfmr) + XlateFuncBind("YangToDb_acl_port_bindings_xfmr", YangToDb_acl_port_bindings_xfmr) + XlateFuncBind("DbToYang_acl_port_bindings_xfmr", DbToYang_acl_port_bindings_xfmr) + XlateFuncBind("YangToDb_acl_forwarding_action_xfmr", YangToDb_acl_forwarding_action_xfmr) + XlateFuncBind("DbToYang_acl_forwarding_action_xfmr", DbToYang_acl_forwarding_action_xfmr) + XlateFuncBind("validate_ipv4", validate_ipv4) + XlateFuncBind("validate_ipv6", validate_ipv6) + XlateFuncBind("acl_post_xfmr", acl_post_xfmr) +} + +const ( + ACL_TABLE = "ACL_TABLE" + RULE_TABLE = "ACL_RULE" + SONIC_ACL_TYPE_IPV4 = "L3" + SONIC_ACL_TYPE_L2 = "L2" + SONIC_ACL_TYPE_IPV6 = "L3V6" + OPENCONFIG_ACL_TYPE_IPV4 = "ACL_IPV4" + OPENCONFIG_ACL_TYPE_IPV6 = "ACL_IPV6" + OPENCONFIG_ACL_TYPE_L2 = "ACL_L2" + ACL_TYPE = "type" + MIN_PRIORITY = 1 + MAX_PRIORITY = 65535 +) + +/* E_OpenconfigAcl_ACL_TYPE */ +var ACL_TYPE_MAP = map[string]string{ + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4), 10): SONIC_ACL_TYPE_IPV4, + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6), 10): SONIC_ACL_TYPE_IPV6, + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2), 10): SONIC_ACL_TYPE_L2, +} + +/* E_OpenconfigAcl_FORWARDING_ACTION */ +var ACL_FORWARDING_ACTION_MAP = map[string]string{ + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_FORWARDING_ACTION_ACCEPT), 10): "FORWARD", + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_FORWARDING_ACTION_DROP), 10): "DROP", + strconv.FormatInt(int64(ocbinds.OpenconfigAcl_FORWARDING_ACTION_REJECT), 10): "REDIRECT", +} + +/* E_OpenconfigPacketMatchTypes_IP_PROTOCOL */ +var IP_PROTOCOL_MAP = map[string]string{ + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_ICMP), 10): "1", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_IGMP), 10): "2", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_TCP), 10): "6", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_UDP), 10): "17", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_RSVP), 10): "46", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_GRE), 10): "47", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_AUTH), 10): "51", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_PIM), 10): "103", + strconv.FormatInt(int64(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_L2TP), 10): "115", +} + +var ETHERTYPE_MAP = map[ocbinds.E_OpenconfigPacketMatchTypes_ETHERTYPE]uint32{ + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_LLDP: 0x88CC, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_VLAN: 0x8100, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_ROCE: 0x8915, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_ARP: 0x0806, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV4: 0x0800, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV6: 0x86DD, + ocbinds.OpenconfigPacketMatchTypes_ETHERTYPE_ETHERTYPE_MPLS: 0x8847, +} + +func getAclRoot(s *ygot.GoStruct) *ocbinds.OpenconfigAcl_Acl { + deviceObj := (*s).(*ocbinds.Device) + return deviceObj.Acl +} + +func getAclTypeOCEnumFromName(val string) (ocbinds.E_OpenconfigAcl_ACL_TYPE, error) { + switch val { + case "ACL_IPV4", "openconfig-acl:ACL_IPV4": + return ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4, nil + case "ACL_IPV6", "openconfig-acl:ACL_IPV6": + return ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6, nil + case "ACL_L2", "openconfig-acl:ACL_L2": + return ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2, nil + default: + return ocbinds.OpenconfigAcl_ACL_TYPE_UNSET, + tlerr.NotSupported("ACL Type '%s' not supported", val) + } +} +func getAclKeyStrFromOCKey(aclname string, acltype ocbinds.E_OpenconfigAcl_ACL_TYPE) string { + aclN := strings.Replace(strings.Replace(aclname, " ", "_", -1), "-", "_", -1) + aclT := acltype.ΛMap()["E_OpenconfigAcl_ACL_TYPE"][int64(acltype)].Name + return aclN + "_" + aclT +} + +func getOCAclKeysFromStrDBKey(aclKey string) (string, ocbinds.E_OpenconfigAcl_ACL_TYPE) { + var aclOrigName string + var aclOrigType ocbinds.E_OpenconfigAcl_ACL_TYPE + + if strings.Contains(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV4) { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV4, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 + } else if strings.Contains(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV6) { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV6, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 + } else if strings.Contains(aclKey, "_"+OPENCONFIG_ACL_TYPE_L2) { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_L2, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 + } + + return aclOrigName, aclOrigType +} + +func getTransportConfigTcpFlags(tcpFlags string) []ocbinds.E_OpenconfigPacketMatchTypes_TCP_FLAGS { + var flags []ocbinds.E_OpenconfigPacketMatchTypes_TCP_FLAGS + if len(tcpFlags) > 0 { + flagStr := strings.Split(tcpFlags, "/")[0] + flagNumber, _ := strconv.ParseUint(strings.Replace(flagStr, "0x", "", -1), 16, 32) + for i := 0; i < 8; i++ { + mask := 1 << uint(i) + if (int(flagNumber) & mask) > 0 { + switch int(flagNumber) & mask { + case 0x01: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_FIN) + case 0x02: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_SYN) + case 0x04: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_RST) + case 0x08: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_PSH) + case 0x10: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ACK) + case 0x20: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_URG) + case 0x40: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ECE) + case 0x80: + flags = append(flags, ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_CWR) + default: + } + } + } + } + return flags +} + +func getL2EtherType(etherType uint64) interface{} { + for k, v := range ETHERTYPE_MAP { + if uint32(etherType) == v { + return k + } + } + return uint16(etherType) +} + +//////////////////////////////////////////// +// Validate callpoints +//////////////////////////////////////////// +var validate_ipv4 ValidateCallpoint = func(inParams XfmrParams) (bool) { + if strings.Contains(inParams.key, "ACL_IPV4") { + return true + } + return false +} +var validate_ipv6 ValidateCallpoint = func(inParams XfmrParams) (bool) { + if strings.Contains(inParams.key, "ACL_IPV6") { + return true + } + return false +} + +//////////////////////////////////////////// +// Post Transformer +//////////////////////////////////////////// +var acl_post_xfmr PostXfmrFunc = func(inParams XfmrParams) (map[string]map[string]db.Value, error) { + log.Info("In Post transformer") + //TODO: check if a default ACL Rule exists, else create one and update the resultMap with default rule + // Return will be the updated result map + return (*inParams.dbDataMap)[inParams.curDb], nil +} + +//////////////////////////////////////////// +// Bi-directoonal overloaded methods +//////////////////////////////////////////// +var YangToDb_acl_forwarding_action_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + if inParams.param == nil { + res_map["PACKET_ACTION"] = "" + return res_map, err + } + action, _ := inParams.param.(ocbinds.E_OpenconfigAcl_FORWARDING_ACTION) + log.Info("YangToDb_acl_forwarding_action_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " forwarding_action: ", action) + res_map["PACKET_ACTION"] = findInMap(ACL_FORWARDING_ACTION_MAP, strconv.FormatInt(int64(action), 10)) + return res_map, err +} +var DbToYang_acl_forwarding_action_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_forwarding_action_xfmr", data, inParams.ygRoot) + oc_action := findInMap(ACL_FORWARDING_ACTION_MAP, data[RULE_TABLE][inParams.key].Field["PACKET_ACTION"]) + n, err := strconv.ParseInt(oc_action, 10, 64) + result["forwarding-action"] = ocbinds.E_OpenconfigAcl_FORWARDING_ACTION(n).ΛMap()["E_OpenconfigAcl_FORWARDING_ACTION"][n].Name + return result, err +} + +var YangToDb_acl_type_field_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + if inParams.param == nil { + res_map[ACL_TYPE] = "" + return res_map, err + } + + acltype, _ := inParams.param.(ocbinds.E_OpenconfigAcl_ACL_TYPE) + log.Info("YangToDb_acl_type_field_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " acltype: ", acltype) + res_map[ACL_TYPE] = findInMap(ACL_TYPE_MAP, strconv.FormatInt(int64(acltype), 10)) + return res_map, err +} +var DbToYang_acl_type_field_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_type_field_xfmr", data, inParams.ygRoot) + oc_acltype := findInMap(ACL_TYPE_MAP, data[ACL_TABLE][inParams.key].Field[ACL_TYPE]) + n, err := strconv.ParseInt(oc_acltype, 10, 64) + result[ACL_TYPE] = ocbinds.E_OpenconfigAcl_ACL_TYPE(n).ΛMap()["E_OpenconfigAcl_ACL_TYPE"][n].Name + return result, err +} + +var YangToDb_acl_set_name_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + log.Info("YangToDb_acl_set_name_xfmr: ") + /*no-op since there is no redis table field to be filled corresponding to name attribute since its part of key */ + return res_map, err +} + +var DbToYang_acl_set_name_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + res_map := make(map[string]interface{}) + var err error + log.Info("DbToYang_acl_set_name_xfmr: ", inParams.key) + /*name attribute corresponds to key in redis table*/ + aclName, _ := getOCAclKeysFromStrDBKey(inParams.key) + res_map["name"] = aclName + log.Info("acl-set/config/name ", res_map) + return res_map, err +} + +var YangToDb_acl_entry_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + var oc_aclType ocbinds.E_OpenconfigAcl_ACL_TYPE + log.Info("YangToDb_acl_entry_key_xfmr: ", inParams.ygRoot, inParams.uri) + pathInfo := NewPathInfo(inParams.uri) + + if len(pathInfo.Vars) < 3 { + err = errors.New("Invalid xpath, key attributes not found") + return entry_key, err + } + + oc_aclType, err = getAclTypeOCEnumFromName(pathInfo.Var("type")) + if err != nil { + err = errors.New("OC Acl type name to OC Acl Enum failed") + return entry_key, err + } + + aclkey := getAclKeyStrFromOCKey(pathInfo.Var("name"), oc_aclType) + var rulekey string + if strings.Contains(pathInfo.Template, "/acl-entry{sequence-id}") { + rulekey = "RULE_" + pathInfo.Var("sequence-id") + } + entry_key = aclkey + "|" + rulekey + + log.Info("YangToDb_acl_entry_key_xfmr - entry_key : ", entry_key) + + return entry_key, err +} + +var DbToYang_acl_entry_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + entry_key := inParams.key + log.Info("DbToYang_acl_entry_key_xfmr: ", entry_key) + + key := strings.Split(entry_key, "|") + if len(key) < 2 { + err = errors.New("Invalid key for acl entries.") + log.Info("Invalid Keys for acl enmtries", entry_key) + return rmap, err + } + + dbAclRule := key[1] + seqId := strings.Replace(dbAclRule, "RULE_", "", 1) + rmap["sequence-id"], _ = strconv.ParseFloat(seqId, 64) + return rmap, err +} + +var YangToDb_acl_entry_sequenceid_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + log.Info("YangToDb_acl_entry_sequenceid_xfmr: ") + /*no-op since there is no redis table field to be filled corresponding to sequenec-id attribute since its part of key */ + return res_map, err +} + +var DbToYang_acl_entry_sequenceid_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + res_map := make(map[string]interface{}) + var err error + log.Info("DbToYang_acl_entry_sequenceid_xfmr: ", inParams.key) + /*sequenec-id attribute corresponds to key in redis table*/ + res, err := DbToYang_acl_entry_key_xfmr(inParams) + log.Info("acl-entry/config/sequence-id ", res) + if err != nil { + return res_map, err + } + if seqId, ok := res["sequence-id"]; !ok { + log.Error("sequence-id not found in acl entry") + return res_map, err + } else { + res_map["sequence-id"] = seqId + } + return res_map, err +} + +var YangToDb_acl_l2_ethertype_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + + if inParams.param == nil { + res_map["ETHER_TYPE"] = "" + return res_map, err + } + ethertypeType := reflect.TypeOf(inParams.param).Elem() + log.Info("YangToDb_acl_ip_protocol_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " ethertypeType: ", ethertypeType) + var b bytes.Buffer + switch ethertypeType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_E_OpenconfigPacketMatchTypes_ETHERTYPE{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_E_OpenconfigPacketMatchTypes_ETHERTYPE) + fmt.Fprintf(&b, "0x%0.4x", ETHERTYPE_MAP[v.E_OpenconfigPacketMatchTypes_ETHERTYPE]) + res_map["ETHER_TYPE"] = b.String() + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_Uint16{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_Uint16) + fmt.Fprintf(&b, "0x%0.4x", v.Uint16) + res_map["ETHER_TYPE"] = b.String() + break + } + return res_map, err +} + +var DbToYang_acl_l2_ethertype_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_l2_ethertype_xfmr", data, inParams.ygRoot) + if _, ok := data[RULE_TABLE]; !ok { + err = errors.New("RULE_TABLE entry not found in the input param") + return result, err + } + + ruleTbl := data[RULE_TABLE] + ruleInst := ruleTbl[inParams.key] + etype, ok := ruleInst.Field["ETHER_TYPE"] + + if ok { + etypeVal, _ := strconv.ParseUint(strings.Replace(etype, "0x", "", -1), 16, 32) + result["protocol"] = getL2EtherType(etypeVal) + } else { + err = errors.New("ETHER_TYPE field not found in DB") + } + return result, nil +} + +var YangToDb_acl_ip_protocol_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + + if inParams.param == nil { + res_map["IP_PROTOCOL"] = "" + return res_map, err + } + protocolType := reflect.TypeOf(inParams.param).Elem() + log.Info("YangToDb_acl_ip_protocol_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " protocolType: ", protocolType) + switch protocolType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL) + res_map["IP_PROTOCOL"] = findInMap(IP_PROTOCOL_MAP, strconv.FormatInt(int64(v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL), 10)) + v = nil + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_Uint8{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_Uint8) + res_map["IP_PROTOCOL"] = strconv.FormatInt(int64(v.Uint8), 10) + break + } + return res_map, err +} + +var DbToYang_acl_ip_protocol_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_ip_protocol_xfmr", data, inParams.ygRoot) + oc_protocol := findByValue(IP_PROTOCOL_MAP, data[RULE_TABLE][inParams.key].Field["IP_PROTOCOL"]) + n, err := strconv.ParseInt(oc_protocol, 10, 64) + result["protocol"] = ocbinds.E_OpenconfigPacketMatchTypes_IP_PROTOCOL(n).ΛMap()["E_OpenconfigPacketMatchTypes_IP_PROTOCOL"][n].Name + return result, err +} + +var YangToDb_acl_source_port_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + if inParams.param == nil { + res_map["L4_SRC_PORT"] = "" + return res_map, err + } + sourceportType := reflect.TypeOf(inParams.param).Elem() + log.Info("YangToDb_acl_ip_protocol_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " sourceportType: ", sourceportType) + switch sourceportType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort) + res_map["L4_SRC_PORT"] = v.E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort.ΛMap()["E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort"][int64(v.E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort)].Name + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_String{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_String) + res_map["L4_SRC_PORT_RANGE"] = strings.Replace(v.String, "..", "-", 1) + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_Uint16{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_Uint16) + res_map["L4_SRC_PORT"] = strconv.FormatInt(int64(v.Uint16), 10) + break + } + + return res_map, err +} + +var DbToYang_acl_source_port_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_source_port_xfmr: ", data, inParams.ygRoot) + result := make(map[string]interface{}) + if _, ok := data[RULE_TABLE]; !ok { + err = errors.New("RULE_TABLE entry not found in the input param") + return result, err + } + ruleTbl := data[RULE_TABLE] + ruleInst := ruleTbl[inParams.key] + port, ok := ruleInst.Field["L4_SRC_PORT"] + if ok { + result["source-port"] = port + return result, nil + } + + portRange, ok := ruleInst.Field["L4_SRC_PORT_RANGE"] + if ok { + result["source-port"] = portRange + return result, nil + } else { + err = errors.New("PORT/PORT_RANGE field not found in DB") + } + return result, err +} + +var YangToDb_acl_destination_port_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + if inParams.param == nil { + res_map["L4_DST_PORT_RANGE"] = "" + return res_map, err + } + destportType := reflect.TypeOf(inParams.param).Elem() + log.Info("YangToDb_acl_ip_protocol_xfmr: ", inParams.ygRoot, " Xpath: ", inParams.uri, " destportType: ", destportType) + switch destportType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort) + res_map["L4_DST_PORT"] = v.E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort.ΛMap()["E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort"][int64(v.E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort)].Name + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_String{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_String) + res_map["L4_DST_PORT_RANGE"] = strings.Replace(v.String, "..", "-", 1) + break + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_Uint16{}): + v := (inParams.param).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_Uint16) + res_map["L4_DST_PORT"] = strconv.FormatInt(int64(v.Uint16), 10) + break + } + return res_map, err +} + +var DbToYang_acl_destination_port_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_destination_port_xfmr: ", data, inParams.ygRoot) + if _, ok := data[RULE_TABLE]; !ok { + err = errors.New("RULE_TABLE entry not found in the input param") + return result, err + } + ruleTbl := data[RULE_TABLE] + ruleInst := ruleTbl[inParams.key] + port, ok := ruleInst.Field["L4_DST_PORT"] + if ok { + result["destination-port"] = port + return result, nil + } + + portRange, ok := ruleInst.Field["L4_DST_PORT_RANGE"] + if ok { + result["destination-port"] = portRange + return result, nil + } else { + err = errors.New("DST PORT/PORT_RANGE field not found in DB") + } + return result, err +} + +var YangToDb_acl_tcp_flags_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + log.Info("YangToDb_acl_tcp_flags_xfmr: ") + var tcpFlags uint32 = 0x00 + var b bytes.Buffer + if inParams.param == nil { + res_map["TCP_FLAGS"] = b.String() + return res_map, err + } + log.Info("YangToDb_acl_tcp_flags_xfmr: ", inParams.ygRoot, inParams.uri) + v := reflect.ValueOf(inParams.param) + + flags := v.Interface().([]ocbinds.E_OpenconfigPacketMatchTypes_TCP_FLAGS) + for _, flag := range flags { + fmt.Println("TCP Flag name: " + flag.ΛMap()["E_OpenconfigPacketMatchTypes_TCP_FLAGS"][int64(flag)].Name) + switch flag { + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_FIN: + tcpFlags |= 0x01 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_SYN: + tcpFlags |= 0x02 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_RST: + tcpFlags |= 0x04 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_PSH: + tcpFlags |= 0x08 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ACK: + tcpFlags |= 0x10 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_URG: + tcpFlags |= 0x20 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ECE: + tcpFlags |= 0x40 + break + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_CWR: + tcpFlags |= 0x80 + break + } + } + fmt.Fprintf(&b, "0x%0.2x/0x%0.2x", tcpFlags, tcpFlags) + res_map["TCP_FLAGS"] = b.String() + return res_map, err +} + +var DbToYang_acl_tcp_flags_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_tcp_flags_xfmr: ", data, inParams.ygRoot) + result := make(map[string]interface{}) + if _, ok := data[RULE_TABLE]; !ok { + err = errors.New("RULE_TABLE entry not found in the input param") + return result, err + } + ruleTbl := data[RULE_TABLE] + ruleInst := ruleTbl[inParams.key] + tcpFlag, ok := ruleInst.Field["TCP_FLAGS"] + if ok { + result["tcp-flags"] = getTransportConfigTcpFlags(tcpFlag) + return result, nil + } + return result, nil +} + +var YangToDb_acl_port_bindings_xfmr SubTreeXfmrYangToDb = func(inParams XfmrParams) (map[string]map[string]db.Value, error) { + var err error + res_map := make(map[string]map[string]db.Value) + aclTableMap := make(map[string]db.Value) + log.Info("YangToDb_acl_port_bindings_xfmr: ", inParams.ygRoot, inParams.uri) + + aclObj := getAclRoot(inParams.ygRoot) + if aclObj.Interfaces == nil { + return res_map, err + } + //aclset := &ocbinds.OpenconfigAcl_Acl_AclSets_AclSet{} + aclInterfacesMap := make(map[string][]string) + for intfId, _ := range aclObj.Interfaces.Interface { + intf := aclObj.Interfaces.Interface[intfId] + if intf != nil { + if intf.IngressAclSets != nil && len(intf.IngressAclSets.IngressAclSet) > 0 { + for inAclKey, _ := range intf.IngressAclSets.IngressAclSet { + aclName := getAclKeyStrFromOCKey(inAclKey.SetName, inAclKey.Type) + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.Id) + _, ok := aclTableMap[aclName] + if !ok { + aclTableMap[aclName] = db.Value{Field: make(map[string]string)} + } + aclTableMap[aclName].Field["stage"] = "INGRESS" + } + } + if intf.EgressAclSets != nil && len(intf.EgressAclSets.EgressAclSet) > 0 { + for outAclKey, _ := range intf.EgressAclSets.EgressAclSet { + aclName := getAclKeyStrFromOCKey(outAclKey.SetName, outAclKey.Type) + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.Id) + _, ok := aclTableMap[aclName] + if !ok { + aclTableMap[aclName] = db.Value{Field: make(map[string]string)} + } + aclTableMap[aclName].Field["stage"] = "EGRESS" + } + } + } + } + for k, _ := range aclInterfacesMap { + val := aclTableMap[k] + (&val).SetList("ports", aclInterfacesMap[k]) + } + res_map[ACL_TABLE] = aclTableMap + return res_map, err +} + +var DbToYang_acl_port_bindings_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_acl_port_bindings_xfmr: ", data, inParams.ygRoot) + + aclTbl := data["ACL_TABLE"] + var ruleTbl map[string]map[string]db.Value + + // repoluate to use existing code + ruleTbl = make(map[string]map[string]db.Value) + for key, element := range data["ACL_RULE"] { + // split into aclKey and ruleKey + tokens := strings.Split(key, "|") + if ruleTbl[tokens[0]] == nil { + ruleTbl[tokens[0]] = make(map[string]db.Value) + } + ruleTbl[tokens[0]][tokens[1]] = db.Value{Field: make(map[string]string)} + ruleTbl[tokens[0]][tokens[1]] = element + } + + pathInfo := NewPathInfo(inParams.uri) + + acl := getAclRoot(inParams.ygRoot) + targetUriPath, _ := getYangPathFromUri(pathInfo.Path) + if isSubtreeRequest(pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}") { + for intfId := range acl.Interfaces.Interface { + intfData := acl.Interfaces.Interface[intfId] + ygot.BuildEmptyTree(intfData) + if isSubtreeRequest(targetUriPath, "/openconfig-acl:acl/interfaces/interface/ingress-acl-sets") { + err = getAclBindingInfoForInterfaceData(aclTbl, ruleTbl, intfData, intfId, "INGRESS") + } else if isSubtreeRequest(targetUriPath, "/openconfig-acl:acl/interfaces/interface/egress-acl-sets") { + err = getAclBindingInfoForInterfaceData(aclTbl, ruleTbl, intfData, intfId, "EGRESS") + } else { + err = getAclBindingInfoForInterfaceData(aclTbl, ruleTbl, intfData, intfId, "INGRESS") + if err != nil { + return err + } + err = getAclBindingInfoForInterfaceData(aclTbl, ruleTbl, intfData, intfId, "EGRESS") + } + } + } else { + err = getAllBindingsInfo(aclTbl, ruleTbl, inParams.ygRoot) + } + + return err +} + +func convertInternalToOCAclRuleBinding(aclTableMap map[string]db.Value, ruleTableMap map[string]map[string]db.Value, priority uint32, seqId int64, direction string, aclSet ygot.GoStruct, entrySet ygot.GoStruct) { + if seqId == -1 { + seqId = int64(MAX_PRIORITY - priority) + } + + var num uint64 + num = 0 + var ruleId uint32 = uint32(seqId) + + if direction == "INGRESS" { + var ingressEntrySet *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_AclEntries_AclEntry + var ok bool + if entrySet == nil { + ingressAclSet := aclSet.(*ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet) + if ingressEntrySet, ok = ingressAclSet.AclEntries.AclEntry[ruleId]; !ok { + ingressEntrySet, _ = ingressAclSet.AclEntries.NewAclEntry(ruleId) + } + } else { + ingressEntrySet = entrySet.(*ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_AclEntries_AclEntry) + } + if ingressEntrySet != nil { + ygot.BuildEmptyTree(ingressEntrySet) + ingressEntrySet.State.SequenceId = &ruleId + ingressEntrySet.State.MatchedPackets = &num + ingressEntrySet.State.MatchedOctets = &num + } + } else if direction == "EGRESS" { + var egressEntrySet *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_AclEntries_AclEntry + var ok bool + if entrySet == nil { + egressAclSet := aclSet.(*ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet) + if egressEntrySet, ok = egressAclSet.AclEntries.AclEntry[ruleId]; !ok { + egressEntrySet, _ = egressAclSet.AclEntries.NewAclEntry(ruleId) + } + } else { + egressEntrySet = entrySet.(*ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_AclEntries_AclEntry) + } + if egressEntrySet != nil { + ygot.BuildEmptyTree(egressEntrySet) + egressEntrySet.State.SequenceId = &ruleId + egressEntrySet.State.MatchedPackets = &num + egressEntrySet.State.MatchedOctets = &num + } + } +} + +func convertInternalToOCAclBinding(aclTableMap map[string]db.Value, ruleTableMap map[string]map[string]db.Value, aclName string, intfId string, direction string, intfAclSet ygot.GoStruct) error { + var err error + if _, ok := aclTableMap[aclName]; !ok { + err = errors.New("Acl entry not found, convertInternalToOCAclBinding") + return err + } else { + aclEntry := aclTableMap[aclName] + if !contains(aclEntry.GetList("ports"), intfId) { + return tlerr.InvalidArgs("Acl %s not binded with %s", aclName, intfId) + } + } + + for ruleName := range ruleTableMap[aclName] { + if ruleName != "DEFAULT_RULE" { + seqId, _ := strconv.Atoi(strings.Replace(ruleName, "RULE_", "", 1)) + convertInternalToOCAclRuleBinding(aclTableMap, ruleTableMap, 0, int64(seqId), direction, intfAclSet, nil) + } + } + + return err +} + +func getAllBindingsInfo(aclTableMap map[string]db.Value, ruleTableMap map[string]map[string]db.Value, ygRoot *ygot.GoStruct) error { + var err error + acl := getAclRoot(ygRoot) + + var interfaces []string + for aclName := range aclTableMap { + aclData := aclTableMap[aclName] + if len(aclData.Get("ports@")) > 0 { + aclIntfs := aclData.GetList("ports") + for i, _ := range aclIntfs { + if !contains(interfaces, aclIntfs[i]) && aclIntfs[i] != "" { + interfaces = append(interfaces, aclIntfs[i]) + } + } + } + } + ygot.BuildEmptyTree(acl) + for _, intfId := range interfaces { + var intfData *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface + intfData, ok := acl.Interfaces.Interface[intfId] + if !ok { + intfData, _ = acl.Interfaces.NewInterface(intfId) + } + ygot.BuildEmptyTree(intfData) + err = getAclBindingInfoForInterfaceData(aclTableMap, ruleTableMap, intfData, intfId, "INGRESS") + err = getAclBindingInfoForInterfaceData(aclTableMap, ruleTableMap, intfData, intfId, "EGRESS") + } + return err +} + +func getAclBindingInfoForInterfaceData(aclTableMap map[string]db.Value, ruleTableMap map[string]map[string]db.Value, intfData *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface, intfId string, direction string) error { + var err error + if intfData != nil { + intfData.Config.Id = intfData.Id + intfData.State.Id = intfData.Id + } + if direction == "INGRESS" { + if intfData.IngressAclSets != nil && len(intfData.IngressAclSets.IngressAclSet) > 0 { + for ingressAclSetKey, _ := range intfData.IngressAclSets.IngressAclSet { + aclName := strings.Replace(strings.Replace(ingressAclSetKey.SetName, " ", "_", -1), "-", "_", -1) + aclType := ingressAclSetKey.Type.ΛMap()["E_OpenconfigAcl_ACL_TYPE"][int64(ingressAclSetKey.Type)].Name + aclKey := aclName + "_" + aclType + + ingressAclSet := intfData.IngressAclSets.IngressAclSet[ingressAclSetKey] + if ingressAclSet != nil && ingressAclSet.AclEntries != nil && len(ingressAclSet.AclEntries.AclEntry) > 0 { + for seqId, _ := range ingressAclSet.AclEntries.AclEntry { + rulekey := "RULE_" + strconv.Itoa(int(seqId)) + entrySet := ingressAclSet.AclEntries.AclEntry[seqId] + _, ok := ruleTableMap[aclKey+"|"+rulekey] + if !ok { + log.Info("Acl Rule not found ", aclKey, rulekey) + err = errors.New("Acl Rule not found ingress, getAclBindingInfoForInterfaceData") + return err + } + convertInternalToOCAclRuleBinding(aclTableMap, ruleTableMap, 0, int64(seqId), direction, nil, entrySet) + } + } else { + ygot.BuildEmptyTree(ingressAclSet) + ingressAclSet.Config = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_Config{SetName: &aclName, Type: ingressAclSetKey.Type} + ingressAclSet.State = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_State{SetName: &aclName, Type: ingressAclSetKey.Type} + err = convertInternalToOCAclBinding(aclTableMap, ruleTableMap, aclKey, intfId, direction, ingressAclSet) + } + } + } else { + err = findAndGetAclBindingInfoForInterfaceData(aclTableMap, ruleTableMap, intfId, direction, intfData) + } + } else if direction == "EGRESS" { + if intfData.EgressAclSets != nil && len(intfData.EgressAclSets.EgressAclSet) > 0 { + for egressAclSetKey, _ := range intfData.EgressAclSets.EgressAclSet { + aclName := strings.Replace(strings.Replace(egressAclSetKey.SetName, " ", "_", -1), "-", "_", -1) + aclType := egressAclSetKey.Type.ΛMap()["E_OpenconfigAcl_ACL_TYPE"][int64(egressAclSetKey.Type)].Name + aclKey := aclName + "_" + aclType + + egressAclSet := intfData.EgressAclSets.EgressAclSet[egressAclSetKey] + if egressAclSet != nil && egressAclSet.AclEntries != nil && len(egressAclSet.AclEntries.AclEntry) > 0 { + for seqId, _ := range egressAclSet.AclEntries.AclEntry { + rulekey := "RULE_" + strconv.Itoa(int(seqId)) + entrySet := egressAclSet.AclEntries.AclEntry[seqId] + _, ok := ruleTableMap[aclKey+"|"+rulekey] + if !ok { + log.Info("Acl Rule not found ", aclKey, rulekey) + err = errors.New("Acl Rule not found egress, getAclBindingInfoForInterfaceData") + return err + } + convertInternalToOCAclRuleBinding(aclTableMap, ruleTableMap, 0, int64(seqId), direction, nil, entrySet) + } + } else { + ygot.BuildEmptyTree(egressAclSet) + egressAclSet.Config = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_Config{SetName: &aclName, Type: egressAclSetKey.Type} + egressAclSet.State = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_State{SetName: &aclName, Type: egressAclSetKey.Type} + err = convertInternalToOCAclBinding(aclTableMap, ruleTableMap, aclKey, intfId, direction, egressAclSet) + } + } + } else { + err = findAndGetAclBindingInfoForInterfaceData(aclTableMap, ruleTableMap, intfId, direction, intfData) + } + } else { + log.Error("Unknown direction") + } + return err +} + +func findAndGetAclBindingInfoForInterfaceData(aclTableMap map[string]db.Value, ruleTableMap map[string]map[string]db.Value, intfId string, direction string, intfData *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface) error { + var err error + for aclName, _ := range aclTableMap { + aclData := aclTableMap[aclName] + aclIntfs := aclData.GetList("ports") + aclType := aclData.Get(ACL_TYPE) + var aclOrigName string + var aclOrigType ocbinds.E_OpenconfigAcl_ACL_TYPE + if SONIC_ACL_TYPE_IPV4 == aclType { + aclOrigName = strings.Replace(aclName, "_"+OPENCONFIG_ACL_TYPE_IPV4, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 + } else if SONIC_ACL_TYPE_IPV6 == aclType { + aclOrigName = strings.Replace(aclName, "_"+OPENCONFIG_ACL_TYPE_IPV6, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 + } else if SONIC_ACL_TYPE_L2 == aclType { + aclOrigName = strings.Replace(aclName, "_"+OPENCONFIG_ACL_TYPE_L2, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 + } + + if contains(aclIntfs, intfId) && direction == aclData.Get("stage") { + if direction == "INGRESS" { + if intfData.IngressAclSets != nil { + aclSetKey := ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_Key{SetName: aclOrigName, Type: aclOrigType} + ingressAclSet, ok := intfData.IngressAclSets.IngressAclSet[aclSetKey] + if !ok { + ingressAclSet, _ = intfData.IngressAclSets.NewIngressAclSet(aclOrigName, aclOrigType) + ygot.BuildEmptyTree(ingressAclSet) + ingressAclSet.Config = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_Config{SetName: &aclOrigName, Type: aclOrigType} + ingressAclSet.State = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_IngressAclSets_IngressAclSet_State{SetName: &aclOrigName, Type: aclOrigType} + } + err = convertInternalToOCAclBinding(aclTableMap, ruleTableMap, aclName, intfId, direction, ingressAclSet) + if err != nil { + return err + } + } + } else if direction == "EGRESS" { + if intfData.EgressAclSets != nil { + aclSetKey := ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_Key{SetName: aclOrigName, Type: aclOrigType} + egressAclSet, ok := intfData.EgressAclSets.EgressAclSet[aclSetKey] + if !ok { + egressAclSet, _ = intfData.EgressAclSets.NewEgressAclSet(aclOrigName, aclOrigType) + ygot.BuildEmptyTree(egressAclSet) + egressAclSet.Config = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_Config{SetName: &aclOrigName, Type: aclOrigType} + egressAclSet.State = &ocbinds.OpenconfigAcl_Acl_Interfaces_Interface_EgressAclSets_EgressAclSet_State{SetName: &aclOrigName, Type: aclOrigType} + } + err = convertInternalToOCAclBinding(aclTableMap, ruleTableMap, aclName, intfId, direction, egressAclSet) + if err != nil { + return err + } + } + } + } + } + return err +} diff --git a/src/translib/transformer/xfmr_interface.go b/src/translib/transformer/xfmr_interface.go new file mode 100644 index 0000000000..7109cf4de8 --- /dev/null +++ b/src/translib/transformer/xfmr_interface.go @@ -0,0 +1,106 @@ +package transformer + +import ( + "github.com/openconfig/ygot/ygot" + "translib/db" + log "github.com/golang/glog" +) + +type XfmrParams struct { + d *db.DB + dbs [db.MaxDB]*db.DB + curDb db.DBNum + ygRoot *ygot.GoStruct + uri string + oper int + key string + dbDataMap *map[db.DBNum]map[string]map[string]db.Value + param interface{} +} + +/** + * KeyXfmrYangToDb type is defined to use for conversion of Yang key to DB Key + * Transformer function definition. + * Param: XfmrParams structure having Database info, YgotRoot, operation, Xpath + * Return: Database keys to access db entry, error + **/ +type KeyXfmrYangToDb func (inParams XfmrParams) (string, error) +/** + * KeyXfmrDbToYang type is defined to use for conversion of DB key to Yang key + * Transformer function definition. + * Param: XfmrParams structure having Database info, operation, Database keys to access db entry + * Return: multi dimensional map to hold the yang key attributes of complete xpath, error + **/ +type KeyXfmrDbToYang func (inParams XfmrParams) (map[string]interface{}, error) + +/** + * FieldXfmrYangToDb type is defined to use for conversion of yang Field to DB field + * Transformer function definition. + * Param: Database info, YgotRoot, operation, Xpath + * Return: multi dimensional map to hold the DB data, error + **/ +type FieldXfmrYangToDb func (inParams XfmrParams) (map[string]string, error) +/** + * FieldXfmrDbtoYang type is defined to use for conversion of DB field to Yang field + * Transformer function definition. + * Param: XfmrParams structure having Database info, operation, DB data in multidimensional map, output param YgotRoot + * Return: error + **/ +type FieldXfmrDbtoYang func (inParams XfmrParams) (map[string]interface{}, error) + +/** + * SubTreeXfmrYangToDb type is defined to use for handling the yang subtree to DB + * Transformer function definition. + * Param: XfmrParams structure having Database info, YgotRoot, operation, Xpath + * Return: multi dimensional map to hold the DB data, error + **/ +type SubTreeXfmrYangToDb func (inParams XfmrParams) (map[string]map[string]db.Value, error) +/** + * SubTreeXfmrDbToYang type is defined to use for handling the DB to Yang subtree + * Transformer function definition. + * Param : XfmrParams structure having Database pointers, current db, operation, DB data in multidimensional map, output param YgotRoot, uri + * Return : error + **/ +type SubTreeXfmrDbToYang func (inParams XfmrParams) (error) +/** + * ValidateCallpoint is used to validate a YANG node during data translation back to YANG as a response to GET + * Param : XfmrParams structure having Database pointers, current db, operation, DB data in multidimensional map, output param YgotRoot, uri + * Return : bool + **/ +type ValidateCallpoint func (inParams XfmrParams) (bool) + +/** + * PostXfmrFunc type is defined to use for handling any default handling operations required as part of the CREATE + * Transformer function definition. + * Param: XfmrParams structure having database pointers, current db, operation, DB data in multidimensional map, YgotRoot, uri + * Return: multi dimensional map to hold the DB data, error + **/ +type PostXfmrFunc func (inParams XfmrParams) (map[string]map[string]db.Value, error) + + +/** + * Xfmr validation interface for validating the callback registration of app modules + * transformer methods. + **/ +type XfmrInterface interface { + xfmrInterfaceValiidate() +} + +func (KeyXfmrYangToDb) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for KeyXfmrYangToDb") +} +func (KeyXfmrDbToYang) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for KeyXfmrDbToYang") +} +func (FieldXfmrYangToDb) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for FieldXfmrYangToDb") +} +func (FieldXfmrDbtoYang) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for FieldXfmrDbtoYang") +} +func (SubTreeXfmrYangToDb) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for SubTreeXfmrYangToDb") +} +func (SubTreeXfmrDbToYang) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for SubTreeXfmrDbToYang") +} diff --git a/src/translib/transformer/xfmr_path_utils.go b/src/translib/transformer/xfmr_path_utils.go new file mode 100644 index 0000000000..8a052cd24c --- /dev/null +++ b/src/translib/transformer/xfmr_path_utils.go @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Broadcom. All rights reserved. +// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +// +/////////////////////////////////////////////////////////////////////// + +package transformer + +import ( + "bytes" + "fmt" + "strings" +) + +// PathInfo structure contains parsed path information. +type PathInfo struct { + Path string + Template string + Vars map[string]string +} + +// Var returns the string value for a path variable. Returns +// empty string if no such variable exists. +func (p *PathInfo) Var(name string) string { + return p.Vars[name] +} + +// NewPathInfo parses given path string into a PathInfo structure. +func NewPathInfo(path string) *PathInfo { + var info PathInfo + info.Path = path + info.Vars = make(map[string]string) + + //TODO optimize using regexp + var template strings.Builder + r := strings.NewReader(path) + + for r.Len() > 0 { + c, _ := r.ReadByte() + if c != '[' { + template.WriteByte(c) + continue + } + + name := readUntil(r, '=') + value := readUntil(r, ']') + if len(name) != 0 { + fmt.Fprintf(&template, "{%s}", name) + info.Vars[name] = value + } + } + + info.Template = template.String() + + return &info +} + +func readUntil(r *strings.Reader, delim byte) string { + var buff strings.Builder + for { + c, err := r.ReadByte() + if err == nil && c != delim { + buff.WriteByte(c) + } else { + break + } + } + + return buff.String() +} + +func RemoveXPATHPredicates(s string) (string, error) { + var b bytes.Buffer + for i := 0; i < len(s); { + ss := s[i:] + si, ei := strings.Index(ss, "["), strings.Index(ss, "]") + switch { + case si == -1 && ei == -1: + // This substring didn't contain a [] pair, therefore write it + // to the buffer. + b.WriteString(ss) + // Move to the last character of the substring. + i += len(ss) + case si == -1 || ei == -1: + // This substring contained a mismatched pair of []s. + return "", fmt.Errorf("Mismatched brackets within substring %s of %s, [ pos: %d, ] pos: %d", ss, s, si, ei) + case si > ei: + // This substring contained a ] before a [. + return "", fmt.Errorf("Incorrect ordering of [] within substring %s of %s, [ pos: %d, ] pos: %d", ss, s, si, ei) + default: + // This substring contained a matched set of []s. + b.WriteString(ss[0:si]) + i += ei + 1 + } + } + + return b.String(), nil +} diff --git a/src/translib/transformer/xlate.go b/src/translib/transformer/xlate.go new file mode 100644 index 0000000000..f5489096f5 --- /dev/null +++ b/src/translib/transformer/xlate.go @@ -0,0 +1,358 @@ +package transformer + +import ( + "fmt" + "encoding/json" + "errors" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "reflect" + "strings" + "translib/db" + "translib/ocbinds" +) + +const ( + GET = 1 + iota + CREATE + REPLACE + UPDATE + DELETE +) + +type KeySpec struct { + dbNum db.DBNum + Ts db.TableSpec + Key db.Key + Child []KeySpec +} + +var XlateFuncs = make(map[string]reflect.Value) + +var ( + ErrParamsNotAdapted = errors.New("The number of params is not adapted.") +) + +func XlateFuncBind(name string, fn interface{}) (err error) { + defer func() { + if e := recover(); e != nil { + err = errors.New(name + " is not valid Xfmr function.") + } + }() + + if _, ok := XlateFuncs[name]; !ok { + v := reflect.ValueOf(fn) + v.Type().NumIn() + XlateFuncs[name] = v + } else { + log.Info("Duplicate entry found in the XlateFunc map " + name) + } + return +} + +func XlateFuncCall(name string, params ...interface{}) (result []reflect.Value, err error) { + if _, ok := XlateFuncs[name]; !ok { + err = errors.New(name + " Xfmr function does not exist.") + return nil, nil + } + if len(params) != XlateFuncs[name].Type().NumIn() { + err = ErrParamsNotAdapted + return nil, nil + } + in := make([]reflect.Value, len(params)) + for k, param := range params { + in[k] = reflect.ValueOf(param) + } + result = XlateFuncs[name].Call(in) + return result, nil +} + +func TraverseDb(dbs [db.MaxDB]*db.DB, spec KeySpec, result *map[db.DBNum]map[string]map[string]db.Value, parentKey *db.Key) error { + var err error + + if spec.Key.Len() > 0 { + // get an entry with a specific key + data, err := dbs[spec.dbNum].GetEntry(&spec.Ts, spec.Key) + if err != nil { + return err + } + + if (*result)[spec.dbNum][spec.Ts.Name] == nil { + (*result)[spec.dbNum][spec.Ts.Name] = map[string]db.Value{strings.Join(spec.Key.Comp, "|"): data} + } else { + (*result)[spec.dbNum][spec.Ts.Name][strings.Join(spec.Key.Comp, "|")] = data + } + + if len(spec.Child) > 0 { + for _, ch := range spec.Child { + err = TraverseDb(dbs, ch, result, &spec.Key) + } + } + } else { + // TODO - GetEntry suuport with regex patten, 'abc*' for optimization + keys, err := dbs[spec.dbNum].GetKeys(&spec.Ts) + if err != nil { + return err + } + for i, _ := range keys { + if parentKey != nil { + // TODO - multi-depth with a custom delimiter + if strings.Index(strings.Join(keys[i].Comp, "|"), strings.Join((*parentKey).Comp, "|")) == -1 { + continue + } + } + spec.Key = keys[i] + err = TraverseDb(dbs, spec, result, parentKey) + } + } + return err +} + +func XlateUriToKeySpec(uri string, ygRoot *ygot.GoStruct, t *interface{}) (*[]KeySpec, error) { + + var err error + var retdbFormat = make([]KeySpec, 0) + + // In case of CVL yang, the tablename and key info is available in the xpath + if isCvlYang(uri) { + /* Extract the xpath and key from input xpath */ + yangXpath, keyStr, tableName := sonicXpathKeyExtract(uri) + retdbFormat = fillCvlKeySpec(yangXpath, tableName, keyStr) + } else { + /* Extract the xpath and key from input xpath */ + yangXpath, keyStr, _ := xpathKeyExtract(nil, ygRoot, 0, uri) + retdbFormat = FillKeySpecs(yangXpath, keyStr, &retdbFormat) + } + + return &retdbFormat, err +} + +func FillKeySpecs(yangXpath string , keyStr string, retdbFormat *[]KeySpec) ([]KeySpec){ + if xSpecMap == nil { + return *retdbFormat + } + _, ok := xSpecMap[yangXpath] + if ok { + xpathInfo := xSpecMap[yangXpath] + if xpathInfo.tableName != nil { + dbFormat := KeySpec{} + dbFormat.Ts.Name = *xpathInfo.tableName + dbFormat.dbNum = xpathInfo.dbIndex + if keyStr != "" { + dbFormat.Key.Comp = append(dbFormat.Key.Comp, keyStr) + } + for _, child := range xpathInfo.childTable { + if xDbSpecMap != nil { + chlen := len(xDbSpecMap[child].yangXpath) + if chlen > 0 { + children := make([]KeySpec, 0) + for _, childXpath := range xDbSpecMap[child].yangXpath { + children = FillKeySpecs(childXpath, "", &children) + dbFormat.Child = append(dbFormat.Child, children...) + } + } + } + } + *retdbFormat = append(*retdbFormat, dbFormat) + } else { + for _, child := range xpathInfo.childTable { + if xDbSpecMap != nil { + chlen := len(xDbSpecMap[child].yangXpath) + if chlen > 0 { + for _, childXpath := range xDbSpecMap[child].yangXpath { + *retdbFormat = FillKeySpecs(childXpath, "", retdbFormat) + } + } + } + } + } + } + return *retdbFormat +} + +func fillCvlKeySpec(yangXpath string , tableName string, keyStr string) ( []KeySpec ) { + + var retdbFormat = make([]KeySpec, 0) + + if tableName != "" { + dbFormat := KeySpec{} + dbFormat.Ts.Name = tableName + dbFormat.dbNum = db.ConfigDB + if keyStr != "" { + dbFormat.Key.Comp = append(dbFormat.Key.Comp, keyStr) + } + retdbFormat = append(retdbFormat, dbFormat) + } else { + // If table name not available in xpath get top container name + tokens:= strings.Split(yangXpath, ":") + container := "/" + tokens[len(tokens)-1] + if xDbSpecMap[container] != nil { + dbInfo := xDbSpecMap[container] + if dbInfo.fieldType == "container" { + for dir, _ := range dbInfo.dbEntry.Dir { + dbFormat := KeySpec{} + dbFormat.Ts.Name = dir + dbFormat.dbNum = db.ConfigDB + retdbFormat = append(retdbFormat, dbFormat) + } + } + } + } + return retdbFormat +} + +func XlateToDb(path string, opcode int, d *db.DB, yg *ygot.GoStruct, yt *interface{}) (map[string]map[string]db.Value, error) { + + var err error + + device := (*yg).(*ocbinds.Device) + jsonStr, err := ygot.EmitJSON(device, &ygot.EmitJSONConfig{ + Format: ygot.RFC7951, + Indent: " ", + SkipValidation: true, + RFC7951Config: &ygot.RFC7951JSONConfig{ + AppendModuleName: true, + }, + }) + + jsonData := make(map[string]interface{}) + err = json.Unmarshal([]byte(jsonStr), &jsonData) + if err != nil { + log.Errorf("Error: failed to unmarshal json.") + return nil, err + } + + // table.key.fields + var result = make(map[string]map[string]db.Value) + switch opcode { + case CREATE: + log.Info("CREATE case") + err = dbMapCreate(d, yg, opcode, path, jsonData, result) + if err != nil { + log.Errorf("Error: Data translation from yang to db failed for create request.") + } + + case UPDATE: + log.Info("UPDATE case") + err = dbMapUpdate(d, yg, opcode, path, jsonData, result) + if err != nil { + log.Errorf("Error: Data translation from yang to db failed for update request.") + } + + case REPLACE: + log.Info("REPLACE case") + err = dbMapUpdate(d, yg, opcode, path, jsonData, result) + if err != nil { + log.Errorf("Error: Data translation from yang to db failed for replace request.") + } + + case DELETE: + log.Info("DELETE case") + err = dbMapDelete(d, yg, opcode, path, jsonData, result) + if err != nil { + log.Errorf("Error: Data translation from yang to db failed for delete request.") + } + } + return result, err +} + +func GetAndXlateFromDB(uri string, ygRoot *ygot.GoStruct, dbs [db.MaxDB]*db.DB) ([]byte, error) { + var err error + var payload []byte + log.Info("received xpath =", uri) + + keySpec, err := XlateUriToKeySpec(uri, ygRoot, nil) + var dbresult = make(map[db.DBNum]map[string]map[string]db.Value) + for i := db.ApplDB; i < db.MaxDB; i++ { + dbresult[i] = make(map[string]map[string]db.Value) + } + + for _, spec := range *keySpec { + err := TraverseDb(dbs, spec, &dbresult, nil) + if err != nil { + log.Error("TraverseDb() failure") + return payload, err + } + } + + payload, err = XlateFromDb(uri, ygRoot, dbs, dbresult) + if err != nil { + log.Error("XlateFromDb() failure.") + return payload, err + } + + return payload, err +} + +func XlateFromDb(uri string, ygRoot *ygot.GoStruct, dbs [db.MaxDB]*db.DB, data map[db.DBNum]map[string]map[string]db.Value) ([]byte, error) { + + var err error + var dbData = make(map[db.DBNum]map[string]map[string]db.Value) + var cdb db.DBNum = db.ConfigDB + + dbData = data + if isCvlYang(uri) { + yangXpath, keyStr, tableName := sonicXpathKeyExtract(uri) + if (tableName != "") { + tokens:= strings.Split(yangXpath, "/") + // Format /module:container/tableName[key]/fieldName + if tokens[len(tokens)-2] == tableName { + fieldName := tokens[len(tokens)-1] + dbData[cdb] = extractFieldFromDb(tableName, keyStr, fieldName, data[cdb]) + } + } + } else { + xpath, _ := RemoveXPATHPredicates(uri) + cdb = xSpecMap[xpath].dbIndex + } + payload, err := dbDataToYangJsonCreate(uri, ygRoot, dbs, &dbData, cdb) + log.Info("Payload generated:", payload) + + if err != nil { + log.Errorf("Error: failed to create json response from DB data.") + return nil, err + } + + result := []byte(payload) + return result, err + +} + +func extractFieldFromDb(tableName string, keyStr string, fieldName string, data map[string]map[string]db.Value) (map[string]map[string]db.Value) { + + var dbVal db.Value + var dbData = make(map[string]map[string]db.Value) + + if tableName != "" && keyStr != "" && fieldName != "" { + if data[tableName][keyStr].Field != nil { + dbData[tableName] = make(map[string]db.Value) + dbVal.Field = make(map[string]string) + dbVal.Field[fieldName] = data[tableName][keyStr].Field[fieldName] + dbData[tableName][keyStr] = dbVal + } + } + return dbData +} + +func GetModuleNmFromPath(uri string) (string, error) { + log.Infof("received uri %s to extract module name from ", uri) + moduleNm, err := uriModuleNameGet(uri) + return moduleNm, err +} + +func GetOrdDBTblList(ygModuleNm string) ([]string, error) { + var result []string + var err error + if dbTblList, ok := xDbSpecOrdTblMap[ygModuleNm]; ok { + result = dbTblList + if len(dbTblList) == 0 { + log.Error("Ordered DB Table list is empty for module name = ", ygModuleNm) + err = fmt.Errorf("Ordered DB Table list is empty for module name %v", ygModuleNm) + + } + } else { + log.Error("No entry found in the map of module names to ordered list of DB Tables for module = ", ygModuleNm) + err = fmt.Errorf("No entry found in the map of module names to ordered list of DB Tables for module = %v", ygModuleNm) + } + return result, err +} diff --git a/src/translib/transformer/xlate_from_db.go b/src/translib/transformer/xlate_from_db.go new file mode 100644 index 0000000000..f763990231 --- /dev/null +++ b/src/translib/transformer/xlate_from_db.go @@ -0,0 +1,473 @@ +package transformer + +import ( + "fmt" + "translib/db" + "strings" + "encoding/json" + "os" + "strconv" + "errors" + "translib/ocbinds" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/ygot/ytypes" + + log "github.com/golang/glog" +) + +type typeMapOfInterface map[string]interface{} + +func xfmrHandlerFunc(inParams XfmrParams) (map[string]interface{}, error) { + result := make(map[string]interface{}) + xpath, _ := RemoveXPATHPredicates(inParams.uri) + log.Infof("Subtree transformer function(\"%v\") invoked for yang path(\"%v\").", xSpecMap[xpath].xfmrFunc, xpath) + _, err := XlateFuncCall(dbToYangXfmrFunc(xSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + log.Infof("Failed to retrieve data for xpath(\"%v\") err(%v).", inParams.uri, err) + return result, err + } + + ocbSch, _ := ocbinds.Schema() + schRoot := ocbSch.RootSchema() + device := (*inParams.ygRoot).(*ocbinds.Device) + + path, _ := ygot.StringToPath(inParams.uri, ygot.StructuredPath, ygot.StringSlicePath) + for _, p := range path.Elem { + pathSlice := strings.Split(p.Name, ":") + p.Name = pathSlice[len(pathSlice)-1] + if len(p.Key) > 0 { + for ekey, ent := range p.Key { + eslice := strings.Split(ent, ":") + p.Key[ekey] = eslice[len(eslice)-1] + } + } + } + + nodeList, nErr := ytypes.GetNode(schRoot, device, path) + if nErr != nil { + log.Infof("Failed to get node for xpath(\"%v\") err(%v).", inParams.uri, err) + return result, err + } + node := nodeList[0].Data + nodeYgot, _:= (node).(ygot.ValidatedGoStruct) + payload, err := ygot.EmitJSON(nodeYgot, &ygot.EmitJSONConfig{ Format: ygot.RFC7951, + Indent: " ", SkipValidation: true, + RFC7951Config: &ygot.RFC7951JSONConfig{ AppendModuleName: false, }, + }) + err = json.Unmarshal([]byte(payload), result) + return result, err +} + +func leafXfmrHandlerFunc(inParams XfmrParams) (map[string]interface{}, error) { + xpath, _ := RemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(dbToYangXfmrFunc(xSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return nil, err + } + fldValMap := ret[0].Interface().(map[string]interface{}) + return fldValMap, nil +} + +func validateHandlerFunc(inParams XfmrParams) (bool) { + xpath, _ := RemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(xSpecMap[xpath].validateFunc, inParams) + if err != nil { + return false + } + return ret[0].Interface().(bool) +} + +func DbValToInt(dbFldVal string, base int, size int, isUint bool) (interface{}, error) { + var res interface{} + var err error + if isUint { + if res, err = strconv.ParseUint(dbFldVal, base, size); err != nil { + log.Warningf("Non Yint%v type for yang leaf-list item %v", size, dbFldVal) + } + } else { + if res, err = strconv.ParseInt(dbFldVal, base, size); err != nil { + log.Warningf("Non Yint %v type for yang leaf-list item %v", size, dbFldVal) + } + } + return res, err +} + +func DbToYangType(yngTerminalNdDtType yang.TypeKind, fldXpath string, dbFldVal string) (interface{}, error) { + log.Infof("Received FieldXpath %v, yngTerminalNdDtType %v and Db field value %v to be converted to yang data-type.", fldXpath, yngTerminalNdDtType, dbFldVal) + var res interface{} + var err error + const INTBASE = 10 + switch yngTerminalNdDtType { + case yang.Ynone: + log.Warning("Yang node data-type is non base yang type") + //TODO - enhance to handle non base data types depending on future use case + err = errors.New("Yang node data-type is non base yang type") + case yang.Yint8: + res, err = DbValToInt(dbFldVal, INTBASE, 8, false) + case yang.Yint16: + res, err = DbValToInt(dbFldVal, INTBASE, 16, false) + case yang.Yint32: + res, err = DbValToInt(dbFldVal, INTBASE, 32, false) + case yang.Yuint8: + res, err = DbValToInt(dbFldVal, INTBASE, 8, true) + case yang.Yuint16: + res, err = DbValToInt(dbFldVal, INTBASE, 16, true) + case yang.Yuint32: + res, err = DbValToInt(dbFldVal, INTBASE, 32, true) + case yang.Ybool: + if res, err = strconv.ParseBool(dbFldVal); err != nil { + log.Warningf("Non Bool type for yang leaf-list item %v", dbFldVal) + } + case yang.Ybinary, yang.Ydecimal64, yang.Yenum, yang.Yidentityref, yang.Yint64, yang.Yuint64, yang.Ystring, yang.Yunion: + // TODO - handle the union type + // Make sure to encode as string, expected by util_types.go: ytypes.yangToJSONType + log.Info("Yenum/Ystring/Yunion(having all members as strings) type for yangXpath ", fldXpath) + res = dbFldVal + case yang.Yempty: + logStr := fmt.Sprintf("Yang data type for xpath %v is Yempty.", fldXpath) + log.Error(logStr) + err = errors.New(logStr) + default: + logStr := fmt.Sprintf("Unrecognized/Unhandled yang-data type(%v) for xpath %v.", fldXpath, yang.TypeKindToName[yngTerminalNdDtType]) + log.Error(logStr) + err = errors.New(logStr) + } + return res, err +} + +/*convert leaf-list in Db to leaf-list in yang*/ +func processLfLstDbToYang(fieldXpath string, dbFldVal string) []interface{} { + valLst := strings.Split(dbFldVal, ",") + var resLst []interface{} + const INTBASE = 10 + yngTerminalNdDtType := xDbSpecMap[fieldXpath].dbEntry.Type.Kind + switch yngTerminalNdDtType { + case yang.Yenum, yang.Ystring, yang.Yunion: + log.Info("DB leaf-list and Yang leaf-list are of same data-type") + for _, fldVal := range valLst { + resLst = append(resLst, fldVal) + } + default: + for _, fldVal := range valLst { + resVal, err := DbToYangType(yngTerminalNdDtType, fieldXpath, fldVal) + if err == nil { + resLst = append(resLst, resVal) + } + } + } + return resLst +} + +/* Traverse db map and create json for cvl yang */ +func directDbToYangJsonCreate(dbDataMap map[string]map[string]db.Value, jsonData string, resultMap map[string]interface{}) error { + var err error + for tblName, tblData := range dbDataMap { + var mapSlice []typeMapOfInterface + for keyStr, dbFldValData := range tblData { + curMap := make(map[string]interface{}) + for field, value := range dbFldValData.Field { + resField := field + if strings.HasSuffix(field, "@") { + fldVals := strings.Split(field, "@") + resField = fldVals[0] + } + fieldXpath := tblName + "/" + resField + xDbSpecMapEntry, ok := xDbSpecMap[fieldXpath] + if !ok { + log.Warningf("No entry found in xDbSpecMap for xpath %v", fieldXpath) + continue + } + if xDbSpecMapEntry.dbEntry == nil { + log.Warningf("Yang entry is nil in xDbSpecMap for xpath %v", fieldXpath) + continue + } + yangType := yangTypeGet(xDbSpecMapEntry.dbEntry) + if yangType == "leaf-list" { + /* this should never happen but just adding for safetty */ + if !strings.HasSuffix(field, "@") { + log.Warningf("Leaf-list in Sonic yang should also be a leaf-list in DB, its not for xpath %v", fieldXpath) + continue + } + resLst := processLfLstDbToYang(fieldXpath, value) + curMap[resField] = resLst + } else { /* yangType is leaf - there are only 2 types of yang terminal node leaf and leaf-list */ + yngTerminalNdDtType := xDbSpecMapEntry.dbEntry.Type.Kind + resVal, err := DbToYangType(yngTerminalNdDtType, fieldXpath, value) + if err != nil { + log.Warningf("Failure in converting Db value type to yang type for xpath", fieldXpath) + } else { + curMap[resField] = resVal + } + } + } //end of for + yangKeys := yangKeyFromEntryGet(xDbSpecMap[tblName].dbEntry) + sonicKeyDataAdd(yangKeys, keyStr, curMap) + if curMap != nil { + mapSlice = append(mapSlice, curMap) + } + } + resultMap[tblName] = mapSlice + } + return err +} + +func tableNameAndKeyFromDbMapGet(dbDataMap map[string]map[string]db.Value) (string, string, error) { + tableName := "" + tableKey := "" + for tn, tblData := range dbDataMap { + tableName = tn + for kname, _ := range tblData { + tableKey = kname + } + } + return tableName, tableKey, nil +} + +func yangListDataFill(dbs [db.MaxDB]*db.DB, ygRoot *ygot.GoStruct, uri string, xpath string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, resultMap map[string]interface{}, tbl string, tblKey string, cdb db.DBNum, validate bool) error { + tblData, ok := (*dbDataMap)[cdb][tbl] + + if ok { + var mapSlice []typeMapOfInterface + for dbKey, _ := range tblData { + curMap := make(map[string]interface{}) + curKeyMap, curUri, _ := dbKeyToYangDataConvert(uri, xpath, dbKey) + if len(xSpecMap[xpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, curUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if len(cmap) > 0 { + mapSlice = append(mapSlice, curMap) + } else { + log.Infof("Empty container returned from overloaded transformer for(\"%v\")", curUri) + } + } else { + _, keyFromCurUri, _ := xpathKeyExtract(dbs[cdb], ygRoot, GET, curUri) + if dbKey == keyFromCurUri { + for k, kv := range curKeyMap { + curMap[k] = kv + } + curXpath, _ := RemoveXPATHPredicates(curUri) + yangDataFill(dbs, ygRoot, curUri, curXpath, dbDataMap, curMap, tbl, dbKey, cdb, validate) + mapSlice = append(mapSlice, curMap) + } + } + } + if len(mapSlice) > 0 { + resultMap[xSpecMap[xpath].yangEntry.Name] = mapSlice + } else { + log.Infof("Empty slice for (\"%v\").\r\n", uri) + } + } + return nil +} + +func terminalNodeProcess(dbs [db.MaxDB]*db.DB, ygRoot *ygot.GoStruct, uri string, xpath string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, tbl string, tblKey string) (map[string]interface{}, error) { + log.Infof("Received xpath - %v, uri - %v, dbDataMap - %v, table - %v, table key - %v", xpath, uri, (*dbDataMap), tbl, tblKey) + var err error + resFldValMap := make(map[string]interface{}) + if xSpecMap[xpath].yangEntry == nil { + logStr := fmt.Sprintf("No yang entry found for xpath %v.", xpath) + err = fmt.Errorf("%v", logStr) + return resFldValMap, err + } + + cdb := xSpecMap[xpath].dbIndex + if len(xSpecMap[xpath].xfmrFunc) > 0 { + _, key, _ := xpathKeyExtract(dbs[cdb], ygRoot, GET, uri) + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, key, dbDataMap, nil) + fldValMap, err := leafXfmrHandlerFunc(inParams) + if err != nil { + logStr := fmt.Sprintf("%Failed to get data from overloaded function for %v -v.", uri, err) + err = fmt.Errorf("%v", logStr) + return resFldValMap, err + } + for lf, val := range fldValMap { + resFldValMap[lf] = val + } + } else { + dbFldName := xSpecMap[xpath].fieldName + /* if there is no transformer extension/annotation then it means leaf-list in yang is also leaflist in db */ + if len(dbFldName) > 0 && !xSpecMap[xpath].isKey { + yangType := yangTypeGet(xSpecMap[xpath].yangEntry) + if yangType == "leaf-list" { + dbFldName += "@" + val, ok := (*dbDataMap)[cdb][tbl][tblKey].Field[dbFldName] + if ok { + resLst := processLfLstDbToYang(xpath, val) + resFldValMap[xSpecMap[xpath].yangEntry.Name] = resLst + } + } else { + val, ok := (*dbDataMap)[cdb][tbl][tblKey].Field[dbFldName] + if ok { + yngTerminalNdDtType := xSpecMap[xpath].yangEntry.Type.Kind + resVal, err := DbToYangType(yngTerminalNdDtType, xpath, val) + if err != nil { + log.Error("Failure in converting Db value type to yang type for field", xpath) + } else { + resFldValMap[xSpecMap[xpath].yangEntry.Name] = resVal + } + } + } + } + } + return resFldValMap, err +} + + +func yangDataFill(dbs [db.MaxDB]*db.DB, ygRoot *ygot.GoStruct, uri string, xpath string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, resultMap map[string]interface{}, tbl string, tblKey string, cdb db.DBNum, validate bool) error { + var err error + isValid := validate + yangNode, ok := xSpecMap[xpath] + + if ok && yangNode.yangEntry != nil { + for yangChldName := range yangNode.yangEntry.Dir { + chldXpath := xpath+"/"+yangChldName + chldUri := uri+"/"+yangChldName + if xSpecMap[chldXpath] != nil && xSpecMap[chldXpath].yangEntry != nil { + _, key, _ := xpathKeyExtract(dbs[cdb], ygRoot, GET, chldUri) + if len(xSpecMap[chldXpath].validateFunc) > 0 && !validate { + // TODO - handle non CONFIG-DB + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, key, dbDataMap, nil) + res := validateHandlerFunc(inParams) + if res != true { + continue + } else { + isValid = res + } + } + chldYangType := yangTypeGet(xSpecMap[chldXpath].yangEntry) + cdb = xSpecMap[chldXpath].dbIndex + if chldYangType == "leaf" || chldYangType == "leaf-list" { + fldValMap, err := terminalNodeProcess(dbs, ygRoot, chldUri, chldXpath, dbDataMap, tbl, tblKey) + if err != nil { + log.Infof("Failed to get data(\"%v\").", chldUri) + } + for lf, val := range fldValMap { + resultMap[lf] = val + } + + } else if chldYangType == "container" { + cname := xSpecMap[chldXpath].yangEntry.Name + if len(xSpecMap[chldXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if len(cmap) > 0 { + resultMap[cname] = cmap + } else { + log.Infof("Empty container(\"%v\").\r\n", chldUri) + } + } else { + cmap := make(map[string]interface{}) + err = yangDataFill(dbs, ygRoot, chldUri, chldXpath, dbDataMap, cmap, tbl, tblKey, cdb, isValid) + if len(cmap) > 0 { + resultMap[cname] = cmap + } else { + log.Infof("Empty container(\"%v\").\r\n", chldUri) + } + } + } else if chldYangType == "list" { + cdb = xSpecMap[chldXpath].dbIndex + if len(xSpecMap[chldXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if len(cmap) > 0 { + resultMap = cmap + } else { + log.Infof("Empty list(\"%v\").\r\n", chldUri) + } + } else { + ynode, ok := xSpecMap[chldXpath] + if ok && ynode.tableName != nil { + lTblName := *ynode.tableName + yangListDataFill(dbs, ygRoot, chldUri, chldXpath, dbDataMap, resultMap, lTblName, "", cdb, isValid) + } + } + } else { + return err + } + } + } + } + return err +} + +/* Traverse linear db-map data and add to nested json data */ +func dbDataToYangJsonCreate(uri string, ygRoot *ygot.GoStruct, dbs [db.MaxDB]*db.DB, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, cdb db.DBNum) (string, error) { + jsonData := "" + resultMap := make(map[string]interface{}) + if isCvlYang(uri) { + directDbToYangJsonCreate((*dbDataMap)[cdb], jsonData, resultMap) + } else { + var d *db.DB + reqXpath, keyName, tableName := xpathKeyExtract(d, ygRoot, GET, uri) + yangNode, ok := xSpecMap[reqXpath] + + if ok { + yangType := yangTypeGet(yangNode.yangEntry) + if yangType == "leaf" || yangType == "leaf-list" { + //fldName := xSpecMap[reqXpath].fieldName + yangName := xSpecMap[reqXpath].yangEntry.Name + tbl, key, _ := tableNameAndKeyFromDbMapGet((*dbDataMap)[cdb]) + validateHandlerFlag := false + if len(xSpecMap[reqXpath].validateFunc) > 0 { + // TODO - handle non CONFIG-DB + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, key, dbDataMap, nil) + res := validateHandlerFunc(inParams) + if !res { + validateHandlerFlag = true + resultMap[yangName] = "" + } + } + if !validateHandlerFlag { + fldValMap, err := terminalNodeProcess(dbs, ygRoot, uri, reqXpath, dbDataMap, tbl, key) + //err := terminalNodeProcess(dbs, ygRoot, uri, reqXpath, dbDataMap, tbl, key) + if err != nil { + log.Infof("Empty terminal node (\"%v\").", uri) + } + resultMap = fldValMap + } + + } else if yangType == "container" { + cname := xSpecMap[reqXpath].yangEntry.Name + cmap := make(map[string]interface{}) + if len(xSpecMap[reqXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, "", dbDataMap, nil) + cmap, _ = xfmrHandlerFunc(inParams) + if len(cmap) > 0 { + resultMap[cname] = cmap + } else { + err := yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, false) + if err != nil { + log.Infof("Empty container(\"%v\").\r\n", uri) + } + } + } else { + err := yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, false) + if err != nil { + log.Infof("Empty container(\"%v\").\r\n", uri) + } + } + } else { + yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, false) + } + } + } + + jsonMapData, _ := json.Marshal(resultMap) + jsonData = fmt.Sprintf("%v", string(jsonMapData)) + jsonDataPrint(jsonData) + return jsonData, nil +} + +func jsonDataPrint(data string) { + fp, err := os.Create("/tmp/dbToYangJson.txt") + if err != nil { + return + } + defer fp.Close() + + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") + fmt.Fprintf (fp, "%v \r\n", data) + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") +} + diff --git a/src/translib/transformer/xlate_to_db.go b/src/translib/transformer/xlate_to_db.go new file mode 100644 index 0000000000..f839e96ddd --- /dev/null +++ b/src/translib/transformer/xlate_to_db.go @@ -0,0 +1,489 @@ +package transformer + +import ( + "errors" + "fmt" + "github.com/openconfig/ygot/ygot" + "os" + "reflect" + "regexp" + "strings" + "translib/db" + "translib/ocbinds" + "github.com/openconfig/ygot/ytypes" + + log "github.com/golang/glog" +) + +const SONIC_TABLE_INDEX = 2 +const SONIC_FIELD_INDEX = 3 + +/* Invoke the post tansformer */ +func postXfmrHandlerFunc(inParams XfmrParams) (map[string]map[string]db.Value, error) { + xpath, _ := RemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(xSpecMap[xpath].xfmrPost, inParams) + if err != nil { + return nil, err + } + retData := ret[0].Interface().(map[string]map[string]db.Value) + log.Info("Post Transformer function :", xSpecMap[xpath].xfmrPost, " Xpath: ", xpath, " retData: ", retData) + return retData, err +} + +/* Fill redis-db map with field & value info */ +func dataToDBMapAdd(tableName string, dbKey string, result map[string]map[string]db.Value, field string, value string) { + _, ok := result[tableName] + if !ok { + result[tableName] = make(map[string]db.Value) + } + + _, ok = result[tableName][dbKey] + if !ok { + result[tableName][dbKey] = db.Value{Field: make(map[string]string)} + } + + result[tableName][dbKey].Field[field] = value + return +} + +/* Fill the redis-db map with data */ +func mapFillData(d *db.DB, ygRoot *ygot.GoStruct, oper int, uri string, dbKey string, result map[string]map[string]db.Value, xpathPrefix string, name string, value interface{}) error { + xpath := xpathPrefix + "/" + name + xpathInfo := xSpecMap[xpath] + log.Infof("name: \"%v\", xpathPrefix(\"%v\").", name, xpathPrefix) + + if xpathInfo == nil { + log.Errorf("Yang path(\"%v\") not found.", xpath) + return errors.New("Invalid URI") + } + + if xpathInfo.tableName == nil { + log.Errorf("Table for yang-path(\"%v\") not found.", xpath) + return errors.New("Invalid table name") + } + + if len(dbKey) == 0 { + log.Errorf("Table key for yang path(\"%v\") not found.", xpath) + return errors.New("Invalid table key") + } + + if xpathInfo.isKey { + return nil + } + if len(xpathInfo.xfmrFunc) > 0 { + uri = uri + "/" + name + + /* field transformer present */ + log.Infof("Transformer function(\"%v\") invoked for yang path(\"%v\").", xpathInfo.xfmrFunc, xpath) + path, _ := ygot.StringToPath(uri, ygot.StructuredPath, ygot.StringSlicePath) + for _, p := range path.Elem { + pathSlice := strings.Split(p.Name, ":") + p.Name = pathSlice[len(pathSlice)-1] + if len(p.Key) > 0 { + for ekey, ent := range p.Key { + eslice := strings.Split(ent, ":") + p.Key[ekey] = eslice[len(eslice)-1] + } + } + } + ocbSch, _ := ocbinds.Schema() + schRoot := ocbSch.RootSchema() + node, nErr := ytypes.GetNode(schRoot, (*ygRoot).(*ocbinds.Device), path) + log.Info("GetNode data: ", node[0].Data, " nErr :", nErr) + if nErr != nil { + return nErr + } + var dbs [db.MaxDB]*db.DB + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, uri, oper, "", nil, node[0].Data) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return err + } + retData := ret[0].Interface().(map[string]string) + log.Info("Transformer function :", xpathInfo.xfmrFunc, " Xpath: ", xpath, " retData: ", retData) + for f, v := range retData { + dataToDBMapAdd(*xpathInfo.tableName, dbKey, result, f, v) + } + return nil + } + + if len(xpathInfo.fieldName) == 0 { + log.Infof("Field for yang-path(\"%v\") not found in DB.", xpath) + return errors.New("Invalid field name") + } + fieldName := xpathInfo.fieldName + valueStr := "" + if xpathInfo.yangEntry.IsLeafList() { + /* Both yang side and Db side('@' suffix field) the data type is leaf-list */ + log.Info("Yang type and Db type is Leaflist for field = ", xpath) + fieldName += "@" + if reflect.ValueOf(value).Kind() != reflect.Slice { + logStr := fmt.Sprintf("Value for yang xpath %v which is a leaf-list should be a slice", xpath) + log.Error(logStr) + err := errors.New(logStr) + return err + } + valData := reflect.ValueOf(value) + for fidx := 0; fidx < valData.Len(); fidx++ { + if fidx > 0 { + valueStr += "," + } + fVal := fmt.Sprintf("%v", valData.Index(fidx).Interface()) + valueStr = valueStr + fVal + } + log.Infof("leaf-list value after conversion to DB format %v : %v", fieldName, valueStr) + + } else { // xpath is a leaf + valueStr = fmt.Sprintf("%v", value) + if strings.Contains(valueStr, ":") { + valueStr = strings.Split(valueStr, ":")[1] + } + } + + dataToDBMapAdd(*xpathInfo.tableName, dbKey, result, fieldName, valueStr) + log.Infof("TblName: \"%v\", key: \"%v\", field: \"%v\", valueStr: \"%v\".", + *xpathInfo.tableName, dbKey, fieldName, valueStr) + return nil +} + +func cvlYangReqToDbMapCreate(jsonData interface{}, result map[string]map[string]db.Value) error { + if reflect.ValueOf(jsonData).Kind() == reflect.Map { + data := reflect.ValueOf(jsonData) + for _, key := range data.MapKeys() { + _, ok := xDbSpecMap[key.String()] + if ok { + directDbMapData(key.String(), data.MapIndex(key).Interface(), result) + } else { + cvlYangReqToDbMapCreate(data.MapIndex(key).Interface(), result) + } + } + } + return nil +} + +func directDbMapData(tableName string, jsonData interface{}, result map[string]map[string]db.Value) bool { + _, ok := xDbSpecMap[tableName] + + if ok && xDbSpecMap[tableName].dbEntry != nil { + dbSpecData := xDbSpecMap[tableName].dbEntry + tblKeyName := strings.Split(dbSpecData.Key, " ") + data := reflect.ValueOf(jsonData) + result[tableName] = make(map[string]db.Value) + + for idx := 0; idx < data.Len(); idx++ { + keyName := "" + d := data.Index(idx).Interface().(map[string]interface{}) + for i, k := range tblKeyName { + if i > 0 { + keyName += "|" + } + keyName += fmt.Sprintf("%v", d[k]) + delete(d, k) + } + + result[tableName][keyName] = db.Value{Field: make(map[string]string)} + for field, value := range d { + fieldXpath := tableName + "/" + field + if _, fieldOk := xDbSpecMap[fieldXpath]; (fieldOk && (xDbSpecMap[fieldXpath].dbEntry != nil)) { + log.Info("Found non-nil yang entry in xDbSpecMap for field xpath = ", fieldXpath) + if xDbSpecMap[fieldXpath].dbEntry.IsLeafList() { + log.Info("Yang type is Leaflist for field = ", field) + field += "@" + fieldDt := reflect.ValueOf(value) + fieldValue := "" + for fidx := 0; fidx < fieldDt.Len(); fidx++ { + if fidx > 0 { + fieldValue += "," + } + fVal := fmt.Sprintf("%v", fieldDt.Index(fidx).Interface()) + fieldValue = fieldValue + fVal + } + result[tableName][keyName].Field[field] = fieldValue + continue + } + } else { + // should ideally never happen , just adding for safety + log.Info("Did not find entry in xDbSpecMap for field xpath = ", fieldXpath) + } + result[tableName][keyName].Field[field] = fmt.Sprintf("%v", value) + } + } + return true + } + return false +} + +/* Get the db table, key and field name for the incoming delete request */ +func dbMapDelete(d *db.DB, ygRoot *ygot.GoStruct, oper int, path string, jsonData interface{}, result map[string]map[string]db.Value) error { + var err error + if isCvlYang(path) { + xpathPrefix, keyName, tableName := sonicXpathKeyExtract(path) + log.Infof("Delete req: path(\"%v\"), key(\"%v\"), xpathPrefix(\"%v\"), tableName(\"%v\").", path, keyName, xpathPrefix, tableName) + err = cvlYangReqToDbMapDelete(xpathPrefix, tableName, keyName, result) + } else { + xpathPrefix, keyName, tableName := xpathKeyExtract(d, ygRoot, oper, path) + log.Infof("Delete req: path(\"%v\"), key(\"%v\"), xpathPrefix(\"%v\"), tableName(\"%v\").", path, keyName, xpathPrefix, tableName) + spec, ok := xSpecMap[xpathPrefix] + if ok { + if spec.tableName != nil { + result[*spec.tableName] = make(map[string]db.Value) + if len(keyName) > 0 { + result[*spec.tableName][keyName] = db.Value{Field: make(map[string]string)} + if spec.yangEntry != nil && spec.yangEntry.Node.Statement().Keyword == "leaf" { + result[*spec.tableName][keyName].Field[spec.fieldName] = "" + } + } + } else if len(spec.childTable) > 0 { + for _, child := range spec.childTable { + result[child] = make(map[string]db.Value) + } + } + } + } + log.Infof("Delete req: path(\"%v\") result(\"%v\").", path, result) + return err +} + +func cvlYangReqToDbMapDelete(xpathPrefix string, tableName string, keyName string, result map[string]map[string]db.Value) error { + if (tableName != "") { + // Specific table entry case + result[tableName] = make(map[string]db.Value) + if (keyName != "") { + // Specific key case + var dbVal db.Value + tokens:= strings.Split(xpathPrefix, "/") + // Format /module:container/tableName[key]/fieldName + if tokens[len(tokens)-2] == tableName { + // Specific leaf case + fieldName := tokens[len(tokens)-1] + dbVal.Field = make(map[string]string) + dbVal.Field[fieldName] = "" + } + result[tableName][keyName] = dbVal + } else { + // Get all keys + } + } else { + // Get all table entries + // If table name not available in xpath get top container name + tokens:= strings.Split(xpathPrefix, ":") + container := "/" + tokens[len(tokens)-1] + if xDbSpecMap[container] != nil { + dbInfo := xDbSpecMap[container] + if dbInfo.fieldType == "container" { + for dir, _ := range dbInfo.dbEntry.Dir { + result[dir] = make(map[string]db.Value) + } + } + } + } + return nil +} + +/* Get the data from incoming update/replace request, create map and fill with dbValue(ie. field:value +to write into redis-db */ +func dbMapUpdate(d *db.DB, ygRoot *ygot.GoStruct, oper int, path string, jsonData interface{}, result map[string]map[string]db.Value) error { + log.Infof("Update/replace req: path(\"%v\").", path) + dbMapCreate(d, ygRoot, oper, path, jsonData, result) + log.Infof("Update/replace req: path(\"%v\") result(\"%v\").", path, result) + printDbData(result, "/tmp/yangToDbDataUpRe.txt") + return nil +} + +/* Get the data from incoming create request, create map and fill with dbValue(ie. field:value +to write into redis-db */ +func dbMapCreate(d *db.DB, ygRoot *ygot.GoStruct, oper int, path string, jsonData interface{}, result map[string]map[string]db.Value) error { + var err error + root := xpathRootNameGet(path) + if isCvlYang(path) { + err = cvlYangReqToDbMapCreate(jsonData, result) + } else { + err = yangReqToDbMapCreate(d, ygRoot, oper, root, "", "", jsonData, result) + } + if err == nil { + if oper == CREATE { + moduleNm := "/" + strings.Split(path, "/")[1] + log.Infof("Module name for path %s is %s", path, moduleNm) + if _, ok := xSpecMap[moduleNm]; ok { + if xSpecMap[moduleNm].yangDataType == "container" { + log.Info("Invoke post transformer: ", xSpecMap[moduleNm].xfmrPost) + dbDataMap := make(map[db.DBNum]map[string]map[string]db.Value) + dbDataMap[db.ConfigDB] = result + var dbs [db.MaxDB]*db.DB + inParams := formXfmrInputRequest(d, dbs, db.ConfigDB, ygRoot, path, oper, "", &dbDataMap, nil) + result, err = postXfmrHandlerFunc(inParams) + } + } else { + log.Errorf("No Entry exists for module %s in XSpecMap. Unable to process post xfmr (\"%v\") path(\"%v\") error (\"%v\").", oper, path, err) + } + } + printDbData(result, "/tmp/yangToDbDataCreate.txt") + } else { + log.Errorf("DBMapCreate req failed for oper (\"%v\") path(\"%v\") error (\"%v\").", oper, path, err) + } + return err +} + +func yangReqToDbMapCreate(d *db.DB, ygRoot *ygot.GoStruct, oper int, uri string, xpathPrefix string, keyName string, jsonData interface{}, result map[string]map[string]db.Value) error { + log.Infof("key(\"%v\"), xpathPrefix(\"%v\").", keyName, xpathPrefix) + var dbs [db.MaxDB]*db.DB + + if reflect.ValueOf(jsonData).Kind() == reflect.Slice { + log.Infof("slice data: key(\"%v\"), xpathPrefix(\"%v\").", keyName, xpathPrefix) + jData := reflect.ValueOf(jsonData) + dataMap := make([]interface{}, jData.Len()) + for idx := 0; idx < jData.Len(); idx++ { + dataMap[idx] = jData.Index(idx).Interface() + } + for _, data := range dataMap { + curKey := "" + curUri, _ := uriWithKeyCreate(uri, xpathPrefix, data) + if len(xSpecMap[xpathPrefix].xfmrKey) > 0 { + /* key transformer present */ + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curUri, oper, "", nil, nil) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xSpecMap[xpathPrefix].xfmrKey), inParams) + if err != nil { + return err + } + curKey = ret[0].Interface().(string) + } else { + curKey = keyCreate(keyName, xpathPrefix, data) + } + yangReqToDbMapCreate(d, ygRoot, oper, curUri, xpathPrefix, curKey, data, result) + } + } else { + if reflect.ValueOf(jsonData).Kind() == reflect.Map { + jData := reflect.ValueOf(jsonData) + for _, key := range jData.MapKeys() { + typeOfValue := reflect.TypeOf(jData.MapIndex(key).Interface()).Kind() + + log.Infof("slice/map data: key(\"%v\"), xpathPrefix(\"%v\").", keyName, xpathPrefix) + xpath := uri + curUri := uri + pathAttr := key.String() + if len(xpathPrefix) > 0 { + if strings.Contains(pathAttr, ":") { + pathAttr = strings.Split(pathAttr, ":")[1] + } + xpath = xpathPrefix + "/" + pathAttr + curUri = uri + "/" + pathAttr + } + + if (typeOfValue == reflect.Map || typeOfValue == reflect.Slice) && xSpecMap[xpath].yangDataType != "leaf-list" { + if xSpecMap[xpath] != nil && len(xSpecMap[xpath].xfmrFunc) > 0 { + /* subtree transformer present */ + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curUri, oper, "", nil, nil) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return nil + } + mapCopy(result, ret[0].Interface().(map[string]map[string]db.Value)) + } else { + yangReqToDbMapCreate(d, ygRoot, oper, curUri, xpath, keyName, jData.MapIndex(key).Interface(), result) + } + } else { + pathAttr := key.String() + if strings.Contains(pathAttr, ":") { + pathAttr = strings.Split(pathAttr, ":")[1] + } + value := jData.MapIndex(key).Interface() + log.Infof("data field: key(\"%v\"), value(\"%v\").", key, value) + err := mapFillData(d, ygRoot, oper, uri, keyName, result, xpathPrefix, + pathAttr, value) + if err != nil { + log.Errorf("Failed constructing data for db write: key(\"%v\"), value(\"%v\"), path(\"%v\").", + pathAttr, value, xpathPrefix) + } + } + } + } + } + return nil +} + +func sonicXpathKeyExtract(path string) (string, string, string) { + xpath, keyStr, tableName := "", "", "" + var err error + xpath, err = RemoveXPATHPredicates(path) + if err != nil { + return xpath, keyStr, tableName + } + rgp := regexp.MustCompile(`\[([^\[\]]*)\]`) + pathsubStr := strings.Split(path , "/") + if len(pathsubStr) > SONIC_TABLE_INDEX { + tableName = strings.Split(pathsubStr[SONIC_TABLE_INDEX], "[")[0] + for i, kname := range rgp.FindAllString(path, -1) { + if i > 0 { keyStr += "|" } + val := strings.Split(kname, "=")[1] + keyStr += strings.TrimRight(val, "]") + } + } + return xpath, keyStr, tableName +} + +/* Extract key vars, create db key and xpath */ +func xpathKeyExtract(d *db.DB, ygRoot *ygot.GoStruct, oper int, path string) (string, string, string) { + keyStr := "" + tableName := "" + rgp := regexp.MustCompile(`\[([^\[\]]*)\]`) + curPathWithKey := "" + var dbs [db.MaxDB]*db.DB + + for _, k := range strings.Split(path, "/") { + curPathWithKey += k + if strings.Contains(k, "[") { + if len(keyStr) > 0 { + keyStr += "|" + } + yangXpath, _ := RemoveXPATHPredicates(curPathWithKey) + _, ok := xSpecMap[yangXpath] + if ok { + if len(xSpecMap[yangXpath].xfmrKey) > 0 { + xfmrFuncName := yangToDbXfmrFunc(xSpecMap[yangXpath].xfmrKey) + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curPathWithKey, oper, "", nil, nil) + ret, err := XlateFuncCall(xfmrFuncName, inParams) + if err != nil { + return "", "", "" + } + keyStr = ret[0].Interface().(string) + } else { + var keyl []string + for _, kname := range rgp.FindAllString(k, -1) { + keyl = append(keyl, strings.TrimRight(strings.TrimLeft(kname, "["), "]")) + } + keyStr += keyFromXpathCreate(keyl) + } + } + } + curPathWithKey += "/" + } + pfxPath, _ := RemoveXPATHPredicates(path) + tblPtr := xSpecMap[pfxPath].tableName + if tblPtr != nil { + tableName = *tblPtr + } + + return pfxPath, keyStr, tableName +} + +/* Debug function to print the map data into file */ +func printDbData(db map[string]map[string]db.Value, fileName string) { + fp, err := os.Create(fileName) + if err != nil { + return + } + defer fp.Close() + + for k, v := range db { + fmt.Fprintf(fp, "-----------------------------------------------------------------\r\n") + fmt.Fprintf(fp, "table name : %v\r\n", k) + for ik, iv := range v { + fmt.Fprintf(fp, " key : %v\r\n", ik) + for k, d := range iv.Field { + fmt.Fprintf(fp, " %v :%v\r\n", k, d) + } + } + } + fmt.Fprintf(fp, "-----------------------------------------------------------------\r\n") + return +} diff --git a/src/translib/transformer/xlate_utils.go b/src/translib/transformer/xlate_utils.go new file mode 100644 index 0000000000..849f34e5f0 --- /dev/null +++ b/src/translib/transformer/xlate_utils.go @@ -0,0 +1,388 @@ +package transformer + +import ( + "fmt" + "strings" + "reflect" + "translib/db" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" + log "github.com/golang/glog" +) + +/* Create db key from datd xpath(request) */ +func keyFromXpathCreate(keyList []string) string { + keyOut := "" + for i, k := range keyList { + if i > 0 { keyOut += "_" } + if strings.Contains(k, ":") { + k = strings.Split(k, ":")[1] + } + keyOut += strings.Split(k, "=")[1] + } + return keyOut +} + +/* Create db key from data xpath(request) */ +func keyCreate(keyPrefix string, xpath string, data interface{}) string { + _, ok := xSpecMap[xpath] + if ok { + if xSpecMap[xpath].yangEntry != nil { + yangEntry := xSpecMap[xpath].yangEntry + if len(keyPrefix) > 0 { keyPrefix += "|" } + keyVal := "" + for i, k := range (strings.Split(yangEntry.Key, " ")) { + if i > 0 { keyVal = keyVal + "_" } + val := fmt.Sprint(data.(map[string]interface{})[k]) + if strings.Contains(val, ":") { + val = strings.Split(val, ":")[1] + } + keyVal += val + } + keyPrefix += string(keyVal) + } + } + return keyPrefix +} + +/* Copy redis-db source to destn map */ +func mapCopy(destnMap map[string]map[string]db.Value, srcMap map[string]map[string]db.Value) { + for table, tableData := range srcMap { + _, ok := destnMap[table] + if !ok { + destnMap[table] = make(map[string]db.Value) + } + for rule, ruleData := range tableData { + _, ok = destnMap[table][rule] + if !ok { + destnMap[table][rule] = db.Value{Field: make(map[string]string)} + } + for field, value := range ruleData.Field { + destnMap[table][rule].Field[field] = value + } + } + } +} + +func parentXpathGet(xpath string) string { + path := "" + if len(xpath) > 0 { + p := strings.Split(xpath, "/") + path = strings.Join(p[:len(p)-1], "/") + } + return path +} + +func isYangResType(ytype string) bool { + if ytype == "choose" || ytype == "case" { + return true + } + return false +} + +func yangTypeGet(entry *yang.Entry) string { + if entry != nil && entry.Node != nil { + return entry.Node.Statement().Keyword + } + return "" +} + +func dbKeyToYangDataConvert(uri string, xpath string, dbKey string) (map[string]interface{}, string, error) { + var err error + if len(uri) == 0 && len(xpath) == 0 && len(dbKey) == 0 { + err = fmt.Errorf("Insufficient input") + return nil, "", err + } + + if _, ok := xSpecMap[xpath]; ok { + if xSpecMap[xpath].yangEntry == nil { + err = fmt.Errorf("Yang Entry not available for xpath ", xpath) + return nil, "", nil + } + } + + var kLvlValList []string + keyDataList := strings.Split(dbKey, "|") + keyNameList := yangKeyFromEntryGet(xSpecMap[xpath].yangEntry) + id := xSpecMap[xpath].keyLevel + uriWithKey := fmt.Sprintf("%v", xpath) + + /* if uri contins key, use it else use xpath */ + if strings.Contains(uri, "[") { + uriWithKey = fmt.Sprintf("%v", uri) + } + + if len(xSpecMap[xpath].xfmrKey) > 0 { + var dbs [db.MaxDB]*db.DB + inParams := formXfmrInputRequest(nil, dbs, db.MaxDB, nil, uri, GET, dbKey, nil, nil) + ret, err := XlateFuncCall(dbToYangXfmrFunc(xSpecMap[xpath].xfmrKey), inParams) + if err != nil { + return nil, "", err + } + rmap := ret[0].Interface().(map[string]interface{}) + for k, v := range rmap { + uriWithKey += fmt.Sprintf("[%v=%v]", k, v) + } + return rmap, uriWithKey, nil + } + kLvlValList = append(kLvlValList, keyDataList[id]) + + if len(keyNameList) > 1 { + kLvlValList = strings.Split(keyDataList[id], "_") + } + + /* TODO: Need to add leaf-ref related code in here and remove this code*/ + kvalExceedFlag := false + chgId := -1 + if len(keyNameList) < len(kLvlValList) { + kvalExceedFlag = true + chgId = len(keyNameList) - 1 + } + + rmap := make(map[string]interface{}) + for i, kname := range keyNameList { + kval := kLvlValList[i] + + /* TODO: Need to add leaf-ref related code in here and remove this code*/ + if kvalExceedFlag && (i == chgId) { + kval = strings.Join(kLvlValList[chgId:], "_") + } + + uriWithKey += fmt.Sprintf("[%v=%v]", kname, kval) + rmap[kname] = kval + } + + return rmap, uriWithKey, nil +} + +func contains(sl []string, str string) bool { + for _, v := range sl { + if v == str { + return true + } + } + return false +} + +func isSubtreeRequest(targetUriPath string, nodePath string) bool { + if len(targetUriPath) > 0 && len(nodePath) > 0 { + return strings.HasPrefix(targetUriPath, nodePath) + } + return false +} + +func getYangPathFromUri(uri string) (string, error) { + var path *gnmi.Path + var err error + + path, err = ygot.StringToPath(uri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + log.Errorf("Error in uri to path conversion: %v", err) + return "", err + } + + yangPath, yperr := ygot.PathToSchemaPath(path) + if yperr != nil { + log.Errorf("Error in Gnmi path to Yang path conversion: %v", yperr) + return "", yperr + } + + return yangPath, err +} + +func yangKeyFromEntryGet(entry *yang.Entry) []string { + var keyList []string + for _, key := range strings.Split(entry.Key, " ") { + keyList = append(keyList, key) + } + return keyList +} + +func isCvlYang(path string) bool { + if strings.HasPrefix(path, "/sonic") { + return true + } + return false +} + +func sonicKeyDataAdd(keyNameList []string, keyStr string, resultMap map[string]interface{}) { + keyValList := strings.Split(keyStr, "|") + if len(keyNameList) != len(keyValList) { + return + } + + for i, keyName := range keyNameList { + resultMap[keyName] = keyValList[i] + } +} + +func yangToDbXfmrFunc(funcName string) string { + return ("YangToDb_" + funcName) +} + +func uriWithKeyCreate (uri string, xpathTmplt string, data interface{}) (string, error) { + var err error + if _, ok := xSpecMap[xpathTmplt]; ok { + yangEntry := xSpecMap[xpathTmplt].yangEntry + if yangEntry != nil { + for _, k := range (strings.Split(yangEntry.Key, " ")) { + uri += fmt.Sprintf("[%v=%v]", k, data.(map[string]interface{})[k]) + } + } else { + err = fmt.Errorf("Yang Entry not available for xpath ", xpathTmplt) + } + } else { + err = fmt.Errorf("No entry in xSpecMap for xpath ", xpathTmplt) + } + return uri, err +} + +func xpathRootNameGet(path string) string { + if len(path) > 0 { + pathl := strings.Split(path, "/") + return ("/" + pathl[1]) + } + return "" +} + +func getDbNum(xpath string ) db.DBNum { + _, ok := xSpecMap[xpath] + if ok { + xpathInfo := xSpecMap[xpath] + return xpathInfo.dbIndex + } + // Default is ConfigDB + return db.ConfigDB +} + +func dbToYangXfmrFunc(funcName string) string { + return ("DbToYang_" + funcName) +} + +func uriModuleNameGet(uri string) (string, error) { + var err error + result := "" + if len(uri) == 0 { + log.Error("Empty uri string supplied") + err = fmt.Errorf("Empty uri string supplied") + return result, err + } + urislice := strings.Split(uri, ":") + if len(urislice) == 1 { + log.Errorf("uri string %s does not have module name", uri) + err = fmt.Errorf("uri string does not have module name: ", uri) + return result, err + } + moduleNm := strings.Split(urislice[0], "/") + result = moduleNm[1] + if len(strings.Trim(result, " ")) == 0 { + log.Error("Empty module name") + err = fmt.Errorf("No module name found in uri %s", uri) + } + log.Info("module name = ", result) + return result, err +} + +func recMap(rMap interface{}, name []string, id int, max int) { + if id == max || id < 0 { + return + } + val := name[id] + if reflect.ValueOf(rMap).Kind() == reflect.Map { + data := reflect.ValueOf(rMap) + dMap := data.Interface().(map[string]interface{}) + _, ok := dMap[val] + if ok { + recMap(dMap[val], name, id+1, max) + } else { + dMap[val] = make(map[string]interface{}) + recMap(dMap[val], name, id+1, max) + } + } + return +} + +func mapCreate(xpath string) map[string]interface{} { + retMap := make(map[string]interface{}) + if len(xpath) > 0 { + attrList := strings.Split(xpath, "/") + alLen := len(attrList) + recMap(retMap, attrList, 1, alLen) + } + return retMap +} + +func mapInstGet(name []string, id int, max int, inMap interface{}) map[string]interface{} { + if inMap == nil { + return nil + } + result := reflect.ValueOf(inMap).Interface().(map[string]interface{}) + if id == max { + return result + } + val := name[id] + if reflect.ValueOf(inMap).Kind() == reflect.Map { + data := reflect.ValueOf(inMap) + dMap := data.Interface().(map[string]interface{}) + _, ok := dMap[val] + if ok { + result = mapInstGet(name, id+1, max, dMap[val]) + } else { + return result + } + } + return result +} + +func mapGet(xpath string, inMap map[string]interface{}) map[string]interface{} { + attrList := strings.Split(xpath, "/") + alLen := len(attrList) + recMap(inMap, attrList, 1, alLen) + retMap := mapInstGet(attrList, 1, alLen, inMap) + return retMap +} + +func formXfmrInputRequest(d *db.DB, dbs [db.MaxDB]*db.DB, cdb db.DBNum, ygRoot *ygot.GoStruct, uri string, oper int, key string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, param interface{}) XfmrParams { + var inParams XfmrParams + inParams.d = d + inParams.dbs = dbs + inParams.curDb = cdb + inParams.ygRoot = ygRoot + inParams.uri = uri + inParams.oper = oper + inParams.key = key + inParams.dbDataMap = dbDataMap + inParams.param = param // generic param + + return inParams +} + +func findByValue(m map[string]string, value string) string { + for key, val := range m { + if val == value { + return key + } + } + return "" +} +func findByKey(m map[string]string, key string) string { + if val, found := m[key]; found { + return val + } + return "" +} +func findInMap(m map[string]string, str string) string { + // Check if str exists as a value in map m. + if val := findByKey(m, str); val != "" { + return val + } + + // Check if str exists as a key in map m. + if val := findByValue(m, str); val != "" { + return val + } + + // str doesn't exist in map m. + return "" +} diff --git a/src/translib/transformer/xspec.go b/src/translib/transformer/xspec.go new file mode 100644 index 0000000000..80774588e4 --- /dev/null +++ b/src/translib/transformer/xspec.go @@ -0,0 +1,451 @@ +package transformer + +import ( + "fmt" + "os" + "strings" + log "github.com/golang/glog" + "translib/db" + + "github.com/openconfig/goyang/pkg/yang" +) + +/* Data needed to construct lookup table from yang */ +type yangXpathInfo struct { + yangDataType string + tableName *string + xfmrTbl *string + childTable []string + dbEntry *yang.Entry + yangEntry *yang.Entry + keyXpath map[int]*[]string + delim string + fieldName string + xfmrFunc string + xfmrPost string + validateFunc string + xfmrKey string + dbIndex db.DBNum + keyLevel int + isKey bool +} + +type dbInfo struct { + fieldType string + dbEntry *yang.Entry + yangXpath []string +} + +var xSpecMap map[string]*yangXpathInfo +var xDbSpecMap map[string]*dbInfo +var xDbSpecOrdTblMap map[string][]string //map of module-name to ordered list of db tables { "sonic-acl" : ["ACL_TABLE", "ACL_RULE"] } + +/* update transformer spec with db-node */ +func updateDbTableData (xpath string, xpathData *yangXpathInfo, tableName string) { + _, ok := xDbSpecMap[tableName] + if ok { + xDbSpecMap[tableName].yangXpath = append(xDbSpecMap[tableName].yangXpath, xpath) + xpathData.dbEntry = xDbSpecMap[tableName].dbEntry + } +} + +/* Recursive api to fill the map with yang details */ +func yangToDbMapFill (keyLevel int, xSpecMap map[string]*yangXpathInfo, entry *yang.Entry, xpathPrefix string) { + xpath := "" + /* create the yang xpath */ + if xSpecMap[xpathPrefix] != nil && xSpecMap[xpathPrefix].yangDataType == "module" { + /* module name is separated from the rest of xpath with ":" */ + xpath = xpathPrefix + ":" + entry.Name + } else { + xpath = xpathPrefix + "/" + entry.Name + } + + xpathData, ok := xSpecMap[xpath] + if !ok { + xpathData = new(yangXpathInfo) + xSpecMap[xpath] = xpathData + xpathData.dbIndex = db.ConfigDB // default value + } else { + xpathData = xSpecMap[xpath] + } + + xpathData.yangDataType = entry.Node.Statement().Keyword + if entry.Node.Statement().Keyword == "list" && xpathData.tableName != nil { + childToUpdateParent(xpath, *xpathData.tableName) + } + + parentXpathData, ok := xSpecMap[xpathPrefix] + /* init current xpath table data with its parent data, change only if needed. */ + if ok { + if xpathData.tableName == nil && parentXpathData.tableName != nil && xpathData.xfmrTbl == nil { + xpathData.tableName = parentXpathData.tableName + } else if xpathData.xfmrTbl == nil && parentXpathData.xfmrTbl != nil { + xpathData.xfmrTbl = parentXpathData.xfmrTbl + } + } + + if ok && parentXpathData.dbIndex != db.ConfigDB { + xpathData.dbIndex = parentXpathData.dbIndex + } + + if ok && len(parentXpathData.validateFunc) > 0 { + xpathData.validateFunc = parentXpathData.validateFunc + } + + if xpathData.yangDataType == "leaf" && len(xpathData.fieldName) == 0 { + if xpathData.tableName != nil && xDbSpecMap[*xpathData.tableName] != nil { + if xDbSpecMap[*xpathData.tableName].dbEntry.Dir[entry.Name] != nil { + xpathData.fieldName = entry.Name + } else if xDbSpecMap[*xpathData.tableName].dbEntry.Dir[strings.ToUpper(entry.Name)] != nil { + xpathData.fieldName = strings.ToUpper(entry.Name) + } + } else if xpathData.xfmrTbl != nil { + /* table transformer present */ + xpathData.fieldName = entry.Name + } + } + + if xpathData.yangDataType == "leaf" && len(xpathData.fieldName) > 0 && xpathData.tableName != nil { + dbPath := *xpathData.tableName + "/" + xpathData.fieldName + if xDbSpecMap[dbPath] != nil { + xDbSpecMap[dbPath].yangXpath = append(xDbSpecMap[dbPath].yangXpath, xpath) + } + } + + /* fill table with key data. */ + curKeyLevel := keyLevel + if len(entry.Key) != 0 { + parentKeyLen := 0 + + /* create list with current keys */ + keyXpath := make([]string, len(strings.Split(entry.Key, " "))) + for id, keyName := range(strings.Split(entry.Key, " ")) { + keyXpath[id] = xpath + "/" + keyName + keyXpathData := new(yangXpathInfo) + xSpecMap[xpath + "/" + keyName] = keyXpathData + xSpecMap[xpath + "/" + keyName].isKey = true + } + + xpathData.keyXpath = make(map[int]*[]string, (parentKeyLen + 1)) + k := 0 + for ; k < parentKeyLen; k++ { + /* copy parent key-list to child key-list*/ + xpathData.keyXpath[k] = parentXpathData.keyXpath[k] + } + xpathData.keyXpath[k] = &keyXpath + xpathData.keyLevel = curKeyLevel + curKeyLevel++ + } else if parentXpathData != nil && parentXpathData.keyXpath != nil { + xpathData.keyXpath = parentXpathData.keyXpath + } + + /* get current obj's children */ + var childList []string + for k := range entry.Dir { + childList = append(childList, k) + } + + xpathData.yangEntry = entry + /* now recurse, filling the map with current node's children info */ + for _, child := range childList { + yangToDbMapFill(curKeyLevel, xSpecMap, entry.Dir[child], xpath) + } +} + +/* Build lookup table based of yang xpath */ +func yangToDbMapBuild(entries map[string]*yang.Entry) { + if entries == nil { + return + } + + if xSpecMap == nil { + xSpecMap = make(map[string]*yangXpathInfo) + } + + for module, e := range entries { + if e == nil || len(e.Dir) == 0 { + continue + } + + /* Start to fill xpath based map with yang data */ + keyLevel := 0 + yangToDbMapFill(keyLevel, xSpecMap, e, "") + + // Fill the ordered map of child tables list for oc yangs + updateSchemaOrderedMap(module, e) + } + mapPrint(xSpecMap, "/tmp/fullSpec.txt") + dbMapPrint() +} + +/* Fill the map with db details */ +func dbMapFill(prefixPath string, curPath string, moduleNm string, trkTpCnt bool, xDbSpecMap map[string]*dbInfo, entry *yang.Entry) { + entryType := entry.Node.Statement().Keyword + if entryType == "list" { + prefixPath = entry.Name + } + + if !isYangResType(entryType) { + dbXpath := prefixPath + if entryType != "list" { + dbXpath = prefixPath + "/" + entry.Name + } + xDbSpecMap[dbXpath] = new(dbInfo) + xDbSpecMap[dbXpath].dbEntry = entry + xDbSpecMap[dbXpath].fieldType = entryType + } + + + var childList []string + for _, k := range entry.DirOKeys { + childList = append(childList, k) + } + + if entryType == "container" && trkTpCnt { + xDbSpecOrdTblMap[moduleNm] = childList + log.Info("xDbSpecOrdTblMap after appending ", xDbSpecOrdTblMap) + trkTpCnt = false + } + + for _, child := range childList { + childPath := prefixPath + "/" + entry.Dir[child].Name + dbMapFill(prefixPath, childPath, moduleNm, trkTpCnt, xDbSpecMap, entry.Dir[child]) + } +} + +/* Build redis db lookup map */ +func dbMapBuild(entries []*yang.Entry) { + if entries == nil { + return + } + xDbSpecMap = make(map[string]*dbInfo) + xDbSpecOrdTblMap = make(map[string][]string) + + for _, e := range entries { + if e == nil || len(e.Dir) == 0 { + continue + } + moduleNm := e.Name + log.Info("Module name", moduleNm) + xDbSpecOrdTblMap[moduleNm] = []string{} + trkTpCnt := true + dbMapFill("", "", moduleNm, trkTpCnt, xDbSpecMap, e) + } +} + +func childToUpdateParent( xpath string, tableName string) { + var xpathData *yangXpathInfo + parent := parentXpathGet(xpath) + if len(parent) == 0 || parent == "/" { + return + } + + _, ok := xSpecMap[parent] + if !ok { + xpathData = new(yangXpathInfo) + xSpecMap[parent] = xpathData + } + xSpecMap[parent].childTable = append(xSpecMap[parent].childTable, tableName) + if xSpecMap[parent].yangEntry != nil && xSpecMap[parent].yangEntry.Node.Statement().Keyword == "list" { + return + } + childToUpdateParent(parent, tableName) +} + +/* Build lookup map based on yang xpath */ +func annotEntryFill(xSpecMap map[string]*yangXpathInfo, xpath string, entry *yang.Entry) { + xpathData := new(yangXpathInfo) + _, ok := xSpecMap[xpath] + if !ok { + fmt.Printf("Xpath not found(%v) \r\n", xpath) + } + + xpathData.dbIndex = db.ConfigDB // default value + /* fill table with yang extension data. */ + if entry != nil && len(entry.Exts) > 0 { + for _, ext := range entry.Exts { + dataTagArr := strings.Split(ext.Keyword, ":") + tagType := dataTagArr[len(dataTagArr)-1] + switch tagType { + case "table-name" : + if xpathData.tableName == nil { + xpathData.tableName = new(string) + } + *xpathData.tableName = ext.NName() + updateDbTableData(xpath, xpathData, *xpathData.tableName) + //childToUpdateParent(xpath, *xpathData.tableName) + case "table-transformer" : + if xpathData.xfmrTbl == nil { + xpathData.xfmrTbl = new(string) + } + *xpathData.xfmrTbl = ext.NName() + case "field-name" : + xpathData.fieldName = ext.NName() + case "subtree-transformer" : + xpathData.xfmrFunc = ext.NName() + case "key-transformer" : + xpathData.xfmrKey = ext.NName() + case "key-delimiter" : + xpathData.delim = ext.NName() + case "field-transformer" : + xpathData.xfmrFunc = ext.NName() + case "post-transformer" : + xpathData.xfmrPost = ext.NName() + case "get-validate" : + xpathData.validateFunc = ext.NName() + case "use-self-key" : + xpathData.keyXpath = nil + case "redis-db-name" : + if ext.NName() == "APPL_DB" { + xpathData.dbIndex = db.ApplDB + } else if ext.NName() == "ASIC_DB" { + xpathData.dbIndex = db.AsicDB + } else if ext.NName() == "COUNTERS_DB" { + xpathData.dbIndex = db.CountersDB + } else if ext.NName() == "LOGLEVEL_DB" { + xpathData.dbIndex = db.LogLevelDB + } else if ext.NName() == "CONFIG_DB" { + xpathData.dbIndex = db.ConfigDB + } else if ext.NName() == "FLEX_COUNTER_DB" { + xpathData.dbIndex = db.FlexCounterDB + } else if ext.NName() == "STATE_DB" { + xpathData.dbIndex = db.StateDB + } else { + xpathData.dbIndex = db.ConfigDB + } + } + } + } + xSpecMap[xpath] = xpathData +} + +/* Build xpath from yang-annotation */ +func xpathFromDevCreate(path string) string { + p := strings.Split(path, "/") + for i, k := range p { + if len(k) > 0 { p[i] = strings.Split(k, ":")[1] } + } + return strings.Join(p[1:], "/") +} + +/* Build lookup map based on yang xpath */ +func annotToDbMapBuild(annotEntries []*yang.Entry) { + if annotEntries == nil { + return + } + if xSpecMap == nil { + xSpecMap = make(map[string]*yangXpathInfo) + } + + for _, e := range annotEntries { + if e != nil && len(e.Deviations) > 0 { + for _, d := range e.Deviations { + xpath := xpathFromDevCreate(d.Name) + xpath = "/" + strings.Replace(e.Name, "-annot", "", -1) + ":" + xpath + for i, deviate := range d.Deviate { + if i == 2 { + for _, ye := range deviate { + annotEntryFill(xSpecMap, xpath, ye) + } + } + } + } + } + } + mapPrint(xSpecMap, "/tmp/annotSpec.txt") +} + +/* Debug function to print the yang xpath lookup map */ +func mapPrint(inMap map[string]*yangXpathInfo, fileName string) { + fp, err := os.Create(fileName) + if err != nil { + return + } + defer fp.Close() + + for k, d := range inMap { + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") + fmt.Fprintf(fp, "%v:\r\n", k) + fmt.Fprintf(fp, " yangDataType: %v\r\n", d.yangDataType) + fmt.Fprintf(fp, " tableName: ") + if d.tableName != nil { + fmt.Fprintf(fp, "%v", *d.tableName) + } + fmt.Fprintf(fp, "\r\n xfmrTbl : ") + if d.xfmrTbl != nil { + fmt.Fprintf(fp, "%v", *d.xfmrTbl) + } + fmt.Fprintf(fp, "\r\n childTbl : %v", d.childTable) + fmt.Fprintf(fp, "\r\n FieldName: %v", d.fieldName) + fmt.Fprintf(fp, "\r\n keyLevel : %v", d.keyLevel) + fmt.Fprintf(fp, "\r\n xfmrKeyFn: %v", d.xfmrKey) + fmt.Fprintf(fp, "\r\n xfmrFunc : %v", d.xfmrFunc) + fmt.Fprintf(fp, "\r\n dbIndex : %v", d.dbIndex) + fmt.Fprintf(fp, "\r\n validateFunc : %v", d.validateFunc) + fmt.Fprintf(fp, "\r\n yangEntry: ") + if d.yangEntry != nil { + fmt.Fprintf(fp, "%v", *d.yangEntry) + } + fmt.Fprintf(fp, "\r\n dbEntry: ") + if d.dbEntry != nil { + fmt.Fprintf(fp, "%v", *d.dbEntry) + } + fmt.Fprintf(fp, "\r\n keyXpath: %d\r\n", d.keyXpath) + for i, kd := range d.keyXpath { + fmt.Fprintf(fp, " %d. %#v\r\n", i, kd) + } + fmt.Fprintf(fp, "\r\n isKey : %v\r\n", d.isKey) + } + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") + +} + +/* Debug function to print redis db lookup map */ +func dbMapPrint() { + fp, err := os.Create("/tmp/dbTmplt.txt") + if err != nil { + return + } + defer fp.Close() + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") + for k, v := range xDbSpecMap { + fmt.Fprintf(fp, " field:%v \r\n", k) + fmt.Fprintf(fp, " type :%v \r\n", v.fieldType) + fmt.Fprintf(fp, " Yang :%v \r\n", v.yangXpath) + fmt.Fprintf(fp, " DB :%v \r\n", v.dbEntry) + fmt.Fprintf (fp, "-----------------------------------------------------------------\r\n") + + } +} + +func updateSchemaOrderedMap(module string, entry *yang.Entry) { + var children []string + if entry.Node.Statement().Keyword == "module" { + for _, dir := range entry.DirOKeys { + // Gives the yang xpath for the top level container + xpath := "/" + module + ":" + dir + _, ok := xSpecMap[xpath] + if ok { + yentry := xSpecMap[xpath].yangEntry + if yentry.Node.Statement().Keyword == "container" { + var keyspec = make([]KeySpec, 0) + keyspec = FillKeySpecs(xpath, "" , &keyspec) + children = updateChildTable(keyspec, &children) + } + } + } + } + xDbSpecOrdTblMap[module] = children +} + +func updateChildTable(keyspec []KeySpec, chlist *[]string) ([]string) { + for _, ks := range keyspec { + if (ks.Ts.Name != "") { + if !contains(*chlist, ks.Ts.Name) { + *chlist = append(*chlist, ks.Ts.Name) + } + } + *chlist = updateChildTable(ks.Child, chlist) + } + return *chlist +} diff --git a/src/translib/translib.go b/src/translib/translib.go index c482dc7567..925e2df53f 100644 --- a/src/translib/translib.go +++ b/src/translib/translib.go @@ -1,8 +1,21 @@ -/////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom Inc. -// -/////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// /* Package translib implements APIs like Create, Get, Subscribe etc. @@ -16,9 +29,6 @@ Redis ABNF format and persisting them in the Redis DB. It can also translate the ABNF format to YANG specific JSON IETF format This package can also talk to non-DB clients. - -Example: TBD - */ package translib @@ -407,7 +417,7 @@ func Get(req GetRequest) (GetResponse, error){ defer closeAllDbs(dbs[:]) - err = (*app).translateGet (dbs) + err = (*app).translateGet (dbs) if err != nil { resp = GetResponse{Payload:payload, ErrSrc:AppErr} diff --git a/src/translib/translib_test.go b/src/translib/translib_test.go index 7d391757b2..9ceb1e1a80 100644 --- a/src/translib/translib_test.go +++ b/src/translib/translib_test.go @@ -1,10 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + package translib_test import ( "fmt" "path/filepath" - "runtime" "reflect" + "runtime" "testing" ) @@ -36,5 +55,5 @@ func equals(tb testing.TB, exp, act interface{}) { } func Test_Create(t *testing.T) { - + } diff --git a/tools/pyang/pyang_plugins/openapi.py b/tools/pyang/pyang_plugins/openapi.py index 649cb7e975..45a407d88d 100644 --- a/tools/pyang/pyang_plugins/openapi.py +++ b/tools/pyang/pyang_plugins/openapi.py @@ -1,6 +1,21 @@ -## Open Api Spec output plugin(swagger 2.0) -## Author: Mohammed Faraaz C -## Company: Broadcom Inc. +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ import optparse import sys diff --git a/tools/swagger_codegen/go-server/src/swagger/routes.go b/tools/swagger_codegen/go-server/src/swagger/routes.go index f21baa80f2..ce305b72aa 100644 --- a/tools/swagger_codegen/go-server/src/swagger/routes.go +++ b/tools/swagger_codegen/go-server/src/swagger/routes.go @@ -1,8 +1,21 @@ -/////////////////////////////////////////////////// -// -// Copyright 2019 Broadcom Inc. -// -/////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// package swagger diff --git a/ygot-modified-files/leaf.go b/ygot-modified-files/leaf.go index 7440e551e8..605029bef4 100644 --- a/ygot-modified-files/leaf.go +++ b/ygot-modified-files/leaf.go @@ -252,7 +252,7 @@ func validateUnion(schema *yang.Entry, value interface{}) util.Errors { // during validation against each matching schema otherwise. func validateMatchingSchemas(schema *yang.Entry, value interface{}) util.Errors { var errors []error - ss := findMatchingSchemasInUnion(schema.Type, value) + ss := findMatchingSchemasInUnion(schema, schema.Type, value) var kk []yang.TypeKind for _, s := range ss { kk = append(kk, s.Type.Kind) @@ -283,17 +283,25 @@ func validateMatchingSchemas(schema *yang.Entry, value interface{}) util.Errors // findMatchingSchemasInUnion returns all schemas in the given union type, // including those within nested unions, that match the Go type of value. // value must not be nil. -func findMatchingSchemasInUnion(ytype *yang.YangType, value interface{}) []*yang.Entry { +func findMatchingSchemasInUnion(schema *yang.Entry, ytype *yang.YangType, value interface{}) []*yang.Entry { var matches []*yang.Entry util.DbgPrint("findMatchingSchemasInUnion for type %T, kind %s", value, reflect.TypeOf(value).Kind()) for _, t := range ytype.Type { if t.Kind == yang.Yunion { // Recursively check all union types within this union. - matches = append(matches, findMatchingSchemasInUnion(t, value)...) + matches = append(matches, findMatchingSchemasInUnion(schema, t, value)...) continue } + if t.Kind == yang.Yleafref { + ns, err := findLeafRefSchema(schema, t.Path) + if err != nil { + log.Warningf("not found base Go type for type %v in union value %s", t.Kind, util.ValueStr(value)) + continue + } + t = ns.Type + } ybt := yangBuiltinTypeToGoType(t.Kind) if reflect.ValueOf(value).Kind() == reflect.Ptr { ybt = ygot.ToPtr(yangBuiltinTypeToGoType(t.Kind))