diff --git a/Makefile b/Makefile index d2f9b1fe07..f78cee25bb 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 @@ -63,6 +75,8 @@ cli: rest-server 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 @@ -80,6 +94,7 @@ codegen: yamlGen: $(MAKE) -C models/yang + $(MAKE) -C models/yang/sonic go-patch: go-deps cd $(BUILD_GOPATH)/src/github.com/openconfig/ygot/; git reset --hard HEAD; git checkout 724a6b18a9224343ef04fe49199dfb6020ce132a 2>/dev/null ; true; \ @@ -93,14 +108,28 @@ 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)/models/yang/sonic/*.yang $(DESTDIR)/usr/models/yang/ + $(INSTALL) -D $(TOPDIR)/models/yang/sonic/common/*.yang $(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..243c4168cd --- /dev/null +++ b/goyang-modified-files/annotate.go @@ -0,0 +1,395 @@ +// 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 = "" + var prefix string = "" + generate(w, e, path, prefix) + // { 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, prefix 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/%s-annot\";\n", e.Prefix.Name) + 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]) + } + } + // Include the module for which annotation is being generated + fmt.Fprintf(w, " import %s { prefix %s; }\n", e.Name, e.Prefix.Name) + + fmt.Fprintln(w) + } + } + + name := e.Name + if prefix == "" && e.Prefix != nil { + prefix = e.Prefix.Name + } + name = prefix + ":" + name + + if (e.Node.Kind() != "module") { + path = path + "/" + 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 { + pathList := strings.Split(a.Name, "/") + pathList = pathList[1:] + for i, pvar := range pathList { + if len(pvar) > 0 && !strings.Contains(pvar, ":") { + pvar = e.Prefix.Name + ":" + pvar + pathList[i] = pvar + } + } + path = "/" + strings.Join(pathList, "/") + handleAugments(w, a, e.Node.(*yang.Module).Grouping, e.Prefix.Name, path) + } + } + } + + for _, k := range names { + generate(w, e.Dir[k], path, prefix) + } + +} + +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 + } + 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..0c9d7edd42 --- /dev/null +++ b/models/yang/annotations/openconfig-acl-annot.yang @@ -0,0 +1,196 @@ +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"; + } + } + + 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..263cf9274d --- /dev/null +++ b/models/yang/common/sonic-extensions.yang @@ -0,0 +1,83 @@ +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 db-name { + argument "db-name"; + description "DB name that will indicate where data is stored. Eg: Config DB, App DB etc"; + } + extension table-transformer { + argument "table-transformer-name"; + description "Db table transformer name.This can be applied to either transform yang value to some different format + or choose a specific DB table based on the type."; + } +} 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/models/yang/sonic/Makefile b/models/yang/sonic/Makefile new file mode 100644 index 0000000000..9141ff952a --- /dev/null +++ b/models/yang/sonic/Makefile @@ -0,0 +1,53 @@ +TOPDIR := ../../../ +SONIC_YANGAPI_DIR := $(TOPDIR)/build/yaml +SONIC_YANGDIR := $(TOPDIR)/models/yang/sonic +SONIC_YANGDIR_DEVIATION := $(TOPDIR)/models/yang/sonic/deviation +SONIC_YANGDIR_COMMON := $(TOPDIR)/models/yang/sonic/common +SONIC_YANGDIR_COMMON_IETF := $(TOPDIR)/models/yang/sonic/common/ietf +SONIC_YANG_MOD_FILES := $(shell find $(SONIC_YANGDIR) -maxdepth 1 -name '*.yang' | sort) +SONIC_YANG_COMMON_FILES := $(shell find $(SONIC_YANGDIR_COMMON) -name '*.yang' | sort) +SONIC_YANG_COMMON_FILES += $(shell find $(SONIC_YANGDIR_COMMON_IETF) -name '*.yang' | sort) + +SONIC_TOOLS_DIR := $(TOPDIR)/tools +SONIC_PYANG_DIR := $(SONIC_TOOLS_DIR)/pyang +SONIC_PYANG_PLUGIN_DIR := $(SONIC_PYANG_DIR)/pyang_plugins +SONIC_PYANG_BIN := pyang + +all: yamlGen allyangs.tree allyangs_tree.html + +#yamlGen: $(SONIC_YANGAPI_DIR)/.done + +allyangs.tree: $(SONIC_YANG_MOD_FILES) $(SONIC_YANG_COMMON_FILES) + $(SONIC_PYANG_BIN) \ + -f tree \ + -o $(SONIC_YANGDIR)/$@ \ + -p $(SONIC_YANGDIR_COMMON):$(SONIC_YANGDIR) \ + $(SONIC_YANG_MOD_FILES) + @echo "+++++ Generation of YANG tree for Sonic Yang modules completed +++++" + +allyangs_tree.html: $(SONIC_YANG_MOD_FILES) $(SONIC_YANG_COMMON_FILES) + $(SONIC_PYANG_BIN) \ + -f jstree \ + -o $(SONIC_YANGDIR)/$@ \ + -p $(SONIC_YANGDIR_COMMON):$(SONIC_YANGDIR) \ + $(SONIC_YANG_MOD_FILES) + @echo "+++++ Generation of HTML tree for Sonic Yang modules completed +++++" + +#====================================================================== +# Generate YAML files for SONiC YANG modules +#====================================================================== +yamlGen: + @echo "+++++ Generating YAML files for Sonic Yang modules +++++" + mkdir -p $(SONIC_YANGAPI_DIR) + $(SONIC_PYANG_BIN) \ + -f swaggerapi \ + --outdir $(SONIC_YANGAPI_DIR) \ + --plugindir $(SONIC_PYANG_PLUGIN_DIR) \ + -p $(SONIC_YANGDIR_COMMON):$(SONIC_YANGDIR) \ + $(SONIC_YANG_MOD_FILES) + @echo "+++++ Generation of YAML files for Sonic Yang modules completed +++++" + +clean: + @echo "Removing files ..." + rm -rf $(SONIC_YANGAPI_DIR) + rm -rf allyangs.tree allyangs_tree.html diff --git a/models/yang/sonic/common/ietf/ietf-inet-types.yang b/models/yang/sonic/common/ietf/ietf-inet-types.yang new file mode 100644 index 0000000000..2f14270dec --- /dev/null +++ b/models/yang/sonic/common/ietf/ietf-inet-types.yang @@ -0,0 +1,457 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - ipv6-address-no-zone"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of types related to protocol fields ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet + protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code Point + that may be used for marking packets in a traffic stream. + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The ipv6-flow-label type represents the flow identifier or Flow + Label in an IPv6 packet header that may be used to + discriminate traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport-layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from . + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of types related to autonomous systems ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASes. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4001: Textual Conventions for Internet Network Addresses + RFC 6793: BGP Support for Four-Octet Autonomous System (AS) + Number Space"; + } + + /*** collection of types related to IP addresses and hostnames ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representation + implies the IP version. This type supports scoped addresses + by allowing zone identifiers in the address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the textual + representation defined in Section 4 of RFC 5952. The + canonical format for the zone index is the numerical + format as described in Section 11.2 of RFC 4007."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-no-zone { + type union { + type inet:ipv4-address-no-zone; + type inet:ipv6-address-no-zone; + } + description + "The ip-address-no-zone type represents an IP address and is + IP version neutral. The format of the textual representation + implies the IP version. This type does not support scoped + addresses since it does not allow zone identifiers in the + address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address-no-zone { + type inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, the IPv6 address is represented + as defined in Section 4 of RFC 5952."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + length "1..253"; + pattern + '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitly or may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be A-labels as per RFC 5890."; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + +} diff --git a/models/yang/sonic/common/ietf/ietf-yang-types.yang b/models/yang/sonic/common/ietf/ietf-yang-types.yang new file mode 100644 index 0000000000..ee58fa3ab0 --- /dev/null +++ b/models/yang/sonic/common/ietf/ietf-yang-types.yang @@ -0,0 +1,474 @@ +module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - yang-identifier + - hex-string + - uuid + - dotted-quad"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier-related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifiers. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type; the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of types related to date and time***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z + all represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using + the time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually + referring to the notion of local time) uses the time-offset + -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence + happened. The specific occurrence must be defined in the + description of any schema node defined using this type. When + the specific occurrence occurred prior to the last time the + associated timeticks attribute was zero, then the timestamp + value is zero. Note that this requires all timestamp values + to be reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML-specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + /*** collection of string types ***/ + + typedef hex-string { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "A hexadecimal string with octets represented as hex digits + separated by colons. The canonical representation uses + lowercase characters."; + } + + typedef uuid { + type string { + pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' + + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + } + description + "A Universally Unique IDentifier in the string representation + defined in RFC 4122. The canonical representation uses + lowercase characters. + + The following is an example of a UUID in string representation: + f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + "; + reference + "RFC 4122: A Universally Unique IDentifier (UUID) URN + Namespace"; + } + + typedef dotted-quad { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; + } + description + "An unsigned 32-bit number expressed in the dotted-quad + notation, i.e., four octets written as decimal numbers + and separated with the '.' (full stop) character."; + } +} diff --git a/models/yang/sonic/common/sonic-common.yang b/models/yang/sonic/common/sonic-common.yang new file mode 100644 index 0000000000..77a81c5f84 --- /dev/null +++ b/models/yang/sonic/common/sonic-common.yang @@ -0,0 +1,41 @@ + +module sonic-common { + namespace "http://github.com/Azure/sonic-common"; + prefix scommon; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + typedef tagging_mode { + type enumeration { + enum untagged; + enum tagged; + enum priority_tagged; + } + } + + typedef admin-status { + type enumeration { + enum up; + enum down; + } + } + + + container operation { + leaf operation { + type string; + } + } +} diff --git a/src/cvl/schema/sonic-common.yang b/models/yang/sonic/common/sonic-extension.yang similarity index 57% rename from src/cvl/schema/sonic-common.yang rename to models/yang/sonic/common/sonic-extension.yang index be94f4645a..f488809586 100644 --- a/src/cvl/schema/sonic-common.yang +++ b/models/yang/sonic/common/sonic-extension.yang @@ -1,41 +1,22 @@ -module sonic-common { - namespace "http://github.com/Azure/sonic-common"; - prefix sv; - - import ietf-yang-types { - prefix yang; - } +module sonic-extension { + namespace "http://github.com/Azure/sonic-extension"; + prefix sonic-ext; organization - "BRCM"; + "SONiC"; contact - "BRCM"; + "SONiC"; description - "SONIC VLAN"; + "SONIC Extension"; - revision 2019-05-15 { + revision 2019-09-18 { description "Initial revision."; } - typedef tagging_mode { - type enumeration { - enum untagged; - enum tagged; - enum priority_tagged; - } - } - - typedef admin-status { - type enumeration { - enum up; - enum down; - } - } - extension custom-handler { description "Node should be handled by custom handler"; @@ -63,24 +44,18 @@ module sonic-common { extension map-list { description "If it is a map list"; - argument "value"; // + argument "value"; } extension map-leaf { description "Map leaf names"; - argument "value"; // + argument "value"; } extension pf-check { description "Platform specific validation"; - argument "handler"; // - } - - container operation { - leaf operation { - type string; - } + argument "handler"; } } diff --git a/models/yang/sonic/sonic-acl.yang b/models/yang/sonic/sonic-acl.yang new file mode 100644 index 0000000000..6c57edcbba --- /dev/null +++ b/models/yang/sonic/sonic-acl.yang @@ -0,0 +1,221 @@ +module sonic-acl { + namespace "http://github.com/Azure/sonic-acl"; + prefix sacl; + yang-version 1.1; + + import ietf-inet-types { + prefix inet; + } + + import sonic-common { + prefix cmn; + } + + import sonic-port { + prefix prt; + } + + import sonic-mirror-session { + prefix sms; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC ACL"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-acl { + + container ACL_TABLE { + + list ACL_TABLE_LIST { + key "aclname"; + + leaf aclname { + type string { + pattern '[a-zA-Z0-9]{1}([-a-zA-Z0-9_]{0,71})'; + length 1..72; + } + } + + leaf policy_desc { + type string { + length 1..255 { + error-app-tag policy-desc-invalid-length; + } + } + } + + leaf stage { + type enumeration { + enum INGRESS; + enum EGRESS; + } + } + + leaf type { + type enumeration { + enum MIRROR; + enum L2; + enum L3; + enum L3V6; + } + } + + leaf-list ports { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + + container ACL_RULE { + + list ACL_RULE_LIST { + key "aclname rulename"; + + leaf aclname { + type leafref { + path "../../../ACL_TABLE/ACL_TABLE_LIST/aclname"; + } + must "(/cmn:operation/cmn:operation != 'DELETE') or " + + "count(current()/../../../ACL_TABLE/ACL_TABLE_LIST[aclname=current()]/ports) = 0" { + error-message "Ports are already bound to this rule."; + } + } + + leaf rulename { + type string; + } + + leaf PRIORITY { + type uint16 { + range "1..65535"{ + error-message "Invalid ACL rule priority."; + } + } + } + + leaf RULE_DESCRIPTION { + type string; + } + + leaf PACKET_ACTION { + type enumeration { + enum FORWARD; + enum DROP; + enum REDIRECT; + } + } + + leaf MIRROR_ACTION { + type leafref { + path "/sms:sonic-mirror-session/sms:MIRROR_SESSION/sms:MIRROR_SESSION_LIST/sms:name"; + } + } + + leaf IP_TYPE { + type enumeration { + enum ANY; + enum IP; + enum IPV4; + enum IPV4ANY; + enum NON_IPV4; + enum IPV6ANY; + enum NON_IPV6; + } + } + + leaf IP_PROTOCOL { + type uint8 { + range "1|2|6|17|46|47|51|103|115"; + } + } + + leaf ETHER_TYPE { + type string { + pattern "(0x88CC)|(0x8100)|(0x8915)|(0x0806)|(0x0800)|(0x86DD)|(0x8847)" { + error-message "Invalid ACL Rule Ether Type"; + error-app-tag ether-type-invalid; + } + } + } + + choice ip_src_dst { + case ipv4_src_dst { + //when "boolean(IP_TYPE[.='ANY' or .='IP' or .='IPV4' or .='IPV4ANY'])"; + leaf SRC_IP { + mandatory true; + type inet:ipv4-prefix; + } + leaf DST_IP { + mandatory true; + type inet:ipv4-prefix; + } + } + case ipv6_src_dst { + //when "boolean(IP_TYPE[.='ANY' or .='IP' or .='IPV6' or .='IPV6ANY'])"; + leaf SRC_IPV6 { + mandatory true; + type inet:ipv6-prefix; + } + leaf DST_IPV6 { + mandatory true; + type inet:ipv6-prefix; + } + } + } + + choice src_port { + case l4_src_port { + leaf L4_SRC_PORT { + type uint16; + } + } + case l4_src_port_range { + leaf L4_SRC_PORT_RANGE { + type string { + pattern "[0-9]{1,5}(-)[0-9]{1,5}"; + } + } + } + } + + choice dst_port { + case l4_dst_port { + leaf L4_DST_PORT { + type uint16; + } + } + case l4_dst_port_range { + leaf L4_DST_PORT_RANGE { + type string { + pattern "[0-9]{1,5}(-)[0-9]{1,5}"; + } + } + } + } + + leaf TCP_FLAGS { + type string { + pattern "0[xX][0-9a-fA-F]{2}[/]0[xX][0-9a-fA-F]{2}"; + } + } + + leaf DSCP { + type uint8; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-interface.yang b/models/yang/sonic/sonic-interface.yang new file mode 100644 index 0000000000..30f1f02c42 --- /dev/null +++ b/models/yang/sonic/sonic-interface.yang @@ -0,0 +1,63 @@ +module sonic-interface { + namespace "http://github.com/Azure/sonic-interface"; + prefix sint; + + import ietf-inet-types { + prefix inet; + } + + import sonic-port { + prefix prt; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-interface { + + container INTERFACE { + + list INTERFACE_LIST { + key "portname"; + + leaf portname{ + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf vrf-name { + type string; + } + + } + + list INTERFACE_IPADDR_LIST { + key "portname ip_prefix"; + + leaf portname{ + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } +} diff --git a/models/yang/sonic/sonic-mirror-session.yang b/models/yang/sonic/sonic-mirror-session.yang new file mode 100644 index 0000000000..ff18b5a14a --- /dev/null +++ b/models/yang/sonic/sonic-mirror-session.yang @@ -0,0 +1,60 @@ +module sonic-mirror-session { + namespace "http://github.com/Azure/sonic-mirror-session"; + prefix sms; + + import ietf-inet-types { + prefix inet; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC MIRROR SESSION"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-mirror-session { + + container MIRROR_SESSION { + + list MIRROR_SESSION_LIST { + key "name"; + + leaf name { + type string; + } + + leaf src_ip { + type inet:ipv4-address; + } + + leaf dst_ip { + type inet:ipv4-address; + } + + leaf gre_type { + type string; + } + + leaf dscp { + type uint8; + } + + leaf ttl { + type uint8; + } + + leaf queue { + type uint8; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-port.yang b/models/yang/sonic/sonic-port.yang new file mode 100644 index 0000000000..ba0959f3b6 --- /dev/null +++ b/models/yang/sonic/sonic-port.yang @@ -0,0 +1,79 @@ +module sonic-port { + namespace "http://github.com/Azure/sonic-port"; + prefix prt; + + import sonic-common { + prefix scommon; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-port { + + container PORT { + + list PORT_LIST { + key "ifname"; + + leaf ifname { + type string { + pattern "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])" { + error-message "Invalid interface name"; + error-app-tag interface-name-invalid; + } + } + } + + leaf index { + type uint16; + } + + leaf speed { + type uint64; + } + + leaf valid_speeds { + type string; + } + + leaf alias { + type string; + } + + leaf description { + type string; + } + + leaf mtu{ + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf lanes { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + } + } + } +} diff --git a/src/CLI/Makefile b/src/CLI/Makefile index b920361a6a..a12bdaad28 100644 --- a/src/CLI/Makefile +++ b/src/CLI/Makefile @@ -1,4 +1,22 @@ SHELL = /bin/bash +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + .ONESHELL: .SHELLFLAGS += -e diff --git a/src/CLI/actioner/sonic-cli-if.py b/src/CLI/actioner/sonic-cli-if.py index 5faf8b53b0..261cb32d7f 100755 --- a/src/CLI/actioner/sonic-cli-if.py +++ b/src/CLI/actioner/sonic-cli-if.py @@ -1,4 +1,22 @@ #!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + import sys import time import json @@ -26,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] ] @@ -39,6 +58,22 @@ def generate_body(func, args): elif func.__name__ == 'patch_openconfig_interfaces_interfaces_interface_config_mtu': keypath = [ args[0] ] body = { "openconfig-interfaces:mtu": int(args[1]) } + elif func.__name__ == 'patch_openconfig_if_ethernet_interfaces_interface_ethernet_config_auto_negotiate': + keypath = [ args[0] ] + if args[1] == "true": + body = { "openconfig-if-ethernet:auto-negotiate": True } + else : + body = { "openconfig-if-ethernet:auto-negotiate": False } + elif func.__name__ == 'patch_openconfig_if_ethernet_interfaces_interface_ethernet_config_port_speed': + keypath = [ args[0] ] + speed_map = {"10MBPS":"SPEED_10MB", "100MBPS":"SPEED_100MB", "1GIGE":"SPEED_1GB", "auto":"SPEED_1GB" } + if args[1] not in speed_map.keys(): + print("%Error: Invalid port speed config") + exit(1) + else: + speed = speed_map.get(args[1]) + + body = { "openconfig-if-ethernet:port-speed": speed } elif func.__name__ == 'patch_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config': sp = args[1].split('/') keypath = [ args[0], 0, sp[0] ] @@ -76,10 +111,81 @@ def run(func, args): c.verify_ssl = False aa = openconfig_interfaces_client.OpenconfigInterfacesApi(api_client=openconfig_interfaces_client.ApiClient(configuration=c)) +# Code for PortChannel cli skeleton + + #create a port-channel + if "PortChannel" in args[0] and func.__name__ == 'patch_openconfig_interfaces_interfaces_interface': + return + + #show port-channels summary + if "PortChannel" in args[0] and func.__name__ == 'get_openconfig_interfaces_interfaces': + + dummy_resp= { + "PORTCHANNEL": [ + { + "members": [ + "Ethernet56", + "Ethernet60" + ], + "min-links": 2, + "mtu": 9100, + "admin_status": "up", + "oper_status": "down", + "name": "PortChannel1", + "id": "1" + }, + { + "members": [], + "min-links": 1, + "mtu": 9100, + "admin_status": "up", + "oper_status": "down", + "name": "PortChannel12", + "id": "12" + }, + { + "members": [], + "min-links": 1, + "mtu": 9100, + "admin_status": "up", + "oper_status": "down", + "name": "PortChannel3", + "id": "3" + } + ] + } + show_cli_output(args[1], dummy_resp) + return + + #add members to port-channel + if func.__name__ == 'patch_openconfig_if_aggregate_interfaces_interface_ethernet_config_aggregate_id': + return + + #remove members from port-channel + if func.__name__ == 'delete_openconfig_if_aggregate_interfaces_interface_ethernet_config_aggregate_id': + return + + #config mtu for port-channel + if "po" in args[0] and func.__name__ == 'patch_openconfig_interfaces_interfaces_interface_config_mtu': + return + + #delete port-channel + if "PortChannel" in args[0] and func.__name__ == 'delete_openconfig_interfaces_interfaces_interface': + return + + #config min-links in port-channel + if func.__name__ == 'patch_openconfig_if_aggregate_interfaces_interface_aggregation_config_min_links': + return + # create a body block 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/actioner/sonic-cli-lldp.py b/src/CLI/actioner/sonic-cli-lldp.py index 7c8e98668d..11d71479b2 100644 --- a/src/CLI/actioner/sonic-cli-lldp.py +++ b/src/CLI/actioner/sonic-cli-lldp.py @@ -1,4 +1,22 @@ #!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + import sys import time import json diff --git a/src/CLI/actioner/sonic-cli-pfm.py b/src/CLI/actioner/sonic-cli-pfm.py index 3007c2111c..155e404961 100644 --- a/src/CLI/actioner/sonic-cli-pfm.py +++ b/src/CLI/actioner/sonic-cli-pfm.py @@ -1,4 +1,22 @@ #!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + import sys import time import json diff --git a/src/CLI/actioner/sonic-cli-sflow.py b/src/CLI/actioner/sonic-cli-sflow.py new file mode 100755 index 0000000000..7ac8ac4d5d --- /dev/null +++ b/src/CLI/actioner/sonic-cli-sflow.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +import sys +import time +import json +import ast +from collections import OrderedDict +from scripts.render_cli import show_cli_output + + +import urllib3 +urllib3.disable_warnings() +plugins = dict() + + +def register(func): + """Register sdk client method as a plug-in""" + plugins[func.__name__] = func + return func + +def call_method(name, args): + method = plugins[name] + return method(args) + +def get_sflow(): + pass + +def get_sflow_interface(): + pass + +def run(func, args): + + # create a body block + if (func.__name__ == 'get_sflow'): + sflow_info = {'sflow' : {'admin_state' : 'enabled', 'polling-interval' : 20, 'agent-id' : 'default'}} + else: + sflow_info = {} + sflow_info['sflow'] = OrderedDict() + for i in range(30): + sflow_info['sflow']['Ethernet'+str(i)] = {'admin_state' : 'enabled', 'sampling_rate' : 4000} + show_cli_output(sys.argv[2], sflow_info) + return + +if __name__ == '__main__': + func = eval(sys.argv[1], globals(), {}) + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-sys.py b/src/CLI/actioner/sonic-cli-sys.py index cfa8cfe74f..1fbbb7317d 100644 --- a/src/CLI/actioner/sonic-cli-sys.py +++ b/src/CLI/actioner/sonic-cli-sys.py @@ -1,4 +1,22 @@ #:!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + import sys import time import json diff --git a/src/CLI/actioner/sonic-cli.py b/src/CLI/actioner/sonic-cli.py index ced1c44db1..f694c29db9 100755 --- a/src/CLI/actioner/sonic-cli.py +++ b/src/CLI/actioner/sonic-cli.py @@ -1,4 +1,22 @@ #!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + import sys import time import json diff --git a/src/CLI/clicfg/mgmt_clish_entities.xsl b/src/CLI/clicfg/mgmt_clish_entities.xsl index aac740d8d5..a21cca8d99 100644 --- a/src/CLI/clicfg/mgmt_clish_entities.xsl +++ b/src/CLI/clicfg/mgmt_clish_entities.xsl @@ -1,7 +1,22 @@ + + - diff --git a/src/CLI/clicfg/mgmt_clish_entities_macro.xsl b/src/CLI/clicfg/mgmt_clish_entities_macro.xsl index fb6fd4d92b..f20fd62e6f 100644 --- a/src/CLI/clicfg/mgmt_clish_entities_macro.xsl +++ b/src/CLI/clicfg/mgmt_clish_entities_macro.xsl @@ -1,4 +1,20 @@ + + diff --git a/src/CLI/clicfg/mgmt_clish_feature_master.xsd b/src/CLI/clicfg/mgmt_clish_feature_master.xsd index 5748b93f42..fd7c330370 100644 --- a/src/CLI/clicfg/mgmt_clish_feature_master.xsd +++ b/src/CLI/clicfg/mgmt_clish_feature_master.xsd @@ -1,4 +1,19 @@ + diff --git a/src/CLI/clicfg/mgmt_clish_features.xsl b/src/CLI/clicfg/mgmt_clish_features.xsl index ba9362e0e9..2c9ebad834 100644 --- a/src/CLI/clicfg/mgmt_clish_features.xsl +++ b/src/CLI/clicfg/mgmt_clish_features.xsl @@ -1,4 +1,20 @@ + + diff --git a/src/CLI/clicfg/mgmt_clish_platform.xml b/src/CLI/clicfg/mgmt_clish_platform.xml index 469b542576..f421e8ce26 100644 --- a/src/CLI/clicfg/mgmt_clish_platform.xml +++ b/src/CLI/clicfg/mgmt_clish_platform.xml @@ -1,4 +1,19 @@ + + + + + + + + + + + + - + - - + > + + - - - - - - - - - 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} + elif test "${if-subcommands}" = "Management"; then + if test "${mgmt-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 eth${mgmt-if-id} show_interface_id.j2 ${__full_line} + fi + 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 get_openconfig_interfaces_interfaces PortChannel show_portchannel.j2 + + + + - + + + + - + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface PortChannel${lag-id} + + + + + + + name="vlan-id" + help="Vlan identifier(1-4094)" + ptype="VLAN_ID" + /> + + + + + + + + + + + + + + + + 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_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_if_aggregate_interfaces_interface_ethernet_config_aggregate_id ${iface} ${lag-id} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_aggregate_interfaces_interface_ethernet_config_aggregate_id ${iface} + + + 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_if_aggregate_interfaces_interface_aggregation_config_min_links ${po_id} ${min-links} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_aggregate_interfaces_interface_aggregation_config_min_links ${po_id} 1 + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${po_id} ${mtu} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${po_id} 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} 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_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_if_ethernet_interfaces_interface_ethernet_config_auto_negotiate ${iface} ${autoneg} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ethernet_interfaces_interface_ethernet_config_auto_negotiate ${iface} false + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ethernet_interfaces_interface_ethernet_config_port_speed ${iface} ${speed} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ethernet_interfaces_interface_ethernet_config_port_speed ${iface} 1000 + - + diff --git a/src/CLI/clitree/cli-xml/ip.xml b/src/CLI/clitree/cli-xml/ip.xml index 3ee39cdc4f..1d974a54b2 100644 --- a/src/CLI/clitree/cli-xml/ip.xml +++ b/src/CLI/clitree/cli-xml/ip.xml @@ -1,4 +1,20 @@ + + + + @@ -63,6 +79,42 @@ python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config_prefix_length ${iface} ${addr} - + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config ${iface} ${addr} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config_prefix_length ${iface} ${addr} + + + diff --git a/src/CLI/clitree/cli-xml/ipv6.xml b/src/CLI/clitree/cli-xml/ipv6.xml index 07eb940e47..4449535a7f 100644 --- a/src/CLI/clitree/cli-xml/ipv6.xml +++ b/src/CLI/clitree/cli-xml/ipv6.xml @@ -1,4 +1,20 @@ + + @@ -42,4 +58,34 @@ + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv6_addresses_address_config ${iface} ${addr} + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv6_addresses_address_config_prefix_length ${iface} ${addr} + + + diff --git a/src/CLI/clitree/cli-xml/lldp.xml b/src/CLI/clitree/cli-xml/lldp.xml index eda05c8d36..d03352e24b 100644 --- a/src/CLI/clitree/cli-xml/lldp.xml +++ b/src/CLI/clitree/cli-xml/lldp.xml @@ -1,4 +1,21 @@ + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py get_sflow show_sflow.j2 + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py get_sflow_interface show_sflow_intf.j2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/sonic_types.xml b/src/CLI/clitree/cli-xml/sonic_types.xml index d571eac411..e7d10c81a8 100644 --- a/src/CLI/clitree/cli-xml/sonic_types.xml +++ b/src/CLI/clitree/cli-xml/sonic_types.xml @@ -1,4 +1,20 @@ + + @@ -25,6 +41,27 @@ help="" /> + + + + + + + + + + diff --git a/src/CLI/clitree/macro/acl_macro.xml b/src/CLI/clitree/macro/acl_macro.xml index 90fcbd57a8..329717c992 100644 --- a/src/CLI/clitree/macro/acl_macro.xml +++ b/src/CLI/clitree/macro/acl_macro.xml @@ -1,4 +1,20 @@ + + diff --git a/src/CLI/clitree/scripts/klish_ins_def_cmd.py b/src/CLI/clitree/scripts/klish_ins_def_cmd.py index 2f873c07c1..01032c07b6 100755 --- a/src/CLI/clitree/scripts/klish_ins_def_cmd.py +++ b/src/CLI/clitree/scripts/klish_ins_def_cmd.py @@ -1,4 +1,22 @@ #!/usr/bin/env python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + """ The script klish_Ins_Def_Cmd.py is used to append the "exit" and "end"commands to the views of the klish XML models except for the views diff --git a/src/CLI/clitree/scripts/klish_insert_pipe.py b/src/CLI/clitree/scripts/klish_insert_pipe.py index 169f87daf3..27c074cad3 100755 --- a/src/CLI/clitree/scripts/klish_insert_pipe.py +++ b/src/CLI/clitree/scripts/klish_insert_pipe.py @@ -1,4 +1,22 @@ #!/usr/bin/python +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + ''' This script extends every show and get COMMAND with pipe option ----------------------------------------------------------------''' diff --git a/src/CLI/clitree/scripts/klish_platform_features_process.sh b/src/CLI/clitree/scripts/klish_platform_features_process.sh index 0fed0d6ea2..976d55e8e9 100755 --- a/src/CLI/clitree/scripts/klish_platform_features_process.sh +++ b/src/CLI/clitree/scripts/klish_platform_features_process.sh @@ -1,4 +1,22 @@ #!/bin/bash +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + #set -x # Validate all platform xml files # For all platform_*.xml, run xmllint feature_master.xsd $i.xml diff --git a/src/CLI/clitree/scripts/klish_preproc_cmdtree.py b/src/CLI/clitree/scripts/klish_preproc_cmdtree.py index 28123baf77..140c859f07 100755 --- a/src/CLI/clitree/scripts/klish_preproc_cmdtree.py +++ b/src/CLI/clitree/scripts/klish_preproc_cmdtree.py @@ -1,4 +1,22 @@ #!/usr/bin/python2.7 +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + ''' CLI parser tree preprocessing script before the parser xml-s are copied to sysroot. These are the steps performed: a. Macro replacement diff --git a/src/CLI/clitree/scripts/klish_replace_macro.py b/src/CLI/clitree/scripts/klish_replace_macro.py index 93d8e087bb..436b1dea82 100755 --- a/src/CLI/clitree/scripts/klish_replace_macro.py +++ b/src/CLI/clitree/scripts/klish_replace_macro.py @@ -1,4 +1,22 @@ #!/usr/bin/python2.7 +########################################################################### +# +# Copyright 2019 Dell, 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. +# +########################################################################### + ''' This script does macro replacement on the xml files which are used by klish to defind CLI strucuture. diff --git a/src/CLI/clitree/scripts/platform_dummy.xml b/src/CLI/clitree/scripts/platform_dummy.xml index 191f0c51cc..8cf798a09f 100644 --- a/src/CLI/clitree/scripts/platform_dummy.xml +++ b/src/CLI/clitree/scripts/platform_dummy.xml @@ -1,4 +1,19 @@ + 1 { + yangList := modelInfo.redisTableToYangList[tableInfo.redisTableName] + yangList = append(yangList, tableName) + //Update the map + modelInfo.redisTableToYangList[tableInfo.redisTableName] = yangList + } + leafRefNodes := xmlquery.Find(listNode, "//type[@name='leafref']") if (leafRefNodes == nil) { //Store the tableInfo in global data - modelInfo.tableInfo[tableName] = tableInfo + modelInfo.tableInfo[tableName] = &tableInfo continue } @@ -321,11 +363,11 @@ func storeModelInfo(modelFile string, module *yparser.YParserModule) { //such mo } } - //Find all 'must' expression and store the agains its parent node + //Find all 'must' expression and store the against its parent node mustExps := xmlquery.Find(listNode, "//must") if (mustExps == nil) { //Update the tableInfo in global data - modelInfo.tableInfo[tableName] = tableInfo + modelInfo.tableInfo[tableName] = &tableInfo continue } @@ -349,7 +391,8 @@ func storeModelInfo(modelFile string, module *yparser.YParserModule) { //such mo } //Update the tableInfo in global data - modelInfo.tableInfo[tableName] = tableInfo + modelInfo.tableInfo[tableName] = &tableInfo + } } @@ -434,6 +477,48 @@ func splitRedisKey(key string) (string, string) { return tblName, key[prefixLen:] } +//Get the YANG list name from Redis key +//This just returns same YANG list name as Redis table name +//when 1:1 mapping is there. For one Redis table to +//multiple YANG list, it returns appropriate YANG list name +//INTERFACE:Ethernet12 returns ==> INTERFACE +//INTERFACE:Ethernet12:1.1.1.0/32 ==> INTERFACE_IPADDR +func getRedisKeyToYangList(tableName, key string) string { + mapArr, exists := modelInfo.redisTableToYangList[tableName] + + if exists == false { + //1:1 mapping case + return tableName + } + + //As of now determine the mapping based on number of keys + var foundIdx int = -1 + numOfKeys := 1 //Assume only one key initially + for keyDelim, _ := range modelInfo.allKeyDelims { + foundIdx = strings.Index(key, keyDelim) + if (foundIdx >= 0) { + //Matched with key delim + keyComps := strings.Split(key, keyDelim) + numOfKeys = len(keyComps) + break + } + } + + //Check which list has number of keys as 'numOfKeys' + for i := 0; i < len(mapArr); i++ { + tblInfo, exists := modelInfo.tableInfo[mapArr[i]] + if exists == true { + if (len(tblInfo.keys) == numOfKeys) { + //Found the YANG list matching the number of keys + return mapArr[i] + } + } + } + + //No matches + return tableName +} + //Convert Redis key to Yang keys, if multiple key components are there, //they are separated based on Yang schema func getRedisToYangKeys(tableName string, redisKey string)[]keyValuePairStruct{ @@ -510,9 +595,9 @@ func (c *CVL) checkPathForTableEntry(tableName string, currentValue string, cfgD //Table name should appear like "../VLAN_MEMBER/tagging_mode' or ' // "/prt:PORT/prt:ifname" //re := regexp.MustCompile(fmt.Sprintf(".*[/]([a-zA-Z]*:)?%s[\\[/]", tblNameSrch)) - tblSrchIdx := strings.Index(xpath, fmt.Sprintf("/%s", tblNameSrch)) //no preifx + tblSrchIdx := strings.Index(xpath, fmt.Sprintf("/%s_LIST", tblNameSrch)) //no preifx if (tblSrchIdx < 0) { - tblSrchIdx = strings.Index(xpath, fmt.Sprintf(":%s", tblNameSrch)) //with prefix + tblSrchIdx = strings.Index(xpath, fmt.Sprintf(":%s_LIST", tblNameSrch)) //with prefix } if (tblSrchIdx < 0) { continue @@ -986,14 +1071,16 @@ func (c *CVL) addLeafRef(config bool, tableName string, name string, value strin //only key is there, value wil be fetched and stored here, //if value can't fetched this entry will be deleted that time - if (c.tmpDbCache[refTableName] == nil) { - c.tmpDbCache[refTableName] = map[string]interface{}{redisKey: nil} + //Strip "_LIST" suffix + refRedisTableName := refTableName[0:len(refTableName) - len("_LIST")] + if (c.tmpDbCache[refRedisTableName] == nil) { + c.tmpDbCache[refRedisTableName] = map[string]interface{}{redisKey: nil} } else { - tblMap := c.tmpDbCache[refTableName] + tblMap := c.tmpDbCache[refRedisTableName] _, exist := tblMap.(map[string]interface{})[redisKey] if (exist == false) { tblMap.(map[string]interface{})[redisKey] = nil - c.tmpDbCache[refTableName] = tblMap + c.tmpDbCache[refRedisTableName] = tblMap } } } @@ -1304,13 +1391,26 @@ parent *yparser.YParserNode) CVLRetCode { func (c *CVL) generateTableData(config bool, jsonNode *jsonquery.Node)(*yparser.YParserNode, CVLErrorInfo) { var cvlErrObj CVLErrorInfo + tableName := fmt.Sprintf("%s",jsonNode.Data) c.batchLeaf = "" + //Every Redis table is mapped as list within a container, + //E.g. ACL_RULE is mapped as + // container ACL_RULE { list ACL_RULE_LIST {} } var topNode *yparser.YParserNode + + // Add top most conatiner e.g. 'container sonic-acl {...}' + if _, exists := modelInfo.tableInfo[tableName]; exists == false { + return nil, cvlErrObj + } topNode = c.yp.AddChildNode(modelInfo.tableInfo[tableName].module, nil, modelInfo.tableInfo[tableName].modelName) + //Add the container node for each list + //e.g. 'container ACL_TABLE { list ACL_TABLE_LIST ...} + listConatinerNode := c.yp.AddChildNode(modelInfo.tableInfo[tableName].module, + topNode, tableName) //Traverse each key instance for jsonNode = jsonNode.FirstChild; jsonNode != nil; jsonNode = jsonNode.NextSibling { @@ -1318,6 +1418,9 @@ func (c *CVL) generateTableData(config bool, jsonNode *jsonquery.Node)(*yparser. //For each field check if is key //If it is key, create list as child of top container // Get all key name/value pairs + if yangListName := getRedisKeyToYangList(tableName, jsonNode.Data); yangListName!= "" { + tableName = yangListName + } keyValuePair := getRedisToYangKeys(tableName, jsonNode.Data) keyCompCount := len(keyValuePair) totalKeyComb := 1 @@ -1332,9 +1435,12 @@ func (c *CVL) generateTableData(config bool, jsonNode *jsonquery.Node)(*yparser. } for ; totalKeyComb > 0 ; totalKeyComb-- { + //Get the YANG list name from Redis table name + //Ideally they are same except when one Redis table is split + //into multiple YANG lists //Add table i.e. create list element - listNode := c.addChildNode(tableName, topNode, tableName) //Add the list to the top node + listNode := c.addChildNode(tableName, listConatinerNode, tableName + "_LIST") //Add the list to the top node //For each key combination //Add keys as leaf to the list diff --git a/src/cvl/cvl_api.go b/src/cvl/cvl_api.go index 26bc6ea7f7..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 ( @@ -104,8 +123,9 @@ func Initialize() CVLRetCode { yparser.Initialize() modelInfo.modelNs = make(map[string]modelNamespace) //redis table to model name - modelInfo.tableInfo = make(map[string]modelTableInfo) //model namespace + modelInfo.tableInfo = make(map[string]*modelTableInfo) //model namespace modelInfo.allKeyDelims = make(map[string]bool) //all key delimiter + modelInfo.redisTableToYangList = make(map[string][]string) //Redis table to Yang list map dbNameToDbNum = map[string]uint8{"APPL_DB": APPL_DB, "CONFIG_DB": CONFIG_DB} /* schema */ 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 74d51df3d0..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 ( @@ -2806,16 +2825,16 @@ func TestBadSchema(t *testing.T) { if _, err := os.Stat("/usr/sbin/schema"); os.IsNotExist(err) { //Corrupt some schema file - exec.Command("/bin/sh", "-c", "/bin/cp schema/sonic-port.yin schema/sonic-port.yin.bad" + - " && /bin/sed -i '1 a ' schema/sonic-port.yin.bad").Output() + exec.Command("/bin/sh", "-c", "/bin/cp testdata/schema/sonic-port.yin testdata/schema/sonic-port.yin.bad" + + " && /bin/sed -i '1 a ' testdata/schema/sonic-port.yin.bad").Output() //Parse bad schema file - if module, _ := yparser.ParseSchemaFile("schema/sonic-port.yin.bad.1"); module != nil { //should fail + if module, _ := yparser.ParseSchemaFile("testdata/schema/sonic-port.yin.bad"); module != nil { //should fail t.Errorf("Bad schema parsing should fail.") } //Revert to - exec.Command("/bin/sh", "-c", "/bin/rm schema/sonic-port.yin.bad").Output() + exec.Command("/bin/sh", "-c", "/bin/rm testdata/schema/sonic-port.yin.bad").Output() } else { //Corrupt some schema file exec.Command("/bin/sh", "-c", "/bin/cp /usr/sbin/schema/sonic-port.yin /usr/sbin/schema/sonic-port.yin.bad" + 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 d08f66c702..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 */ @@ -76,15 +95,15 @@ int lyd_data_validate_all(const char *data, const char *depData, const char *oth int lyd_multi_new_leaf(struct lyd_node *parent, const struct lys_module *module, const char *leafVal) { char s[4048]; - char *name, *val; + char *name, *val, *saveptr; strcpy(s, leafVal); - name = strtok(s, "#"); + name = strtok_r(s, "#", &saveptr); while (name != NULL) { - val = strtok(NULL, "#"); + val = strtok_r(NULL, "#", &saveptr); if (val != NULL) { if (NULL == lyd_new_leaf(parent, module, name, val)) @@ -93,7 +112,7 @@ int lyd_multi_new_leaf(struct lyd_node *parent, const struct lys_module *module, } } - name = strtok(NULL, "#"); + name = strtok_r(NULL, "#", &saveptr); } return 0; @@ -250,7 +269,12 @@ func ParseSchemaFile(modelFile string) (*YParserModule, YParserError) { //Add child node to a parent node func(yp *YParser) AddChildNode(module *YParserModule, parent *YParserNode, name string) *YParserNode { - return (*YParserNode)(C.lyd_new((*C.struct_lyd_node)(parent), (*C.struct_lys_module)(module), C.CString(name))) + ret := (*YParserNode)(C.lyd_new((*C.struct_lyd_node)(parent), (*C.struct_lys_module)(module), C.CString(name))) + if (ret == nil) { + TRACE_LOG(INFO_DEBUG, TRACE_YPARSER, "Failed parsing node %s\n", name) + } + + return ret } //Add child node to a parent node 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 1408bb1f58..3af437be35 100644 --- a/src/cvl/schema/Makefile +++ b/src/cvl/schema/Makefile @@ -1,26 +1,64 @@ -src_files=$(wildcard *.yang) -out=$(patsubst %.yang, %.yin, $(src_files)) -out_ext=$(patsubst %.yang, %.tree, $(src_files)) +################################################################################ +# # +# 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:schema +sonic_yang=../../../models/yang/sonic +sonic_yang_common=../../../models/yang/sonic/common +pyang_plugin_dir=../../../tools/pyang/pyang_plugins -schema: $(out) +src_files=$(wildcard $(sonic_yang)/*.yang) +src_files += $(wildcard $(sonic_yang_common)/*.yang) +out=$(patsubst %.yang, %.yin, $(shell ls -1 $(sonic_yang)/*.yang | cut -d'/' -f7)) +out_common=$(patsubst %.yang, %.yin, $(shell ls -1 $(sonic_yang_common)/*.yang | cut -d'/' -f8)) +out_tree=$(patsubst %.yang, %.tree, $(src_files)) +search_path=$(sonic_yang):$(sonic_yang_common):$(sonic_yang_common)/ietf -%.yin:%.yang - @echo "Generating $@ ..." - @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ +#all: yamlGen allyangs.tree allyangs_tree.html schema +all: schema + +schema: $(out) $(out_common) + + +schema-tree: $(out_tree) + +#Build YANG models +%.yin:$(sonic_yang)/%.yang + @echo "Generating `basename $@` ..." + @devFile="`echo $@ | cut -d . -f1`-deviation.yang"; \ + if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ + 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 $@` ..." + @devFile="`echo $@ | cut -d . -f1`-deviation.yang"; \ if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ - pyang -p ./ietf/ -f yin $$devOpt $< -o $@.tmp - @xmllint --noblanks $@.tmp > $@ - @rm -rf $@.tmp + pyang -p $(search_path) --plugindir $(pyang_plugin_dir) \ + -f yin-cvl $$devOpt $< -o `basename $@` %.tree:%.yang - @echo "Generating $@ ..." + @echo "Generating `basename $@` ..." @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ - pyang -p ./ietf/ -f tree $$devOpt $< -o $@ + pyang -p $(search_path) -f tree $$devOpt $< -o `basename $@` clean: @echo "Removing files ..." - rm -rf $(out) - rm -rf $(out_ext) + rm -rf *.yin *.tree diff --git a/src/cvl/schema/sonic-acl.yang b/src/cvl/schema/sonic-acl.yang deleted file mode 100644 index 61ed679d8d..0000000000 --- a/src/cvl/schema/sonic-acl.yang +++ /dev/null @@ -1,245 +0,0 @@ -module sonic-acl { - namespace "http://github.com/Azure/sonic-acl"; - prefix sacl; - yang-version 1.1; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - import sonic-portchannel { - prefix spc; - } - - import sonic-mirror-session { - prefix sms; - } - - import sonic-pf-limits { - prefix spf; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC ACL"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-acl { - - list ACL_TABLE { - key "aclname"; - - /* must "count(/prt:sonic-port/prt:PORT) > 0"; */ - - leaf aclname { - type string { - pattern '[a-zA-Z0-9]{1}([-a-zA-Z0-9_]{0,71})'; - length 1..72; - } - } - - leaf policy_desc { - type string { - length 1..255 { - error-app-tag policy-desc-invalid-length; - } - } - } - - leaf stage { - type enumeration { - enum INGRESS; - enum EGRESS; - } - } - - leaf type { - type enumeration { - enum MIRROR; - enum L2; - enum L3; - enum L3V6; - } - } - - leaf-list ports { - type union { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - type leafref { - path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:name"; - } - } - } - } - - list ACL_RULE { - key "aclname rulename"; - scommon:pf-check "ACL_CheckAclLimits"; - - /* Limit for number of dynamic ACL rules */ - /*must "count(../ACL_RULE) > /spf:sonic-pf-limits/acl/MAX_ACL_RULES" { - error-message "Number of ACL rules reached max platform limit."; - } - must "PRIORITY > /spf:sonic-pf-limits/acl/MAX_PRIORITY" { - error-message "Invalid ACL rule priority."; - } - must "count(../ACL_TABLE) > 0 and count(/prt:sonic-port/prt:PORT) > 0"; //Temporary work-around - */ - - leaf aclname { - type leafref { - path "../../ACL_TABLE/aclname"; - } - must "(/scommon:operation/scommon:operation != 'DELETE') or " + - "count(current()/../../ACL_TABLE[aclname=current()]/ports) = 0" { - error-message "Ports are already bound to this rule."; - } - } - - leaf rulename { - type string; - } - - leaf PRIORITY { - type uint16 { - range "1..65535"{ - error-message "Invalid ACL rule priority."; - } - } - } - - leaf RULE_DESCRIPTION { - type string; - } - - leaf PACKET_ACTION { - type enumeration { - enum FORWARD; - enum DROP; - enum REDIRECT; - } - } - - leaf MIRROR_ACTION { - type leafref { - path "/sms:sonic-mirror-session/sms:MIRROR_SESSION/sms:name"; - } - } - - leaf IP_TYPE { - type enumeration { - enum ANY; - enum IP; - enum IPV4; - enum IPV4ANY; - enum NON_IPV4; - enum IPV6ANY; - enum NON_IPV6; - } - } - - leaf IP_PROTOCOL { - type uint8 { - range "1|2|6|17|46|47|51|103|115"; - } - } - - leaf ETHER_TYPE { - type string{ - pattern "(0x88CC)|(0x8100)|(0x8915)|(0x0806)|(0x0800)|(0x86DD)|(0x8847)"{ - error-message "Invalid ACL Rule Ether Type"; - error-app-tag ether-type-invalid; - } - } - } - - choice ip_src_dst { - case ipv4_src_dst { - //when "boolean(IP_TYPE[.='ANY' or .='IP' or .='IPV4' or .='IPV4ANY'])"; - leaf SRC_IP { - mandatory true; - type inet:ipv4-prefix; - } - leaf DST_IP { - mandatory true; - type inet:ipv4-prefix; - } - } - case ipv6_src_dst { - //when "boolean(IP_TYPE[.='ANY' or .='IP' or .='IPV6' or .='IPV6ANY'])"; - leaf SRC_IPV6 { - mandatory true; - type inet:ipv6-prefix; - } - leaf DST_IPV6 { - mandatory true; - type inet:ipv6-prefix; - } - } - } - - choice src_port { - case l4_src_port { - leaf L4_SRC_PORT { - type uint16; - } - } - case l4_src_port_range { - leaf L4_SRC_PORT_RANGE { - type string { - pattern "[0-9]{1,5}(-)[0-9]{1,5}"; - } - } - } - } - - choice dst_port { - case l4_dst_port { - leaf L4_DST_PORT { - type uint16; - } - } - case l4_dst_port_range { - leaf L4_DST_PORT_RANGE { - type string { - pattern "[0-9]{1,5}(-)[0-9]{1,5}"; - } - } - } - } - - leaf TCP_FLAGS { - type string { - pattern "0[xX][0-9a-fA-F]{2}[/]0[xX][0-9a-fA-F]{2}"; - } - } - - leaf DSCP { - type uint8; - } - } - } -} diff --git a/src/cvl/schema/sonic-bgp-neighbor.yang b/src/cvl/schema/sonic-bgp-neighbor.yang deleted file mode 100644 index f1cf321615..0000000000 --- a/src/cvl/schema/sonic-bgp-neighbor.yang +++ /dev/null @@ -1,88 +0,0 @@ -module sonic-bgp-neighbor { - namespace "http://github.com/Azure/sonic-bgp-neighbor"; - prefix sbn; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC BGP NEIGHBOR"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-bgp-neighbor { - list BGP_NEIGHBOR { - key "ipaddress"; - - leaf ipaddress{ - type inet:ip-address; - } - - leaf rrclient { - type uint8 { - range "0..255"; - } - } - - leaf admin_status{ - type scommon:admin-status; - } - - leaf peer_addr{ - type inet:ip-address; - } - - leaf name { - type string; - } - - leaf local_addr { - type inet:ipv4-address; - } - - leaf nhopself { - type uint8 { - range "0..255"; - } - } - - leaf holdtime { - type uint8 { - range "0..255"; - } - } - - leaf asn { - type uint64; - } - - leaf keepalive { - type uint8 { - range "0..255"; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-buffer-pg.yang b/src/cvl/schema/sonic-buffer-pg.yang deleted file mode 100644 index 96af02e24c..0000000000 --- a/src/cvl/schema/sonic-buffer-pg.yang +++ /dev/null @@ -1,69 +0,0 @@ -module sonic-buffer-pg { - namespace "http://github.com/Azure/sonic-buffer-pg"; - prefix bpg; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - import sonic-buffer-profile { - prefix bpf; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC BUFFER PG"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-buffer-pg { - list BUFFER_PG { - key "ifname pg_num"; - scommon:key-pattern "BUFFER_PG|({ifname},)*|{pg_num}"; //special pattern used for extracting keys from redis-key and fill populate the yang instance - // Total list instance = number(key1) * number(key2) * number(key3) - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - - leaf pg_num { - type string { - pattern "[0-7]((-)[0-7])?"{ - error-message "Invalid Buffer PG number"; - error-app-tag pg-num-invalid; - } - } - } - - leaf profile { //Hash reference key - type leafref { - path "/bpf:sonic-buffer-profile/bpf:BUFFER_PROFILE/bpf:name"; - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-buffer-pool.yang b/src/cvl/schema/sonic-buffer-pool.yang deleted file mode 100644 index 2c6b6b3689..0000000000 --- a/src/cvl/schema/sonic-buffer-pool.yang +++ /dev/null @@ -1,60 +0,0 @@ -module sonic-buffer-pool { - namespace "http://github.com/Azure/sonic-buffer-pool"; - prefix bpl; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC BUFFER POOL"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-buffer-pool { - - list BUFFER_POOL { - key "name"; - - leaf name { - type string; - } - - leaf type { - type enumeration { - enum ingress; - enum egress; - } - } - - leaf mode { - type enumeration { - enum static; - enum dynamic; - } - } - - leaf size { - type uint64; - } - - } - } -} diff --git a/src/cvl/schema/sonic-buffer-profile.yang b/src/cvl/schema/sonic-buffer-profile.yang deleted file mode 100644 index 7d4c1c3dc5..0000000000 --- a/src/cvl/schema/sonic-buffer-profile.yang +++ /dev/null @@ -1,76 +0,0 @@ -module sonic-buffer-profile { - namespace "http://github.com/Azure/sonic-buffer-profile"; - prefix bpf; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-buffer-pool { - prefix bpl; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC BUFFER PROFILE"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-buffer-profile { - - list BUFFER_PROFILE { - key "name"; - - leaf name { - type string; - } - - leaf static_th { - type uint64; - } - - leaf dynamic_th { - type int64; - } - - leaf size { - type uint64; - } - - leaf pool { - type leafref { - path "/bpl:sonic-buffer-pool/bpl:BUFFER_POOL/bpl:name"; - } - } - - leaf xon_offset { - type uint64; - } - - leaf xon { - type uint64; - } - - leaf xoff { - type uint64; - } - } - } -} diff --git a/src/cvl/schema/sonic-cablelength.yang b/src/cvl/schema/sonic-cablelength.yang deleted file mode 100644 index 3109294732..0000000000 --- a/src/cvl/schema/sonic-cablelength.yang +++ /dev/null @@ -1,64 +0,0 @@ -module sonic-cablelength { - namespace "http://github.com/Azure/sonic-cablelength"; - prefix scl; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC CABLELENGTH"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-cablelength { - list CABLE_LENGTH { - key "name"; - scommon:map-list true; //special conversion for map tables - scommon:map-leaf "port length"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 - - leaf name { - type string; - } - - list CABLE_LENGTH { //this is list inside list for storing mapping between two fields - key "port length"; - - leaf port { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - - } - - leaf length { - type string { - pattern "[0-9]?[0-9]m"; - } - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-device-metadata.yang b/src/cvl/schema/sonic-device-metadata.yang deleted file mode 100644 index 77ef8a1731..0000000000 --- a/src/cvl/schema/sonic-device-metadata.yang +++ /dev/null @@ -1,93 +0,0 @@ -module sonic-device-metadata { - namespace "http://github.com/Azure/sonic-device-metadata"; - prefix sdm; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - import sonic-bgp-neighbor { - prefix sbn; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC DEVICE METADATA"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-device-metadata { - list DEVICE_METADATA { - key "name"; - - leaf name{ - type string; - } - - leaf hwsku { - type string; - } - - leaf hostname { - type string; - } - - leaf platform { - type string; - } - - leaf mac { - type yang:mac-address; - } - - leaf bgp_asn { - type leafref { - path "/sbn:sonic-bgp-neighbor/sbn:BGP_NEIGHBOR/sbn:asn"; - } - } - - leaf default_pfcwd_status { - type enumeration { - enum enable; - enum disable; - } - } - - leaf default_bgp_status { - type scommon:admin-status; - } - - leaf docker_routing_config_mode { - type enumeration { - enum unified; - enum separated; - } - } - - leaf deployment_id { - type uint8 { - range "0..255"; - } - } - - leaf type { - type enumeration { - enum ToRRouter; - enum LeafRouter; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-device-neighbor.yang b/src/cvl/schema/sonic-device-neighbor.yang deleted file mode 100644 index cba3eed0fc..0000000000 --- a/src/cvl/schema/sonic-device-neighbor.yang +++ /dev/null @@ -1,76 +0,0 @@ -module sonic-device-neighbor { - namespace "http://github.com/Azure/sonic-device-neighbor"; - prefix sdn; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC DEVICE NEIGHBOR"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-device-neighbor { - list DEVICE_NEIGHBOR { - key "name"; - - leaf name{ - type string; - } - - leaf mgmt_addr{ - type inet:ip-address; - } - - leaf hwsku { - type string; - } - - leaf lo_addr { - type inet:ip-address; - } - - leaf local_port { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - - leaf type { - type enumeration { - enum ToRRouter; - enum LeafRouter; - } - } - - leaf port { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-dscp-tc-map.yang b/src/cvl/schema/sonic-dscp-tc-map.yang deleted file mode 100644 index 449310367a..0000000000 --- a/src/cvl/schema/sonic-dscp-tc-map.yang +++ /dev/null @@ -1,62 +0,0 @@ -module sonic-dscp-tc-map { - namespace "http://github.com/Azure/sonic-dscp-tc-map"; - prefix dtm; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix sif; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC DSCP_TO_TC_MAP"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-dscp-tc-map { - list DSCP_TO_TC_MAP { - key "name"; - scommon:map-list true; //special conversion for map tables - scommon:map-leaf "dscp tc_num"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 - - leaf name { - type string; - } - - list DSCP_TO_TC_MAP { //this is list inside list for storing mapping between two fields - key "dscp tc_num"; - - leaf tc_num { - type string { - pattern "[0-9]?"{ - error-message "Invalid Traffic Class number"; - error-app-tag tc-num-invalid; - } - } - } - - leaf dscp { - type string { - pattern "[1-9][0-9]?|[0-9]?"; - } - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-interface.yang b/src/cvl/schema/sonic-interface.yang deleted file mode 100644 index 1ddf94c115..0000000000 --- a/src/cvl/schema/sonic-interface.yang +++ /dev/null @@ -1,52 +0,0 @@ -module sonic-interface { - namespace "http://github.com/Azure/sonic-interface"; - prefix sint; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC INTERFACE"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-interface { - list INTERFACE { - key "portname ip_prefix"; - - leaf portname{ - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - - leaf ip_prefix { - mandatory true; - type inet:ip-prefix; - - } - } - } -} diff --git a/src/cvl/schema/sonic-mgmt-interface.yang b/src/cvl/schema/sonic-mgmt-interface.yang new file mode 100644 index 0000000000..0095b9f6b4 --- /dev/null +++ b/src/cvl/schema/sonic-mgmt-interface.yang @@ -0,0 +1,55 @@ +module sonic-mgmt-interface { + namespace "http://github.com/Azure/sonic-mgmt-interface"; + prefix sint; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + import sonic-common { + prefix scommon; + } + + import sonic-mgmt-port { + prefix mgmtprt; + } + + organization + "DELL"; + + contact + "DELL"; + + description + "SONIC MANAGEMENT INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-mgmt-interface { + list MGMT_INTERFACE { + key "portname ip_prefix"; + + leaf portname{ + type leafref { + path "/mgmtprt:sonic-mgmt-port/mgmtprt:MGMT_PORT/mgmtprt:ifname"; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + leaf gwaddr { + type inet:ip-prefix; + } + } + } +} diff --git a/src/cvl/schema/sonic-mgmt-port.yang b/src/cvl/schema/sonic-mgmt-port.yang new file mode 100644 index 0000000000..dec56e1e78 --- /dev/null +++ b/src/cvl/schema/sonic-mgmt-port.yang @@ -0,0 +1,66 @@ +module sonic-mgmt-port { + namespace "http://github.com/Azure/sonic-mgmt-port"; + prefix prt; + + import ietf-yang-types { + prefix yang; + } + + import sonic-common { + prefix scommon; + } + + organization + "DELL"; + + contact + "DELL"; + + description + "SONIC Management Interface"; + + revision 2019-09-17 { + description + "Initial revision."; + } + + + container sonic-mgmt-port { + list MGMT_PORT { + key "ifname"; + + leaf ifname { + type string { + pattern "eth([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])"{ + error-message "Invalid interface name"; + error-app-tag interface-name-invalid; + } + } + } + + leaf speed { + type uint64; + } + + leaf autoneg { + type boolean; + } + + leaf alias { + type string; + } + + leaf description { + type string; + } + + leaf mtu{ + type uint32; + } + + leaf admin_status { + type scommon:admin-status; + } + } + } +} diff --git a/src/cvl/schema/sonic-mirror-session.yang b/src/cvl/schema/sonic-mirror-session.yang deleted file mode 100644 index 36bb32cc81..0000000000 --- a/src/cvl/schema/sonic-mirror-session.yang +++ /dev/null @@ -1,65 +0,0 @@ -module sonic-mirror-session { - namespace "http://github.com/Azure/sonic-mirror-session"; - prefix sms; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC MIRROR SESSION"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-mirror-session { - - list MIRROR_SESSION { - key "name"; - - leaf name { - type string; - } - - leaf src_ip { - type inet:ipv4-address; - } - - leaf dst_ip { - type inet:ipv4-address; - } - - leaf gre_type { - type string; - } - - leaf dscp { - type uint8; - } - - leaf ttl { - type uint8; - } - - leaf queue { - type uint8; - } - } - } -} diff --git a/src/cvl/schema/sonic-pfc-priority-queue-map.yang b/src/cvl/schema/sonic-pfc-priority-queue-map.yang deleted file mode 100644 index e1a08c07cb..0000000000 --- a/src/cvl/schema/sonic-pfc-priority-queue-map.yang +++ /dev/null @@ -1,55 +0,0 @@ -module sonic-pfc-priority-queue-map { - namespace "http://github.com/Azure/sonic-pfc-priority-queue-map"; - prefix ppq; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC MAP_PFC_PRIORITY_TO_QUEUE"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-pfc-priority-queue-map { - list MAP_PFC_PRIORITY_TO_QUEUE { - key "name"; - scommon:map-list true; //special conversion for map tables - scommon:map-leaf "pfc_priority qindex"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 - - leaf name { - type string; - } - - list MAP_PFC_PRIORITY_TO_QUEUE { //this is list inside list for storing mapping between two fields - key "pfc_priority qindex"; - - leaf pfc_priority { - type string { - pattern "[0-9]?"; - } - } - - leaf qindex { - type string { - pattern "[0-9]?"; - } - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-port.yang b/src/cvl/schema/sonic-port.yang deleted file mode 100644 index 5e9e893a39..0000000000 --- a/src/cvl/schema/sonic-port.yang +++ /dev/null @@ -1,79 +0,0 @@ -module sonic-port { - namespace "http://github.com/Azure/sonic-port"; - prefix prt; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC VLAN"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-port { - list PORT { - key "ifname"; - - leaf ifname { - type string { - pattern "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])"{ - error-message "Invalid interface name"; - error-app-tag interface-name-invalid; - } - } - } - - leaf index { - type uint16; - } - - leaf speed { - type uint64; - } - - leaf valid_speeds { - type string; - } - - leaf alias { - type string; - } - - leaf description { - type string; - } - - leaf mtu{ - type uint32 { - range "1312..9216" { - error-message "Invalid MTU value"; - error-app-tag mtu-invalid; - } - } - } - - leaf lanes { - type string; - } - - leaf admin_status { - type scommon:admin-status; - } - } - } -} diff --git a/src/cvl/schema/sonic-portchannel-interface.yang b/src/cvl/schema/sonic-portchannel-interface.yang deleted file mode 100644 index 2bfc4cc006..0000000000 --- a/src/cvl/schema/sonic-portchannel-interface.yang +++ /dev/null @@ -1,52 +0,0 @@ -module sonic-portchannel-interface { - namespace "http://github.com/Azure/sonic-portchannel-interface"; - prefix spchint; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-portchannel { - prefix spc; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC PORTCHANNEL INTERFACE"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-portchannel-interface { - list PORTCHANNEL_INTERFACE { - key "pch_name ip_prefix"; - - leaf pch_name{ - type leafref { - path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:name"; - } - } - - leaf ip_prefix { - mandatory true; - type inet:ip-prefix; - - } - } - } -} diff --git a/src/cvl/schema/sonic-portchannel.yang b/src/cvl/schema/sonic-portchannel.yang deleted file mode 100644 index 25b08d5785..0000000000 --- a/src/cvl/schema/sonic-portchannel.yang +++ /dev/null @@ -1,77 +0,0 @@ -module sonic-portchannel { - namespace "http://github.com/Azure/sonic-portchannel"; - prefix spc; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC PORTCHANNEL"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-portchannel { - - list PORTCHANNEL { - key "name"; - - max-elements 3; - - leaf name { - type string; - } - - leaf admin_status { - type scommon:admin-status; - } - - leaf mtu { - type uint16; - } - - leaf min_links { - type uint8; - } - - leaf fallback { - type boolean; - } - } - - list PORTCHANNEL_MEMBER { - key "name ifname"; - scommon:key-delim "|"; - scommon:key-pattern "PORTCHANNEL|{name}|{ifname}"; - - leaf name { - type leafref { - path "../../PORTCHANNEL/name"; - } - } - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-queue.yang b/src/cvl/schema/sonic-queue.yang deleted file mode 100644 index e3be11691b..0000000000 --- a/src/cvl/schema/sonic-queue.yang +++ /dev/null @@ -1,74 +0,0 @@ -module sonic-queue { - namespace "http://github.com/Azure/sonic-queue"; - prefix squeue; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - import sonic-scheduler { - prefix sch; - } - - import sonic-wred-profile { - prefix wrd; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC QUEUE"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-queue { - list QUEUE { - key "ifname qindex"; - scommon:key-pattern "QUEUE|({ifname},)*|{qindex}"; //special pattern used for extracting keys from redis-key and populate the yang instance - // Total list instances = number(key1) * number(key2) * number(key3) - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - - leaf qindex { - type string { - pattern "[0-8]((-)[0-8])?"{ - error-message "Invalid Q-index"; - error-app-tag qindex-invalid; - } - } - } - - leaf scheduler { - type leafref { - path "/sch:sonic-scheduler/sch:SCHEDULER/sch:name"; - } - } - - leaf wred_profile { - type leafref { - path "/wrd:sonic-wred-profile/wrd:WRED_PROFILE/wrd:name"; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-scheduler.yang b/src/cvl/schema/sonic-scheduler.yang deleted file mode 100644 index 0b0745185b..0000000000 --- a/src/cvl/schema/sonic-scheduler.yang +++ /dev/null @@ -1,56 +0,0 @@ -module sonic-scheduler { - namespace "http://github.com/Azure/sonic-scheduler"; - prefix sch; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC SCHEDULER"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-scheduler { - list SCHEDULER { - key "name"; - - leaf name{ - type string; - } - - leaf type { - type enumeration { - enum DWRR; - enum WRR; - enum PRIORITY; - } - } - - leaf weight { - type uint8 { - range "0..255"; - } - } - - leaf priority { - type uint8 { - range "0..9"; - } - } - } - } -} diff --git a/src/cvl/schema/sonic-tc-priority-group-map.yang b/src/cvl/schema/sonic-tc-priority-group-map.yang deleted file mode 100644 index 6f7ec329d7..0000000000 --- a/src/cvl/schema/sonic-tc-priority-group-map.yang +++ /dev/null @@ -1,55 +0,0 @@ -module sonic-tc-priority-group-map { - namespace "http://github.com/Azure/sonic-tc-priority-group-map"; - prefix tpg; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC TC_TO_PRIORITY_GROUP_MAP"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-tc-priority-group-map { - list TC_TO_PRIORITY_GROUP_MAP { - key "name"; - scommon:map-list "true"; //special conversion for map tables - scommon:map-leaf "tc_num pg_num"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 - - leaf name { - type string; - } - - list TC_TO_PRIORITY_GROUP_MAP { //this is list inside list for storing mapping between two fields - key "tc_num pg_num"; - - leaf tc_num { - type string { - pattern "[0-9]?"; - } - } - - leaf pg_num { - type string { - pattern "[0-7]?"; - } - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-tc-queue-map.yang b/src/cvl/schema/sonic-tc-queue-map.yang deleted file mode 100644 index a2473d3985..0000000000 --- a/src/cvl/schema/sonic-tc-queue-map.yang +++ /dev/null @@ -1,55 +0,0 @@ -module sonic-tc-queue-map { - namespace "http://github.com/Azure/sonic-tc-queue-map"; - prefix tqm; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC TC_TO_QUEUE_MAP"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-tc-queue-map { - list TC_TO_QUEUE_MAP { - key "name"; - scommon:map-list "true"; //special conversion for map tables - scommon:map-leaf "tc_num qindex"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, qindex=7 - - leaf name { - type string; - } - - list TC_TO_QUEUE_MAP { //this is list inside list for storing mapping between two fields - key "tc_num qindex"; - - leaf tc_num { - type string { - pattern "[0-9]?"; - } - } - - leaf qindex { - type string { - pattern "[0-9]?"; - } - } - } - - } - } -} diff --git a/src/cvl/schema/sonic-vlan-interface.yang b/src/cvl/schema/sonic-vlan-interface.yang deleted file mode 100644 index 7f186b9ff0..0000000000 --- a/src/cvl/schema/sonic-vlan-interface.yang +++ /dev/null @@ -1,52 +0,0 @@ -module sonic-vlan-interface { - namespace "http://github.com/Azure/sonic-vlan-interface"; - prefix svint; - - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; - } - - import sonic-vlan { - prefix svlan; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC VLAN INTERFACE"; - - revision 2019-07-02 { - description - "Initial revision."; - } - - container sonic-vlan-interface { - list VLAN_INTERFACE { - key "portname ip_prefix"; - - leaf portname{ - type leafref { - path "/svlan:sonic-vlan/svlan:VLAN/svlan:name"; - } - } - - leaf ip_prefix { - mandatory true; - type inet:ip-prefix; - - } - } - } -} diff --git a/src/cvl/schema/sonic-vlan.yang b/src/cvl/schema/sonic-vlan.yang deleted file mode 100644 index f8c0bc1b48..0000000000 --- a/src/cvl/schema/sonic-vlan.yang +++ /dev/null @@ -1,107 +0,0 @@ -module sonic-vlan { - namespace "http://github.com/Azure/sonic-vlan"; - prefix svlan; - yang-version 1.1; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - import sonic-port { - prefix prt; - } - - import sonic-portchannel { - prefix spc; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC VLAN"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - - container sonic-vlan { - - list VLAN { - key "name"; - must "./name = concat('Vlan', string(./vlanid))"{ - error-app-tag vlan-invalid; - } - - leaf name { - type string { - pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])" { - error-message "Invalid Vlan name pattern"; - error-app-tag vlan-name-invalid; - } - } - } - - leaf vlanid { - mandatory true; - type uint16 { - range "1..4095" { - error-message "Vlan ID out of range"; - error-app-tag vlanid-invalid; - } - } - } - - leaf-list members { - must "count(../members[text()=/spc:sonic-portchannel/" + - "spc:PORTCHANNEL_MEMBER[spc:ifname=current()]/spc:name]) = 0 and " + - "count(../members[text()=/spc:sonic-portchannel/" + - "spc:PORTCHANNEL_MEMBER[spc:name=current()]/spc:ifname]) = 0 " { - error-message "A vlan interface member cannot be part of portchannel which is already a vlan member"; - } - - - type union { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - type leafref { - path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:name"; - } - } - } - } - - list VLAN_MEMBER { - key "name ifname"; - - leaf name { - type leafref { - path "../../VLAN/name"; - } - } - - leaf ifname { - type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; - } - } - - leaf tagging_mode { - type scommon:tagging_mode; - default tagged; - } - } - - } - -} diff --git a/src/cvl/schema/sonic-wred-profile.yang b/src/cvl/schema/sonic-wred-profile.yang deleted file mode 100644 index 96ddd89d64..0000000000 --- a/src/cvl/schema/sonic-wred-profile.yang +++ /dev/null @@ -1,84 +0,0 @@ -module sonic-wred-profile { - namespace "http://github.com/Azure/sonic-wred-profile"; - prefix wrd; - - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; - - contact - "BRCM"; - - description - "SONIC WRED_PROFILE"; - - revision 2019-05-15 { - description - "Initial revision."; - } - - container sonic-wred-profile { - list WRED_PROFILE { - key "name"; - - leaf name{ - type string; - } - - leaf yellow_min_threshold { - type uint64; - } - - leaf green_min_threshold { - type uint64; - } - - leaf red_min_threshold { - type uint64; - } - leaf yellow_max_threshold { - type uint64; - } - - leaf green_max_threshold { - type uint64; - } - - leaf red_max_threshold { - type uint64; - } - - leaf ecn { - type enumeration { - enum ecn_none; - enum ecn_green; - enum ecn_yellow; - enum ecn_red; - enum ecn_green_yellow; - enum ecn_green_red; - enum ecn_yellow_red; - enum ecn_all; - } - } - - leaf wred_green_enable { - type boolean; - } - - leaf wred_yellow_enable { - type boolean; - } - - leaf wred_red_enable { - type boolean; - } - } - } -} diff --git a/src/cvl/testdata/schema/Makefile b/src/cvl/testdata/schema/Makefile new file mode 100644 index 0000000000..43fee356bf --- /dev/null +++ b/src/cvl/testdata/schema/Makefile @@ -0,0 +1,48 @@ +################################################################################ +# # +# 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 +pyang_plugin_dir=../../../../tools/pyang/pyang_plugins +src_files=$(wildcard *.yang) +out=$(patsubst %.yang, %.yin, $(src_files)) +out_ext=$(patsubst %.yang, %.tree, $(src_files)) + +all:schema + +schema: $(out) + +schema-tree: $(out_ext) + +%.yin:%.yang + @echo "Generating `basename $@` ..." + @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ + if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ + pyang -p $(sonic_yang):$(sonic_yang)/common:$(sonic_yang)/common/ietf \ + --plugindir $(pyang_plugin_dir) -f yin-cvl $$devOpt $< -o `basename $@` + +%.tree:%.yang + @echo "Generating `basename $@` ..." + @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ + if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ + pyang -p $(sonic_yang):$(sonic_yang)/common:$(sonic_yang)/common/ietf \ + -f tree $$devOpt $< -o `basename $@` + +clean: + @echo "Removing files ..." + rm -rf *.yin *.tree diff --git a/src/cvl/schema/sonic-acl-dev.yang b/src/cvl/testdata/schema/sonic-acl-deviation.yang similarity index 65% rename from src/cvl/schema/sonic-acl-dev.yang rename to src/cvl/testdata/schema/sonic-acl-deviation.yang index 963a88303c..c1d701c29d 100644 --- a/src/cvl/schema/sonic-acl-dev.yang +++ b/src/cvl/testdata/schema/sonic-acl-deviation.yang @@ -1,33 +1,20 @@ -module sonic-acl-dev { - namespace "http://github.com/Azure/sonic-acl-dev"; +module sonic-acl-deviation { + namespace "http://github.com/Azure/sonic-acl-deviation"; prefix acld; yang-version 1.1; - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - import sonic-acl { prefix sacl; } - import sonic-common { - prefix scommon; - } - - organization - "BRCM"; + "SONiC"; contact - "BRCM"; + "SONiC"; description - "SONIC ACLi Deviations"; + "SONIC ACL Deviations"; revision 2019-05-15 { description diff --git a/src/cvl/testdata/schema/sonic-bgp-neighbor.yang b/src/cvl/testdata/schema/sonic-bgp-neighbor.yang new file mode 100644 index 0000000000..14504d6f9b --- /dev/null +++ b/src/cvl/testdata/schema/sonic-bgp-neighbor.yang @@ -0,0 +1,84 @@ +module sonic-bgp-neighbor { + namespace "http://github.com/Azure/sonic-bgp-neighbor"; + prefix sbn; + + import ietf-inet-types { + prefix inet; + } + + import sonic-common { + prefix scommon; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC BGP NEIGHBOR"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-bgp-neighbor { + + container BGP_NEIGHBOR { + + list BGP_NEIGHBOR_LIST { + key "ipaddress"; + + leaf ipaddress{ + type inet:ip-address; + } + + leaf rrclient { + type uint8 { + range "0..255"; + } + } + + leaf admin_status{ + type scommon:admin-status; + } + + leaf peer_addr{ + type inet:ip-address; + } + + leaf name { + type string; + } + + leaf local_addr { + type inet:ipv4-address; + } + + leaf nhopself { + type uint8 { + range "0..255"; + } + } + + leaf holdtime { + type uint8 { + range "0..255"; + } + } + + leaf asn { + type uint64; + } + + leaf keepalive { + type uint8 { + range "0..255"; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-buffer-pg.yang b/src/cvl/testdata/schema/sonic-buffer-pg.yang new file mode 100644 index 0000000000..27918081e8 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-buffer-pg.yang @@ -0,0 +1,66 @@ +module sonic-buffer-pg { + namespace "http://github.com/Azure/sonic-buffer-pg"; + prefix bpg; + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-port { + prefix prt; + } + + import sonic-buffer-profile { + prefix bpf; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC BUFFER PG"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-buffer-pg { + + container BUFFER_PG { + + list BUFFER_PG_LIST { + key "ifname pg_num"; + sonic-ext:key-pattern "BUFFER_PG|({ifname},)*|{pg_num}"; //special pattern used for extracting keys from + //redis-key and fill populate the yang instance + // Total list instance = number(key1) * number(key2) * number(key3) + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf pg_num { + type string { + pattern "[0-7]((-)[0-7])?" { + error-message "Invalid Buffer PG number"; + error-app-tag pg-num-invalid; + } + } + } + + leaf profile { //Hash reference key + type leafref { + path "/bpf:sonic-buffer-profile/bpf:BUFFER_PROFILE/bpf:BUFFER_PROFILE_LIST/bpf:name"; + } + } + + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-buffer-pool.yang b/src/cvl/testdata/schema/sonic-buffer-pool.yang new file mode 100644 index 0000000000..5f935b3f94 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-buffer-pool.yang @@ -0,0 +1,51 @@ +module sonic-buffer-pool { + namespace "http://github.com/Azure/sonic-buffer-pool"; + prefix bpl; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC BUFFER POOL"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-buffer-pool { + + container BUFFER_POOL { + + list BUFFER_POOL_LIST { + key "name"; + + leaf name { + type string; + } + + leaf type { + type enumeration { + enum ingress; + enum egress; + } + } + + leaf mode { + type enumeration { + enum static; + enum dynamic; + } + } + + leaf size { + type uint64; + } + + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-buffer-profile.yang b/src/cvl/testdata/schema/sonic-buffer-profile.yang new file mode 100644 index 0000000000..71f077654d --- /dev/null +++ b/src/cvl/testdata/schema/sonic-buffer-profile.yang @@ -0,0 +1,67 @@ +module sonic-buffer-profile { + namespace "http://github.com/Azure/sonic-buffer-profile"; + prefix bpf; + + import sonic-buffer-pool { + prefix bpl; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC BUFFER PROFILE"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-buffer-profile { + + container BUFFER_PROFILE { + + list BUFFER_PROFILE_LIST { + key "name"; + + leaf name { + type string; + } + + leaf static_th { + type uint64; + } + + leaf dynamic_th { + type int64; + } + + leaf size { + type uint64; + } + + leaf pool { + type leafref { + path "/bpl:sonic-buffer-pool/bpl:BUFFER_POOL/bpl:BUFFER_POOL_LIST/bpl:name"; + } + } + + leaf xon_offset { + type uint64; + } + + leaf xon { + type uint64; + } + + leaf xoff { + type uint64; + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-cablelength.yang b/src/cvl/testdata/schema/sonic-cablelength.yang new file mode 100644 index 0000000000..af4746211b --- /dev/null +++ b/src/cvl/testdata/schema/sonic-cablelength.yang @@ -0,0 +1,60 @@ +module sonic-cablelength { + namespace "http://github.com/Azure/sonic-cablelength"; + prefix scl; + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-port { + prefix prt; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC CABLELENGTH"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-cablelength { + + container CABLE_LENGTH { + + list CABLE_LENGTH_LIST { + key "name"; + sonic-ext:map-list true; //special conversion for map tables + sonic-ext:map-leaf "port length"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 + + leaf name { + type string; + } + + list CABLE_LENGTH { //this is list inside list for storing mapping between two fields + key "port length"; + + leaf port { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + + } + + leaf length { + type string { + pattern "[0-9]?[0-9]m"; + } + } + } + + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-device-metadata.yang b/src/cvl/testdata/schema/sonic-device-metadata.yang new file mode 100644 index 0000000000..014f88cd47 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-device-metadata.yang @@ -0,0 +1,97 @@ +module sonic-device-metadata { + namespace "http://github.com/Azure/sonic-device-metadata"; + prefix sdm; + + import ietf-yang-types { + prefix yang; + } + + import sonic-common { + prefix scommon; + } + + import sonic-bgp-neighbor { + prefix sbn; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC DEVICE METADATA"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-device-metadata { + + container DEVICE_METADATA { + + list DEVICE_METADATA_LIST { + key "name"; + + leaf name{ + type string; + } + + leaf hwsku { + type string; + } + + leaf hostname { + type string; + } + + leaf platform { + type string; + } + + leaf mac { + type yang:mac-address; + } + + leaf bgp_asn { + type leafref { + path "/sbn:sonic-bgp-neighbor/sbn:BGP_NEIGHBOR/sbn:BGP_NEIGHBOR_LIST/sbn:asn"; + } + } + + leaf default_pfcwd_status { + type enumeration { + enum enable; + enum disable; + } + } + + leaf default_bgp_status { + type scommon:admin-status; + } + + leaf docker_routing_config_mode { + type enumeration { + enum unified; + enum separated; + } + } + + leaf deployment_id { + type uint8 { + range "0..255"; + } + } + + leaf type { + type enumeration { + enum ToRRouter; + enum LeafRouter; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-device-neighbor.yang b/src/cvl/testdata/schema/sonic-device-neighbor.yang new file mode 100644 index 0000000000..a91302ac24 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-device-neighbor.yang @@ -0,0 +1,72 @@ +module sonic-device-neighbor { + namespace "http://github.com/Azure/sonic-device-neighbor"; + prefix sdn; + + import ietf-inet-types { + prefix inet; + } + + import sonic-port { + prefix prt; + } + + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC DEVICE NEIGHBOR"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-device-neighbor { + + container DEVICE_NEIGHBOR { + + list DEVICE_NEIGHBOR_LIST { + key "name"; + + leaf name{ + type string; + } + + leaf mgmt_addr{ + type inet:ip-address; + } + + leaf hwsku { + type string; + } + + leaf lo_addr { + type inet:ip-address; + } + + leaf local_port { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf type { + type enumeration { + enum ToRRouter; + enum LeafRouter; + } + } + + leaf port { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-dscp-tc-map.yang b/src/cvl/testdata/schema/sonic-dscp-tc-map.yang new file mode 100644 index 0000000000..14dccddd3d --- /dev/null +++ b/src/cvl/testdata/schema/sonic-dscp-tc-map.yang @@ -0,0 +1,58 @@ +module sonic-dscp-tc-map { + namespace "http://github.com/Azure/sonic-dscp-tc-map"; + prefix dtm; + + import sonic-extension { + prefix sonic-ext; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC DSCP_TO_TC_MAP"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-dscp-tc-map { + + container DSCP_TO_TC_MAP { + + list DSCP_TO_TC_MAP_LIST { + key "name"; + sonic-ext:map-list true; //special conversion for map tables + sonic-ext:map-leaf "dscp tc_num"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 + + leaf name { + type string; + } + + list DSCP_TO_TC_MAP { //this is list inside list for storing mapping between two fields + key "dscp tc_num"; + + leaf tc_num { + type string { + pattern "[0-9]?"{ + error-message "Invalid Traffic Class number"; + error-app-tag tc-num-invalid; + } + } + } + + leaf dscp { + type string { + pattern "[1-9][0-9]?|[0-9]?"; + } + } + } + + } + } + } +} diff --git a/src/cvl/schema/sonic-pf-limits.yang b/src/cvl/testdata/schema/sonic-pf-limits.yang similarity index 73% rename from src/cvl/schema/sonic-pf-limits.yang rename to src/cvl/testdata/schema/sonic-pf-limits.yang index 984e649888..658a6e9a8d 100644 --- a/src/cvl/schema/sonic-pf-limits.yang +++ b/src/cvl/testdata/schema/sonic-pf-limits.yang @@ -3,23 +3,15 @@ module sonic-pf-limits { prefix spf; yang-version 1.1; - import ietf-yang-types { - prefix yang; - } - - import ietf-inet-types { - prefix inet; - } - - import sonic-common { - prefix scommon; + import sonic-extension { + prefix sonic-ext; } organization - "BRCM"; + "SONiC"; contact - "BRCM"; + "SONiC"; description "SONIC Platform constrainst"; @@ -30,7 +22,7 @@ module sonic-pf-limits { } container sonic-pf-limits { - scommon:db-name "APPL_DB"; + sonic-ext:db-name "APPL_DB"; container acl { leaf MAX_ACL_RULES { diff --git a/src/cvl/testdata/schema/sonic-pfc-priority-queue-map.yang b/src/cvl/testdata/schema/sonic-pfc-priority-queue-map.yang new file mode 100644 index 0000000000..ad51874d69 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-pfc-priority-queue-map.yang @@ -0,0 +1,55 @@ +module sonic-pfc-priority-queue-map { + namespace "http://github.com/Azure/sonic-pfc-priority-queue-map"; + prefix ppq; + + import sonic-extension { + prefix sonic-ext; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC MAP_PFC_PRIORITY_TO_QUEUE"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-pfc-priority-queue-map { + + container MAP_PFC_PRIORITY_TO_QUEUE { + + list MAP_PFC_PRIORITY_TO_QUEUE_LIST { + key "name"; + sonic-ext:map-list true; //special conversion for map tables + sonic-ext:map-leaf "pfc_priority qindex"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 + + leaf name { + type string; + } + + list MAP_PFC_PRIORITY_TO_QUEUE { //this is list inside list for storing mapping between two fields + key "pfc_priority qindex"; + + leaf pfc_priority { + type string { + pattern "[0-9]?"; + } + } + + leaf qindex { + type string { + pattern "[0-9]?"; + } + } + } + + } + } + } +} diff --git a/src/cvl/schema/sonic-port-qos-map.yang b/src/cvl/testdata/schema/sonic-port-qos-map.yang similarity index 66% rename from src/cvl/schema/sonic-port-qos-map.yang rename to src/cvl/testdata/schema/sonic-port-qos-map.yang index 04838ae775..f9785cb46a 100644 --- a/src/cvl/schema/sonic-port-qos-map.yang +++ b/src/cvl/testdata/schema/sonic-port-qos-map.yang @@ -2,12 +2,8 @@ module sonic-port-qos-map { namespace "http://github.com/Azure/sonic-port-qos-map"; prefix pqm; - import ietf-yang-types { - prefix yang; - } - - import sonic-common { - prefix scommon; + import sonic-extension { + prefix sonic-ext; } import sonic-port { @@ -31,10 +27,10 @@ module sonic-port-qos-map { } organization - "BRCM"; + "SONiC"; contact - "BRCM"; + "SONiC"; description "SONIC PORT_QOS_MAP"; @@ -48,24 +44,24 @@ module sonic-port-qos-map { list PORT_QOS_MAP { key "ifname"; - scommon:key-pattern "PORT_QOS_MAP|({ifname},)*"; //special pattern used for extracting keys from redis-key and fill populate the yang instance + sonic-ext:key-pattern "PORT_QOS_MAP|({ifname},)*"; //special pattern used for extracting keys from redis-key and fill populate the yang instance // Total list instance = number(key1) * number(key2) * number(key3) leaf ifname { type leafref { - path "/prt:sonic-port/prt:PORT/prt:ifname"; + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; } } leaf tc_to_pg_map { type leafref { - path "/tpg:sonic-tc-priority-group-map/tpg:TC_TO_PRIORITY_GROUP_MAP/tpg:name"; + path "/tpg:sonic-tc-priority-group-map/tpg:TC_TO_PRIORITY_GROUP_MAP/tpg:TC_TO_PRIORITY_GROUP_MAP_LIST/tpg:name"; } } leaf tc_to_queue_map { type leafref { - path "/tqm:sonic-tc-queue-map/tqm:TC_TO_QUEUE_MAP/tqm:name"; + path "/tqm:sonic-tc-queue-map/tqm:TC_TO_QUEUE_MAP/tqm:TC_TO_QUEUE_MAP_LIST/tqm:name"; } } @@ -77,13 +73,13 @@ module sonic-port-qos-map { leaf pfc_to_queue_map { type leafref { - path "/ppq:sonic-pfc-priority-queue-map/ppq:MAP_PFC_PRIORITY_TO_QUEUE/ppq:name"; + path "/ppq:sonic-pfc-priority-queue-map/ppq:MAP_PFC_PRIORITY_TO_QUEUE/ppq:MAP_PFC_PRIORITY_TO_QUEUE_LIST/ppq:name"; } } leaf dscp_to_tc_map { type leafref { - path "/dtm:sonic-dscp-tc-map/dtm:DSCP_TO_TC_MAP/dtm:name"; + path "/dtm:sonic-dscp-tc-map/dtm:DSCP_TO_TC_MAP/dtm:DSCP_TO_TC_MAP_LIST/dtm:name"; } } } diff --git a/src/cvl/testdata/schema/sonic-portchannel-interface.yang b/src/cvl/testdata/schema/sonic-portchannel-interface.yang new file mode 100644 index 0000000000..45d92787d2 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-portchannel-interface.yang @@ -0,0 +1,48 @@ +module sonic-portchannel-interface { + namespace "http://github.com/Azure/sonic-portchannel-interface"; + prefix spchint; + + import ietf-inet-types { + prefix inet; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PORTCHANNEL INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-portchannel-interface { + + container PORTCHANNEL_INTERFACE { + + list PORTCHANNEL_INTERFACE_LIST { + key "pch_name ip_prefix"; + + leaf pch_name{ + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-portchannel.yang b/src/cvl/testdata/schema/sonic-portchannel.yang new file mode 100644 index 0000000000..afe308f4e5 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-portchannel.yang @@ -0,0 +1,77 @@ +module sonic-portchannel { + namespace "http://github.com/Azure/sonic-portchannel"; + prefix spc; + + import sonic-common { + prefix scommon; + } + + import sonic-port { + prefix prt; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PORTCHANNEL"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-portchannel { + + container PORTCHANNEL { + + list PORTCHANNEL_LIST { + key "name"; + + max-elements 3; + + leaf name { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint16; + } + + leaf min_links { + type uint8; + } + + leaf fallback { + type boolean; + } + } + } + + container PORTCHANNEL_MEMBER { + + list PORTCHANNEL_MEMBER_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../PORTCHANNEL/PORTCHANNEL_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-queue.yang b/src/cvl/testdata/schema/sonic-queue.yang new file mode 100644 index 0000000000..92a7f4c96f --- /dev/null +++ b/src/cvl/testdata/schema/sonic-queue.yang @@ -0,0 +1,74 @@ +module sonic-queue { + namespace "http://github.com/Azure/sonic-queue"; + prefix squeue; + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-port { + prefix prt; + } + + import sonic-scheduler { + prefix sch; + } + + import sonic-wred-profile { + prefix wrd; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC QUEUE"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-queue { + + container QUEUE { + + list QUEUE_LIST { + key "ifname qindex"; + sonic-ext:key-pattern "QUEUE|({ifname},)*|{qindex}"; //special pattern used for extracting keys from redis-key and populate the yang instance + // Total list instances = number(key1) * number(key2) * number(key3) + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf qindex { + type string { + pattern "[0-8]((-)[0-8])?"{ + error-message "Invalid Q-index"; + error-app-tag qindex-invalid; + } + } + } + + leaf scheduler { + type leafref { + path "/sch:sonic-scheduler/sch:SCHEDULER/sch:SCHEDULER_LIST/sch:name"; + } + } + + leaf wred_profile { + type leafref { + path "/wrd:sonic-wred-profile/wrd:WRED_PROFILE/wrd:WRED_PROFILE_LIST/wrd:name"; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-scheduler.yang b/src/cvl/testdata/schema/sonic-scheduler.yang new file mode 100644 index 0000000000..2fc7997f71 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-scheduler.yang @@ -0,0 +1,52 @@ +module sonic-scheduler { + namespace "http://github.com/Azure/sonic-scheduler"; + prefix sch; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC SCHEDULER"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-scheduler { + + container SCHEDULER { + + list SCHEDULER_LIST { + key "name"; + + leaf name{ + type string; + } + + leaf type { + type enumeration { + enum DWRR; + enum WRR; + enum PRIORITY; + } + } + + leaf weight { + type uint8 { + range "0..255"; + } + } + + leaf priority { + type uint8 { + range "0..9"; + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-tc-priority-group-map.yang b/src/cvl/testdata/schema/sonic-tc-priority-group-map.yang new file mode 100644 index 0000000000..ed89a281ec --- /dev/null +++ b/src/cvl/testdata/schema/sonic-tc-priority-group-map.yang @@ -0,0 +1,54 @@ +module sonic-tc-priority-group-map { + namespace "http://github.com/Azure/sonic-tc-priority-group-map"; + prefix tpg; + + import sonic-extension { + prefix sonic-ext; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC TC_TO_PRIORITY_GROUP_MAP"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-tc-priority-group-map { + + container TC_TO_PRIORITY_GROUP_MAP { + + list TC_TO_PRIORITY_GROUP_MAP_LIST { + key "name"; + sonic-ext:map-list "true"; //special conversion for map tables + sonic-ext:map-leaf "tc_num pg_num"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, dscp=7 + + leaf name { + type string; + } + + list TC_TO_PRIORITY_GROUP_MAP { //this is list inside list for storing mapping between two fields + key "tc_num pg_num"; + + leaf tc_num { + type string { + pattern "[0-9]?"; + } + } + + leaf pg_num { + type string { + pattern "[0-7]?"; + } + } + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-tc-queue-map.yang b/src/cvl/testdata/schema/sonic-tc-queue-map.yang new file mode 100644 index 0000000000..37ab1c8113 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-tc-queue-map.yang @@ -0,0 +1,55 @@ +module sonic-tc-queue-map { + namespace "http://github.com/Azure/sonic-tc-queue-map"; + prefix tqm; + + import sonic-extension { + prefix sonic-ext; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC TC_TO_QUEUE_MAP"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-tc-queue-map { + + container TC_TO_QUEUE_MAP { + + list TC_TO_QUEUE_MAP_LIST { + key "name"; + sonic-ext:map-list "true"; //special conversion for map tables + sonic-ext:map-leaf "tc_num qindex"; //every key:value pair is mapped to list keys, e.g. "1":"7" ==> tc_num=1, qindex=7 + + leaf name { + type string; + } + + list TC_TO_QUEUE_MAP { //this is list inside list for storing mapping between two fields + key "tc_num qindex"; + + leaf tc_num { + type string { + pattern "[0-9]?"; + } + } + + leaf qindex { + type string { + pattern "[0-9]?"; + } + } + } + + } + } + } +} diff --git a/src/cvl/schema/sonic-vlan-dev.yang b/src/cvl/testdata/schema/sonic-vlan-deviation.yang similarity index 74% rename from src/cvl/schema/sonic-vlan-dev.yang rename to src/cvl/testdata/schema/sonic-vlan-deviation.yang index 9ee9e6eede..ff426fb30c 100644 --- a/src/cvl/schema/sonic-vlan-dev.yang +++ b/src/cvl/testdata/schema/sonic-vlan-deviation.yang @@ -1,21 +1,19 @@ -module sonic-vlan-dev { - namespace "http://github.com/Azure/sonic-vlan-dev"; +module sonic-vlan-deviation { + namespace "http://github.com/Azure/sonic-vlan-deviation"; prefix svd; yang-version 1.1; - import ietf-yang-types { - prefix yang; - } - + /* import sonic-vlan { prefix svlan; } + */ organization - "BRCM"; + "SONiC"; contact - "BRCM"; + "SONiC"; description "SONIC VLAN deviation file"; diff --git a/src/cvl/testdata/schema/sonic-vlan-interface.yang b/src/cvl/testdata/schema/sonic-vlan-interface.yang new file mode 100644 index 0000000000..554feb1f58 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-vlan-interface.yang @@ -0,0 +1,48 @@ +module sonic-vlan-interface { + namespace "http://github.com/Azure/sonic-vlan-interface"; + prefix svint; + + import ietf-inet-types { + prefix inet; + } + + import sonic-vlan { + prefix svlan; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN INTERFACE"; + + revision 2019-07-02 { + description + "Initial revision."; + } + + container sonic-vlan-interface { + + container VLAN_INTERFACE { + + list VLAN_INTERFACE_LIST { + key "portname ip_prefix"; + + leaf portname{ + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf ip_prefix { + mandatory true; + type inet:ip-prefix; + + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-vlan.yang b/src/cvl/testdata/schema/sonic-vlan.yang new file mode 100644 index 0000000000..1170960df1 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-vlan.yang @@ -0,0 +1,107 @@ +module sonic-vlan { + namespace "http://github.com/Azure/sonic-vlan"; + prefix svlan; + yang-version 1.1; + + import sonic-common { + prefix scommon; + } + + import sonic-port { + prefix prt; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + + container sonic-vlan { + + container VLAN { + + list VLAN_LIST { + key "name"; + must "./name = concat('Vlan', string(./vlanid))"{ + error-app-tag vlan-invalid; + } + + leaf name { + type string { + pattern "Vlan(409[0-5]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])" { + error-message "Invalid Vlan name pattern"; + error-app-tag vlan-name-invalid; + } + } + } + + leaf vlanid { + mandatory true; + type uint16 { + range "1..4095" { + error-message "Vlan ID out of range"; + error-app-tag vlanid-invalid; + } + } + } + + leaf-list members { + must "count(../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + + "spc:PORTCHANNEL_MEMBER_LIST[spc:ifname=current()]/spc:name]) = 0 and " + + "count(../members[text()=/spc:sonic-portchannel/spc:PORTCHANNEL_MEMBER/" + + "spc:PORTCHANNEL_MEMBER_LIST[spc:name=current()]/spc:ifname]) = 0 " { + error-message "A vlan interface member cannot be part of portchannel which is already a vlan member"; + } + + + type union { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + } + } + } + + container VLAN_MEMBER { + + list VLAN_MEMBER_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../VLAN/VLAN_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf tagging_mode { + type scommon:tagging_mode; + default tagged; + } + } + } + } +} diff --git a/src/cvl/testdata/schema/sonic-wred-profile.yang b/src/cvl/testdata/schema/sonic-wred-profile.yang new file mode 100644 index 0000000000..e3c5abe363 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-wred-profile.yang @@ -0,0 +1,80 @@ +module sonic-wred-profile { + namespace "http://github.com/Azure/sonic-wred-profile"; + prefix wrd; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC WRED_PROFILE"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-wred-profile { + + container WRED_PROFILE { + + list WRED_PROFILE_LIST { + key "name"; + + leaf name{ + type string; + } + + leaf yellow_min_threshold { + type uint64; + } + + leaf green_min_threshold { + type uint64; + } + + leaf red_min_threshold { + type uint64; + } + leaf yellow_max_threshold { + type uint64; + } + + leaf green_max_threshold { + type uint64; + } + + leaf red_max_threshold { + type uint64; + } + + leaf ecn { + type enumeration { + enum ecn_none; + enum ecn_green; + enum ecn_yellow; + enum ecn_red; + enum ecn_green_yellow; + enum ecn_green_red; + enum ecn_yellow_red; + enum ecn_all; + } + } + + leaf wred_green_enable { + type boolean; + } + + leaf wred_yellow_enable { + type boolean; + } + + leaf wred_red_enable { + type boolean; + } + } + } + } +} diff --git a/src/cvl/tests/Makefile b/src/cvl/tests/Makefile index 36ddbc1a33..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 ../../..) @@ -9,6 +28,7 @@ all:tests tests: $(OUT) %:%.go + make -C ../testdata/schema @echo "Building $@ ..." GOPATH=$(GOPATH) $(GO) build -gcflags="all=-N -l" $< 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..0eb6901f44 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) @@ -43,6 +56,7 @@ $(REST_BUILD_DIR)/: # Invokes yang and model make to generate swagger artifcats. $(REST_BIN): $(REST_SRCS) | $(REST_BUILD_DIR)/ $(MAKE) -C $(TOPDIR)/models/yang + $(MAKE) -C $(TOPDIR)/models/yang/sonic $(MAKE) -C $(TOPDIR)/models ifeq ($(SONIC_COVERAGE_ON),y) GOPATH=$(REST_GOPATH) $(GO) test -coverpkg=".././..." -c -o $@ main/main.go main/main_test.go 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..7571bb025c --- /dev/null +++ b/src/translib/common_app.go @@ -0,0 +1,485 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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 + var resPayload []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 + } + + resPayload, 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(resPayload, &dat) + } else { + log.Warning("processGet. targetObj is null. Unable to Unmarshal payload") + resPayload = payload + } + + return GetResponse{Payload: resPayload}, 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 if any leaf-list exists + A leaf-list field in redis has "@" suffix as per swsssdk convention. + */ + resTblRw := db.Value{Field: map[string]string{}} + resTblRw = checkAndProcessLeafList(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 if any leaf-list exists */ + resTblRw := checkAndProcessLeafList(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 +} + +/*check if any field is leaf-list , if yes perform merge*/ +func checkAndProcessLeafList(existingEntry db.Value, tblRw db.Value, opcode int, d *db.DB, tblNm string, tblKey string) db.Value { + 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 len(mergeTblRw.Field) == 0 { + 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/intf_app.go b/src/translib/intf_app.go index ce686e714e..e57ab932d8 100644 --- a/src/translib/intf_app.go +++ b/src/translib/intf_app.go @@ -1,3 +1,21 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Dell, 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 translib import ( diff --git a/src/translib/lldp_app.go b/src/translib/lldp_app.go index 7210c78357..1b185279a6 100644 --- a/src/translib/lldp_app.go +++ b/src/translib/lldp_app.go @@ -1,3 +1,21 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Dell, 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 translib 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/pfm_app.go b/src/translib/pfm_app.go index 9d5d0029e0..feb24d6f09 100644 --- a/src/translib/pfm_app.go +++ b/src/translib/pfm_app.go @@ -1,3 +1,21 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Dell, 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 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/sys_app.go b/src/translib/sys_app.go index 7340786c10..33f5f8902d 100644 --- a/src/translib/sys_app.go +++ b/src/translib/sys_app.go @@ -1,3 +1,21 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Dell, 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 translib import ( 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..05eb21a292 --- /dev/null +++ b/src/translib/transformer/transformer.go @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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) + oc_annot_entries := make([]*yang.Entry, len(names)) + sonic_annot_entries := make([]*yang.Entry, len(names)) + + for _, n := range names { + if strings.Contains(n, "annot") && strings.Contains(n, "sonic") { + sonic_annot_entries = append(sonic_annot_entries, yang.ToEntry(mods[n])) + } else if strings.Contains(n, "annot") { + oc_annot_entries = append(oc_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) + annotDbSpecMap(sonic_annot_entries) + annotToDbMapBuild(oc_annot_entries) + yangToDbMapBuild(oc_entries) + + return err +} diff --git a/src/translib/transformer/xconst.go b/src/translib/transformer/xconst.go new file mode 100644 index 0000000000..a1fb5f9f46 --- /dev/null +++ b/src/translib/transformer/xconst.go @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 transformer + +const ( + YANG_MODULE = "module" + YANG_LIST = "list" + YANG_CONTAINER = "container" + YANG_LEAF = "leaf" + YANG_LEAF_LIST = "leaflist" + + YANG_ANNOT_DB_NAME = "db-name" + YANG_ANNOT_TABLE_NAME = "table-name" + YANG_ANNOT_FIELD_NAME = "field-name" + YANG_ANNOT_KEY_DELIM = "key-delimiter" + YANG_ANNOT_TABLE_XFMR = "table-transformer" + YANG_ANNOT_FIELD_XFMR = "field-transformer" + YANG_ANNOT_KEY_XFMR = "key-transformer" + YANG_ANNOT_POST_XFMR = "post-transformer" + YANG_ANNOT_SUBTREE_XFMR = "subtree-transformer" + YANG_ANNOT_VALIDATE_FUNC = "get-validate" + + REDIS_DB_TYPE_APPLN = "APPL_DB" + REDIS_DB_TYPE_ASIC = "ASIC_DB" + REDIS_DB_TYPE_CONFIG = "CONFIG_DB" + REDIS_DB_TYPE_COUNTER = "COUNTERS_DB" + REDIS_DB_TYPE_LOG_LVL = "LOGLEVEL_DB" + REDIS_DB_TYPE_STATE = "STATE_DB" + REDIS_DB_TYPE_FLX_COUNTER = "FLEX_COUNTER_DB" + + XPATH_SEP_FWD_SLASH = "/" + XFMR_EMPTY_STRING = "" + SONIC_TABLE_INDEX = 2 + +) diff --git a/src/translib/transformer/xfmr_acl.go b/src/translib/transformer/xfmr_acl.go new file mode 100644 index 0000000000..fd8b694366 --- /dev/null +++ b/src/translib/transformer/xfmr_acl.go @@ -0,0 +1,945 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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..35e17ed004 --- /dev/null +++ b/src/translib/transformer/xfmr_interface.go @@ -0,0 +1,135 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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) + + +/** + * TableXfmrFunc type is defined to use for table transformer function for dynamic derviation of redis table. + * Param: XfmrParams structure having database pointers, current db, operation, DB data in multidimensional map, YgotRoot, uri + * Return: List of table names, error + **/ +type TableXfmrFunc func (inParams XfmrParams) ([]string, 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") +} +func (TableXfmrFunc) xfmrInterfaceValiidate () { + log.Info("xfmrInterfaceValiidate for TableXfmrFunc") +} 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..692353e675 --- /dev/null +++ b/src/translib/transformer/xlate.go @@ -0,0 +1,407 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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, err + } + 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 + var dbOpts db.Options + + dbOpts = getDBOptions(spec.dbNum) + separator := dbOpts.KeySeparator + log.Infof("key separator for table %v in Db %v is %v", spec.Ts.Name, spec.dbNum, separator) + + 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, separator): data} + } else { + (*result)[spec.dbNum][spec.Ts.Name][strings.Join(spec.Key.Comp, separator)] = 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, separator), strings.Join((*parentKey).Comp, separator)) == -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 SONIC yang, the tablename and key info is available in the xpath + if isSonicYang(uri) { + /* Extract the xpath and key from input xpath */ + xpath, keyStr, tableName := sonicXpathKeyExtract(uri) + retdbFormat = fillSonicKeySpec(xpath, tableName, keyStr) + } else { + /* Extract the xpath and key from input xpath */ + xpath, keyStr, _ := xpathKeyExtract(nil, ygRoot, 0, uri) + retdbFormat = FillKeySpecs(xpath, keyStr, &retdbFormat) + } + + return &retdbFormat, err +} + +func FillKeySpecs(yangXpath string , keyStr string, retdbFormat *[]KeySpec) ([]KeySpec){ + if xYangSpecMap == nil { + return *retdbFormat + } + _, ok := xYangSpecMap[yangXpath] + if ok { + xpathInfo := xYangSpecMap[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 { + if _, ok := xDbSpecMap[child]; ok { + 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 { + if _, ok := xDbSpecMap[child]; ok { + chlen := len(xDbSpecMap[child].yangXpath) + if chlen > 0 { + for _, childXpath := range xDbSpecMap[child].yangXpath { + *retdbFormat = FillKeySpecs(childXpath, "", retdbFormat) + } + } + } + } + } + } + } + return *retdbFormat +} + +func fillSonicKeySpec(xpath string , tableName string, keyStr string) ( []KeySpec ) { + + var retdbFormat = make([]KeySpec, 0) + + if tableName != "" { + dbFormat := KeySpec{} + dbFormat.Ts.Name = tableName + cdb := db.ConfigDB + if _, ok := xDbSpecMap[tableName]; ok { + cdb = xDbSpecMap[tableName].dbIndex + } + dbFormat.dbNum = cdb + 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 + container := xpath + if xDbSpecMap != nil { + if _, ok := xDbSpecMap[container]; ok { + dbInfo := xDbSpecMap[container] + if dbInfo.fieldType == "container" { + for dir, _ := range dbInfo.dbEntry.Dir { + _, ok := xDbSpecMap[dir] + if ok && xDbSpecMap[dir].dbEntry.Node.Statement().Keyword == "container" { + cdb := xDbSpecMap[dir].dbIndex + dbFormat := KeySpec{} + dbFormat.Ts.Name = dir + dbFormat.dbNum = cdb + 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 result []byte + var dbData = make(map[db.DBNum]map[string]map[string]db.Value) + var cdb db.DBNum = db.ConfigDB + + dbData = data + if isSonicYang(uri) { + xpath, keyStr, tableName := sonicXpathKeyExtract(uri) + if (tableName != "") { + dbInfo, ok := xDbSpecMap[tableName] + if !ok { + log.Warningf("No entry in xDbSpecMap for xpath %v", tableName) + } else { + cdb = dbInfo.dbIndex + } + tokens:= strings.Split(xpath, "/") + // Format /module:container/tableName/listname[key]/fieldName + if tokens[SONIC_TABLE_INDEX] == tableName { + fieldName := tokens[len(tokens)-1] + dbSpecField := tableName + "/" + fieldName + _, ok := xDbSpecMap[dbSpecField] + if ok && xDbSpecMap[dbSpecField].fieldType == "leaf" { + dbData[cdb] = extractFieldFromDb(tableName, keyStr, fieldName, data[cdb]) + } + } + } + } else { + xpath, _ := XfmrRemoveXPATHPredicates(uri) + if _, ok := xYangSpecMap[xpath]; ok { + cdb = xYangSpecMap[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..09b79b8a25 --- /dev/null +++ b/src/translib/transformer/xlate_from_db.go @@ -0,0 +1,763 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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, _ := XfmrRemoveXPATHPredicates(inParams.uri) + log.Infof("Subtree transformer function(\"%v\") invoked for yang path(\"%v\").", xYangSpecMap[xpath].xfmrFunc, xpath) + _, err := XlateFuncCall(dbToYangXfmrFunc(xYangSpecMap[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, nodeErr := ytypes.GetNode(schRoot, device, path) + if nodeErr != 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, _ := XfmrRemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(dbToYangXfmrFunc(xYangSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return nil, err + } + if ret != nil { + fldValMap := ret[0].Interface().(map[string]interface{}) + return fldValMap, nil + } else { + return nil, nil + } +} + +func validateHandlerFunc(inParams XfmrParams) (bool) { + xpath, _ := XfmrRemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(xYangSpecMap[xpath].validateFunc, inParams) + if err != nil { + return false + } + return ret[0].Interface().(bool) +} + +func xfmrTblHandlerFunc(xfmrTblFunc string, inParams XfmrParams) []string { + ret, err := XlateFuncCall(xfmrTblFunc, inParams) + if err != nil { + return []string{} + } + return ret[0].Interface().([]string) +} + + +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, yang.Yleafref: + // TODO handle leaf-ref base type + 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 +} + +func sonicDbToYangTerminalNodeFill(tblName string, field string, value string, resultMap map[string]interface{}) { + resField := field + if len(value) == 0 { + return + } + 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) + return + } + if xDbSpecMapEntry.dbEntry == nil { + log.Warningf("Yang entry is nil in xDbSpecMap for xpath %v", fieldXpath) + return + } + + yangType := yangTypeGet(xDbSpecMapEntry.dbEntry) + if yangType == YANG_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) + return + } + resLst := processLfLstDbToYang(fieldXpath, value) + resultMap[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 { + resultMap[resField] = resVal + } + } + return +} + +func sonicDbToYangListFill(uri string, xpath string, dbIdx db.DBNum, table string, key string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value) []typeMapOfInterface { + var mapSlice []typeMapOfInterface + dbTblData := (*dbDataMap)[dbIdx][table] + + for keyStr, _ := range dbTblData { + curMap := make(map[string]interface{}) + sonicDbToYangDataFill(uri, xpath, dbIdx, table, keyStr, dbDataMap, curMap) + dbSpecData, ok := xDbSpecMap[table] + if ok && dbSpecData.keyName == nil { + yangKeys := yangKeyFromEntryGet(xDbSpecMap[xpath].dbEntry) + sonicKeyDataAdd(dbIdx, yangKeys, keyStr, curMap) + } + if curMap != nil { + mapSlice = append(mapSlice, curMap) + } + } + return mapSlice +} + +func sonicDbToYangDataFill(uri string, xpath string, dbIdx db.DBNum, table string, key string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, resultMap map[string]interface{}) { + yangNode, ok := xDbSpecMap[xpath] + + if ok && yangNode.dbEntry != nil { + xpathPrefix := table + if len(table) > 0 { xpathPrefix += "/" } + + for yangChldName := range yangNode.dbEntry.Dir { + chldXpath := xpathPrefix+yangChldName + if xDbSpecMap[chldXpath] != nil && xDbSpecMap[chldXpath].dbEntry != nil { + chldYangType := yangTypeGet(xDbSpecMap[chldXpath].dbEntry) + + if chldYangType == YANG_LEAF || chldYangType == YANG_LEAF_LIST { + log.Infof("tbl(%v), k(%v), yc(%v)", table, key, yangChldName) + fldName := yangChldName + if chldYangType == YANG_LEAF_LIST { + fldName = fldName + "@" + } + sonicDbToYangTerminalNodeFill(table, fldName, (*dbDataMap)[dbIdx][table][key].Field[fldName], resultMap) + } else if chldYangType == YANG_CONTAINER { + curMap := make(map[string]interface{}) + curUri := xpath + "/" + yangChldName + // container can have a static key, so extract key for current container + _, curKey, curTable := sonicXpathKeyExtract(curUri) + // use table-name as xpath from now on + sonicDbToYangDataFill(curUri, curTable, xDbSpecMap[curTable].dbIndex, curTable, curKey, dbDataMap, curMap) + if len(curMap) > 0 { + resultMap[yangChldName] = curMap + } else { + log.Infof("Empty container for xpath(%v)", curUri) + } + } else if chldYangType == YANG_LIST { + var mapSlice []typeMapOfInterface + curUri := xpath + "/" + yangChldName + mapSlice = sonicDbToYangListFill(curUri, curUri, dbIdx, table, key, dbDataMap) + if len(key) > 0 && len(mapSlice) == 1 {// Single instance query. Don't return array of maps + for k, val := range mapSlice[0] { + resultMap[k] = val + } + + } else if len(mapSlice) > 0 { + resultMap[yangChldName] = mapSlice + } else { + log.Infof("Empty list for xpath(%v)", curUri) + } + } + } + } + } + return +} + +/* Traverse db map and create json for cvl yang */ +func directDbToYangJsonCreate(uri string, dbDataMap *map[db.DBNum]map[string]map[string]db.Value, resultMap map[string]interface{}) (string, error) { + xpath, key, table := sonicXpathKeyExtract(uri) + + if len(xpath) > 0 { + var dbNode *dbInfo + + if len(table) > 0 { + tokens:= strings.Split(xpath, "/") + if tokens[SONIC_TABLE_INDEX] == table { + fieldName := tokens[len(tokens)-1] + dbSpecField := table + "/" + fieldName + _, ok := xDbSpecMap[dbSpecField] + if ok && (xDbSpecMap[dbSpecField].fieldType == YANG_LEAF || xDbSpecMap[dbSpecField].fieldType == YANG_LEAF_LIST) { + dbNode = xDbSpecMap[dbSpecField] + xpath = dbSpecField + } else { + dbNode = xDbSpecMap[table] + } + } + } else { + dbNode, _ = xDbSpecMap[xpath] + } + + if dbNode != nil && dbNode.dbEntry != nil { + cdb := db.ConfigDB + yangType := yangTypeGet(dbNode.dbEntry) + if len(table) > 0 { + cdb = xDbSpecMap[table].dbIndex + } + + if yangType == YANG_LEAF || yangType == YANG_LEAF_LIST { + fldName := xDbSpecMap[xpath].dbEntry.Name + if yangType == YANG_LEAF_LIST { + fldName = fldName + "@" + } + sonicDbToYangTerminalNodeFill(table, fldName, (*dbDataMap)[cdb][table][key].Field[fldName], resultMap) + } else if yangType == YANG_CONTAINER { + if len(table) > 0 { + xpath = table + } + sonicDbToYangDataFill(uri, xpath, cdb, table, key, dbDataMap, resultMap) + } else if yangType == YANG_LIST { + mapSlice := sonicDbToYangListFill(uri, xpath, cdb, table, key, dbDataMap) + if len(key) > 0 && len(mapSlice) == 1 {// Single instance query. Don't return array of maps + for k, val := range mapSlice[0] { + resultMap[k] = val + } + + } else if len(mapSlice) > 0 { + pathl := strings.Split(xpath, "/") + lname := pathl[len(pathl) - 1] + resultMap[lname] = mapSlice + } + } + } + } + + jsonMapData, _ := json.Marshal(resultMap) + jsonData := fmt.Sprintf("%v", string(jsonMapData)) + jsonDataPrint(jsonData) + return jsonData, nil +} + +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 fillDbDataMapForTbl(uri string, xpath string, tblName string, tblKey string, cdb db.DBNum, dbs [db.MaxDB]*db.DB) (map[db.DBNum]map[string]map[string]db.Value, error) { + var err error + dbresult := make(map[db.DBNum]map[string]map[string]db.Value) + dbresult[cdb] = make(map[string]map[string]db.Value) + dbFormat := KeySpec{} + dbFormat.Ts.Name = tblName + dbFormat.dbNum = cdb + if tblKey != "" { + dbFormat.Key.Comp = append(dbFormat.Key.Comp, tblKey) + } + err = TraverseDb(dbs, dbFormat, &dbresult, nil) + if err != nil { + log.Errorf("TraverseDb() failure for tbl(DB num) %v(%v) for xpath %v", tblName, cdb, xpath) + return nil, err + } + if _, ok := dbresult[cdb]; !ok { + logStr := fmt.Sprintf("TraverseDb() did not populate Db data for tbl(DB num) %v(%v) for xpath %v", tblName, cdb, xpath) + err = fmt.Errorf("%v", logStr) + return nil, err + } + return dbresult, err + +} + +// Assumption: All tables are from the same DB +func dbDataFromTblXfmrGet(tbl string, inParams XfmrParams, dbDataMap *map[db.DBNum]map[string]map[string]db.Value) error { + xpath, _ := XfmrRemoveXPATHPredicates(inParams.uri) + curDbDataMap, err := fillDbDataMapForTbl(inParams.uri, xpath, tbl, inParams.key, inParams.curDb, inParams.dbs) + if err == nil { + mapCopy((*dbDataMap)[inParams.curDb], curDbDataMap[inParams.curDb]) + } + return 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 { + var tblList []string + tblXfmr := false + + if tbl == "" && xYangSpecMap[xpath].xfmrTbl != nil { + xfmrTblFunc := *xYangSpecMap[xpath].xfmrTbl + if len(xfmrTblFunc) > 0 { + tblXfmr = true + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, tblKey, dbDataMap, nil) + tblList = xfmrTblHandlerFunc(xfmrTblFunc, inParams) + if len(tblList) != 0 { + for _, curTbl := range tblList { + dbDataFromTblXfmrGet(curTbl, inParams, dbDataMap) + } + } + } + } else if tbl != "" && !tblXfmr { + tblList = append(tblList, tbl) + } + + for _, tbl = range(tblList) { + 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, dbs[cdb].Opts.KeySeparator) + if len(xYangSpecMap[xpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, curUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if cmap != nil && 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, _ := XfmrRemoveXPATHPredicates(curUri) + yangDataFill(dbs, ygRoot, curUri, curXpath, dbDataMap, curMap, tbl, dbKey, cdb, validate) + mapSlice = append(mapSlice, curMap) + } + } + } + if len(mapSlice) > 0 { + resultMap[xYangSpecMap[xpath].yangEntry.Name] = mapSlice + } else { + log.Infof("Empty slice for (\"%v\").\r\n", uri) + } + } + }// end of tblList for + 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 xYangSpecMap[xpath].yangEntry == nil { + logStr := fmt.Sprintf("No yang entry found for xpath %v.", xpath) + err = fmt.Errorf("%v", logStr) + return resFldValMap, err + } + + cdb := xYangSpecMap[xpath].dbIndex + if len(xYangSpecMap[xpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, tblKey, 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 + } + if fldValMap != nil { + for lf, val := range fldValMap { + resFldValMap[lf] = val + } + } + } else { + dbFldName := xYangSpecMap[xpath].fieldName + if dbFldName == "NONE" { + return resFldValMap, err + } + /* if there is no transformer extension/annotation then it means leaf-list in yang is also leaflist in db */ + if len(dbFldName) > 0 && !xYangSpecMap[xpath].isKey { + yangType := yangTypeGet(xYangSpecMap[xpath].yangEntry) + if yangType == YANG_LEAF_LIST { + dbFldName += "@" + val, ok := (*dbDataMap)[cdb][tbl][tblKey].Field[dbFldName] + if ok { + resLst := processLfLstDbToYang(xpath, val) + resFldValMap[xYangSpecMap[xpath].yangEntry.Name] = resLst + } + } else { + val, ok := (*dbDataMap)[cdb][tbl][tblKey].Field[dbFldName] + if ok { + yngTerminalNdDtType := xYangSpecMap[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[xYangSpecMap[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 := xYangSpecMap[xpath] + + if ok && yangNode.yangEntry != nil { + for yangChldName := range yangNode.yangEntry.Dir { + chldXpath := xpath+"/"+yangChldName + chldUri := uri+"/"+yangChldName + if xYangSpecMap[chldXpath] != nil && xYangSpecMap[chldXpath].yangEntry != nil { + cdb = xYangSpecMap[chldXpath].dbIndex + if len(xYangSpecMap[chldXpath].validateFunc) > 0 && !validate { + _, key, _ := xpathKeyExtract(dbs[cdb], ygRoot, GET, chldUri) + // 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(xYangSpecMap[chldXpath].yangEntry) + if chldYangType == YANG_LEAF || chldYangType == YANG_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 == YANG_CONTAINER { + cname := xYangSpecMap[chldXpath].yangEntry.Name + if xYangSpecMap[chldXpath].xfmrTbl != nil { + xfmrTblFunc := *xYangSpecMap[chldXpath].xfmrTbl + if len(xfmrTblFunc) > 0 { + //inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil) + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, tblKey, dbDataMap, nil) + tblList := xfmrTblHandlerFunc(xfmrTblFunc, inParams) + if len(tblList) > 1 { + log.Warningf("Table transformer returned more than one table for container %v", chldXpath) + } + if len(tblList) == 0 { + continue + } + dbDataFromTblXfmrGet(tblList[0], inParams, dbDataMap) + tbl = tblList[0] + } + } + if len(xYangSpecMap[chldXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if cmap != nil && len(cmap) > 0 { + resultMap[cname] = cmap + } else { + log.Infof("Empty container(\"%v\").\r\n", chldUri) + } + continue + } 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 == YANG_LIST { + cdb = xYangSpecMap[chldXpath].dbIndex + if len(xYangSpecMap[chldXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil) + cmap, _ := xfmrHandlerFunc(inParams) + if cmap != nil && len(cmap) > 0 { + resultMap = cmap + } else { + log.Infof("Empty list(\"%v\").\r\n", chldUri) + } + } else { + ynode, ok := xYangSpecMap[chldXpath] + lTblName := "" + 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 isSonicYang(uri) { + return directDbToYangJsonCreate(uri, dbDataMap, resultMap) + } else { + var d *db.DB + reqXpath, keyName, tableName := xpathKeyExtract(d, ygRoot, GET, uri) + yangNode, ok := xYangSpecMap[reqXpath] + if ok { + yangType := yangTypeGet(yangNode.yangEntry) + validateHandlerFlag := false + tableXfmrFlag := false + IsValidate := false + if len(xYangSpecMap[reqXpath].validateFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, keyName, dbDataMap, nil) + res := validateHandlerFunc(inParams) + if !res { + validateHandlerFlag = true + /* cannot immediately return from her since reXpath yangtype decides the return type */ + } else { + IsValidate = res + } + } + isList := false + switch yangType { + case YANG_LIST: + isList = true + case YANG_LEAF, YANG_LEAF_LIST, YANG_CONTAINER: + isList = false + default: + log.Infof("Unknown yang object type for path %v", reqXpath) + isList = true //do not want non-list processing to happen + } + /*If yangtype is a list separate code path is to be taken in case of table transformer + since that code path already handles the calling of table transformer and subsequent processing + */ + if (!validateHandlerFlag) && (!isList) { + if xYangSpecMap[reqXpath].xfmrTbl != nil { + xfmrTblFunc := *xYangSpecMap[reqXpath].xfmrTbl + if len(xfmrTblFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, keyName, dbDataMap, nil) + tblList := xfmrTblHandlerFunc(xfmrTblFunc, inParams) + if len(tblList) > 1 { + log.Warningf("Table transformer returned more than one table for container %v", reqXpath) + tableXfmrFlag = true + } + if len(tblList) == 0 { + log.Warningf("Table transformer returned no table for conatiner %v", reqXpath) + tableXfmrFlag = true + } + if !tableXfmrFlag { + dbDataFromTblXfmrGet(tblList[0], inParams, dbDataMap) + } + } else { + log.Warningf("empty table transformer function name for xpath - %v", reqXpath) + tableXfmrFlag = true + } + } + } + for { + if yangType == YANG_LEAF || yangType == YANG_LEAF_LIST { + yangName := xYangSpecMap[reqXpath].yangEntry.Name + if validateHandlerFlag || tableXfmrFlag { + resultMap[yangName] = "" + break + } + tbl, key, _ := tableNameAndKeyFromDbMapGet((*dbDataMap)[cdb]) + fldValMap, err := terminalNodeProcess(dbs, ygRoot, uri, reqXpath, dbDataMap, tbl, key) + if err != nil { + log.Infof("Empty terminal node (\"%v\").", uri) + } + resultMap = fldValMap + break + + } else if yangType == YANG_CONTAINER { + cname := xYangSpecMap[reqXpath].yangEntry.Name + cmap := make(map[string]interface{}) + if validateHandlerFlag || tableXfmrFlag { + resultMap[cname] = cmap + break + } + if len(xYangSpecMap[reqXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, "", dbDataMap, nil) + cmap, _ = xfmrHandlerFunc(inParams) + if cmap != nil && len(cmap) > 0 { + resultMap[cname] = cmap + break + } + } + err := yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, IsValidate) + if err != nil { + log.Infof("Empty container(\"%v\").\r\n", uri) + } + break + } else if yangType == YANG_LIST { + var err error + var logStr string + if xYangSpecMap[reqXpath].xfmrTbl != nil { + /* pass empty table string since in case of list table transformer can have multiple tables returned, + which are already being fetched and handled in yangListDataFill + */ + err = yangListDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, "", keyName, cdb, IsValidate) + logStr = fmt.Sprintf("yangListDataFill failed for list case(\"%v\").\r\n", uri) + } else { + err = yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, IsValidate) + logStr = fmt.Sprintf("yangDataFill failed for list(\"%v\").\r\n", uri) + + } + if err != nil { + log.Infof("%v", logStr) + } + break + } else { + log.Warningf("Unknown yang object type for path %v", reqXpath) + break + } + } //end of for + } + } + + 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..9ca32132bc --- /dev/null +++ b/src/translib/transformer/xlate_to_db.go @@ -0,0 +1,673 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 transformer + +import ( + "errors" + "fmt" + "github.com/openconfig/ygot/ygot" + "os" + "reflect" + "regexp" + "strings" + "translib/db" + "translib/ocbinds" + "github.com/openconfig/ygot/ytypes" + "github.com/openconfig/goyang/pkg/yang" + + log "github.com/golang/glog" +) + +/* Invoke the post tansformer */ +func postXfmrHandlerFunc(inParams XfmrParams) (map[string]map[string]db.Value, error) { + xpath, _ := XfmrRemoveXPATHPredicates(inParams.uri) + ret, err := XlateFuncCall(xYangSpecMap[xpath].xfmrPost, inParams) + if err != nil { + return nil, err + } + retData := ret[0].Interface().(map[string]map[string]db.Value) + log.Info("Post Transformer function :", xYangSpecMap[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)} + } + + if field == "NONE" { + result[tableName][dbKey].Field["NULL"] = "NULL" + } + + result[tableName][dbKey].Field[field] = value + return +} + +func tblNameFromTblXfmrGet(xfmrTblFunc string, inParams XfmrParams) (string, error){ + tblList := xfmrTblHandlerFunc(xfmrTblFunc, inParams) + if len(tblList) != 1 { + logStr := fmt.Sprintf("Invalid return value(%v) from table transformer for (%v)", tblList, inParams.uri) + log.Error(logStr) + err := errors.New(logStr) + return "", err + } + return tblList[0], nil +} + +/* 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 { + var dbs [db.MaxDB]*db.DB + var err error + xpath := xpathPrefix + "/" + name + xpathInfo, ok := xYangSpecMap[xpath] + log.Infof("name: \"%v\", xpathPrefix(\"%v\").", name, xpathPrefix) + + if !ok || xpathInfo == nil { + log.Errorf("Yang path(\"%v\") not found.", xpath) + return errors.New("Invalid URI") + } + + if xpathInfo.tableName == nil && xpathInfo.xfmrTbl == 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 + } + + tableName := "" + if xpathInfo.xfmrTbl != nil { + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, uri, oper, "", nil, "") + // expecting only one table name from tbl-xfmr + tableName, err = tblNameFromTblXfmrGet(*xYangSpecMap[xpath].xfmrTbl, inParams) + if err != nil { + return err + } + } else { + tableName = *xpathInfo.tableName + } + + 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 + } + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, uri, oper, "", nil, node[0].Data) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return err + } + if ret != nil { + retData := ret[0].Interface().(map[string]string) + log.Info("Transformer function :", xpathInfo.xfmrFunc, " Xpath: ", xpath, " retData: ", retData) + for f, v := range retData { + dataToDBMapAdd(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(tableName, dbKey, result, fieldName, valueStr) + log.Infof("TblName: \"%v\", key: \"%v\", field: \"%v\", valueStr: \"%v\".", tableName, dbKey, + fieldName, valueStr) + return nil +} + +func sonicYangReqToDbMapCreate(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 { + sonicYangReqToDbMapCreate(data.MapIndex(key).Interface(), result) + } + } + } + return nil +} + +func dbMapDataFill(uri string, tableName string, keyName string, d map[string]interface{}, result map[string]map[string]db.Value) { + 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 +} + +func dbMapListDataFill(uri string, tableName string, dbEntry *yang.Entry, jsonData interface{}, result map[string]map[string]db.Value) { + data := reflect.ValueOf(jsonData) + tblKeyName := strings.Split(dbEntry.Key, " ") + 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) + } + dbMapDataFill(uri, tableName, keyName, d, result) + } + return +} + +func directDbMapData(uri string, tableName string, jsonData interface{}, result map[string]map[string]db.Value) bool { + _, ok := xDbSpecMap[tableName] + if ok && xDbSpecMap[tableName].dbEntry != nil { + data := reflect.ValueOf(jsonData).Interface().(map[string]interface{}) + key := "" + dbSpecData := xDbSpecMap[tableName] + result[tableName] = make(map[string]db.Value) + + if dbSpecData.keyName != nil { + key = *dbSpecData.keyName + log.Infof("Fill data for container uri(%v), key(%v)", uri, key) + dbMapDataFill(uri, tableName, key, data, result) + return true + } + + for k, v := range data { + xpath := tableName + "/" + k + curDbSpecData, ok := xDbSpecMap[xpath] + if ok && curDbSpecData.dbEntry != nil { + eType := yangTypeGet(curDbSpecData.dbEntry) + switch eType { + case "list": + log.Infof("Fill data for list uri(%v)", uri) + dbMapListDataFill(uri, tableName, curDbSpecData.dbEntry, v, result) + default: + log.Infof("Invalid node type for uri(%v)", uri) + } + } + } + } + return true +} + +/* 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 isSonicYang(path) { + xpathPrefix, keyName, tableName := sonicXpathKeyExtract(path) + log.Infof("Delete req: path(\"%v\"), key(\"%v\"), xpathPrefix(\"%v\"), tableName(\"%v\").", path, keyName, xpathPrefix, tableName) + err = sonicYangReqToDbMapDelete(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 := xYangSpecMap[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 sonicYangReqToDbMapDelete(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, "/") + if tokens[SONIC_TABLE_INDEX] == tableName { + fieldName := tokens[len(tokens)-1] + dbSpecField := tableName + "/" + fieldName + _, ok := xDbSpecMap[dbSpecField] + if ok { + yangType := xDbSpecMap[dbSpecField].fieldType + // terminal node case + if yangType == YANG_LEAF_LIST { + fieldName = fieldName + "@" + dbVal.Field = make(map[string]string) + dbVal.Field[fieldName] = "" + } + if yangType == YANG_LEAF { + 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 + _, ok := xDbSpecMap[xpathPrefix] + if ok && xDbSpecMap[xpathPrefix] != nil { + dbInfo := xDbSpecMap[xpathPrefix] + 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 isSonicYang(path) { + err = sonicYangReqToDbMapCreate(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 := xYangSpecMap[moduleNm]; ok { + if xYangSpecMap[moduleNm].yangDataType == "container" && len(xYangSpecMap[moduleNm].xfmrPost) > 0 { + log.Info("Invoke post transformer: ", xYangSpecMap[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 xYangSpecMap. 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 yangNodeForUriGet(uri string, ygRoot *ygot.GoStruct) (interface{}, error) { + 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 nil, nErr + } + return node[0].Data, nil +} + +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) + _, ok := xYangSpecMap[xpathPrefix] + if ok && len(xYangSpecMap[xpathPrefix].xfmrKey) > 0 { + /* key transformer present */ + curYgotNode, nodeErr := yangNodeForUriGet(curUri, ygRoot) + if nodeErr != nil { + curYgotNode = nil + } + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curUri, oper, "", nil, curYgotNode) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[xpathPrefix].xfmrKey), inParams) + if err != nil { + return err + } + if ret != nil { + curKey = ret[0].Interface().(string) + } + } else if xYangSpecMap[xpathPrefix].keyName != nil { + curKey = *xYangSpecMap[xpathPrefix].keyName + } else { + curKey = keyCreate(keyName, xpathPrefix, data, d.Opts.KeySeparator) + } + 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 + curKey := keyName + pathAttr := key.String() + if len(xpathPrefix) > 0 { + if strings.Contains(pathAttr, ":") { + pathAttr = strings.Split(pathAttr, ":")[1] + } + xpath = xpathPrefix + "/" + pathAttr + curUri = uri + "/" + pathAttr + } + _, ok := xYangSpecMap[xpath] + log.Infof("slice/map data: curKey(\"%v\"), xpath(\"%v\"), curUri(\"%v\").", + curKey, xpath, curUri) + if ok && xYangSpecMap[xpath] != nil && len(xYangSpecMap[xpath].xfmrKey) > 0 { + /* key transformer present */ + curYgotNode, nodeErr := yangNodeForUriGet(curUri, ygRoot) + if nodeErr != nil { + curYgotNode = nil + } + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curUri, oper, "", nil, curYgotNode) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[xpath].xfmrKey), inParams) + if err != nil { + return err + } + if ret != nil { + curKey = ret[0].Interface().(string) + } + } else if xYangSpecMap[xpath].keyName != nil { + curKey = *xYangSpecMap[xpath].keyName + } + + if (typeOfValue == reflect.Map || typeOfValue == reflect.Slice) && xYangSpecMap[xpath].yangDataType != "leaf-list" { + if ok && xYangSpecMap[xpath] != nil && len(xYangSpecMap[xpath].xfmrFunc) > 0 { + /* subtree transformer present */ + curYgotNode, nodeErr := yangNodeForUriGet(curUri, ygRoot) + if nodeErr != nil { + curYgotNode = nil + } + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curUri, oper, "", nil, curYgotNode) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[xpath].xfmrFunc), inParams) + if err != nil { + return nil + } + if ret != nil { + mapCopy(result, ret[0].Interface().(map[string]map[string]db.Value)) + } + } else { + yangReqToDbMapCreate(d, ygRoot, oper, curUri, xpath, curKey, 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, curKey, 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 = XfmrRemoveXPATHPredicates(path) + if err != nil { + return xpath, keyStr, tableName + } + rgp := regexp.MustCompile(`\[([^\[\]]*)\]`) + pathsubStr := strings.Split(path , "/") + if len(pathsubStr) > SONIC_TABLE_INDEX { + if strings.Contains(pathsubStr[2], "[") { + tableName = strings.Split(pathsubStr[SONIC_TABLE_INDEX], "[")[0] + } else { + tableName = pathsubStr[SONIC_TABLE_INDEX] + } + dbInfo, ok := xDbSpecMap[tableName] + cdb := db.ConfigDB + if !ok { + log.Infof("No entry in xDbSpecMap for xpath %v in order to fetch DB index.", tableName) + } else { + cdb = dbInfo.dbIndex + } + dbOpts := getDBOptions(cdb) + for i, kname := range rgp.FindAllString(path, -1) { + if i > 0 { keyStr += dbOpts.KeySeparator } + 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 := "" + pfxPath := "" + rgp := regexp.MustCompile(`\[([^\[\]]*)\]`) + curPathWithKey := "" + cdb := db.ConfigDB + var dbs [db.MaxDB]*db.DB + + pfxPath, _ = XfmrRemoveXPATHPredicates(path) + xpathInfo, ok := xYangSpecMap[pfxPath] + if !ok { + log.Errorf("No entry found in xYangSpecMap for xpath %v.", pfxPath) + return pfxPath, keyStr, tableName + } + cdb = xpathInfo.dbIndex + dbOpts := getDBOptions(cdb) + keySeparator := dbOpts.KeySeparator + if len(xpathInfo.delim) > 0 { + keySeparator = xpathInfo.delim + } + + for _, k := range strings.Split(path, "/") { + curPathWithKey += k + if strings.Contains(k, "[") { + if len(keyStr) > 0 { + keyStr += keySeparator + } + yangXpath, _ := XfmrRemoveXPATHPredicates(curPathWithKey) + _, ok := xYangSpecMap[yangXpath] + if ok { + if len(xYangSpecMap[yangXpath].xfmrKey) > 0 { + xfmrFuncName := yangToDbXfmrFunc(xYangSpecMap[yangXpath].xfmrKey) + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curPathWithKey, oper, "", nil, nil) + ret, err := XlateFuncCall(xfmrFuncName, inParams) + if err != nil { + return "", "", "" + } + if ret != nil { + keyStr = ret[0].Interface().(string) + } + } else if xYangSpecMap[yangXpath].keyName != nil { + keyStr += *xYangSpecMap[yangXpath].keyName + } else { + /* multi-leaf yang key together forms a single key-string in redis. + There should be key-transformer, if not then the yang key leaves + will be concatenated with respective default DB type key-delimiter + */ + for idx, kname := range rgp.FindAllString(k, -1) { + if idx > 0 { keyStr += keySeparator } + keyl := strings.TrimRight(strings.TrimLeft(kname, "["), "]") + if strings.Contains(keyl, ":") { + keyl = strings.Split(keyl, ":")[1] + } + keyStr += strings.Split(keyl, "=")[1] + } + } + } + } + curPathWithKey += "/" + } + tblPtr := xpathInfo.tableName + if tblPtr != nil { + tableName = *tblPtr + } else if xpathInfo.xfmrTbl != nil { + inParams := formXfmrInputRequest(d, dbs, cdb, ygRoot, curPathWithKey, oper, "", nil, nil) + tableName, _ = tblNameFromTblXfmrGet(*xpathInfo.xfmrTbl, inParams) + } + 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..05d2bc1715 --- /dev/null +++ b/src/translib/transformer/xlate_utils.go @@ -0,0 +1,479 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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 data xpath(request) */ +func keyCreate(keyPrefix string, xpath string, data interface{}, dbKeySep string) string { + _, ok := xYangSpecMap[xpath] + if ok { + if xYangSpecMap[xpath].yangEntry != nil { + yangEntry := xYangSpecMap[xpath].yangEntry + delim := dbKeySep + if len(xYangSpecMap[xpath].delim) > 0 { + delim = xYangSpecMap[xpath].delim + log.Infof("key concatenater(\"%v\") found for xpath %v ", delim, xpath) + } + + if len(keyPrefix) > 0 { keyPrefix += delim } + keyVal := "" + for i, k := range (strings.Split(yangEntry.Key, " ")) { + if i > 0 { keyVal = keyVal + delim } + 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, dbKeySep 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 := xYangSpecMap[xpath]; ok { + if xYangSpecMap[xpath].yangEntry == nil { + err = fmt.Errorf("Yang Entry not available for xpath ", xpath) + return nil, "", nil + } + } + + var kLvlValList []string + keyDataList := strings.Split(dbKey, dbKeySep) + keyNameList := yangKeyFromEntryGet(xYangSpecMap[xpath].yangEntry) + id := xYangSpecMap[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) + } + keyConcat := dbKeySep + if len(xYangSpecMap[xpath].delim) > 0 { + keyConcat = xYangSpecMap[xpath].delim + log.Infof("Found key concatenater(\"%v\") for xpath %v", keyConcat, xpath) + } + + if len(xYangSpecMap[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(xYangSpecMap[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 + } + + if len(keyDataList) == 0 || len(keyNameList) == 0 { + return nil, "", nil + } + + kLvlValList = append(kLvlValList, keyDataList[id]) + + if len(keyNameList) > 1 { + kLvlValList = strings.Split(keyDataList[id], keyConcat) + } + + /* 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:], keyConcat) + } + + 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 isSonicYang(path string) bool { + if strings.HasPrefix(path, "/sonic") { + return true + } + return false +} + +func sonicKeyDataAdd(dbIndex db.DBNum, keyNameList []string, keyStr string, resultMap map[string]interface{}) { + var dbOpts db.Options + dbOpts = getDBOptions(dbIndex) + keySeparator := dbOpts.KeySeparator + keyValList := strings.Split(keyStr, keySeparator) + + 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 := xYangSpecMap[xpathTmplt]; ok { + yangEntry := xYangSpecMap[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 xYangSpecMap 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 := xYangSpecMap[xpath] + if ok { + xpathInfo := xYangSpecMap[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 "" +} + +func getDBOptions(dbNo db.DBNum) db.Options { + var opt db.Options + + switch dbNo { + case db.ApplDB, db.CountersDB: + opt = getDBOptionsWithSeparator(dbNo, "", ":", ":") + break + case db.FlexCounterDB, db.AsicDB, db.LogLevelDB, db.ConfigDB, db.StateDB: + opt = getDBOptionsWithSeparator(dbNo, "", "|", "|") + break + } + + return opt +} + +func getDBOptionsWithSeparator(dbNo db.DBNum, initIndicator string, tableSeparator string, keySeparator string) db.Options { + return(db.Options { + DBNo : dbNo, + InitIndicator : initIndicator, + TableNameSeparator: tableSeparator, + KeySeparator : keySeparator, + }) +} + +func stripAugmentedModuleNames(xpath string) string { + pathList := strings.Split(xpath, "/") + pathList = pathList[1:] + for i, pvar := range pathList { + if (i > 0) && strings.Contains(pvar, ":") { + pvar = strings.Split(pvar,":")[1] + pathList[i] = pvar + } + } + path := "/" + strings.Join(pathList, "/") + return path +} + +func XfmrRemoveXPATHPredicates(xpath string) (string, error) { + pathList := strings.Split(xpath, "/") + pathList = pathList[1:] + for i, pvar := range pathList { + if strings.Contains(pvar, "[") && strings.Contains(pvar, "]") { + si, ei := strings.Index(pvar, "["), strings.Index(pvar, "]") + // substring contains [] entries + if (si < ei) { + pvar = strings.Split(pvar, "[")[0] + pathList[i] = pvar + + } else { + // This substring contained a ] before a [. + return "", fmt.Errorf("Incorrect ordering of [] within substring %s of %s, [ pos: %d, ] pos: %d", pvar, xpath, si, ei) + } + } else if strings.Contains(pvar, "[") || strings.Contains(pvar, "]") { + // This substring contained a mismatched pair of []s. + return "", fmt.Errorf("Mismatched brackets within substring %s of %s", pvar, xpath) + } + if (i > 0) && strings.Contains(pvar, ":") { + pvar = strings.Split(pvar,":")[1] + pathList[i] = pvar + } + } + path := "/" + strings.Join(pathList, "/") + return path,nil +} + diff --git a/src/translib/transformer/xspec.go b/src/translib/transformer/xspec.go new file mode 100644 index 0000000000..85cff79cd3 --- /dev/null +++ b/src/translib/transformer/xspec.go @@ -0,0 +1,594 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, 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 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 + keyName *string + dbIndex db.DBNum + keyLevel int + isKey bool +} + +type dbInfo struct { + dbIndex db.DBNum + keyName *string + fieldType string + dbEntry *yang.Entry + yangXpath []string +} + +var xYangSpecMap 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, xYangSpecMap map[string]*yangXpathInfo, entry *yang.Entry, xpathPrefix string) { + xpath := "" + /* create the yang xpath */ + if xYangSpecMap[xpathPrefix] != nil && xYangSpecMap[xpathPrefix].yangDataType == "module" { + /* module name is separated from the rest of xpath with ":" */ + xpath = xpathPrefix + ":" + entry.Name + } else { + xpath = xpathPrefix + "/" + entry.Name + } + + xpathData, ok := xYangSpecMap[xpath] + if !ok { + xpathData = new(yangXpathInfo) + xYangSpecMap[xpath] = xpathData + xpathData.dbIndex = db.ConfigDB // default value + } else { + xpathData = xYangSpecMap[xpath] + } + + xpathData.yangDataType = entry.Node.Statement().Keyword + if entry.Node.Statement().Keyword == "list" && xpathData.tableName != nil { + childToUpdateParent(xpath, *xpathData.tableName) + } + + parentXpathData, ok := xYangSpecMap[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 && xpathData.dbIndex == db.ConfigDB && parentXpathData.dbIndex != db.ConfigDB { + // If DB Index is not annotated and parent DB index is annotated inherit the DB Index of the parent + xpathData.dbIndex = parentXpathData.dbIndex + } + + if ok && len(parentXpathData.validateFunc) > 0 { + xpathData.validateFunc = parentXpathData.validateFunc + } + + if ok && len(parentXpathData.xfmrFunc) > 0 && len(xpathData.xfmrFunc) == 0 { + xpathData.xfmrFunc = parentXpathData.xfmrFunc + } + + 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) + xYangSpecMap[xpath + "/" + keyName] = keyXpathData + xYangSpecMap[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, xYangSpecMap, entry.Dir[child], xpath) + } +} + +/* Build lookup table based of yang xpath */ +func yangToDbMapBuild(entries map[string]*yang.Entry) { + if entries == nil { + return + } + + if xYangSpecMap == nil { + xYangSpecMap = 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, xYangSpecMap, e, "") + + // Fill the ordered map of child tables list for oc yangs + updateSchemaOrderedMap(module, e) + } + mapPrint(xYangSpecMap, "/tmp/fullSpec.txt") + dbMapPrint("/tmp/dbSpecMap.txt") +} + +/* Fill the map with db details */ +func dbMapFill(tableName string, curPath string, moduleNm string, trkTpCnt bool, xDbSpecMap map[string]*dbInfo, entry *yang.Entry) { + entryType := entry.Node.Statement().Keyword + + if entry.Name != moduleNm { + if entryType == "container" { + tableName = entry.Name + } + + if !isYangResType(entryType) { + dbXpath := tableName + if entryType != "container" { + dbXpath = tableName + "/" + entry.Name + } + xDbSpecMap[dbXpath] = new(dbInfo) + xDbSpecMap[dbXpath].dbIndex = db.MaxDB + xDbSpecMap[dbXpath].dbEntry = entry + xDbSpecMap[dbXpath].fieldType = entryType + if entryType == "container" { + xDbSpecMap[dbXpath].dbIndex = db.ConfigDB + if entry.Exts != nil && len(entry.Exts) > 0 { + for _, ext := range entry.Exts { + dataTagArr := strings.Split(ext.Keyword, ":") + tagType := dataTagArr[len(dataTagArr)-1] + switch tagType { + case "key-name" : + if xDbSpecMap[dbXpath].keyName == nil { + xDbSpecMap[dbXpath].keyName = new(string) + } + *xDbSpecMap[dbXpath].keyName = ext.NName() + default : + log.Infof("Unsupported ext type(%v) for xpath(%v).", tagType, dbXpath) + } + } + } + } + } + } else { + moduleXpath := "/" + moduleNm + ":" + entry.Name + xDbSpecMap[moduleXpath] = new(dbInfo) + xDbSpecMap[moduleXpath].dbEntry = entry + xDbSpecMap[moduleXpath].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 := tableName + "/" + entry.Dir[child].Name + dbMapFill(tableName, 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.Infof("Module name(%v)", moduleNm) + 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 := xYangSpecMap[parent] + if !ok { + xpathData = new(yangXpathInfo) + xYangSpecMap[parent] = xpathData + } + xYangSpecMap[parent].childTable = append(xYangSpecMap[parent].childTable, tableName) + if xYangSpecMap[parent].yangEntry != nil && + xYangSpecMap[parent].yangEntry.Node.Statement().Keyword == "list" { + return + } + childToUpdateParent(parent, tableName) +} + +/* Build lookup map based on yang xpath */ +func annotEntryFill(xYangSpecMap map[string]*yangXpathInfo, xpath string, entry *yang.Entry) { + xpathData := new(yangXpathInfo) + _, ok := xYangSpecMap[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 "key-name" : + if xpathData.keyName == nil { + xpathData.keyName = new(string) + } + *xpathData.keyName = ext.NName() + 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 "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 + } + } + } + } + xYangSpecMap[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 xYangSpecMap == nil { + xYangSpecMap = 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(xYangSpecMap, xpath, ye) + } + } + } + } + } + } + mapPrint(xYangSpecMap, "/tmp/annotSpec.txt") +} + +func annotDbSpecMapFill(xDbSpecMap map[string]*dbInfo, dbXpath string, entry *yang.Entry) error { + var err error + var dbXpathData *dbInfo + var ok bool + + //Currently sonic-yang annotation is supported for "list" type only. + listName := strings.Split(dbXpath, "/") + if len(listName) < 3 { + log.Errorf("Invalid list xpath length(%v) \r\n", dbXpath) + return err + } + dbXpathData, ok = xDbSpecMap[listName[2]] + if !ok { + log.Errorf("DB spec-map data not found(%v) \r\n", dbXpath) + return err + } + log.Infof("Annotate dbSpecMap for (%v)(listName:%v)\r\n", dbXpath, listName[2]) + dbXpathData.dbIndex = db.ConfigDB // default value + + /* fill table with cvl 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 "key-name" : + if dbXpathData.keyName == nil { + dbXpathData.keyName = new(string) + } + *dbXpathData.keyName = ext.NName() + case "db-name" : + if ext.NName() == "APPL_DB" { + dbXpathData.dbIndex = db.ApplDB + } else if ext.NName() == "ASIC_DB" { + dbXpathData.dbIndex = db.AsicDB + } else if ext.NName() == "COUNTERS_DB" { + dbXpathData.dbIndex = db.CountersDB + } else if ext.NName() == "LOGLEVEL_DB" { + dbXpathData.dbIndex = db.LogLevelDB + } else if ext.NName() == "CONFIG_DB" { + dbXpathData.dbIndex = db.ConfigDB + } else if ext.NName() == "FLEX_COUNTER_DB" { + dbXpathData.dbIndex = db.FlexCounterDB + } else if ext.NName() == "STATE_DB" { + dbXpathData.dbIndex = db.StateDB + } else { + dbXpathData.dbIndex = db.ConfigDB + } + default : + } + } + } + + dbMapPrint("/tmp/dbSpecMapFull.txt") + return err +} + +func annotDbSpecMap(annotEntries []*yang.Entry) { + if annotEntries == nil || xDbSpecMap == nil { + return + } + 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 { + annotDbSpecMapFill(xDbSpecMap, xpath, ye) + } + } + } + } + } + } +} + +/* 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 keyName : ") + if d.keyName != nil { + fmt.Fprintf(fp, "%v", *d.keyName) + } + 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( fname string) { + fp, err := os.Create(fname) + 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, " db-type :%v \r\n", v.dbIndex) + fmt.Fprintf(fp, " KeyName: ") + if v.keyName != nil { + fmt.Fprintf(fp, "%v", *v.keyName) + } + fmt.Fprintf(fp, "\r\n oc-yang :%v \r\n", v.yangXpath) + fmt.Fprintf(fp, " cvl-yang :%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 := xYangSpecMap[xpath] + if ok { + yentry := xYangSpecMap[xpath].yangEntry + if yentry.Node.Statement().Keyword == "container" { + var keyspec = make([]KeySpec, 0) + keyspec = FillKeySpecs(xpath, "" , &keyspec) + children = updateChildTable(keyspec, &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/pyang/pyang_plugins/yin_cvl.py b/tools/pyang/pyang_plugins/yin_cvl.py new file mode 100644 index 0000000000..71689003b0 --- /dev/null +++ b/tools/pyang/pyang_plugins/yin_cvl.py @@ -0,0 +1,179 @@ +################################################################################ +# # +# 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. # +# # +################################################################################ +"""CVL YIN output plugin""" + +from xml.sax.saxutils import quoteattr +from xml.sax.saxutils import escape + +import optparse +import re + +from pyang import plugin +from pyang import util +from pyang import grammar +from pyang import syntax +from pyang import statements + +new_line ='' #replace with '\n' for adding new line +indent_space = '' #replace with ' ' for indentation +ns_indent_space = '' #replace with ' ' for indentation +yin_namespace = "urn:ietf:params:xml:ns:yang:yin:1" +revision_added = False + +def pyang_plugin_init(): + plugin.register_plugin(YINPluginCVL()) + +class YINPluginCVL(plugin.PyangPlugin): + def add_output_format(self, fmts): + fmts['yin-cvl'] = self + def emit(self, ctx, modules, fd): + module = modules[0] + emit_yin(ctx, module, fd) + +def emit_yin(ctx, module, fd): + fd.write('' + new_line) + fd.write(('<%s name="%s"' + new_line) % (module.keyword, module.arg)) + fd.write(ns_indent_space * len(module.keyword) + ns_indent_space + ' xmlns="%s"' % yin_namespace) + + prefix = module.search_one('prefix') + if prefix is not None: + namespace = module.search_one('namespace') + fd.write('' + new_line) + fd.write(ns_indent_space * len(module.keyword)) + fd.write(ns_indent_space + ' xmlns:' + prefix.arg + '=' + + quoteattr(namespace.arg)) + else: + belongs_to = module.search_one('belongs-to') + if belongs_to is not None: + prefix = belongs_to.search_one('prefix') + if prefix is not None: + # read the parent module in order to find the namespace uri + res = ctx.read_module(belongs_to.arg, extra={'no_include':True}) + if res is not None: + namespace = res.search_one('namespace') + if namespace is None or namespace.arg is None: + pass + else: + # success - namespace found + fd.write('' + new_line) + fd.write(sonic-acl.yin * len(module.keyword)) + fd.write(sonic-acl.yin + ' xmlns:' + prefix.arg + '=' + + quoteattr(namespace.arg)) + + for imp in module.search('import'): + prefix = imp.search_one('prefix') + if prefix is not None: + rev = None + r = imp.search_one('revision-date') + if r is not None: + rev = r.arg + mod = statements.modulename_to_module(module, imp.arg, rev) + if mod is not None: + ns = mod.search_one('namespace') + if ns is not None: + fd.write('' + new_line) + fd.write(ns_indent_space * len(module.keyword)) + fd.write(ns_indent_space + ' xmlns:' + prefix.arg + '=' + + quoteattr(ns.arg)) + fd.write('>' + new_line) + + substmts = module.substmts + for s in substmts: + emit_stmt(ctx, module, s, fd, indent_space, indent_space) + fd.write(('' + new_line) % module.keyword) + +def emit_stmt(ctx, module, stmt, fd, indent, indentstep): + global revision_added + + if stmt.raw_keyword == "revision" and revision_added == False: + revision_added = True + elif stmt.raw_keyword == "revision" and revision_added == True: + #Only add the latest revision + return + + #Don't keep the following keywords as they are not used in CVL + # stmt.raw_keyword == "revision" or + if ((stmt.raw_keyword == "organization" or + stmt.raw_keyword == "contact" or + stmt.raw_keyword == "rpc" or + stmt.raw_keyword == "notification" or + stmt.raw_keyword == "description") or + (len(stmt.substmts) > 0 and stmt.substmts[0].raw_keyword == "config" and + stmt.substmts[0].arg == "false")): + return + + if util.is_prefixed(stmt.raw_keyword): + # this is an extension. need to find its definition + (prefix, identifier) = stmt.raw_keyword + tag = prefix + ':' + identifier + if stmt.i_extension is not None: + ext_arg = stmt.i_extension.search_one('argument') + if ext_arg is not None: + yin_element = ext_arg.search_one('yin-element') + if yin_element is not None and yin_element.arg == 'true': + argname = prefix + ':' + ext_arg.arg + argiselem = True + else: + # explicit false or no yin-element given + argname = ext_arg.arg + argiselem = False + else: + argiselem = False + argname = None + else: + argiselem = False + argname = None + else: + (argname, argiselem) = syntax.yin_map[stmt.raw_keyword] + tag = stmt.raw_keyword + if argiselem == False or argname is None: + if argname is None: + attr = '' + else: + attr = ' ' + argname + '=' + quoteattr(stmt.arg) + if len(stmt.substmts) == 0: + fd.write(indent + '<' + tag + attr + '/>' + new_line) + else: + fd.write(indent + '<' + tag + attr + '>' + new_line) + for s in stmt.substmts: + emit_stmt(ctx, module, s, fd, indent + indentstep, + indentstep) + fd.write(indent + '' + new_line) + else: + fd.write(indent + '<' + tag + '>' + new_line) + fd.write(indent + indentstep + '<' + argname + '>' + \ + escape(stmt.arg) + \ + '' + new_line) + substmts = stmt.substmts + + for s in substmts: + emit_stmt(ctx, module, s, fd, indent + indentstep, indentstep) + + fd.write(indent + '' + new_line) + +def fmt_text(indent, data): + res = [] + for line in re.split("(\n)", escape(data)): + if line == '': + continue + if line == '' + new_line: + res.extend(line) + else: + res.extend(indent + line) + return ''.join(res) 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))